summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-25 09:38:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-25 09:38:50 -0700
commit93e4b3076b5f2d853462b9777d083c77fc0b7b23 (patch)
tree9e4eb974354d490ba5b74f05dfb1a3f5e4dc5469 /drivers/misc
parent5f5ef9c407cfdc524a1aaeb4196d933f61ecf21a (diff)
parent8992f32c57607bdfaf5de2a2cd26b3b71f3a9d55 (diff)
downloadlinux-next-93e4b3076b5f2d853462b9777d083c77fc0b7b23.tar.gz
linux-next-93e4b3076b5f2d853462b9777d083c77fc0b7b23.zip
Merge tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc/IIO/etc driver updates from Greg KH: "Here is the big set of char, misc, iio, counter, fpga, and other small driver subsystems for 7.3-rc1. Overall, due to some driver removals we only added a bit more code than removed, which was a nice change. Highlights in this merge request are: - Loads of IIO driver updates and additions - binder driver updates (more on that below...) - Removal of the SGI XP and GRU drivers as they are not used anymore and turn out to be pretty insecure overall - Removal of the obsolete ibmasm driver as it's not being used anymore - Coresight driver updates and additions - Mei driver udpates - Counter driver updates - FPGA driver updates - ICC driver updates - lots and lots of other tiny driver updates to resolve reported issues All of these have been in linux-next for a while" * tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (513 commits) iio: chemical: atlas-sensor: use iio_trigger_poll_nested() to fix remove UAF iio: adc: pac1921: fix wrong channel used in trigger handler read iio: light: gp2ap002: re-enable irq if runtime suspend fails iio: light: gp2ap002: Fix unbalanced runtime PM on repeated event writes iio: light: apds9306: fix PM reference leak in apds9306_read_data() iio: gyro: mpu3050: fix sign of raw angular velocity readings iio: srf04: fix pm_runtime handling on probe error path iio: adc: ad4080: configure backend data size iio: adc: adi-axi-adc: add data size support for AD408X backend iio: chemical: atlas-sensor: fix PM reference leak in buffer postenable iio: dac: ad5446: fix OF module device table iio: light: opt4001: Fix reversed GENMASK() arguments in fault count mask iio: light: opt4001: Reject integration times with a non-zero seconds part iio: light: opt4001: Fix incompatible pointer type passed to div_u64_rem() iio: light: opt4001: Fix power down clearing bits of the wrong register iio: light: opt4060: Fix incorrect register name in threshold read error message iio: light: opt4060: Fix pointer type passed to div_u64_rem() iio: light: opt4060: Reject integration times with a non-zero seconds part iio: light: ltrf216a: fix runtime PM reference leak in error path iio: pressure: dps310: fix NULL pointer dereference on ACPI probe ...
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig67
-rw-r--r--drivers/misc/Makefile5
-rw-r--r--drivers/misc/ad525x_dpot-i2c.c53
-rw-r--r--drivers/misc/ad525x_dpot-spi.c59
-rw-r--r--drivers/misc/ad525x_dpot.c177
-rw-r--r--drivers/misc/ad525x_dpot.h3
-rw-r--r--drivers/misc/amd-sbi/Kconfig13
-rw-r--r--drivers/misc/amd-sbi/Makefile3
-rw-r--r--drivers/misc/amd-sbi/rmi-core.c4
-rw-r--r--drivers/misc/amd-sbi/rmi-i2c.c6
-rw-r--r--drivers/misc/amd-sbi/tsi-core.c207
-rw-r--r--drivers/misc/amd-sbi/tsi-core.h29
-rw-r--r--drivers/misc/amd-sbi/tsi.c234
-rw-r--r--drivers/misc/apds9802als.c2
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_dev.c17
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_msg.c24
-rw-r--r--drivers/misc/bh1770glc.c6
-rw-r--r--drivers/misc/cardreader/alcor_pci.c2
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.c2
-rw-r--r--drivers/misc/cb710/core.c5
-rw-r--r--drivers/misc/ds1682.c6
-rw-r--r--drivers/misc/eeprom/Kconfig93
-rw-r--r--drivers/misc/eeprom/Makefile5
-rw-r--r--drivers/misc/eeprom/at24.c871
-rw-r--r--drivers/misc/eeprom/at25.c585
-rw-r--r--drivers/misc/eeprom/ee1004.c351
-rw-r--r--drivers/misc/eeprom/eeprom_93xx46.c560
-rw-r--r--drivers/misc/eeprom/idt_89hpesx.c220
-rw-r--r--drivers/misc/eeprom/m24lr.c606
-rw-r--r--drivers/misc/eeprom/max6875.c2
-rw-r--r--drivers/misc/enclosure.c4
-rw-r--r--drivers/misc/fastrpc.c260
-rw-r--r--drivers/misc/gehc-achc.c4
-rw-r--r--drivers/misc/genwqe/card_base.c88
-rw-r--r--drivers/misc/genwqe/card_utils.c2
-rw-r--r--drivers/misc/hi6421v600-irq.c7
-rw-r--r--drivers/misc/hisi_hikey_usb.c274
-rw-r--r--drivers/misc/hmc6352.c2
-rw-r--r--drivers/misc/hpilo.c13
-rw-r--r--drivers/misc/ibmasm/Makefile16
-rw-r--r--drivers/misc/ibmasm/command.c173
-rw-r--r--drivers/misc/ibmasm/dot_command.c138
-rw-r--r--drivers/misc/ibmasm/dot_command.h64
-rw-r--r--drivers/misc/ibmasm/event.c163
-rw-r--r--drivers/misc/ibmasm/heartbeat.c88
-rw-r--r--drivers/misc/ibmasm/i2o.h63
-rw-r--r--drivers/misc/ibmasm/ibmasm.h209
-rw-r--r--drivers/misc/ibmasm/ibmasmfs.c604
-rw-r--r--drivers/misc/ibmasm/lowlevel.c75
-rw-r--r--drivers/misc/ibmasm/lowlevel.h123
-rw-r--r--drivers/misc/ibmasm/module.c224
-rw-r--r--drivers/misc/ibmasm/r_heartbeat.c86
-rw-r--r--drivers/misc/ibmasm/remote.c273
-rw-r--r--drivers/misc/ibmasm/remote.h257
-rw-r--r--drivers/misc/ibmasm/uart.c58
-rw-r--r--drivers/misc/ibmvmc.c21
-rw-r--r--drivers/misc/ics932s401.c2
-rw-r--r--drivers/misc/isl29003.c4
-rw-r--r--drivers/misc/isl29020.c2
-rw-r--r--drivers/misc/issei/Kconfig29
-rw-r--r--drivers/misc/issei/Makefile15
-rw-r--r--drivers/misc/issei/cdev.c446
-rw-r--r--drivers/misc/issei/cdev.h16
-rw-r--r--drivers/misc/issei/dma.c154
-rw-r--r--drivers/misc/issei/dma.h69
-rw-r--r--drivers/misc/issei/fw_client.c240
-rw-r--r--drivers/misc/issei/fw_client.h45
-rw-r--r--drivers/misc/issei/ham.c169
-rw-r--r--drivers/misc/issei/ham.h20
-rw-r--r--drivers/misc/issei/host_client.c519
-rw-r--r--drivers/misc/issei/host_client.h75
-rw-r--r--drivers/misc/issei/hw_heci.c550
-rw-r--r--drivers/misc/issei/hw_heci.h47
-rw-r--r--drivers/misc/issei/hw_heci_regs.h35
-rw-r--r--drivers/misc/issei/hw_msg.h163
-rw-r--r--drivers/misc/issei/issei_dev.h163
-rw-r--r--drivers/misc/issei/main.c296
-rw-r--r--drivers/misc/issei/pci_heci.c151
-rw-r--r--drivers/misc/keba/cp500.c17
-rw-r--r--drivers/misc/keba/lan9252.c4
-rw-r--r--drivers/misc/lan966x_pci.c1
-rw-r--r--drivers/misc/lattice-ecp3-config.c4
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c8
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_spi.c2
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c2
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c4
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c4
-rw-r--r--drivers/misc/mei/gsc-me.c4
-rw-r--r--drivers/misc/mei/mei_lb.c4
-rw-r--r--drivers/misc/mei/pci-csc.c2
-rw-r--r--drivers/misc/mei/pci-me.c212
-rw-r--r--drivers/misc/mei/pci-txe.c6
-rw-r--r--drivers/misc/mei/platform-vsc.c2
-rw-r--r--drivers/misc/mei/vsc-tp.c10
-rw-r--r--drivers/misc/mrvl_cn10k_dpi.c6
-rw-r--r--drivers/misc/nsm.c7
-rw-r--r--drivers/misc/ntsync.c3
-rw-r--r--drivers/misc/ocxl/pci.c2
-rw-r--r--drivers/misc/open-dice.c53
-rw-r--r--drivers/misc/pch_phub.c59
-rw-r--r--drivers/misc/pci_endpoint_test.c16
-rw-r--r--drivers/misc/phantom.c6
-rw-r--r--drivers/misc/pvpanic/pvpanic-mmio.c8
-rw-r--r--drivers/misc/pvpanic/pvpanic-pci.c4
-rw-r--r--drivers/misc/qcom-coincell.c4
-rw-r--r--drivers/misc/rp1/rp1_pci.c29
-rw-r--r--drivers/misc/sgi-gru/Makefile6
-rw-r--r--drivers/misc/sgi-gru/gru.h76
-rw-r--r--drivers/misc/sgi-gru/gru_instructions.h726
-rw-r--r--drivers/misc/sgi-gru/grufault.c903
-rw-r--r--drivers/misc/sgi-gru/grufile.c540
-rw-r--r--drivers/misc/sgi-gru/gruhandles.c192
-rw-r--r--drivers/misc/sgi-gru/gruhandles.h517
-rw-r--r--drivers/misc/sgi-gru/grukdump.c223
-rw-r--r--drivers/misc/sgi-gru/grukservices.c1157
-rw-r--r--drivers/misc/sgi-gru/grukservices.h201
-rw-r--r--drivers/misc/sgi-gru/grulib.h153
-rw-r--r--drivers/misc/sgi-gru/grumain.c969
-rw-r--r--drivers/misc/sgi-gru/gruprocfs.c308
-rw-r--r--drivers/misc/sgi-gru/grutables.h659
-rw-r--r--drivers/misc/sgi-gru/grutlbpurge.c316
-rw-r--r--drivers/misc/sgi-xp/Makefile13
-rw-r--r--drivers/misc/sgi-xp/xp.h341
-rw-r--r--drivers/misc/sgi-xp/xp_main.c261
-rw-r--r--drivers/misc/sgi-xp/xp_uv.c151
-rw-r--r--drivers/misc/sgi-xp/xpc.h732
-rw-r--r--drivers/misc/sgi-xp/xpc_channel.c1011
-rw-r--r--drivers/misc/sgi-xp/xpc_main.c1309
-rw-r--r--drivers/misc/sgi-xp/xpc_partition.c545
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c1727
-rw-r--r--drivers/misc/sgi-xp/xpnet.c599
-rw-r--r--drivers/misc/sram.c2
-rw-r--r--drivers/misc/ti_fpc202.c2
-rw-r--r--drivers/misc/tps6594-esm.c2
-rw-r--r--drivers/misc/tps6594-pfsm.c2
-rw-r--r--drivers/misc/tsl2550.c2
-rw-r--r--drivers/misc/vcpu_stall_detector.c4
-rw-r--r--drivers/misc/vmw_vmci/vmci_event.c4
-rw-r--r--drivers/misc/vmw_vmci/vmci_guest.c4
-rw-r--r--drivers/misc/xilinx_sdfec.c78
140 files changed, 4592 insertions, 20342 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 390256ed91f4..7364931dad3a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -57,28 +57,6 @@ config DUMMY_IRQ
The sole purpose of this module is to help with debugging of systems on
which spurious IRQs would happen on disabled IRQ vector.
-config IBM_ASM
- tristate "Device driver for IBM RSA service processor"
- depends on X86 && PCI && INPUT
- depends on SERIAL_8250 || SERIAL_8250=n
- help
- This option enables device driver support for in-band access to the
- IBM RSA (Condor) service processor in eServer xSeries systems.
- The ibmasm device driver allows user space application to access
- ASM (Advanced Systems Management) functions on the service
- processor. The driver is meant to be used in conjunction with
- a user space API.
- The ibmasm driver also enables the OS to use the UART on the
- service processor board as a regular serial port. To make use of
- this feature serial driver support (CONFIG_SERIAL_8250) must be
- enabled.
-
- WARNING: This software may not be supported or function
- correctly on your IBM server. Please consult the IBM ServerProven
- website <https://www-03.ibm.com/systems/info/x86servers/serverproven/compat/us/>
- for information on the specific driver level and support statement
- for your IBM server.
-
config IBMVMC
tristate "IBM Virtual Management Channel support"
depends on PPC_PSERIES
@@ -185,19 +163,6 @@ config ENCLOSURE_SERVICES
driver (SCSI/ATA) which supports enclosures
or a SCSI enclosure device (SES) to use these services.
-config SGI_XP
- tristate "Support communication between SGI SSIs"
- depends on NET
- depends on X86_UV && SMP
- depends on X86_64 || BROKEN
- select SGI_GRU if X86_64 && SMP
- help
- An SGI machine can be divided into multiple Single System
- Images which act independently of each other and have
- hardware based memory protection from the others. Enabling
- this feature will allow for direct communication between SSIs
- based on a network adapter and DMA messaging.
-
config SMPRO_ERRMON
tristate "Ampere Computing SMPro error monitor driver"
depends on MFD_SMPRO || COMPILE_TEST
@@ -310,27 +275,6 @@ config QCOM_FASTRPC
applications DSP processor. Say M if you want to enable this
module.
-config SGI_GRU
- tristate "SGI GRU driver"
- depends on X86_UV && SMP
- select MMU_NOTIFIER
- help
- The GRU is a hardware resource located in the system chipset. The GRU
- contains memory that can be mmapped into the user address space.
- This memory is used to communicate with the GRU to perform functions
- such as load/store, scatter/gather, bcopy, AMOs, etc. The GRU is
- directly accessed by user instructions using user virtual addresses.
- GRU instructions (ex., bcopy) use user virtual addresses for operands.
-
- If you are not running on a SGI UV system, say N.
-
-config SGI_GRU_DEBUG
- bool "SGI GRU driver debug"
- depends on SGI_GRU
- help
- This option enables additional debugging code for the SGI GRU driver.
- If you are unsure, say N.
-
config APDS9802ALS
tristate "Medfield Avago APDS9802 ALS Sensor module"
depends on I2C
@@ -494,16 +438,6 @@ config MISC_RTSX
tristate
default MISC_RTSX_PCI || MISC_RTSX_USB
-config HISI_HIKEY_USB
- tristate "USB GPIO Hub on HiSilicon Hikey 960/970 Platform"
- depends on (OF && GPIOLIB) || COMPILE_TEST
- depends on USB_ROLE_SWITCH
- help
- If you say yes here this adds support for the on-board USB GPIO hub
- found on HiKey 960/970 boards, which is necessary to support
- switching between the dual-role USB-C port and the USB-A host ports
- using only one USB controller.
-
config OPEN_DICE
tristate "Open Profile for DICE driver"
depends on OF_RESERVED_MEM
@@ -651,4 +585,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig"
source "drivers/misc/keba/Kconfig"
source "drivers/misc/amd-sbi/Kconfig"
source "drivers/misc/rp1/Kconfig"
+source "drivers/misc/issei/Kconfig"
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fed47c7672b9..e8d8d5d88c0d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -3,7 +3,6 @@
# Makefile for misc devices that really don't fit anywhere else.
#
-obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_IBMVMC) += ibmvmc.o
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
@@ -22,8 +21,6 @@ obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
-obj-$(CONFIG_SGI_XP) += sgi-xp/
-obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o
obj-$(CONFIG_SMPRO_MISC) += smpro-misc.o
obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o
@@ -56,7 +53,6 @@ obj-y += cardreader/
obj-$(CONFIG_PVPANIC) += pvpanic/
obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
-obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
obj-$(CONFIG_NTSYNC) += ntsync.o
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
obj-$(CONFIG_OPEN_DICE) += open-dice.o
@@ -74,3 +70,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o
obj-y += keba/
obj-y += amd-sbi/
obj-$(CONFIG_MISC_RP1) += rp1/
+obj-$(CONFIG_INTEL_SSEI) += issei/
diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c
index 469478f7a1d3..a74b0266ea16 100644
--- a/drivers/misc/ad525x_dpot-i2c.c
+++ b/drivers/misc/ad525x_dpot-i2c.c
@@ -73,38 +73,39 @@ static void ad_dpot_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id ad_dpot_id[] = {
- {"ad5258", AD5258_ID},
- {"ad5259", AD5259_ID},
- {"ad5251", AD5251_ID},
- {"ad5252", AD5252_ID},
- {"ad5253", AD5253_ID},
- {"ad5254", AD5254_ID},
- {"ad5255", AD5255_ID},
- {"ad5241", AD5241_ID},
- {"ad5242", AD5242_ID},
- {"ad5243", AD5243_ID},
- {"ad5245", AD5245_ID},
- {"ad5246", AD5246_ID},
- {"ad5247", AD5247_ID},
- {"ad5248", AD5248_ID},
- {"ad5280", AD5280_ID},
- {"ad5282", AD5282_ID},
- {"adn2860", ADN2860_ID},
- {"ad5273", AD5273_ID},
- {"ad5161", AD5161_ID},
- {"ad5171", AD5171_ID},
- {"ad5170", AD5170_ID},
- {"ad5172", AD5172_ID},
- {"ad5173", AD5173_ID},
- {"ad5272", AD5272_ID},
- {"ad5274", AD5274_ID},
- {}
+ { .name = "ad5258", .driver_data = AD5258_ID },
+ { .name = "ad5259", .driver_data = AD5259_ID },
+ { .name = "ad5251", .driver_data = AD5251_ID },
+ { .name = "ad5252", .driver_data = AD5252_ID },
+ { .name = "ad5253", .driver_data = AD5253_ID },
+ { .name = "ad5254", .driver_data = AD5254_ID },
+ { .name = "ad5255", .driver_data = AD5255_ID },
+ { .name = "ad5241", .driver_data = AD5241_ID },
+ { .name = "ad5242", .driver_data = AD5242_ID },
+ { .name = "ad5243", .driver_data = AD5243_ID },
+ { .name = "ad5245", .driver_data = AD5245_ID },
+ { .name = "ad5246", .driver_data = AD5246_ID },
+ { .name = "ad5247", .driver_data = AD5247_ID },
+ { .name = "ad5248", .driver_data = AD5248_ID },
+ { .name = "ad5280", .driver_data = AD5280_ID },
+ { .name = "ad5282", .driver_data = AD5282_ID },
+ { .name = "adn2860", .driver_data = ADN2860_ID },
+ { .name = "ad5273", .driver_data = AD5273_ID },
+ { .name = "ad5161", .driver_data = AD5161_ID },
+ { .name = "ad5171", .driver_data = AD5171_ID },
+ { .name = "ad5170", .driver_data = AD5170_ID },
+ { .name = "ad5172", .driver_data = AD5172_ID },
+ { .name = "ad5173", .driver_data = AD5173_ID },
+ { .name = "ad5272", .driver_data = AD5272_ID },
+ { .name = "ad5274", .driver_data = AD5274_ID },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ad_dpot_id);
static struct i2c_driver ad_dpot_i2c_driver = {
.driver = {
.name = "ad_dpot",
+ .dev_groups = ad_dpot_groups,
},
.probe = ad_dpot_i2c_probe,
.remove = ad_dpot_i2c_remove,
diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c
index 263055bda48b..c4c2ec52a83d 100644
--- a/drivers/misc/ad525x_dpot-spi.c
+++ b/drivers/misc/ad525x_dpot-spi.c
@@ -96,41 +96,42 @@ static void ad_dpot_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id ad_dpot_spi_id[] = {
- {"ad5160", AD5160_ID},
- {"ad5161", AD5161_ID},
- {"ad5162", AD5162_ID},
- {"ad5165", AD5165_ID},
- {"ad5200", AD5200_ID},
- {"ad5201", AD5201_ID},
- {"ad5203", AD5203_ID},
- {"ad5204", AD5204_ID},
- {"ad5206", AD5206_ID},
- {"ad5207", AD5207_ID},
- {"ad5231", AD5231_ID},
- {"ad5232", AD5232_ID},
- {"ad5233", AD5233_ID},
- {"ad5235", AD5235_ID},
- {"ad5260", AD5260_ID},
- {"ad5262", AD5262_ID},
- {"ad5263", AD5263_ID},
- {"ad5290", AD5290_ID},
- {"ad5291", AD5291_ID},
- {"ad5292", AD5292_ID},
- {"ad5293", AD5293_ID},
- {"ad7376", AD7376_ID},
- {"ad8400", AD8400_ID},
- {"ad8402", AD8402_ID},
- {"ad8403", AD8403_ID},
- {"adn2850", ADN2850_ID},
- {"ad5270", AD5270_ID},
- {"ad5271", AD5271_ID},
- {}
+ { .name = "ad5160", .driver_data = AD5160_ID },
+ { .name = "ad5161", .driver_data = AD5161_ID },
+ { .name = "ad5162", .driver_data = AD5162_ID },
+ { .name = "ad5165", .driver_data = AD5165_ID },
+ { .name = "ad5200", .driver_data = AD5200_ID },
+ { .name = "ad5201", .driver_data = AD5201_ID },
+ { .name = "ad5203", .driver_data = AD5203_ID },
+ { .name = "ad5204", .driver_data = AD5204_ID },
+ { .name = "ad5206", .driver_data = AD5206_ID },
+ { .name = "ad5207", .driver_data = AD5207_ID },
+ { .name = "ad5231", .driver_data = AD5231_ID },
+ { .name = "ad5232", .driver_data = AD5232_ID },
+ { .name = "ad5233", .driver_data = AD5233_ID },
+ { .name = "ad5235", .driver_data = AD5235_ID },
+ { .name = "ad5260", .driver_data = AD5260_ID },
+ { .name = "ad5262", .driver_data = AD5262_ID },
+ { .name = "ad5263", .driver_data = AD5263_ID },
+ { .name = "ad5290", .driver_data = AD5290_ID },
+ { .name = "ad5291", .driver_data = AD5291_ID },
+ { .name = "ad5292", .driver_data = AD5292_ID },
+ { .name = "ad5293", .driver_data = AD5293_ID },
+ { .name = "ad7376", .driver_data = AD7376_ID },
+ { .name = "ad8400", .driver_data = AD8400_ID },
+ { .name = "ad8402", .driver_data = AD8402_ID },
+ { .name = "ad8403", .driver_data = AD8403_ID },
+ { .name = "adn2850", .driver_data = ADN2850_ID },
+ { .name = "ad5270", .driver_data = AD5270_ID },
+ { .name = "ad5271", .driver_data = AD5271_ID },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad_dpot_spi_id);
static struct spi_driver ad_dpot_spi_driver = {
.driver = {
.name = "ad_dpot",
+ .dev_groups = ad_dpot_groups,
},
.probe = ad_dpot_spi_probe,
.remove = ad_dpot_spi_remove,
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index 57bead9fba1b..a4e22fd4a107 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -630,66 +630,132 @@ static struct attribute *ad525x_attributes_commands[] = {
NULL
};
-static const struct attribute_group ad525x_group_commands = {
- .attrs = ad525x_attributes_commands,
+static struct attribute *ad525x_attributes[] = {
+ &dev_attr_rdac0.attr,
+ &dev_attr_rdac1.attr,
+ &dev_attr_rdac2.attr,
+ &dev_attr_rdac3.attr,
+ &dev_attr_rdac4.attr,
+ &dev_attr_rdac5.attr,
+ &dev_attr_eeprom0.attr,
+ &dev_attr_eeprom1.attr,
+ &dev_attr_eeprom2.attr,
+ &dev_attr_eeprom3.attr,
+ &dev_attr_eeprom4.attr,
+ &dev_attr_eeprom5.attr,
+ &dev_attr_tolerance0.attr,
+ &dev_attr_tolerance1.attr,
+ &dev_attr_tolerance2.attr,
+ &dev_attr_tolerance3.attr,
+ &dev_attr_tolerance4.attr,
+ &dev_attr_tolerance5.attr,
+ &dev_attr_otp0.attr,
+ &dev_attr_otp1.attr,
+ &dev_attr_otp2.attr,
+ &dev_attr_otp3.attr,
+ &dev_attr_otp4.attr,
+ &dev_attr_otp5.attr,
+ &dev_attr_otp0en.attr,
+ &dev_attr_otp1en.attr,
+ &dev_attr_otp2en.attr,
+ &dev_attr_otp3en.attr,
+ &dev_attr_otp4en.attr,
+ &dev_attr_otp5en.attr,
+ &dev_attr_inc_all.attr,
+ &dev_attr_dec_all.attr,
+ &dev_attr_inc_all_6db.attr,
+ &dev_attr_dec_all_6db.attr,
+ NULL
};
-static int ad_dpot_add_files(struct device *dev,
- unsigned int features, unsigned int rdac)
+static int ad525x_attr_index(struct attribute *attr,
+ const struct attribute * const *attrs)
{
- int err = sysfs_create_file(&dev->kobj,
- dpot_attrib_wipers[rdac]);
- if (features & F_CMD_EEP)
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_eeprom[rdac]);
- if (features & F_CMD_TOL)
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_tolerance[rdac]);
- if (features & F_CMD_OTP) {
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_otp_en[rdac]);
- err |= sysfs_create_file(&dev->kobj,
- dpot_attrib_otp[rdac]);
- }
+ int i;
- if (err)
- dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
- rdac);
+ for (i = 0; attrs[i]; i++)
+ if (attr == attrs[i])
+ return i;
- return err;
+ return -ENOENT;
}
-static inline void ad_dpot_remove_files(struct device *dev,
- unsigned int features, unsigned int rdac)
+static bool ad525x_is_command_attr(struct attribute *attr)
{
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_wipers[rdac]);
- if (features & F_CMD_EEP)
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_eeprom[rdac]);
- if (features & F_CMD_TOL)
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_tolerance[rdac]);
- if (features & F_CMD_OTP) {
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_otp_en[rdac]);
- sysfs_remove_file(&dev->kobj,
- dpot_attrib_otp[rdac]);
+ int i;
+
+ for (i = 0; ad525x_attributes_commands[i]; i++) {
+ if (attr == ad525x_attributes_commands[i])
+ return true;
}
+
+ return false;
+}
+
+static umode_t ad525x_is_visible(struct kobject *kobj, struct attribute *attr,
+ int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct dpot_data *data = dev_get_drvdata(dev);
+ int rdac;
+
+ if (!data)
+ return 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_wipers);
+ if (rdac >= 0)
+ return data->wipers & BIT(rdac) ? attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_eeprom);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_EEP) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_tolerance);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_TOL) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_otp);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ?
+ attr->mode : 0;
+
+ rdac = ad525x_attr_index(attr, dpot_attrib_otp_en);
+ if (rdac >= 0)
+ return (data->wipers & BIT(rdac)) && (data->feat & F_CMD_OTP) ?
+ attr->mode : 0;
+
+ if (ad525x_is_command_attr(attr))
+ return data->feat & F_CMD_INC ? attr->mode : 0;
+
+ return attr->mode;
}
+static const struct attribute_group ad525x_group = {
+ .attrs = ad525x_attributes,
+ .is_visible = ad525x_is_visible,
+};
+
+const struct attribute_group *ad_dpot_groups[] = {
+ &ad525x_group,
+ NULL
+};
+EXPORT_SYMBOL(ad_dpot_groups);
+
int ad_dpot_probe(struct device *dev,
struct ad_dpot_bus_data *bdata, unsigned long devid,
const char *name)
{
struct dpot_data *data;
- int i, err = 0;
+ int i;
data = kzalloc_obj(struct dpot_data);
if (!data) {
- err = -ENOMEM;
- goto exit;
+ dev_err(dev, "failed to create client for %s ID 0x%lX\n",
+ name, devid);
+ return -ENOMEM;
}
dev_set_drvdata(dev, data);
@@ -705,51 +771,22 @@ int ad_dpot_probe(struct device *dev,
data->wipers = DPOT_WIPERS(devid);
for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i)) {
- err = ad_dpot_add_files(dev, data->feat, i);
- if (err)
- goto exit_remove_files;
+ if (data->wipers & BIT(i)) {
/* power-up midscale */
if (data->feat & F_RDACS_WONLY)
data->rdac_cache[i] = data->max_pos / 2;
}
- if (data->feat & F_CMD_INC)
- err = sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-
- if (err) {
- dev_err(dev, "failed to register sysfs hooks\n");
- goto exit_free;
- }
-
dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
name, data->max_pos);
return 0;
-
-exit_remove_files:
- for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i))
- ad_dpot_remove_files(dev, data->feat, i);
-
-exit_free:
- kfree(data);
- dev_set_drvdata(dev, NULL);
-exit:
- dev_err(dev, "failed to create client for %s ID 0x%lX\n",
- name, devid);
- return err;
}
EXPORT_SYMBOL(ad_dpot_probe);
void ad_dpot_remove(struct device *dev)
{
struct dpot_data *data = dev_get_drvdata(dev);
- int i;
-
- for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
- if (data->wipers & (1 << i))
- ad_dpot_remove_files(dev, data->feat, i);
kfree(data);
}
diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h
index 72a9d6801937..2e877c89523b 100644
--- a/drivers/misc/ad525x_dpot.h
+++ b/drivers/misc/ad525x_dpot.h
@@ -10,6 +10,8 @@
#include <linux/types.h>
+struct attribute_group;
+
#define DPOT_CONF(features, wipers, max_pos, uid) \
(((features) << 18) | (((wipers) & 0xFF) << 10) | \
((max_pos & 0xF) << 6) | (uid & 0x3F))
@@ -210,5 +212,6 @@ struct ad_dpot_bus_data {
int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata,
unsigned long devid, const char *name);
void ad_dpot_remove(struct device *dev);
+extern const struct attribute_group *ad_dpot_groups[];
#endif
diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig
index 30e7fad7356c..1a96b71f8506 100644
--- a/drivers/misc/amd-sbi/Kconfig
+++ b/drivers/misc/amd-sbi/Kconfig
@@ -20,3 +20,16 @@ config AMD_SBRMI_HWMON
This provides support for RMI device hardware monitoring. If enabled,
a hardware monitoring device will be created for each socket in
the system.
+
+config AMD_SBTSI
+ tristate "AMD side band TSI support"
+ depends on I3C_OR_I2C
+ depends on ARM || ARM64 || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ Enables support for the AMD SB-TSI (Side Band Temperature Sensor
+ Interface) driver, which provides access to emulated CPU temperature
+ sensors on AMD SoCs via an I2C/I3C connected BMC device.
+
+ This driver can also be built as a module. If so, the module will
+ be called sbtsi.
diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile
index 38eaaa651fd9..ce9321f5c601 100644
--- a/drivers/misc/amd-sbi/Makefile
+++ b/drivers/misc/amd-sbi/Makefile
@@ -2,3 +2,6 @@
sbrmi-i2c-objs += rmi-i2c.o rmi-core.o
sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON) += rmi-hwmon.o
obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o
+# SBTSI Configuration
+sbtsi-objs += tsi.o tsi-core.o
+obj-$(CONFIG_AMD_SBTSI) += sbtsi.o
diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c
index d4238ebad3c6..95c9109101b5 100644
--- a/drivers/misc/amd-sbi/rmi-core.c
+++ b/drivers/misc/amd-sbi/rmi-core.c
@@ -581,6 +581,8 @@ int create_misc_rmi_device(struct sbrmi_data *data,
GFP_KERNEL,
"sbrmi-%x",
data->dev_static_addr);
+ if (!data->sbrmi_misc_dev.name)
+ return -ENOMEM;
data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR;
data->sbrmi_misc_dev.fops = &sbrmi_fops;
data->sbrmi_misc_dev.parent = dev;
@@ -588,6 +590,8 @@ int create_misc_rmi_device(struct sbrmi_data *data,
GFP_KERNEL,
"sbrmi-%x",
data->dev_static_addr);
+ if (!data->sbrmi_misc_dev.nodename)
+ return -ENOMEM;
data->sbrmi_misc_dev.mode = 0600;
return misc_register(&data->sbrmi_misc_dev);
diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c
index 37e5ea83bf97..463a9a757464 100644
--- a/drivers/misc/amd-sbi/rmi-i2c.c
+++ b/drivers/misc/amd-sbi/rmi-i2c.c
@@ -141,8 +141,8 @@ static void sbrmi_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id sbrmi_id[] = {
- {"sbrmi-i2c"},
- {}
+ { .name = "sbrmi-i2c" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sbrmi_id);
@@ -226,7 +226,7 @@ static const struct i3c_device_id sbrmi_i3c_id[] = {
I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */
I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */
I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */
- {}
+ { }
};
MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id);
diff --git a/drivers/misc/amd-sbi/tsi-core.c b/drivers/misc/amd-sbi/tsi-core.c
new file mode 100644
index 000000000000..5c178702c67a
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi-core.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tsi-core.c - file defining SB-TSI protocols compliant
+ * AMD SoC device.
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <uapi/misc/amd-apml.h>
+#include "tsi-core.h"
+
+static inline struct sbtsi_i3c_priv *to_sbtsi_i3c_priv(struct sbtsi_data *data)
+{
+ return container_of(data, struct sbtsi_i3c_priv, data);
+}
+
+void sbtsi_data_release(struct kref *kref)
+{
+ struct sbtsi_data *data = container_of(kref, struct sbtsi_data, kref);
+
+ mutex_destroy(&data->lock);
+ if (data->is_i3c)
+ kfree(to_sbtsi_i3c_priv(data));
+ else
+ kfree(data);
+}
+
+/* I2C transfer function */
+static int sbtsi_i2c_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+ if (is_read) {
+ int ret = i2c_smbus_read_byte_data(data->client, reg);
+
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return 0;
+ }
+ return i2c_smbus_write_byte_data(data->client, reg, *val);
+}
+
+/* I3C read transfer function */
+static int sbtsi_i3c_read(struct sbtsi_data *data, u8 reg, u8 *val)
+{
+ struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data);
+ struct i3c_xfer xfers[2] = { };
+ int ret;
+
+ priv->tx[0] = reg;
+
+ /* Write the register address (DMA_TO_DEVICE). */
+ xfers[0].rnw = false;
+ xfers[0].len = 1;
+ xfers[0].data.out = priv->tx;
+
+ /* Read the data byte into a separate buffer (DMA_FROM_DEVICE). */
+ xfers[1].rnw = true;
+ xfers[1].len = 1;
+ xfers[1].data.in = &priv->rx;
+
+ ret = i3c_device_do_xfers(data->i3cdev, xfers, 2, I3C_SDR);
+ if (ret)
+ return ret;
+
+ *val = priv->rx;
+ return ret;
+}
+
+/* I3C write transfer function */
+static int sbtsi_i3c_write(struct sbtsi_data *data, u8 reg, u8 val)
+{
+ struct sbtsi_i3c_priv *priv = to_sbtsi_i3c_priv(data);
+ struct i3c_xfer xfers = {
+ .rnw = false,
+ .len = 2,
+ .data.out = priv->tx,
+ };
+
+ priv->tx[0] = reg;
+ priv->tx[1] = val;
+
+ return i3c_device_do_xfers(data->i3cdev, &xfers, 1, I3C_SDR);
+}
+
+/* Unified transfer function for I2C and I3C access */
+int sbtsi_xfer(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+ if (data->is_i3c)
+ return is_read ? sbtsi_i3c_read(data, reg, val)
+ : sbtsi_i3c_write(data, reg, *val);
+ return sbtsi_i2c_xfer(data, reg, val, is_read);
+}
+EXPORT_SYMBOL_GPL(sbtsi_xfer);
+
+/*
+ * The mutex protects against concurrent register transfers to the device
+ * over the shared bus.
+ */
+static int sbtsi_xfer_ioctl(struct sbtsi_data *data, u8 reg, u8 *val, bool is_read)
+{
+ guard(sbtsi)(data);
+
+ if (data->detached)
+ return -ENODEV;
+
+ return sbtsi_xfer(data, reg, val, is_read);
+}
+
+static int apml_tsi_reg_xfer(struct sbtsi_data *data,
+ struct apml_tsi_xfer_msg __user *arg)
+{
+ struct apml_tsi_xfer_msg msg = { 0 };
+ int ret;
+
+ if (copy_from_user(&msg, arg, sizeof(struct apml_tsi_xfer_msg)))
+ return -EFAULT;
+
+ /*
+ * rflag is a boolean direction flag (0 = write, 1 = read). Reject
+ * any other value so the upper values stay reserved for future
+ * extensions instead of being silently treated as a read.
+ */
+ if (msg.pad || msg.rflag > 1)
+ return -EINVAL;
+
+ ret = sbtsi_xfer_ioctl(data, msg.reg_addr, &msg.data_in_out, msg.rflag);
+
+ if (msg.rflag && !ret) {
+ if (copy_to_user(arg, &msg, sizeof(struct apml_tsi_xfer_msg)))
+ return -EFAULT;
+ }
+ return ret;
+}
+
+static int sbtsi_open(struct inode *inode, struct file *fp)
+{
+ struct sbtsi_data *data;
+
+ data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev);
+ scoped_guard(sbtsi, data) {
+ if (data->detached)
+ return -ENODEV;
+ }
+
+ kref_get(&data->kref);
+
+ return 0;
+}
+
+static int sbtsi_release(struct inode *inode, struct file *fp)
+{
+ struct sbtsi_data *data;
+
+ data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev);
+ kref_put(&data->kref, sbtsi_data_release);
+ return 0;
+}
+
+static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct sbtsi_data *data;
+
+ data = container_of(fp->private_data, struct sbtsi_data, sbtsi_misc_dev);
+ switch (cmd) {
+ case SBTSI_IOCTL_REG_XFER_CMD:
+ return apml_tsi_reg_xfer(data, argp);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations sbtsi_fops = {
+ .owner = THIS_MODULE,
+ .open = sbtsi_open,
+ .release = sbtsi_release,
+ .unlocked_ioctl = sbtsi_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+};
+
+int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev)
+{
+ int ret;
+
+ data->sbtsi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL,
+ "sbtsi-%x", data->dev_addr);
+ if (!data->sbtsi_misc_dev.name)
+ return -ENOMEM;
+ data->sbtsi_misc_dev.minor = MISC_DYNAMIC_MINOR;
+ data->sbtsi_misc_dev.fops = &sbtsi_fops;
+ data->sbtsi_misc_dev.parent = dev;
+ data->sbtsi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL,
+ "sbtsi-%x", data->dev_addr);
+ if (!data->sbtsi_misc_dev.nodename)
+ return -ENOMEM;
+ data->sbtsi_misc_dev.mode = 0600;
+
+ ret = misc_register(&data->sbtsi_misc_dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/misc/amd-sbi/tsi-core.h b/drivers/misc/amd-sbi/tsi-core.h
new file mode 100644
index 000000000000..4cf55c46230e
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi-core.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * AMD SBTSI core driver private definitions.
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _LINUX_TSI_CORE_H_
+#define _LINUX_TSI_CORE_H_
+
+#include <linux/cache.h>
+#include <linux/misc/tsi.h>
+
+/**
+ * struct sbtsi_i3c_priv - per-device state for I3C SBTSI (includes DMA-safe buffers)
+ * @data: public device state exposed via dev_set_drvdata()
+ * @tx: outgoing I3C bytes (DMA_TO_DEVICE); [0] register address, [1] value
+ * @rx: incoming I3C data byte (DMA_FROM_DEVICE)
+ */
+struct sbtsi_i3c_priv {
+ struct sbtsi_data data;
+ u8 tx[2];
+ u8 rx __aligned(ARCH_DMA_MINALIGN);
+};
+
+int create_misc_tsi_device(struct sbtsi_data *data, struct device *dev);
+
+void sbtsi_data_release(struct kref *kref);
+#endif /* _LINUX_TSI_CORE_H_ */
diff --git a/drivers/misc/amd-sbi/tsi.c b/drivers/misc/amd-sbi/tsi.c
new file mode 100644
index 000000000000..f06f417f451c
--- /dev/null
+++ b/drivers/misc/amd-sbi/tsi.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tsi.c - AMD SBTSI I2C/I3C core driver. Probes the SBTSI device over I2C/I3C
+ * and publishes an auxiliary device on the auxiliary bus.
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include "tsi-core.h"
+
+#define SBTSI_REG_CONFIG 0x03 /* RO */
+
+/*
+ * Bit for reporting value with temperature measurement range.
+ * bit == 0: Use default temperature range (0C to 255.875C).
+ * bit == 1: Use extended temperature range (-49C to +206.875C).
+ */
+#define SBTSI_CONFIG_EXT_RANGE_SHIFT 2
+
+/*
+ * ReadOrder bit specifies the reading order of integer and decimal part of
+ * CPU temperature for atomic reads. If bit == 0, reading integer part triggers
+ * latching of the decimal part, so integer part should be read first.
+ */
+#define SBTSI_CONFIG_READ_ORDER_SHIFT 5
+
+static void sbtsi_adev_release(struct device *dev)
+{
+ kfree(to_auxiliary_dev(dev));
+}
+
+static void sbtsi_unregister_hwmon_adev(void *_adev)
+{
+ struct auxiliary_device *adev = _adev;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static void sbtsi_misc_unregister(void *arg)
+{
+ struct sbtsi_data *data = arg;
+
+ misc_deregister(&data->sbtsi_misc_dev);
+
+ guard(sbtsi)(data);
+ data->detached = true;
+}
+
+static void sbtsi_driver_unref(void *arg)
+{
+ struct sbtsi_data *data = arg;
+
+ kref_put(&data->kref, sbtsi_data_release);
+}
+
+/*
+ * Create and publish an auxiliary device. The hwmon driver in
+ * drivers/hwmon/sbtsi_temp.c binds to this device.
+ *
+ * @dev: I2C device (parent of the auxiliary device)
+ * @dev_addr: I2C address — used as the auxiliary device instance ID so that
+ * each socket gets a unique name.
+ */
+static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = kzalloc_obj(*adev);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->name = AMD_SBTSI_AUX_HWMON;
+ adev->id = dev_addr;
+ adev->dev.parent = dev;
+ adev->dev.release = sbtsi_adev_release;
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ kfree(adev);
+ return ret;
+ }
+
+ ret = __auxiliary_device_add(adev, AMD_SBTSI_ADEV);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev);
+}
+
+static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data)
+{
+ u8 val;
+ int err;
+
+ mutex_init(&data->lock);
+ kref_init(&data->kref);
+
+ err = devm_add_action_or_reset(dev, sbtsi_driver_unref, data);
+ if (err)
+ return err;
+
+ err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true);
+ if (err)
+ return err;
+
+ data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val);
+ data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val);
+
+ dev_set_drvdata(dev, data);
+ err = sbtsi_create_hwmon_adev(dev, data->dev_addr);
+ if (err < 0)
+ return err;
+
+ err = create_misc_tsi_device(data, dev);
+ if (err)
+ return err;
+
+ return devm_add_action_or_reset(dev, sbtsi_misc_unregister, data);
+}
+
+static int sbtsi_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct sbtsi_data *data;
+
+ data = kzalloc_obj(*data);
+ if (!data)
+ return -ENOMEM;
+
+ data->is_i3c = false;
+ data->client = client;
+
+ /* In a multi-socket system, devices that are otherwise identical do not
+ * share the same static address; each instance resides at a unique I2C
+ * client address on the same or different bus. Use the I2C client
+ * address as the auxiliary device instance ID to ensure each socket
+ * receives a distinct auxiliary device name.
+ */
+ data->dev_addr = client->addr;
+ return sbtsi_probe_common(dev, data);
+}
+
+static const struct i2c_device_id sbtsi_id[] = {
+ { .name = "sbtsi" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sbtsi_id);
+
+static const struct of_device_id __maybe_unused sbtsi_of_match[] = {
+ {
+ .compatible = "amd,sbtsi",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sbtsi_of_match);
+
+static struct i2c_driver sbtsi_driver = {
+ .driver = {
+ .name = "sbtsi-i2c",
+ .of_match_table = of_match_ptr(sbtsi_of_match),
+ },
+ .probe = sbtsi_i2c_probe,
+ .id_table = sbtsi_id,
+};
+
+static int sbtsi_i3c_probe(struct i3c_device *i3cdev)
+{
+ struct device *dev = i3cdev_to_dev(i3cdev);
+ struct i3c_device_info devinfo;
+ struct sbtsi_i3c_priv *i3c_priv;
+ struct sbtsi_data *data;
+
+ /*
+ * AMD OOB devices differ on basis of Instance ID,
+ * for SBTSI, instance ID is 0.
+ * As the device Id match is not on basis of Instance ID,
+ * add the below check to probe the SBTSI device only and
+ * not other OOB devices.
+ */
+ i3c_device_get_info(i3cdev, &devinfo);
+ if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0)
+ return -ENXIO;
+
+ i3c_priv = kzalloc_obj(*i3c_priv);
+ if (!i3c_priv)
+ return -ENOMEM;
+
+ data = &i3c_priv->data;
+ data->i3cdev = i3cdev;
+ data->is_i3c = true;
+ /*
+ * In a multi-socket system, otherwise identical devices do not share
+ * the same address; each instance is enumerated with a distinct dynamic
+ * (assigned) address on the I3C bus. Use that address (passed in as
+ * dev_addr) as the auxiliary device instance ID so that every socket
+ * gets a unique auxiliary device name.
+ */
+ data->dev_addr = devinfo.dyn_addr;
+
+ return sbtsi_probe_common(dev, data);
+}
+
+static const struct i3c_device_id sbtsi_i3c_id[] = {
+ /* PID for AMD SBTSI device */
+ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL), /* Socket:0, Turin and Genoa */
+ I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */
+ I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */
+ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */
+ I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */
+ {}
+};
+MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id);
+
+static struct i3c_driver sbtsi_i3c_driver = {
+ .driver = {
+ .name = "sbtsi-i3c",
+ },
+ .probe = sbtsi_i3c_probe,
+ .id_table = sbtsi_i3c_id,
+};
+
+module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver);
+
+MODULE_DESCRIPTION("AMD SB-TSI I2C/I3C core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c
index fc504bd8d916..1488e28819b7 100644
--- a/drivers/misc/apds9802als.c
+++ b/drivers/misc/apds9802als.c
@@ -285,7 +285,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend,
#endif /* CONFIG_PM */
static const struct i2c_device_id apds9802als_id[] = {
- { DRIVER_NAME },
+ { .name = DRIVER_NAME },
{ }
};
diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c
index 5773ffb46f0f..88ee3a417616 100644
--- a/drivers/misc/bcm-vk/bcm_vk_dev.c
+++ b/drivers/misc/bcm-vk/bcm_vk_dev.c
@@ -1370,21 +1370,15 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_msgq_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
- if (err) {
- dev_err(dev, "failed to request msgq IRQ %d for MSIX %d\n",
- pdev->irq + vk->num_irqs, vk->num_irqs + 1);
+ if (err)
goto err_irq;
- }
}
/* one irq for notification from VK */
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_notf_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
- if (err) {
- dev_err(dev, "failed to request notf IRQ %d for MSIX %d\n",
- pdev->irq + vk->num_irqs, vk->num_irqs + 1);
+ if (err)
goto err_irq;
- }
vk->num_irqs++;
for (i = 0;
@@ -1393,11 +1387,8 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_tty_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
- if (err) {
- dev_err(dev, "failed request tty IRQ %d for MSIX %d\n",
- pdev->irq + vk->num_irqs, vk->num_irqs + 1);
+ if (err)
goto err_irq;
- }
bcm_vk_tty_set_irq_enabled(vk, i);
}
@@ -1632,7 +1623,7 @@ static void bcm_vk_shutdown(struct pci_dev *pdev)
}
static const struct pci_device_id bcm_vk_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE), },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_VALKYRIE) },
{ }
};
MODULE_DEVICE_TABLE(pci, bcm_vk_ids);
diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c
index 3916ec07ecad..17114092a284 100644
--- a/drivers/misc/bcm-vk/bcm_vk_msg.c
+++ b/drivers/misc/bcm-vk/bcm_vk_msg.c
@@ -9,6 +9,7 @@
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/poll.h>
#include <linux/sizes.h>
#include <linux/spinlock.h>
@@ -108,7 +109,8 @@ u32 msgq_avail_space(const struct bcm_vk_msgq __iomem *msgq,
bool bcm_vk_drv_access_ok(struct bcm_vk *vk)
{
- return (!!atomic_read(&vk->msgq_inited));
+ /* Pair with the release store after message queue initialization. */
+ return !!atomic_read_acquire(&vk->msgq_inited);
}
void bcm_vk_set_host_alert(struct bcm_vk *vk, u32 bit_mask)
@@ -501,7 +503,8 @@ int bcm_vk_sync_msgq(struct bcm_vk *vk, bool force_sync)
msgq++;
}
}
- atomic_set(&vk->msgq_inited, 1);
+ /* Publish message queue info before allowing driver access. */
+ atomic_set_release(&vk->msgq_inited, 1);
return ret;
}
@@ -1088,6 +1091,7 @@ ssize_t bcm_vk_write(struct file *p_file,
u32 q_num;
u32 msg_size;
u32 msgq_size;
+ size_t entry_size;
if (!bcm_vk_drv_access_ok(vk))
return -EPERM;
@@ -1095,20 +1099,26 @@ ssize_t bcm_vk_write(struct file *p_file,
dev_dbg(dev, "Msg count %zu\n", count);
/* first, do sanity check where count should be multiple of basic blk */
- if (count & (VK_MSGQ_BLK_SIZE - 1)) {
- dev_err(dev, "Failure with size %zu not multiple of %zu\n",
+ if (!count || count & (VK_MSGQ_BLK_SIZE - 1)) {
+ dev_err(dev, "Failure with size %zu not a positive multiple of %zu\n",
count, VK_MSGQ_BLK_SIZE);
rc = -EINVAL;
goto write_err;
}
+ if (check_add_overflow(sizeof(*entry), count, &entry_size) ||
+ check_add_overflow(entry_size, vk->ib_sgl_size, &entry_size)) {
+ rc = -EOVERFLOW;
+ goto write_err;
+ }
+
/* allocate the work entry + buffer for size count and inband sgl */
- entry = kzalloc(sizeof(*entry) + count + vk->ib_sgl_size,
- GFP_KERNEL);
+ entry = kzalloc(entry_size, GFP_KERNEL);
if (!entry) {
rc = -ENOMEM;
goto write_err;
}
+ entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT;
/* now copy msg from user space, and then formulate the work entry */
if (copy_from_user(&entry->to_v_msg[0], buf, count)) {
@@ -1116,7 +1126,6 @@ ssize_t bcm_vk_write(struct file *p_file,
goto write_free_ent;
}
- entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT;
entry->ctx = ctx;
/* do a check on the blk size which could not exceed queue space */
@@ -1353,4 +1362,3 @@ void bcm_vk_msg_remove(struct bcm_vk *vk)
bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_v_msg_chan, NULL);
bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_h_msg_chan, NULL);
}
-
diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c
index 45f8fc69a711..98fc5970b2bb 100644
--- a/drivers/misc/bh1770glc.c
+++ b/drivers/misc/bh1770glc.c
@@ -1363,9 +1363,9 @@ static int bh1770_runtime_resume(struct device *dev)
#endif
static const struct i2c_device_id bh1770_id[] = {
- { "bh1770glc" },
- { "sfh7770" },
- {}
+ { .name = "bh1770glc" },
+ { .name = "sfh7770" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, bh1770_id);
diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c
index 8e7ea2c9142d..9e0f4f91763e 100644
--- a/drivers/misc/cardreader/alcor_pci.c
+++ b/drivers/misc/cardreader/alcor_pci.c
@@ -47,7 +47,7 @@ static const struct pci_device_id pci_ids[] = {
.driver_data = (kernel_ulong_t)&au6621_cfg },
{ PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6625),
.driver_data = (kernel_ulong_t)&au6625_cfg },
- {},
+ { }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c
index c4d54ca2fa80..c6e602523538 100644
--- a/drivers/misc/cardreader/rtsx_pcr.c
+++ b/drivers/misc/cardreader/rtsx_pcr.c
@@ -1196,6 +1196,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
/* Gating real mcu clock */
err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1,
RTS5261_MCU_CLOCK_GATING, 0);
+ if (err < 0)
+ return err;
err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL,
SSC_POWER_DOWN, 0);
} else {
diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c
index 2dd212f04fed..8e0d11e59c4e 100644
--- a/drivers/misc/cb710/core.c
+++ b/drivers/misc/cb710/core.c
@@ -292,9 +292,8 @@ static void cb710_remove_one(struct pci_dev *pdev)
}
static const struct pci_device_id cb710_pci_tbl[] = {
- { PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH,
- PCI_ANY_ID, PCI_ANY_ID, },
- { 0, }
+ { PCI_DEVICE(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_CB710_FLASH) },
+ { }
};
static SIMPLE_DEV_PM_OPS(cb710_pm_ops, cb710_suspend, cb710_resume);
diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c
index cb09e056531a..4500d7d0ea77 100644
--- a/drivers/misc/ds1682.c
+++ b/drivers/misc/ds1682.c
@@ -271,14 +271,14 @@ static void ds1682_remove(struct i2c_client *client)
}
static const struct i2c_device_id ds1682_id[] = {
- { "ds1682" },
+ { .name = "ds1682" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1682_id);
static const struct of_device_id ds1682_of_match[] = {
- { .compatible = "dallas,ds1682", },
- {}
+ { .compatible = "dallas,ds1682" },
+ { }
};
MODULE_DEVICE_TABLE(of, ds1682_of_match);
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 4d0ce47aa282..d6af02588c66 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -1,52 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "EEPROM support"
-config EEPROM_AT24
- tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
- depends on I2C && SYSFS
- select NVMEM
- select NVMEM_SYSFS
- select REGMAP
- select REGMAP_I2C
- help
- Enable this driver to get read/write support to most I2C EEPROMs
- and compatible devices like FRAMs, SRAMs, ROMs etc. After you
- configure the driver to know about each chip on your target
- board. Use these generic chip names, instead of vendor-specific
- ones like at24c64, 24lc02 or fm24c04:
-
- 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
- 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048
-
- Unless you like data loss puzzles, always be sure that any chip
- you configure as a 24c32 (32 kbit) or larger is NOT really a
- 24c16 (16 kbit) or smaller, and vice versa. Marking the chip
- as read-only won't help recover from this. Also, if your chip
- has any software write-protect mechanism you may want to review the
- code to make sure this driver won't turn it on by accident.
-
- If you use this with an SMBus adapter instead of an I2C adapter,
- full functionality is not available. Only smaller devices are
- supported (24c16 and below, max 4 kByte).
-
- This driver can also be built as a module. If so, the module
- will be called at24.
-
-config EEPROM_AT25
- tristate "SPI EEPROMs (FRAMs) from most vendors"
- depends on SPI && SYSFS
- select NVMEM
- select NVMEM_SYSFS
- select SPI_MEM
- help
- Enable this driver to get read/write support to most SPI EEPROMs
- and Cypress FRAMs,
- after you configure the board init code to know about each eeprom
- on your target board.
-
- This driver can also be built as a module. If so, the module
- will be called at25.
-
config EEPROM_MAX6875
tristate "Maxim MAX6874/5 power supply supervisor"
depends on I2C
@@ -69,22 +23,6 @@ config EEPROM_93CX6
If unsure, say N.
-config EEPROM_93XX46
- tristate "Microwire EEPROM 93XX46 support"
- depends on SPI && SYSFS
- select REGMAP
- select NVMEM
- select NVMEM_SYSFS
- help
- Driver for the microwire EEPROM chipsets 93xx46x. The driver
- supports both read and write commands and also the command to
- erase the whole EEPROM.
-
- This driver can also be built as a module. If so, the module
- will be called eeprom_93xx46.
-
- If unsure, say N.
-
config EEPROM_DIGSY_MTC_CFG
bool "DigsyMTC display configuration EEPROMs device"
depends on GPIO_MPC5200 && SPI_GPIO
@@ -107,35 +45,4 @@ config EEPROM_IDT_89HPESX
This driver can also be built as a module. If so, the module
will be called idt_89hpesx.
-config EEPROM_EE1004
- tristate "SPD EEPROMs on DDR4 memory modules"
- depends on I2C && SYSFS
- select NVMEM
- select NVMEM_SYSFS
- help
- Enable this driver to get read support to SPD EEPROMs following
- the JEDEC EE1004 standard. These are typically found on DDR4
- SDRAM memory modules.
-
- This driver can also be built as a module. If so, the module
- will be called ee1004.
-
-config EEPROM_M24LR
- tristate "STMicroelectronics M24LR RFID/NFC EEPROM support"
- depends on I2C && SYSFS
- select REGMAP_I2C
- select NVMEM
- select NVMEM_SYSFS
- help
- This enables support for STMicroelectronics M24LR RFID/NFC EEPROM
- chips. These dual-interface devices expose two I2C addresses:
- one for EEPROM memory access and another for control and system
- configuration (e.g. UID, password handling).
-
- This driver provides a sysfs interface for control functions and
- integrates with the nvmem subsystem for EEPROM access.
-
- To compile this driver as a module, choose M here: the
- module will be called m24lr.
-
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 8f311fd6a4ce..44a20cf025a1 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1,10 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_EEPROM_AT24) += at24.o
-obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
-obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o
-obj-$(CONFIG_EEPROM_EE1004) += ee1004.o
-obj-$(CONFIG_EEPROM_M24LR) += m24lr.o
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
deleted file mode 100644
index 772c4d9fa651..000000000000
--- a/drivers/misc/eeprom/at24.c
+++ /dev/null
@@ -1,871 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * at24.c - handle most I2C EEPROMs
- *
- * Copyright (C) 2005-2007 David Brownell
- * Copyright (C) 2008 Wolfram Sang, Pengutronix
- */
-
-#include <linux/acpi.h>
-#include <linux/bitops.h>
-#include <linux/capability.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/nvmem-provider.h>
-#include <linux/pm_runtime.h>
-#include <linux/property.h>
-#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-
-/* Address pointer is 16 bit. */
-#define AT24_FLAG_ADDR16 BIT(7)
-/* sysfs-entry will be read-only. */
-#define AT24_FLAG_READONLY BIT(6)
-/* sysfs-entry will be world-readable. */
-#define AT24_FLAG_IRUGO BIT(5)
-/* Take always 8 addresses (24c00). */
-#define AT24_FLAG_TAKE8ADDR BIT(4)
-/* Factory-programmed serial number. */
-#define AT24_FLAG_SERIAL BIT(3)
-/* Factory-programmed mac address. */
-#define AT24_FLAG_MAC BIT(2)
-/* Does not auto-rollover reads to the next slave address. */
-#define AT24_FLAG_NO_RDROL BIT(1)
-
-/*
- * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
- * Differences between different vendor product lines (like Atmel AT24C or
- * MicroChip 24LC, etc) won't much matter for typical read/write access.
- * There are also I2C RAM chips, likewise interchangeable. One example
- * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
- *
- * However, misconfiguration can lose data. "Set 16-bit memory address"
- * to a part with 8-bit addressing will overwrite data. Writing with too
- * big a page size also loses data. And it's not safe to assume that the
- * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
- * uses 0x51, for just one example.
- *
- * Accordingly, explicit board-specific configuration data should be used
- * in almost all cases. (One partial exception is an SMBus used to access
- * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
- *
- * So this driver uses "new style" I2C driver binding, expecting to be
- * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
- * similar kernel-resident tables; or, configuration data coming from
- * a bootloader.
- *
- * Other than binding model, current differences from "eeprom" driver are
- * that this one handles write access and isn't restricted to 24c02 devices.
- * It also handles larger devices (32 kbit and up) with two-byte addresses,
- * which won't work on pure SMBus systems.
- */
-
-struct at24_data {
- /*
- * Lock protects against activities from other Linux tasks,
- * but not from changes by other I2C masters.
- */
- struct mutex lock;
-
- unsigned int write_max;
- unsigned int num_addresses;
- unsigned int offset_adj;
-
- u32 byte_len;
- u16 page_size;
- u8 flags;
-
- struct nvmem_device *nvmem;
- struct regulator *vcc_reg;
- void (*read_post)(unsigned int off, char *buf, size_t count);
-
- /*
- * Some chips tie up multiple I2C addresses; dummy devices reserve
- * them for us.
- */
- u8 bank_addr_shift;
- struct regmap *client_regmaps[] __counted_by(num_addresses);
-};
-
-/*
- * This parameter is to help this driver avoid blocking other drivers out
- * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
- * clock, one 256 byte read takes about 1/43 second which is excessive;
- * but the 1/170 second it takes at 400 kHz may be quite reasonable; and
- * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
- *
- * This value is forced to be a power of two so that writes align on pages.
- */
-static unsigned int at24_io_limit = 128;
-module_param_named(io_limit, at24_io_limit, uint, 0);
-MODULE_PARM_DESC(at24_io_limit, "Maximum bytes per I/O (default 128)");
-
-/*
- * Specs often allow 5 msec for a page write, sometimes 20 msec;
- * it's important to recover from write timeouts.
- */
-static unsigned int at24_write_timeout = 25;
-module_param_named(write_timeout, at24_write_timeout, uint, 0);
-MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
-
-struct at24_chip_data {
- u32 byte_len;
- u8 flags;
- u8 bank_addr_shift;
- void (*read_post)(unsigned int off, char *buf, size_t count);
-};
-
-#define AT24_CHIP_DATA(_name, _len, _flags) \
- static const struct at24_chip_data _name = { \
- .byte_len = _len, .flags = _flags, \
- }
-
-#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \
- static const struct at24_chip_data _name = { \
- .byte_len = _len, .flags = _flags, \
- .read_post = _read_post, \
- }
-
-#define AT24_CHIP_DATA_BS(_name, _len, _flags, _bank_addr_shift) \
- static const struct at24_chip_data _name = { \
- .byte_len = _len, .flags = _flags, \
- .bank_addr_shift = _bank_addr_shift \
- }
-
-static void at24_read_post_vaio(unsigned int off, char *buf, size_t count)
-{
- int i;
-
- if (capable(CAP_SYS_ADMIN))
- return;
-
- /*
- * Hide VAIO private settings to regular users:
- * - BIOS passwords: bytes 0x00 to 0x0f
- * - UUID: bytes 0x10 to 0x1f
- * - Serial number: 0xc0 to 0xdf
- */
- for (i = 0; i < count; i++) {
- if ((off + i <= 0x1f) ||
- (off + i >= 0xc0 && off + i <= 0xdf))
- buf[i] = 0;
- }
-}
-
-/* needs 8 addresses as A0-A2 are ignored */
-AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR);
-/* old variants can't be handled with this generic entry! */
-AT24_CHIP_DATA(at24_data_24c01, 1024 / 8, 0);
-AT24_CHIP_DATA(at24_data_24cs01, 16,
- AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24c02, 2048 / 8, 0);
-AT24_CHIP_DATA(at24_data_24cs02, 16,
- AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24mac402, 48 / 8,
- AT24_FLAG_MAC | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24mac602, 64 / 8,
- AT24_FLAG_MAC | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24aa025e48, 48 / 8,
- AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24aa025e64, 64 / 8,
- AT24_FLAG_READONLY);
-/* spd is a 24c02 in memory DIMMs */
-AT24_CHIP_DATA(at24_data_spd, 2048 / 8,
- AT24_FLAG_READONLY | AT24_FLAG_IRUGO);
-/* 24c02_vaio is a 24c02 on some Sony laptops */
-AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8,
- AT24_FLAG_READONLY | AT24_FLAG_IRUGO,
- at24_read_post_vaio);
-AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0);
-AT24_CHIP_DATA(at24_data_24cs04, 16,
- AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
-/* 24rf08 quirk is handled at i2c-core */
-AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, 0);
-AT24_CHIP_DATA(at24_data_24cs08, 16,
- AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, 0);
-AT24_CHIP_DATA(at24_data_24cs16, 16,
- AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16);
-/* M24C32-D Additional Write lockable page (M24C32-D order codes) */
-AT24_CHIP_DATA(at24_data_24c32d_wlp, 32, AT24_FLAG_ADDR16);
-AT24_CHIP_DATA(at24_data_24cs32, 16,
- AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16);
-/* M24C64-D Additional Write lockable page (M24C64-D order codes) */
-AT24_CHIP_DATA(at24_data_24c64d_wlp, 32, AT24_FLAG_ADDR16);
-AT24_CHIP_DATA(at24_data_24cs64, 16,
- AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
-AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16);
-AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16);
-/* M24256E Additional Write lockable page (M24256E-F order codes) */
-AT24_CHIP_DATA(at24_data_24256e_wlp, 64, AT24_FLAG_ADDR16);
-AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16);
-AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16);
-AT24_CHIP_DATA_BS(at24_data_24c1025, 1048576 / 8, AT24_FLAG_ADDR16, 2);
-AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16);
-/* identical to 24c08 ? */
-AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0);
-
-static const struct i2c_device_id at24_ids[] = {
- { .name = "24c00", .driver_data = (kernel_ulong_t)&at24_data_24c00 },
- { .name = "24c01", .driver_data = (kernel_ulong_t)&at24_data_24c01 },
- { .name = "24cs01", .driver_data = (kernel_ulong_t)&at24_data_24cs01 },
- { .name = "24c02", .driver_data = (kernel_ulong_t)&at24_data_24c02 },
- { .name = "24cs02", .driver_data = (kernel_ulong_t)&at24_data_24cs02 },
- { .name = "24mac402", .driver_data = (kernel_ulong_t)&at24_data_24mac402 },
- { .name = "24mac602", .driver_data = (kernel_ulong_t)&at24_data_24mac602 },
- { .name = "24aa025e48", .driver_data = (kernel_ulong_t)&at24_data_24aa025e48 },
- { .name = "24aa025e64", .driver_data = (kernel_ulong_t)&at24_data_24aa025e64 },
- { .name = "spd", .driver_data = (kernel_ulong_t)&at24_data_spd },
- { .name = "24c02-vaio", .driver_data = (kernel_ulong_t)&at24_data_24c02_vaio },
- { .name = "24c04", .driver_data = (kernel_ulong_t)&at24_data_24c04 },
- { .name = "24cs04", .driver_data = (kernel_ulong_t)&at24_data_24cs04 },
- { .name = "24c08", .driver_data = (kernel_ulong_t)&at24_data_24c08 },
- { .name = "24cs08", .driver_data = (kernel_ulong_t)&at24_data_24cs08 },
- { .name = "24c16", .driver_data = (kernel_ulong_t)&at24_data_24c16 },
- { .name = "24cs16", .driver_data = (kernel_ulong_t)&at24_data_24cs16 },
- { .name = "24c32", .driver_data = (kernel_ulong_t)&at24_data_24c32 },
- { .name = "24c32d-wl", .driver_data = (kernel_ulong_t)&at24_data_24c32d_wlp },
- { .name = "24cs32", .driver_data = (kernel_ulong_t)&at24_data_24cs32 },
- { .name = "24c64", .driver_data = (kernel_ulong_t)&at24_data_24c64 },
- { .name = "24c64-wl", .driver_data = (kernel_ulong_t)&at24_data_24c64d_wlp },
- { .name = "24cs64", .driver_data = (kernel_ulong_t)&at24_data_24cs64 },
- { .name = "24c128", .driver_data = (kernel_ulong_t)&at24_data_24c128 },
- { .name = "24c256", .driver_data = (kernel_ulong_t)&at24_data_24c256 },
- { .name = "24256e-wl", .driver_data = (kernel_ulong_t)&at24_data_24256e_wlp },
- { .name = "24c512", .driver_data = (kernel_ulong_t)&at24_data_24c512 },
- { .name = "24c1024", .driver_data = (kernel_ulong_t)&at24_data_24c1024 },
- { .name = "24c1025", .driver_data = (kernel_ulong_t)&at24_data_24c1025 },
- { .name = "24c2048", .driver_data = (kernel_ulong_t)&at24_data_24c2048 },
- { .name = "at24", .driver_data = 0 },
- { /* END OF LIST */ }
-};
-MODULE_DEVICE_TABLE(i2c, at24_ids);
-
-static const struct of_device_id at24_of_match[] = {
- { .compatible = "atmel,24c00", .data = &at24_data_24c00 },
- { .compatible = "atmel,24c01", .data = &at24_data_24c01 },
- { .compatible = "atmel,24cs01", .data = &at24_data_24cs01 },
- { .compatible = "atmel,24c02", .data = &at24_data_24c02 },
- { .compatible = "atmel,24cs02", .data = &at24_data_24cs02 },
- { .compatible = "atmel,24mac402", .data = &at24_data_24mac402 },
- { .compatible = "atmel,24mac602", .data = &at24_data_24mac602 },
- { .compatible = "atmel,spd", .data = &at24_data_spd },
- { .compatible = "atmel,24c04", .data = &at24_data_24c04 },
- { .compatible = "atmel,24cs04", .data = &at24_data_24cs04 },
- { .compatible = "atmel,24c08", .data = &at24_data_24c08 },
- { .compatible = "atmel,24cs08", .data = &at24_data_24cs08 },
- { .compatible = "atmel,24c16", .data = &at24_data_24c16 },
- { .compatible = "atmel,24cs16", .data = &at24_data_24cs16 },
- { .compatible = "atmel,24c32", .data = &at24_data_24c32 },
- { .compatible = "atmel,24c32d-wl", .data = &at24_data_24c32d_wlp },
- { .compatible = "atmel,24cs32", .data = &at24_data_24cs32 },
- { .compatible = "atmel,24c64", .data = &at24_data_24c64 },
- { .compatible = "atmel,24c64d-wl", .data = &at24_data_24c64d_wlp },
- { .compatible = "atmel,24cs64", .data = &at24_data_24cs64 },
- { .compatible = "atmel,24c128", .data = &at24_data_24c128 },
- { .compatible = "atmel,24c256", .data = &at24_data_24c256 },
- { .compatible = "atmel,24c512", .data = &at24_data_24c512 },
- { .compatible = "atmel,24c1024", .data = &at24_data_24c1024 },
- { .compatible = "atmel,24c1025", .data = &at24_data_24c1025 },
- { .compatible = "atmel,24c2048", .data = &at24_data_24c2048 },
- { .compatible = "microchip,24aa025e48", .data = &at24_data_24aa025e48 },
- { .compatible = "microchip,24aa025e64", .data = &at24_data_24aa025e64 },
- { .compatible = "st,24256e-wl", .data = &at24_data_24256e_wlp },
- { /* END OF LIST */ },
-};
-MODULE_DEVICE_TABLE(of, at24_of_match);
-
-static const struct acpi_device_id at24_acpi_ids[] = {
- { "INT3499", (kernel_ulong_t)&at24_data_INT3499 },
- { "TPF0001", (kernel_ulong_t)&at24_data_24c1024 },
- { /* END OF LIST */ }
-};
-MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
-
-/*
- * This routine supports chips which consume multiple I2C addresses. It
- * computes the addressing information to be used for a given r/w request.
- * Assumes that sanity checks for offset happened at sysfs-layer.
- *
- * Slave address and byte offset derive from the offset. Always
- * set the byte address; on a multi-master board, another master
- * may have changed the chip's "current" address pointer.
- */
-static struct regmap *at24_translate_offset(struct at24_data *at24,
- unsigned int *offset)
-{
- unsigned int i;
-
- if (at24->flags & AT24_FLAG_ADDR16) {
- i = *offset >> 16;
- *offset &= 0xffff;
- } else {
- i = *offset >> 8;
- *offset &= 0xff;
- }
-
- return at24->client_regmaps[i];
-}
-
-static struct device *at24_base_client_dev(struct at24_data *at24)
-{
- return regmap_get_device(at24->client_regmaps[0]);
-}
-
-static size_t at24_adjust_read_count(struct at24_data *at24,
- unsigned int offset, size_t count)
-{
- unsigned int bits;
- size_t remainder;
-
- /*
- * In case of multi-address chips that don't rollover reads to
- * the next slave address: truncate the count to the slave boundary,
- * so that the read never straddles slaves.
- */
- if (at24->flags & AT24_FLAG_NO_RDROL) {
- bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8;
- remainder = BIT(bits) - offset;
- if (count > remainder)
- count = remainder;
- }
-
- if (count > at24_io_limit)
- count = at24_io_limit;
-
- return count;
-}
-
-static ssize_t at24_regmap_read(struct at24_data *at24, char *buf,
- unsigned int offset, size_t count)
-{
- unsigned long timeout, read_time;
- struct regmap *regmap;
- int ret;
-
- regmap = at24_translate_offset(at24, &offset);
- count = at24_adjust_read_count(at24, offset, count);
-
- /* adjust offset for mac and serial read ops */
- offset += at24->offset_adj;
-
- timeout = jiffies + msecs_to_jiffies(at24_write_timeout);
- do {
- /*
- * The timestamp shall be taken before the actual operation
- * to avoid a premature timeout in case of high CPU load.
- */
- read_time = jiffies;
-
- ret = regmap_bulk_read(regmap, offset, buf, count);
- dev_dbg(regmap_get_device(regmap), "read %zu@%d --> %d (%ld)\n",
- count, offset, ret, jiffies);
- if (!ret)
- return count;
-
- usleep_range(1000, 1500);
- } while (time_before(read_time, timeout));
-
- return -ETIMEDOUT;
-}
-
-/*
- * Note that if the hardware write-protect pin is pulled high, the whole
- * chip is normally write protected. But there are plenty of product
- * variants here, including OTP fuses and partial chip protect.
- *
- * We only use page mode writes; the alternative is sloooow. These routines
- * write at most one page.
- */
-
-static size_t at24_adjust_write_count(struct at24_data *at24,
- unsigned int offset, size_t count)
-{
- unsigned int next_page;
-
- /* write_max is at most a page */
- if (count > at24->write_max)
- count = at24->write_max;
-
- /* Never roll over backwards, to the start of this page */
- next_page = roundup(offset + 1, at24->page_size);
- if (offset + count > next_page)
- count = next_page - offset;
-
- return count;
-}
-
-static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf,
- unsigned int offset, size_t count)
-{
- unsigned long timeout, write_time;
- struct regmap *regmap;
- int ret;
-
- regmap = at24_translate_offset(at24, &offset);
- count = at24_adjust_write_count(at24, offset, count);
- timeout = jiffies + msecs_to_jiffies(at24_write_timeout);
-
- do {
- /*
- * The timestamp shall be taken before the actual operation
- * to avoid a premature timeout in case of high CPU load.
- */
- write_time = jiffies;
-
- ret = regmap_bulk_write(regmap, offset, buf, count);
- dev_dbg(regmap_get_device(regmap), "write %zu@%d --> %d (%ld)\n",
- count, offset, ret, jiffies);
- if (!ret)
- return count;
-
- usleep_range(1000, 1500);
- } while (time_before(write_time, timeout));
-
- return -ETIMEDOUT;
-}
-
-static int at24_read(void *priv, unsigned int off, void *val, size_t count)
-{
- struct at24_data *at24;
- struct device *dev;
- char *buf = val;
- int i, ret;
-
- at24 = priv;
- dev = at24_base_client_dev(at24);
-
- if (unlikely(!count))
- return count;
-
- if (off + count > at24->byte_len)
- return -EINVAL;
-
- ret = pm_runtime_resume_and_get(dev);
- if (ret)
- return ret;
- /*
- * Read data from chip, protecting against concurrent updates
- * from this host, but not from other I2C masters.
- */
- mutex_lock(&at24->lock);
-
- for (i = 0; count; i += ret, count -= ret) {
- ret = at24_regmap_read(at24, buf + i, off + i, count);
- if (ret < 0) {
- mutex_unlock(&at24->lock);
- pm_runtime_put(dev);
- return ret;
- }
- }
-
- mutex_unlock(&at24->lock);
-
- pm_runtime_put(dev);
-
- if (unlikely(at24->read_post))
- at24->read_post(off, buf, i);
-
- return 0;
-}
-
-static int at24_write(void *priv, unsigned int off, void *val, size_t count)
-{
- struct at24_data *at24;
- struct device *dev;
- char *buf = val;
- int ret;
-
- at24 = priv;
- dev = at24_base_client_dev(at24);
-
- if (unlikely(!count))
- return -EINVAL;
-
- if (off + count > at24->byte_len)
- return -EINVAL;
-
- ret = pm_runtime_resume_and_get(dev);
- if (ret)
- return ret;
- /*
- * Write data to chip, protecting against concurrent updates
- * from this host, but not from other I2C masters.
- */
- mutex_lock(&at24->lock);
-
- while (count) {
- ret = at24_regmap_write(at24, buf, off, count);
- if (ret < 0) {
- mutex_unlock(&at24->lock);
- pm_runtime_put(dev);
- return ret;
- }
- buf += ret;
- off += ret;
- count -= ret;
- }
-
- mutex_unlock(&at24->lock);
-
- pm_runtime_put(dev);
-
- return 0;
-}
-
-static int at24_make_dummy_client(struct at24_data *at24, unsigned int index,
- struct i2c_client *base_client,
- struct regmap_config *regmap_config)
-{
- struct i2c_client *dummy_client;
- struct regmap *regmap;
-
- dummy_client = devm_i2c_new_dummy_device(&base_client->dev,
- base_client->adapter,
- base_client->addr +
- (index << at24->bank_addr_shift));
- if (IS_ERR(dummy_client))
- return PTR_ERR(dummy_client);
-
- regmap = devm_regmap_init_i2c(dummy_client, regmap_config);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- at24->client_regmaps[index] = regmap;
-
- return 0;
-}
-
-static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
-{
- if (flags & AT24_FLAG_MAC) {
- /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */
- return 0xa0 - byte_len;
- } else if (flags & AT24_FLAG_SERIAL && flags & AT24_FLAG_ADDR16) {
- /*
- * For 16 bit address pointers, the word address must contain
- * a '10' sequence in bits 11 and 10 regardless of the
- * intended position of the address pointer.
- */
- return 0x0800;
- } else if (flags & AT24_FLAG_SERIAL) {
- /*
- * Otherwise the word address must begin with a '10' sequence,
- * regardless of the intended address.
- */
- return 0x0080;
- } else {
- return 0;
- }
-}
-
-static void at24_probe_temp_sensor(struct i2c_client *client)
-{
- struct at24_data *at24 = i2c_get_clientdata(client);
- struct i2c_board_info info = { .type = "jc42" };
- int ret;
- u8 val;
-
- /*
- * Byte 2 has value 11 for DDR3, earlier versions don't
- * support the thermal sensor present flag
- */
- ret = at24_read(at24, 2, &val, 1);
- if (ret || val != 11)
- return;
-
- /* Byte 32, bit 7 is set if temp sensor is present */
- ret = at24_read(at24, 32, &val, 1);
- if (ret || !(val & BIT(7)))
- return;
-
- info.addr = 0x18 | (client->addr & 7);
-
- i2c_new_client_device(client->adapter, &info);
-}
-
-static int at24_probe(struct i2c_client *client)
-{
- struct regmap_config regmap_config = { };
- struct nvmem_config nvmem_config = { };
- u32 byte_len, page_size, flags, addrw;
- const struct at24_chip_data *cdata;
- struct device *dev = &client->dev;
- bool i2c_fn_i2c, i2c_fn_block;
- unsigned int i, num_addresses;
- struct at24_data *at24;
- bool full_power;
- struct regmap *regmap;
- bool writable;
- u8 test_byte;
- int err;
-
- i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
- i2c_fn_block = i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_WRITE_I2C_BLOCK);
-
- cdata = i2c_get_match_data(client);
- if (!cdata)
- return -ENODEV;
-
- err = device_property_read_u32(dev, "pagesize", &page_size);
- if (err)
- /*
- * This is slow, but we can't know all eeproms, so we better
- * play safe. Specifying custom eeprom-types via device tree
- * or properties is recommended anyhow.
- */
- page_size = 1;
-
- flags = cdata->flags;
- if (device_property_present(dev, "read-only"))
- flags |= AT24_FLAG_READONLY;
- if (device_property_present(dev, "no-read-rollover"))
- flags |= AT24_FLAG_NO_RDROL;
-
- err = device_property_read_u32(dev, "address-width", &addrw);
- if (!err) {
- switch (addrw) {
- case 8:
- if (flags & AT24_FLAG_ADDR16)
- dev_warn(dev,
- "Override address width to be 8, while default is 16\n");
- flags &= ~AT24_FLAG_ADDR16;
- break;
- case 16:
- flags |= AT24_FLAG_ADDR16;
- break;
- default:
- dev_warn(dev, "Bad \"address-width\" property: %u\n",
- addrw);
- }
- }
-
- err = device_property_read_u32(dev, "size", &byte_len);
- if (err)
- byte_len = cdata->byte_len;
-
- if (!i2c_fn_i2c && !i2c_fn_block)
- page_size = 1;
-
- if (!page_size)
- return dev_err_probe(dev, -EINVAL, "page_size must not be 0!\n");
-
- if (!is_power_of_2(page_size))
- dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
-
- err = device_property_read_u32(dev, "num-addresses", &num_addresses);
- if (err) {
- if (flags & AT24_FLAG_TAKE8ADDR)
- num_addresses = 8;
- else
- num_addresses = DIV_ROUND_UP(byte_len,
- (flags & AT24_FLAG_ADDR16) ? 65536 : 256);
- }
-
- if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC))
- return dev_err_probe(dev, -EINVAL,
- "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
-
- regmap_config.val_bits = 8;
- regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8;
- regmap_config.disable_locking = true;
-
- regmap = devm_regmap_init_i2c(client, &regmap_config);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- at24 = devm_kzalloc(dev, struct_size(at24, client_regmaps, num_addresses),
- GFP_KERNEL);
- if (!at24)
- return -ENOMEM;
-
- mutex_init(&at24->lock);
- at24->byte_len = byte_len;
- at24->page_size = page_size;
- at24->flags = flags;
- at24->read_post = cdata->read_post;
- at24->bank_addr_shift = cdata->bank_addr_shift;
- at24->num_addresses = num_addresses;
- at24->offset_adj = at24_get_offset_adj(flags, byte_len);
- at24->client_regmaps[0] = regmap;
-
- at24->vcc_reg = devm_regulator_get(dev, "vcc");
- if (IS_ERR(at24->vcc_reg))
- return PTR_ERR(at24->vcc_reg);
-
- writable = !(flags & AT24_FLAG_READONLY);
- if (writable) {
- at24->write_max = min_t(unsigned int,
- page_size, at24_io_limit);
- if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX)
- at24->write_max = I2C_SMBUS_BLOCK_MAX;
- }
-
- /* use dummy devices for multiple-address chips */
- for (i = 1; i < num_addresses; i++) {
- err = at24_make_dummy_client(at24, i, client, &regmap_config);
- if (err)
- return err;
- }
-
- /*
- * We initialize nvmem_config.id to NVMEM_DEVID_AUTO even if the
- * label property is set as some platform can have multiple eeproms
- * with same label and we can not register each of those with same
- * label. Failing to register those eeproms trigger cascade failure
- * on such platform.
- */
- nvmem_config.id = NVMEM_DEVID_AUTO;
-
- if (device_property_present(dev, "label")) {
- err = device_property_read_string(dev, "label",
- &nvmem_config.name);
- if (err)
- return err;
- } else {
- nvmem_config.name = dev_name(dev);
- }
-
- nvmem_config.type = NVMEM_TYPE_EEPROM;
- nvmem_config.dev = dev;
- nvmem_config.read_only = !writable;
- nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO);
- nvmem_config.owner = THIS_MODULE;
- nvmem_config.compat = true;
- nvmem_config.base_dev = dev;
- nvmem_config.reg_read = at24_read;
- nvmem_config.reg_write = at24_write;
- nvmem_config.priv = at24;
- nvmem_config.stride = 1;
- nvmem_config.word_size = 1;
- nvmem_config.size = byte_len;
-
- i2c_set_clientdata(client, at24);
-
- full_power = acpi_dev_state_d0(&client->dev);
- if (full_power) {
- err = regulator_enable(at24->vcc_reg);
- if (err)
- return dev_err_probe(dev, err, "Failed to enable vcc regulator\n");
-
- pm_runtime_set_active(dev);
- }
- pm_runtime_enable(dev);
-
- /*
- * Perform a one-byte test read to verify that the chip is functional,
- * unless powering on the device is to be avoided during probe (i.e.
- * it's powered off right now).
- */
- if (full_power) {
- err = at24_read(at24, 0, &test_byte, 1);
- if (err) {
- pm_runtime_disable(dev);
- if (!pm_runtime_status_suspended(dev))
- regulator_disable(at24->vcc_reg);
- return -ENODEV;
- }
- }
-
- at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
- if (IS_ERR(at24->nvmem)) {
- pm_runtime_disable(dev);
- if (!pm_runtime_status_suspended(dev))
- regulator_disable(at24->vcc_reg);
- return dev_err_probe(dev, PTR_ERR(at24->nvmem),
- "failed to register nvmem\n");
- }
-
- /* If this a SPD EEPROM, probe for DDR3 thermal sensor */
- if (cdata == &at24_data_spd)
- at24_probe_temp_sensor(client);
-
- pm_runtime_idle(dev);
-
- if (writable)
- dev_info(dev, "%u byte %s EEPROM, writable, %u bytes/write\n",
- byte_len, client->name, at24->write_max);
- else
- dev_info(dev, "%u byte %s EEPROM, read-only\n",
- byte_len, client->name);
-
- return 0;
-}
-
-static void at24_remove(struct i2c_client *client)
-{
- struct at24_data *at24 = i2c_get_clientdata(client);
-
- pm_runtime_disable(&client->dev);
- if (acpi_dev_state_d0(&client->dev)) {
- if (!pm_runtime_status_suspended(&client->dev))
- regulator_disable(at24->vcc_reg);
- pm_runtime_set_suspended(&client->dev);
- }
-}
-
-static int __maybe_unused at24_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct at24_data *at24 = i2c_get_clientdata(client);
-
- return regulator_disable(at24->vcc_reg);
-}
-
-static int __maybe_unused at24_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct at24_data *at24 = i2c_get_clientdata(client);
-
- return regulator_enable(at24->vcc_reg);
-}
-
-static const struct dev_pm_ops at24_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(at24_suspend, at24_resume, NULL)
-};
-
-static struct i2c_driver at24_driver = {
- .driver = {
- .name = "at24",
- .pm = &at24_pm_ops,
- .of_match_table = at24_of_match,
- .acpi_match_table = at24_acpi_ids,
- },
- .probe = at24_probe,
- .remove = at24_remove,
- .id_table = at24_ids,
- .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE,
-};
-
-static int __init at24_init(void)
-{
- if (!at24_io_limit) {
- pr_err("at24: at24_io_limit must not be 0!\n");
- return -EINVAL;
- }
-
- at24_io_limit = rounddown_pow_of_two(at24_io_limit);
- return i2c_add_driver(&at24_driver);
-}
-module_init(at24_init);
-
-static void __exit at24_exit(void)
-{
- i2c_del_driver(&at24_driver);
-}
-module_exit(at24_exit);
-
-MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
-MODULE_AUTHOR("David Brownell and Wolfram Sang");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
deleted file mode 100644
index bc2cfb75d9bb..000000000000
--- a/drivers/misc/eeprom/at25.c
+++ /dev/null
@@ -1,585 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Driver for most of the SPI EEPROMs, such as Atmel AT25 models
- * and Cypress FRAMs FM25 models.
- *
- * Copyright (C) 2006 David Brownell
- */
-
-#include <linux/bits.h>
-#include <linux/cleanup.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/iopoll.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/property.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <linux/spi/eeprom.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi-mem.h>
-
-#include <linux/nvmem-provider.h>
-
-/*
- * NOTE: this is an *EEPROM* driver. The vagaries of product naming
- * mean that some AT25 products are EEPROMs, and others are FLASH.
- * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver,
- * not this one!
- *
- * EEPROMs that can be used with this driver include, for example:
- * AT25M02, AT25128B
- */
-
-#define FM25_SN_LEN 8 /* serial number length */
-#define FM25_MAX_ID_LEN 9 /* ID length */
-#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */
-
-struct at25_data {
- struct spi_eeprom chip;
- struct spi_mem *spimem;
- struct mutex lock;
- unsigned addrlen;
- struct nvmem_config nvmem_config;
- struct nvmem_device *nvmem;
- u8 sernum[FM25_SN_LEN];
- u8 id[FM25_MAX_ID_LEN];
- u8 id_len;
-};
-
-#define AT25_WREN 0x06 /* latch the write enable */
-#define AT25_WRDI 0x04 /* reset the write enable */
-#define AT25_RDSR 0x05 /* read status register */
-#define AT25_WRSR 0x01 /* write status register */
-#define AT25_READ 0x03 /* read byte(s) */
-#define AT25_WRITE 0x02 /* write byte(s)/sector */
-#define FM25_SLEEP 0xb9 /* enter sleep mode */
-#define FM25_RDID 0x9f /* read device ID */
-#define FM25_RDSN 0xc3 /* read S/N */
-
-#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */
-#define AT25_SR_WEN 0x02 /* write enable (latched) */
-#define AT25_SR_BP0 0x04 /* BP for software writeprotect */
-#define AT25_SR_BP1 0x08
-#define AT25_SR_WPEN 0x80 /* writeprotect enable */
-
-#define AT25_INSTR_BIT3 0x08 /* additional address bit in instr */
-
-/*
- * Specs often allow 5ms for a page write, sometimes 20ms;
- * it's important to recover from write timeouts.
- */
-#define EE_TIMEOUT 25
-
-/*-------------------------------------------------------------------------*/
-
-#define io_limit PAGE_SIZE /* bytes */
-
-/* Handle the address MSB as part of instruction byte */
-static u8 at25_instr(struct at25_data *at25, u8 instr, unsigned int off)
-{
- if (!(at25->chip.flags & EE_INSTR_BIT3_IS_ADDR))
- return instr;
- if (off < BIT(at25->addrlen * 8))
- return instr;
- return instr | AT25_INSTR_BIT3;
-}
-
-static int at25_ee_read(void *priv, unsigned int offset,
- void *val, size_t count)
-{
- u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL);
- struct at25_data *at25 = priv;
- char *buf = val;
- unsigned int msg_offset = offset;
- size_t bytes_left = count;
- size_t segment;
- int status;
-
- if (!bounce)
- return -ENOMEM;
-
- if (unlikely(offset >= at25->chip.byte_len))
- return -EINVAL;
- if ((offset + count) > at25->chip.byte_len)
- count = at25->chip.byte_len - offset;
- if (unlikely(!count))
- return -EINVAL;
-
- do {
- struct spi_mem_op op;
-
- segment = min(bytes_left, io_limit);
-
- op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_READ,
- msg_offset), 1),
- SPI_MEM_OP_ADDR(at25->addrlen, msg_offset, 1),
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_IN(segment, bounce, 1));
-
- status = spi_mem_adjust_op_size(at25->spimem, &op);
- if (status)
- return status;
- segment = op.data.nbytes;
-
- mutex_lock(&at25->lock);
- status = spi_mem_exec_op(at25->spimem, &op);
- mutex_unlock(&at25->lock);
- if (status)
- return status;
- memcpy(buf, bounce, segment);
-
- msg_offset += segment;
- buf += segment;
- bytes_left -= segment;
- } while (bytes_left > 0);
-
- dev_dbg(&at25->spimem->spi->dev, "read %zu bytes at %d\n",
- count, offset);
- return 0;
-}
-
-/*
- * Read extra registers as ID or serial number
- *
- * Allow for the callers to provide @buf on stack (not necessary DMA-capable)
- * by allocating a bounce buffer internally.
- */
-static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command,
- int len)
-{
- u8 *bounce __free(kfree) = kmalloc(len, GFP_KERNEL);
- struct spi_mem_op op;
- int status;
-
- if (!bounce)
- return -ENOMEM;
-
- op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(command, 1),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_IN(len, bounce, 1));
-
- status = spi_mem_exec_op(at25->spimem, &op);
- dev_dbg(&at25->spimem->spi->dev, "read %d aux bytes --> %d\n", len, status);
- if (status)
- return status;
-
- memcpy(buf, bounce, len);
-
- return 0;
-}
-
-static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct at25_data *at25;
-
- at25 = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%*ph\n", (int)sizeof(at25->sernum), at25->sernum);
-}
-static DEVICE_ATTR_RO(sernum);
-
-static ssize_t jedec_id_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct at25_data *at25;
-
- at25 = dev_get_drvdata(dev);
-
- if (!at25->id_len)
- return -EOPNOTSUPP;
-
- return sysfs_emit(buf, "%*phN\n", at25->id_len, at25->id);
-}
-static DEVICE_ATTR_RO(jedec_id);
-
-static struct attribute *at25_attrs[] = {
- &dev_attr_sernum.attr,
- &dev_attr_jedec_id.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(at25);
-
-/*
- * Poll Read Status Register with timeout
- *
- * Return:
- * 0, if the chip is ready
- * [positive] Status Register value as-is, if the chip is busy
- * [negative] error code in case of read failure
- */
-static int at25_wait_ready(struct at25_data *at25)
-{
- u8 *bounce __free(kfree) = kmalloc(1, GFP_KERNEL);
- struct spi_mem_op op;
- int status;
-
- if (!bounce)
- return -ENOMEM;
-
- op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_RDSR, 1),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_IN(1, bounce, 1));
-
- read_poll_timeout(spi_mem_exec_op, status,
- status || !(bounce[0] & AT25_SR_nRDY), false,
- USEC_PER_MSEC, USEC_PER_MSEC * EE_TIMEOUT,
- at25->spimem, &op);
- if (status < 0)
- return status;
- if (!(bounce[0] & AT25_SR_nRDY))
- return 0;
-
- return bounce[0];
-}
-
-static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
-{
- u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL);
- struct at25_data *at25 = priv;
- const char *buf = val;
- unsigned int buf_size;
- int status;
-
- if (unlikely(off >= at25->chip.byte_len))
- return -EFBIG;
- if ((off + count) > at25->chip.byte_len)
- count = at25->chip.byte_len - off;
- if (unlikely(!count))
- return -EINVAL;
-
- buf_size = at25->chip.page_size;
-
- if (!bounce)
- return -ENOMEM;
-
- /*
- * For write, rollover is within the page ... so we write at
- * most one page, then manually roll over to the next page.
- */
- guard(mutex)(&at25->lock);
- do {
- struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_WREN, 1),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_NO_DATA);
- unsigned int segment;
-
- status = spi_mem_exec_op(at25->spimem, &op);
- if (status < 0) {
- dev_dbg(&at25->spimem->spi->dev, "WREN --> %d\n", status);
- return status;
- }
-
- /* Write as much of a page as we can */
- segment = buf_size - (off % buf_size);
- if (segment > count)
- segment = count;
- if (segment > io_limit)
- segment = io_limit;
-
- op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_WRITE, off),
- 1),
- SPI_MEM_OP_ADDR(at25->addrlen, off, 1),
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_OUT(segment, bounce, 1));
-
- status = spi_mem_adjust_op_size(at25->spimem, &op);
- if (status)
- return status;
- segment = op.data.nbytes;
-
- memcpy(bounce, buf, segment);
-
- status = spi_mem_exec_op(at25->spimem, &op);
- dev_dbg(&at25->spimem->spi->dev, "write %u bytes at %u --> %d\n",
- segment, off, status);
- if (status)
- return status;
-
- /*
- * REVISIT this should detect (or prevent) failed writes
- * to read-only sections of the EEPROM...
- */
-
- status = at25_wait_ready(at25);
- if (status < 0) {
- dev_err_probe(&at25->spimem->spi->dev, status,
- "Read Status Redister command failed\n");
- return status;
- }
- if (status) {
- dev_dbg(&at25->spimem->spi->dev,
- "Status %02x\n", status);
- dev_err(&at25->spimem->spi->dev,
- "write %u bytes offset %u, timeout after %u msecs\n",
- segment, off, EE_TIMEOUT);
- return -ETIMEDOUT;
- }
-
- off += segment;
- buf += segment;
- count -= segment;
-
- } while (count > 0);
-
- return status;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
-{
- u32 val;
- int err;
-
- strscpy(chip->name, "at25", sizeof(chip->name));
-
- err = device_property_read_u32(dev, "size", &val);
- if (err)
- err = device_property_read_u32(dev, "at25,byte-len", &val);
- if (err) {
- dev_err(dev, "Error: missing \"size\" property\n");
- return err;
- }
- chip->byte_len = val;
-
- err = device_property_read_u32(dev, "pagesize", &val);
- if (err)
- err = device_property_read_u32(dev, "at25,page-size", &val);
- if (err) {
- dev_err(dev, "Error: missing \"pagesize\" property\n");
- return err;
- }
- chip->page_size = val;
-
- err = device_property_read_u32(dev, "address-width", &val);
- if (err) {
- err = device_property_read_u32(dev, "at25,addr-mode", &val);
- if (err) {
- dev_err(dev, "Error: missing \"address-width\" property\n");
- return err;
- }
- chip->flags = (u16)val;
- } else {
- switch (val) {
- case 9:
- chip->flags |= EE_INSTR_BIT3_IS_ADDR;
- fallthrough;
- case 8:
- chip->flags |= EE_ADDR1;
- break;
- case 16:
- chip->flags |= EE_ADDR2;
- break;
- case 24:
- chip->flags |= EE_ADDR3;
- break;
- default:
- dev_err(dev,
- "Error: bad \"address-width\" property: %u\n",
- val);
- return -ENODEV;
- }
- if (device_property_present(dev, "read-only"))
- chip->flags |= EE_READONLY;
- }
- return 0;
-}
-
-static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
-{
- struct at25_data *at25 = container_of(chip, struct at25_data, chip);
- u8 sernum[FM25_SN_LEN];
- u8 id[FM25_MAX_ID_LEN];
- u32 val;
- int i;
-
- strscpy(chip->name, "fm25", sizeof(chip->name));
-
- if (!device_property_read_u32(dev, "size", &val)) {
- chip->byte_len = val;
- } else {
- /* Get ID of chip */
- fm25_aux_read(at25, id, FM25_RDID, FM25_MAX_ID_LEN);
-
- /* Store the unprocessed ID for exposing via sysfs */
- memcpy(at25->id, id, FM25_MAX_ID_LEN);
- at25->id_len = FM25_MAX_ID_LEN;
-
- /* There are inside-out FRAM variations, detect them and reverse the ID bytes */
- if (id[6] == 0x7f && id[2] == 0xc2)
- for (i = 0; i < ARRAY_SIZE(id) / 2; i++) {
- u8 tmp = id[i];
- int j = ARRAY_SIZE(id) - i - 1;
-
- id[i] = id[j];
- id[j] = tmp;
- }
-
- if (id[6] == 0xc2) {
- at25->id_len = 9;
- switch (id[7]) {
- case 0x21 ... 0x26:
- chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024;
- break;
- case 0x2a ... 0x30:
- /* CY15B102QN ... CY15B116QN */
- chip->byte_len = BIT(((id[7] >> 1) & 0xf) + 13);
- break;
- default:
- dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]);
- return -ENODEV;
- }
- } else if (id[2] == 0x82 && id[3] == 0x06) {
- at25->id_len = 8;
- switch (id[1]) {
- case 0x51 ... 0x54:
- /* CY15B102QSN ... CY15B204QSN */
- chip->byte_len = BIT(((id[0] >> 3) & 0x1F) + 9);
- break;
- default:
- dev_err(dev, "Error: unsupported product id %02x\n", id[1]);
- return -ENODEV;
- }
- } else {
- dev_err(dev, "Error: unrecognized JEDEC ID format: %*ph\n",
- FM25_MAX_ID_LEN, id);
- return -ENODEV;
- }
-
- fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN);
- /* Swap byte order */
- for (i = 0; i < FM25_SN_LEN; i++)
- at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i];
- }
-
- if (chip->byte_len > 64 * 1024)
- chip->flags |= EE_ADDR3;
- else
- chip->flags |= EE_ADDR2;
-
- chip->page_size = PAGE_SIZE;
- return 0;
-}
-
-static const struct of_device_id at25_of_match[] = {
- { .compatible = "atmel,at25" },
- { .compatible = "cypress,fm25" },
- { }
-};
-MODULE_DEVICE_TABLE(of, at25_of_match);
-
-static const struct spi_device_id at25_spi_ids[] = {
- { .name = "at25" },
- { .name = "fm25" },
- { }
-};
-MODULE_DEVICE_TABLE(spi, at25_spi_ids);
-
-static int at25_probe(struct spi_mem *mem)
-{
- struct spi_device *spi = mem->spi;
- struct spi_eeprom *pdata;
- struct at25_data *at25;
- bool is_fram;
- int err;
-
- at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL);
- if (!at25)
- return -ENOMEM;
-
- at25->spimem = mem;
-
- /*
- * Ping the chip ... the status register is pretty portable,
- * unlike probing manufacturer IDs.
- */
- err = at25_wait_ready(at25);
- if (err < 0)
- return dev_err_probe(&spi->dev, err, "Read Status Register command failed\n");
- if (err) {
- dev_err(&spi->dev, "Not ready (%02x)\n", err);
- return -ENXIO;
- }
-
- mutex_init(&at25->lock);
- spi_set_drvdata(spi, at25);
-
- is_fram = fwnode_device_is_compatible(dev_fwnode(&spi->dev), "cypress,fm25");
-
- /* Chip description */
- pdata = dev_get_platdata(&spi->dev);
- if (pdata) {
- at25->chip = *pdata;
- } else {
- if (is_fram)
- err = at25_fram_to_chip(&spi->dev, &at25->chip);
- else
- err = at25_fw_to_chip(&spi->dev, &at25->chip);
- if (err)
- return err;
- }
-
- /* For now we only support 8/16/24 bit addressing */
- if (at25->chip.flags & EE_ADDR1)
- at25->addrlen = 1;
- else if (at25->chip.flags & EE_ADDR2)
- at25->addrlen = 2;
- else if (at25->chip.flags & EE_ADDR3)
- at25->addrlen = 3;
- else {
- dev_dbg(&spi->dev, "unsupported address type\n");
- return -EINVAL;
- }
-
- at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM;
- at25->nvmem_config.name = dev_name(&spi->dev);
- at25->nvmem_config.dev = &spi->dev;
- at25->nvmem_config.read_only = at25->chip.flags & EE_READONLY;
- at25->nvmem_config.root_only = true;
- at25->nvmem_config.owner = THIS_MODULE;
- at25->nvmem_config.compat = true;
- at25->nvmem_config.base_dev = &spi->dev;
- at25->nvmem_config.reg_read = at25_ee_read;
- at25->nvmem_config.reg_write = at25_ee_write;
- at25->nvmem_config.priv = at25;
- at25->nvmem_config.stride = 1;
- at25->nvmem_config.word_size = 1;
- at25->nvmem_config.size = at25->chip.byte_len;
-
- at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config);
- if (IS_ERR(at25->nvmem))
- return PTR_ERR(at25->nvmem);
-
- dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n",
- (at25->chip.byte_len < 1024) ?
- at25->chip.byte_len : (at25->chip.byte_len / 1024),
- (at25->chip.byte_len < 1024) ? "Byte" : "KByte",
- at25->chip.name, is_fram ? "fram" : "eeprom",
- (at25->chip.flags & EE_READONLY) ? " (readonly)" : "",
- at25->chip.page_size);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static struct spi_mem_driver at25_driver = {
- .spidrv = {
- .driver = {
- .name = "at25",
- .of_match_table = at25_of_match,
- .dev_groups = at25_groups,
- },
- .id_table = at25_spi_ids,
- },
- .probe = at25_probe,
-};
-
-module_spi_mem_driver(at25_driver);
-
-MODULE_DESCRIPTION("Driver for most SPI EEPROMs");
-MODULE_AUTHOR("David Brownell");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c
deleted file mode 100644
index 923f404a44c0..000000000000
--- a/drivers/misc/eeprom/ee1004.c
+++ /dev/null
@@ -1,351 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * ee1004 - driver for DDR4 SPD EEPROMs
- *
- * Copyright (C) 2017-2019 Jean Delvare
- *
- * Based on the at24 driver:
- * Copyright (C) 2005-2007 David Brownell
- * Copyright (C) 2008 Wolfram Sang, Pengutronix
- */
-
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/nvmem-provider.h>
-
-/*
- * DDR4 memory modules use special EEPROMs following the Jedec EE1004
- * specification. These are 512-byte EEPROMs using a single I2C address
- * in the 0x50-0x57 range for data. One of two 256-byte page is selected
- * by writing a command to I2C address 0x36 or 0x37 on the same I2C bus.
- *
- * Therefore we need to request these 2 additional addresses, and serialize
- * access to all such EEPROMs with a single mutex.
- *
- * We assume it is safe to read up to 32 bytes at once from these EEPROMs.
- * We use SMBus access even if I2C is available, these EEPROMs are small
- * enough, and reading from them infrequent enough, that we favor simplicity
- * over performance.
- */
-
-#define EE1004_MAX_BUSSES 8
-#define EE1004_ADDR_SET_PAGE 0x36
-#define EE1004_NUM_PAGES 2
-#define EE1004_PAGE_SIZE 256
-#define EE1004_PAGE_SHIFT 8
-#define EE1004_EEPROM_SIZE (EE1004_PAGE_SIZE * EE1004_NUM_PAGES)
-
-/*
- * Mutex protects ee1004_set_page and ee1004_dev_count, and must be held
- * from page selection to end of read.
- */
-static DEFINE_MUTEX(ee1004_bus_lock);
-
-static struct ee1004_bus_data {
- struct i2c_adapter *adap;
- struct i2c_client *set_page[EE1004_NUM_PAGES];
- unsigned int dev_count;
- int current_page;
-} ee1004_bus_data[EE1004_MAX_BUSSES];
-
-static const struct i2c_device_id ee1004_ids[] = {
- { "ee1004" },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ee1004_ids);
-
-/*-------------------------------------------------------------------------*/
-
-static struct ee1004_bus_data *ee1004_get_bus_data(struct i2c_adapter *adap)
-{
- int i;
-
- for (i = 0; i < EE1004_MAX_BUSSES; i++)
- if (ee1004_bus_data[i].adap == adap)
- return ee1004_bus_data + i;
-
- /* If not existent yet, create new entry */
- for (i = 0; i < EE1004_MAX_BUSSES; i++)
- if (!ee1004_bus_data[i].adap) {
- ee1004_bus_data[i].adap = adap;
- return ee1004_bus_data + i;
- }
-
- return NULL;
-}
-
-static int ee1004_get_current_page(struct ee1004_bus_data *bd)
-{
- int err;
-
- err = i2c_smbus_read_byte(bd->set_page[0]);
- if (err == -ENXIO) {
- /* Nack means page 1 is selected */
- return 1;
- }
- if (err < 0) {
- /* Anything else is a real error, bail out */
- return err;
- }
-
- /* Ack means page 0 is selected, returned value meaningless */
- return 0;
-}
-
-static int ee1004_set_current_page(struct i2c_client *client, int page)
-{
- struct ee1004_bus_data *bd = i2c_get_clientdata(client);
- int ret;
-
- if (page == bd->current_page)
- return 0;
-
- /* Data is ignored */
- ret = i2c_smbus_write_byte(bd->set_page[page], 0x00);
- /*
- * Don't give up just yet. Some memory modules will select the page
- * but not ack the command. Check which page is selected now.
- */
- if (ret == -ENXIO && ee1004_get_current_page(bd) == page)
- ret = 0;
- if (ret < 0) {
- dev_err(&client->dev, "Failed to select page %d (%d)\n", page, ret);
- return ret;
- }
-
- dev_dbg(&client->dev, "Selected page %d\n", page);
- bd->current_page = page;
-
- return 0;
-}
-
-static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf,
- unsigned int offset, size_t count)
-{
- int status, page;
-
- page = offset >> EE1004_PAGE_SHIFT;
- offset &= (1 << EE1004_PAGE_SHIFT) - 1;
-
- status = ee1004_set_current_page(client, page);
- if (status)
- return status;
-
- /* Can't cross page boundaries */
- if (offset + count > EE1004_PAGE_SIZE)
- count = EE1004_PAGE_SIZE - offset;
-
- if (count > I2C_SMBUS_BLOCK_MAX)
- count = I2C_SMBUS_BLOCK_MAX;
-
- return i2c_smbus_read_i2c_block_data_or_emulated(client, offset, count, buf);
-}
-
-static int ee1004_read(void *priv, unsigned int off, void *val, size_t count)
-{
- struct i2c_client *client = priv;
- char *buf = val;
- int ret;
-
- if (unlikely(!count))
- return count;
-
- if (off + count > EE1004_EEPROM_SIZE)
- return -EINVAL;
-
- /*
- * Read data from chip, protecting against concurrent access to
- * other EE1004 SPD EEPROMs on the same adapter.
- */
- mutex_lock(&ee1004_bus_lock);
-
- while (count) {
- ret = ee1004_eeprom_read(client, buf, off, count);
- if (ret < 0) {
- mutex_unlock(&ee1004_bus_lock);
- return ret;
- }
-
- buf += ret;
- off += ret;
- count -= ret;
- }
-
- mutex_unlock(&ee1004_bus_lock);
-
- return 0;
-}
-
-static void ee1004_probe_temp_sensor(struct i2c_client *client)
-{
- struct i2c_board_info info = { .type = "jc42" };
- unsigned short addr = 0x18 | (client->addr & 7);
- unsigned short addr_list[] = { addr, I2C_CLIENT_END };
- u8 data[2];
- int ret;
-
- /* byte 14, bit 7 is set if temp sensor is present */
- ret = ee1004_eeprom_read(client, data, 14, 1);
- if (ret != 1)
- return;
-
- if (!(data[0] & BIT(7))) {
- /*
- * If the SPD data suggests that there is no temperature
- * sensor, it may still be there for SPD revision 1.0.
- * See SPD Annex L, Revision 1 and 2, for details.
- * Check DIMM type and SPD revision; if it is a DDR4
- * with SPD revision 1.0, check the thermal sensor address
- * and instantiate the jc42 driver if a chip is found at
- * that address.
- * It is not necessary to check if there is a chip at the
- * temperature sensor address since i2c_new_scanned_device()
- * will do that and return silently if no chip is found.
- */
- ret = ee1004_eeprom_read(client, data, 1, 2);
- if (ret != 2 || data[0] != 0x10 || data[1] != 0x0c)
- return;
- }
- i2c_new_scanned_device(client->adapter, &info, addr_list, NULL);
-}
-
-static void ee1004_cleanup(int idx, struct ee1004_bus_data *bd)
-{
- if (--bd->dev_count == 0) {
- while (--idx >= 0)
- i2c_unregister_device(bd->set_page[idx]);
- memset(bd, 0, sizeof(struct ee1004_bus_data));
- }
-}
-
-static void ee1004_cleanup_bus_data(void *data)
-{
- struct ee1004_bus_data *bd = data;
-
- /* Remove page select clients if this is the last device */
- mutex_lock(&ee1004_bus_lock);
- ee1004_cleanup(EE1004_NUM_PAGES, bd);
- mutex_unlock(&ee1004_bus_lock);
-}
-
-static int ee1004_init_bus_data(struct i2c_client *client)
-{
- struct ee1004_bus_data *bd;
- int err, cnr = 0;
-
- bd = ee1004_get_bus_data(client->adapter);
- if (!bd)
- return dev_err_probe(&client->dev, -ENOSPC, "Only %d busses supported",
- EE1004_MAX_BUSSES);
-
- i2c_set_clientdata(client, bd);
-
- if (++bd->dev_count == 1) {
- /* Use 2 dummy devices for page select command */
- for (cnr = 0; cnr < EE1004_NUM_PAGES; cnr++) {
- struct i2c_client *cl;
-
- cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr);
- if (IS_ERR(cl)) {
- err = PTR_ERR(cl);
- goto err_out;
- }
-
- bd->set_page[cnr] = cl;
- }
-
- /* Remember current page to avoid unneeded page select */
- err = ee1004_get_current_page(bd);
- if (err < 0)
- goto err_out;
-
- dev_dbg(&client->dev, "Currently selected page: %d\n", err);
- bd->current_page = err;
- }
-
- return 0;
-
-err_out:
- ee1004_cleanup(cnr, bd);
-
- return err;
-}
-
-static int ee1004_probe(struct i2c_client *client)
-{
- struct nvmem_config config = {
- .dev = &client->dev,
- .name = dev_name(&client->dev),
- .id = NVMEM_DEVID_NONE,
- .owner = THIS_MODULE,
- .type = NVMEM_TYPE_EEPROM,
- .read_only = true,
- .root_only = false,
- .reg_read = ee1004_read,
- .size = EE1004_EEPROM_SIZE,
- .word_size = 1,
- .stride = 1,
- .priv = client,
- .compat = true,
- .base_dev = &client->dev,
- };
- struct nvmem_device *ndev;
- int err;
-
- /* Make sure we can operate on this adapter */
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_I2C_BLOCK) &&
- !i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA))
- return -EPFNOSUPPORT;
-
- err = i2c_smbus_read_byte(client);
- if (err < 0)
- return -ENODEV;
-
- mutex_lock(&ee1004_bus_lock);
-
- err = ee1004_init_bus_data(client);
- if (err < 0) {
- mutex_unlock(&ee1004_bus_lock);
- return err;
- }
-
- ee1004_probe_temp_sensor(client);
-
- mutex_unlock(&ee1004_bus_lock);
-
- err = devm_add_action_or_reset(&client->dev, ee1004_cleanup_bus_data,
- i2c_get_clientdata(client));
- if (err < 0)
- return err;
-
- ndev = devm_nvmem_register(&client->dev, &config);
- if (IS_ERR(ndev))
- return PTR_ERR(ndev);
-
- dev_info(&client->dev,
- "%u byte EE1004-compliant SPD EEPROM, read-only\n",
- EE1004_EEPROM_SIZE);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static struct i2c_driver ee1004_driver = {
- .driver = {
- .name = "ee1004",
- },
- .probe = ee1004_probe,
- .id_table = ee1004_ids,
-};
-module_i2c_driver(ee1004_driver);
-
-MODULE_DESCRIPTION("Driver for EE1004-compliant DDR4 SPD EEPROMs");
-MODULE_AUTHOR("Jean Delvare");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c
deleted file mode 100644
index f9c3ab52c2f9..000000000000
--- a/drivers/misc/eeprom/eeprom_93xx46.c
+++ /dev/null
@@ -1,560 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Driver for 93xx46 EEPROMs
- *
- * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
- */
-
-#include <linux/array_size.h>
-#include <linux/bits.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/gpio/consumer.h>
-#include <linux/kstrtox.h>
-#include <linux/log2.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/property.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/string_choices.h>
-
-#include <linux/nvmem-provider.h>
-
-struct eeprom_93xx46_platform_data {
- unsigned char flags;
-#define EE_ADDR8 0x01 /* 8 bit addr. cfg */
-#define EE_ADDR16 0x02 /* 16 bit addr. cfg */
-#define EE_READONLY 0x08 /* forbid writing */
-#define EE_SIZE1K 0x10 /* 1 kb of data, that is a 93xx46 */
-#define EE_SIZE2K 0x20 /* 2 kb of data, that is a 93xx56 */
-#define EE_SIZE4K 0x40 /* 4 kb of data, that is a 93xx66 */
-
- unsigned int quirks;
-/* Single word read transfers only; no sequential read. */
-#define EEPROM_93XX46_QUIRK_SINGLE_WORD_READ (1 << 0)
-/* Instructions such as EWEN are (addrlen + 2) in length. */
-#define EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH (1 << 1)
-/* Add extra cycle after address during a read */
-#define EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE BIT(2)
-
- struct gpio_desc *select;
-};
-
-#define OP_START 0x4
-#define OP_WRITE (OP_START | 0x1)
-#define OP_READ (OP_START | 0x2)
-/* The following addresses are offset for the 1K EEPROM variant in 16-bit mode */
-#define ADDR_EWDS 0x00
-#define ADDR_ERAL 0x20
-#define ADDR_EWEN 0x30
-
-struct eeprom_93xx46_devtype_data {
- unsigned int quirks;
- unsigned char flags;
-};
-
-static const struct eeprom_93xx46_devtype_data at93c46_data = {
- .flags = EE_SIZE1K,
-};
-
-static const struct eeprom_93xx46_devtype_data at93c56_data = {
- .flags = EE_SIZE2K,
-};
-
-static const struct eeprom_93xx46_devtype_data at93c66_data = {
- .flags = EE_SIZE4K,
-};
-
-static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = {
- .flags = EE_SIZE1K,
- .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ |
- EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH,
-};
-
-static const struct eeprom_93xx46_devtype_data microchip_93lc46b_data = {
- .flags = EE_SIZE1K,
- .quirks = EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE,
-};
-
-struct eeprom_93xx46_dev {
- struct spi_device *spi;
- struct eeprom_93xx46_platform_data *pdata;
- struct mutex lock;
- struct nvmem_config nvmem_config;
- struct nvmem_device *nvmem;
- int addrlen;
- int size;
-};
-
-static inline bool has_quirk_single_word_read(struct eeprom_93xx46_dev *edev)
-{
- return edev->pdata->quirks & EEPROM_93XX46_QUIRK_SINGLE_WORD_READ;
-}
-
-static inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev)
-{
- return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH;
-}
-
-static inline bool has_quirk_extra_read_cycle(struct eeprom_93xx46_dev *edev)
-{
- return edev->pdata->quirks & EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE;
-}
-
-static int eeprom_93xx46_read(void *priv, unsigned int off,
- void *val, size_t count)
-{
- struct eeprom_93xx46_dev *edev = priv;
- char *buf = val;
- int err = 0;
- int bits;
-
- if (unlikely(off >= edev->size))
- return 0;
- if ((off + count) > edev->size)
- count = edev->size - off;
- if (unlikely(!count))
- return count;
-
- mutex_lock(&edev->lock);
-
- gpiod_set_value_cansleep(edev->pdata->select, 1);
-
- /* The opcode in front of the address is three bits. */
- bits = edev->addrlen + 3;
-
- while (count) {
- struct spi_message m;
- struct spi_transfer t[2] = {};
- u16 cmd_addr = OP_READ << edev->addrlen;
- size_t nbytes = count;
-
- if (edev->pdata->flags & EE_ADDR8) {
- cmd_addr |= off;
- if (has_quirk_single_word_read(edev))
- nbytes = 1;
- } else {
- cmd_addr |= (off >> 1);
- if (has_quirk_single_word_read(edev))
- nbytes = 2;
- }
-
- dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
- cmd_addr, edev->spi->max_speed_hz);
-
- if (has_quirk_extra_read_cycle(edev)) {
- cmd_addr <<= 1;
- bits += 1;
- }
-
- t[0].tx_buf = (char *)&cmd_addr;
- t[0].len = 2;
- t[0].bits_per_word = bits;
-
- t[1].rx_buf = buf;
- t[1].len = count;
- t[1].bits_per_word = 8;
-
- spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t));
-
- err = spi_sync(edev->spi, &m);
- /* have to wait at least Tcsl ns */
- ndelay(250);
-
- if (err) {
- dev_err(&edev->spi->dev, "read %zu bytes at %u: err. %d\n",
- nbytes, off, err);
- break;
- }
-
- buf += nbytes;
- off += nbytes;
- count -= nbytes;
- }
-
- gpiod_set_value_cansleep(edev->pdata->select, 0);
-
- mutex_unlock(&edev->lock);
-
- return err;
-}
-
-static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
-{
- struct spi_message m;
- struct spi_transfer t = {};
- int bits, ret;
- u16 cmd_addr;
-
- /* The opcode in front of the address is three bits. */
- bits = edev->addrlen + 3;
-
- cmd_addr = OP_START << edev->addrlen;
- cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << (edev->addrlen - 6);
-
- if (has_quirk_instruction_length(edev)) {
- cmd_addr <<= 2;
- bits += 2;
- }
-
- dev_dbg(&edev->spi->dev, "ew %s cmd 0x%04x, %d bits\n",
- str_enable_disable(is_on), cmd_addr, bits);
-
- t.tx_buf = &cmd_addr;
- t.len = 2;
- t.bits_per_word = bits;
-
- spi_message_init_with_transfers(&m, &t, 1);
-
- mutex_lock(&edev->lock);
-
- gpiod_set_value_cansleep(edev->pdata->select, 1);
-
- ret = spi_sync(edev->spi, &m);
- /* have to wait at least Tcsl ns */
- ndelay(250);
- if (ret)
- dev_err(&edev->spi->dev, "erase/write %s error %d\n",
- str_enable_disable(is_on), ret);
-
- gpiod_set_value_cansleep(edev->pdata->select, 0);
-
- mutex_unlock(&edev->lock);
- return ret;
-}
-
-static ssize_t
-eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
- const char *buf, unsigned int off)
-{
- struct spi_message m;
- struct spi_transfer t[2] = {};
- int bits, data_len, ret;
- u16 cmd_addr;
-
- if (unlikely(off >= edev->size))
- return -EINVAL;
-
- /* The opcode in front of the address is three bits. */
- bits = edev->addrlen + 3;
-
- cmd_addr = OP_WRITE << edev->addrlen;
-
- if (edev->pdata->flags & EE_ADDR8) {
- cmd_addr |= off;
- data_len = 1;
- } else {
- cmd_addr |= (off >> 1);
- data_len = 2;
- }
-
- dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr);
-
- t[0].tx_buf = (char *)&cmd_addr;
- t[0].len = 2;
- t[0].bits_per_word = bits;
-
- t[1].tx_buf = buf;
- t[1].len = data_len;
- t[1].bits_per_word = 8;
-
- spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t));
-
- ret = spi_sync(edev->spi, &m);
- /* have to wait program cycle time Twc ms */
- mdelay(6);
- return ret;
-}
-
-static int eeprom_93xx46_write(void *priv, unsigned int off,
- void *val, size_t count)
-{
- struct eeprom_93xx46_dev *edev = priv;
- char *buf = val;
- int ret, step = 1;
- unsigned int i;
-
- if (unlikely(off >= edev->size))
- return -EFBIG;
- if ((off + count) > edev->size)
- count = edev->size - off;
- if (unlikely(!count))
- return count;
-
- /* only write even number of bytes on 16-bit devices */
- if (edev->pdata->flags & EE_ADDR16) {
- step = 2;
- count &= ~1;
- }
-
- /* erase/write enable */
- ret = eeprom_93xx46_ew(edev, 1);
- if (ret)
- return ret;
-
- mutex_lock(&edev->lock);
-
- gpiod_set_value_cansleep(edev->pdata->select, 1);
-
- for (i = 0; i < count; i += step) {
- ret = eeprom_93xx46_write_word(edev, &buf[i], off + i);
- if (ret) {
- dev_err(&edev->spi->dev, "write failed at %u: %d\n", off + i, ret);
- break;
- }
- }
-
- gpiod_set_value_cansleep(edev->pdata->select, 0);
-
- mutex_unlock(&edev->lock);
-
- /* erase/write disable */
- eeprom_93xx46_ew(edev, 0);
- return ret;
-}
-
-static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
-{
- struct spi_message m;
- struct spi_transfer t = {};
- int bits, ret;
- u16 cmd_addr;
-
- /* The opcode in front of the address is three bits. */
- bits = edev->addrlen + 3;
-
- cmd_addr = OP_START << edev->addrlen;
- cmd_addr |= ADDR_ERAL << (edev->addrlen - 6);
-
- if (has_quirk_instruction_length(edev)) {
- cmd_addr <<= 2;
- bits += 2;
- }
-
- dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits);
-
- t.tx_buf = &cmd_addr;
- t.len = 2;
- t.bits_per_word = bits;
-
- spi_message_init_with_transfers(&m, &t, 1);
-
- mutex_lock(&edev->lock);
-
- gpiod_set_value_cansleep(edev->pdata->select, 1);
-
- ret = spi_sync(edev->spi, &m);
- if (ret)
- dev_err(&edev->spi->dev, "erase error %d\n", ret);
- /* have to wait erase cycle time Tec ms */
- mdelay(6);
-
- gpiod_set_value_cansleep(edev->pdata->select, 0);
-
- mutex_unlock(&edev->lock);
- return ret;
-}
-
-static ssize_t erase_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev);
- bool erase;
- int ret;
-
- ret = kstrtobool(buf, &erase);
- if (ret)
- return ret;
-
- if (erase) {
- ret = eeprom_93xx46_ew(edev, 1);
- if (ret)
- return ret;
- ret = eeprom_93xx46_eral(edev);
- if (ret)
- return ret;
- ret = eeprom_93xx46_ew(edev, 0);
- if (ret)
- return ret;
- }
- return count;
-}
-static DEVICE_ATTR_WO(erase);
-
-static const struct of_device_id eeprom_93xx46_of_table[] = {
- { .compatible = "eeprom-93xx46", .data = &at93c46_data, },
- { .compatible = "atmel,at93c46", .data = &at93c46_data, },
- { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, },
- { .compatible = "atmel,at93c56", .data = &at93c56_data, },
- { .compatible = "atmel,at93c66", .data = &at93c66_data, },
- { .compatible = "microchip,93lc46b", .data = &microchip_93lc46b_data, },
- {}
-};
-MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table);
-
-static const struct spi_device_id eeprom_93xx46_spi_ids[] = {
- { .name = "eeprom-93xx46",
- .driver_data = (kernel_ulong_t)&at93c46_data, },
- { .name = "at93c46",
- .driver_data = (kernel_ulong_t)&at93c46_data, },
- { .name = "at93c46d",
- .driver_data = (kernel_ulong_t)&atmel_at93c46d_data, },
- { .name = "at93c56",
- .driver_data = (kernel_ulong_t)&at93c56_data, },
- { .name = "at93c66",
- .driver_data = (kernel_ulong_t)&at93c66_data, },
- { .name = "93lc46b",
- .driver_data = (kernel_ulong_t)&microchip_93lc46b_data, },
- {}
-};
-MODULE_DEVICE_TABLE(spi, eeprom_93xx46_spi_ids);
-
-static int eeprom_93xx46_probe_fw(struct device *dev)
-{
- const struct eeprom_93xx46_devtype_data *data;
- struct eeprom_93xx46_platform_data *pd;
- u32 tmp;
- int ret;
-
- pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return -ENOMEM;
-
- ret = device_property_read_u32(dev, "data-size", &tmp);
- if (ret < 0) {
- dev_err(dev, "data-size property not found\n");
- return ret;
- }
-
- if (tmp == 8) {
- pd->flags |= EE_ADDR8;
- } else if (tmp == 16) {
- pd->flags |= EE_ADDR16;
- } else {
- dev_err(dev, "invalid data-size (%d)\n", tmp);
- return -EINVAL;
- }
-
- if (device_property_read_bool(dev, "read-only"))
- pd->flags |= EE_READONLY;
-
- pd->select = devm_gpiod_get_optional(dev, "select", GPIOD_OUT_LOW);
- if (IS_ERR(pd->select))
- return PTR_ERR(pd->select);
- gpiod_set_consumer_name(pd->select, "93xx46 EEPROMs OE");
-
- data = spi_get_device_match_data(to_spi_device(dev));
- if (data) {
- pd->quirks = data->quirks;
- pd->flags |= data->flags;
- }
-
- dev->platform_data = pd;
-
- return 0;
-}
-
-static int eeprom_93xx46_probe(struct spi_device *spi)
-{
- struct eeprom_93xx46_platform_data *pd;
- struct eeprom_93xx46_dev *edev;
- struct device *dev = &spi->dev;
- int err;
-
- err = eeprom_93xx46_probe_fw(dev);
- if (err < 0)
- return err;
-
- pd = spi->dev.platform_data;
- if (!pd) {
- dev_err(&spi->dev, "missing platform data\n");
- return -ENODEV;
- }
-
- edev = devm_kzalloc(&spi->dev, sizeof(*edev), GFP_KERNEL);
- if (!edev)
- return -ENOMEM;
-
- if (pd->flags & EE_SIZE1K)
- edev->size = 128;
- else if (pd->flags & EE_SIZE2K)
- edev->size = 256;
- else if (pd->flags & EE_SIZE4K)
- edev->size = 512;
- else {
- dev_err(&spi->dev, "unspecified size\n");
- return -EINVAL;
- }
-
- if (pd->flags & EE_ADDR8)
- edev->addrlen = ilog2(edev->size);
- else if (pd->flags & EE_ADDR16)
- edev->addrlen = ilog2(edev->size) - 1;
- else {
- dev_err(&spi->dev, "unspecified address type\n");
- return -EINVAL;
- }
-
- mutex_init(&edev->lock);
-
- edev->spi = spi;
- edev->pdata = pd;
-
- edev->nvmem_config.type = NVMEM_TYPE_EEPROM;
- edev->nvmem_config.name = dev_name(&spi->dev);
- edev->nvmem_config.dev = &spi->dev;
- edev->nvmem_config.read_only = pd->flags & EE_READONLY;
- edev->nvmem_config.root_only = true;
- edev->nvmem_config.owner = THIS_MODULE;
- edev->nvmem_config.compat = true;
- edev->nvmem_config.base_dev = &spi->dev;
- edev->nvmem_config.reg_read = eeprom_93xx46_read;
- edev->nvmem_config.reg_write = eeprom_93xx46_write;
- edev->nvmem_config.priv = edev;
- edev->nvmem_config.stride = 4;
- edev->nvmem_config.word_size = 1;
- edev->nvmem_config.size = edev->size;
-
- edev->nvmem = devm_nvmem_register(&spi->dev, &edev->nvmem_config);
- if (IS_ERR(edev->nvmem))
- return PTR_ERR(edev->nvmem);
-
- dev_info(&spi->dev, "%d-bit eeprom containing %d bytes %s\n",
- (pd->flags & EE_ADDR8) ? 8 : 16,
- edev->size,
- (pd->flags & EE_READONLY) ? "(readonly)" : "");
-
- if (!(pd->flags & EE_READONLY)) {
- if (device_create_file(&spi->dev, &dev_attr_erase))
- dev_err(&spi->dev, "can't create erase interface\n");
- }
-
- spi_set_drvdata(spi, edev);
- return 0;
-}
-
-static void eeprom_93xx46_remove(struct spi_device *spi)
-{
- struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi);
-
- if (!(edev->pdata->flags & EE_READONLY))
- device_remove_file(&spi->dev, &dev_attr_erase);
-}
-
-static struct spi_driver eeprom_93xx46_driver = {
- .driver = {
- .name = "93xx46",
- .of_match_table = eeprom_93xx46_of_table,
- },
- .probe = eeprom_93xx46_probe,
- .remove = eeprom_93xx46_remove,
- .id_table = eeprom_93xx46_spi_ids,
-};
-
-module_spi_driver(eeprom_93xx46_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
-MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
-MODULE_ALIAS("spi:93xx46");
diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c
index e056d2dea8c3..ea7478cfdd27 100644
--- a/drivers/misc/eeprom/idt_89hpesx.c
+++ b/drivers/misc/eeprom/idt_89hpesx.c
@@ -1375,12 +1375,12 @@ static void idt_remove(struct i2c_client *client)
* ee_ids - array of supported EEPROMs
*/
static const struct i2c_device_id ee_ids[] = {
- { "24c32", 4096},
- { "24c64", 8192},
- { "24c128", 16384},
- { "24c256", 32768},
- { "24c512", 65536},
- {}
+ { .name = "24c32", .driver_data = 4096 },
+ { .name = "24c64", .driver_data = 8192 },
+ { .name = "24c128", .driver_data = 16384 },
+ { .name = "24c256", .driver_data = 32768 },
+ { .name = "24c512", .driver_data = 65536 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ee_ids);
@@ -1388,115 +1388,115 @@ MODULE_DEVICE_TABLE(i2c, ee_ids);
* idt_ids - supported IDT 89HPESx devices
*/
static const struct i2c_device_id idt_ids[] = {
- { "89hpes8nt2" },
- { "89hpes12nt3" },
-
- { "89hpes24nt6ag2" },
- { "89hpes32nt8ag2" },
- { "89hpes32nt8bg2" },
- { "89hpes12nt12g2" },
- { "89hpes16nt16g2" },
- { "89hpes24nt24g2" },
- { "89hpes32nt24ag2" },
- { "89hpes32nt24bg2" },
-
- { "89hpes12n3" },
- { "89hpes12n3a" },
- { "89hpes24n3" },
- { "89hpes24n3a" },
-
- { "89hpes32h8" },
- { "89hpes32h8g2" },
- { "89hpes48h12" },
- { "89hpes48h12g2" },
- { "89hpes48h12ag2" },
- { "89hpes16h16" },
- { "89hpes22h16" },
- { "89hpes22h16g2" },
- { "89hpes34h16" },
- { "89hpes34h16g2" },
- { "89hpes64h16" },
- { "89hpes64h16g2" },
- { "89hpes64h16ag2" },
-
- /* { "89hpes3t3" }, // No SMBus-slave iface */
- { "89hpes12t3g2" },
- { "89hpes24t3g2" },
- /* { "89hpes4t4" }, // No SMBus-slave iface */
- { "89hpes16t4" },
- { "89hpes4t4g2" },
- { "89hpes10t4g2" },
- { "89hpes16t4g2" },
- { "89hpes16t4ag2" },
- { "89hpes5t5" },
- { "89hpes6t5" },
- { "89hpes8t5" },
- { "89hpes8t5a" },
- { "89hpes24t6" },
- { "89hpes6t6g2" },
- { "89hpes24t6g2" },
- { "89hpes16t7" },
- { "89hpes32t8" },
- { "89hpes32t8g2" },
- { "89hpes48t12" },
- { "89hpes48t12g2" },
+ { .name = "89hpes8nt2" },
+ { .name = "89hpes12nt3" },
+
+ { .name = "89hpes24nt6ag2" },
+ { .name = "89hpes32nt8ag2" },
+ { .name = "89hpes32nt8bg2" },
+ { .name = "89hpes12nt12g2" },
+ { .name = "89hpes16nt16g2" },
+ { .name = "89hpes24nt24g2" },
+ { .name = "89hpes32nt24ag2" },
+ { .name = "89hpes32nt24bg2" },
+
+ { .name = "89hpes12n3" },
+ { .name = "89hpes12n3a" },
+ { .name = "89hpes24n3" },
+ { .name = "89hpes24n3a" },
+
+ { .name = "89hpes32h8" },
+ { .name = "89hpes32h8g2" },
+ { .name = "89hpes48h12" },
+ { .name = "89hpes48h12g2" },
+ { .name = "89hpes48h12ag2" },
+ { .name = "89hpes16h16" },
+ { .name = "89hpes22h16" },
+ { .name = "89hpes22h16g2" },
+ { .name = "89hpes34h16" },
+ { .name = "89hpes34h16g2" },
+ { .name = "89hpes64h16" },
+ { .name = "89hpes64h16g2" },
+ { .name = "89hpes64h16ag2" },
+
+ /* { .name = "89hpes3t3" }, // No SMBus-slave iface */
+ { .name = "89hpes12t3g2" },
+ { .name = "89hpes24t3g2" },
+ /* { .name = "89hpes4t4" }, // No SMBus-slave iface */
+ { .name = "89hpes16t4" },
+ { .name = "89hpes4t4g2" },
+ { .name = "89hpes10t4g2" },
+ { .name = "89hpes16t4g2" },
+ { .name = "89hpes16t4ag2" },
+ { .name = "89hpes5t5" },
+ { .name = "89hpes6t5" },
+ { .name = "89hpes8t5" },
+ { .name = "89hpes8t5a" },
+ { .name = "89hpes24t6" },
+ { .name = "89hpes6t6g2" },
+ { .name = "89hpes24t6g2" },
+ { .name = "89hpes16t7" },
+ { .name = "89hpes32t8" },
+ { .name = "89hpes32t8g2" },
+ { .name = "89hpes48t12" },
+ { .name = "89hpes48t12g2" },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, idt_ids);
static const struct of_device_id idt_of_match[] = {
- { .compatible = "idt,89hpes8nt2", },
- { .compatible = "idt,89hpes12nt3", },
-
- { .compatible = "idt,89hpes24nt6ag2", },
- { .compatible = "idt,89hpes32nt8ag2", },
- { .compatible = "idt,89hpes32nt8bg2", },
- { .compatible = "idt,89hpes12nt12g2", },
- { .compatible = "idt,89hpes16nt16g2", },
- { .compatible = "idt,89hpes24nt24g2", },
- { .compatible = "idt,89hpes32nt24ag2", },
- { .compatible = "idt,89hpes32nt24bg2", },
-
- { .compatible = "idt,89hpes12n3", },
- { .compatible = "idt,89hpes12n3a", },
- { .compatible = "idt,89hpes24n3", },
- { .compatible = "idt,89hpes24n3a", },
-
- { .compatible = "idt,89hpes32h8", },
- { .compatible = "idt,89hpes32h8g2", },
- { .compatible = "idt,89hpes48h12", },
- { .compatible = "idt,89hpes48h12g2", },
- { .compatible = "idt,89hpes48h12ag2", },
- { .compatible = "idt,89hpes16h16", },
- { .compatible = "idt,89hpes22h16", },
- { .compatible = "idt,89hpes22h16g2", },
- { .compatible = "idt,89hpes34h16", },
- { .compatible = "idt,89hpes34h16g2", },
- { .compatible = "idt,89hpes64h16", },
- { .compatible = "idt,89hpes64h16g2", },
- { .compatible = "idt,89hpes64h16ag2", },
-
- { .compatible = "idt,89hpes12t3g2", },
- { .compatible = "idt,89hpes24t3g2", },
-
- { .compatible = "idt,89hpes16t4", },
- { .compatible = "idt,89hpes4t4g2", },
- { .compatible = "idt,89hpes10t4g2", },
- { .compatible = "idt,89hpes16t4g2", },
- { .compatible = "idt,89hpes16t4ag2", },
- { .compatible = "idt,89hpes5t5", },
- { .compatible = "idt,89hpes6t5", },
- { .compatible = "idt,89hpes8t5", },
- { .compatible = "idt,89hpes8t5a", },
- { .compatible = "idt,89hpes24t6", },
- { .compatible = "idt,89hpes6t6g2", },
- { .compatible = "idt,89hpes24t6g2", },
- { .compatible = "idt,89hpes16t7", },
- { .compatible = "idt,89hpes32t8", },
- { .compatible = "idt,89hpes32t8g2", },
- { .compatible = "idt,89hpes48t12", },
- { .compatible = "idt,89hpes48t12g2", },
- { },
+ { .compatible = "idt,89hpes8nt2" },
+ { .compatible = "idt,89hpes12nt3" },
+
+ { .compatible = "idt,89hpes24nt6ag2" },
+ { .compatible = "idt,89hpes32nt8ag2" },
+ { .compatible = "idt,89hpes32nt8bg2" },
+ { .compatible = "idt,89hpes12nt12g2" },
+ { .compatible = "idt,89hpes16nt16g2" },
+ { .compatible = "idt,89hpes24nt24g2" },
+ { .compatible = "idt,89hpes32nt24ag2" },
+ { .compatible = "idt,89hpes32nt24bg2" },
+
+ { .compatible = "idt,89hpes12n3" },
+ { .compatible = "idt,89hpes12n3a" },
+ { .compatible = "idt,89hpes24n3" },
+ { .compatible = "idt,89hpes24n3a" },
+
+ { .compatible = "idt,89hpes32h8" },
+ { .compatible = "idt,89hpes32h8g2" },
+ { .compatible = "idt,89hpes48h12" },
+ { .compatible = "idt,89hpes48h12g2" },
+ { .compatible = "idt,89hpes48h12ag2" },
+ { .compatible = "idt,89hpes16h16" },
+ { .compatible = "idt,89hpes22h16" },
+ { .compatible = "idt,89hpes22h16g2" },
+ { .compatible = "idt,89hpes34h16" },
+ { .compatible = "idt,89hpes34h16g2" },
+ { .compatible = "idt,89hpes64h16" },
+ { .compatible = "idt,89hpes64h16g2" },
+ { .compatible = "idt,89hpes64h16ag2" },
+
+ { .compatible = "idt,89hpes12t3g2" },
+ { .compatible = "idt,89hpes24t3g2" },
+
+ { .compatible = "idt,89hpes16t4" },
+ { .compatible = "idt,89hpes4t4g2" },
+ { .compatible = "idt,89hpes10t4g2" },
+ { .compatible = "idt,89hpes16t4g2" },
+ { .compatible = "idt,89hpes16t4ag2" },
+ { .compatible = "idt,89hpes5t5" },
+ { .compatible = "idt,89hpes6t5" },
+ { .compatible = "idt,89hpes8t5" },
+ { .compatible = "idt,89hpes8t5a" },
+ { .compatible = "idt,89hpes24t6" },
+ { .compatible = "idt,89hpes6t6g2" },
+ { .compatible = "idt,89hpes24t6g2" },
+ { .compatible = "idt,89hpes16t7" },
+ { .compatible = "idt,89hpes32t8" },
+ { .compatible = "idt,89hpes32t8g2" },
+ { .compatible = "idt,89hpes48t12" },
+ { .compatible = "idt,89hpes48t12g2" },
+ { }
};
MODULE_DEVICE_TABLE(of, idt_of_match);
diff --git a/drivers/misc/eeprom/m24lr.c b/drivers/misc/eeprom/m24lr.c
deleted file mode 100644
index 7a9fd45a8e46..000000000000
--- a/drivers/misc/eeprom/m24lr.c
+++ /dev/null
@@ -1,606 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * m24lr.c - Sysfs control interface for ST M24LR series RFID/NFC chips
- *
- * Copyright (c) 2025 Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
- *
- * This driver implements both the sysfs-based control interface and EEPROM
- * access for STMicroelectronics M24LR series chips (e.g., M24LR04E-R).
- * It provides access to control registers for features such as password
- * authentication, memory protection, and device configuration. In addition,
- * it manages read and write operations to the EEPROM region of the chip.
- */
-
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/nvmem-provider.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/regmap.h>
-
-#define M24LR_WRITE_TIMEOUT 25u
-#define M24LR_READ_TIMEOUT (M24LR_WRITE_TIMEOUT)
-
-/**
- * struct m24lr_chip - describes chip-specific sysfs layout
- * @sss_len: the length of the sss region
- * @page_size: chip-specific limit on the maximum number of bytes allowed
- * in a single write operation.
- * @eeprom_size: size of the EEPROM in byte
- *
- * Supports multiple M24LR chip variants (e.g., M24LRxx) by allowing each
- * to define its own set of sysfs attributes, depending on its available
- * registers and features.
- */
-struct m24lr_chip {
- unsigned int sss_len;
- unsigned int page_size;
- unsigned int eeprom_size;
-};
-
-/**
- * struct m24lr - core driver data for M24LR chip control
- * @uid: 64 bits unique identifier stored in the device
- * @sss_len: the length of the sss region
- * @page_size: chip-specific limit on the maximum number of bytes allowed
- * in a single write operation.
- * @eeprom_size: size of the EEPROM in byte
- * @ctl_regmap: regmap interface for accessing the system parameter sector
- * @eeprom_regmap: regmap interface for accessing the EEPROM
- * @lock: mutex to synchronize operations to the device
- *
- * Central data structure holding the state and resources used by the
- * M24LR device driver.
- */
-struct m24lr {
- u64 uid;
- unsigned int sss_len;
- unsigned int page_size;
- unsigned int eeprom_size;
- struct regmap *ctl_regmap;
- struct regmap *eeprom_regmap;
- struct mutex lock; /* synchronize operations to the device */
-};
-
-static const struct regmap_range m24lr_ctl_vo_ranges[] = {
- regmap_reg_range(0, 63),
-};
-
-static const struct regmap_access_table m24lr_ctl_vo_table = {
- .yes_ranges = m24lr_ctl_vo_ranges,
- .n_yes_ranges = ARRAY_SIZE(m24lr_ctl_vo_ranges),
-};
-
-static const struct regmap_config m24lr_ctl_regmap_conf = {
- .name = "m24lr_ctl",
- .reg_stride = 1,
- .reg_bits = 16,
- .val_bits = 8,
- .disable_locking = false,
- .cache_type = REGCACHE_RBTREE,/* Flat can't be used, there's huge gap */
- .volatile_table = &m24lr_ctl_vo_table,
-};
-
-/* Chip descriptor for M24LR04E-R variant */
-static const struct m24lr_chip m24lr04e_r_chip = {
- .page_size = 4,
- .eeprom_size = 512,
- .sss_len = 4,
-};
-
-/* Chip descriptor for M24LR16E-R variant */
-static const struct m24lr_chip m24lr16e_r_chip = {
- .page_size = 4,
- .eeprom_size = 2048,
- .sss_len = 16,
-};
-
-/* Chip descriptor for M24LR64E-R variant */
-static const struct m24lr_chip m24lr64e_r_chip = {
- .page_size = 4,
- .eeprom_size = 8192,
- .sss_len = 64,
-};
-
-static const struct i2c_device_id m24lr_ids[] = {
- { "m24lr04e-r", (kernel_ulong_t)&m24lr04e_r_chip},
- { "m24lr16e-r", (kernel_ulong_t)&m24lr16e_r_chip},
- { "m24lr64e-r", (kernel_ulong_t)&m24lr64e_r_chip},
- { }
-};
-MODULE_DEVICE_TABLE(i2c, m24lr_ids);
-
-static const struct of_device_id m24lr_of_match[] = {
- { .compatible = "st,m24lr04e-r", .data = &m24lr04e_r_chip},
- { .compatible = "st,m24lr16e-r", .data = &m24lr16e_r_chip},
- { .compatible = "st,m24lr64e-r", .data = &m24lr64e_r_chip},
- { }
-};
-MODULE_DEVICE_TABLE(of, m24lr_of_match);
-
-/**
- * m24lr_regmap_read - read data using regmap with retry on failure
- * @regmap: regmap instance for the device
- * @buf: buffer to store the read data
- * @size: number of bytes to read
- * @offset: starting register address
- *
- * Attempts to read a block of data from the device with retries and timeout.
- * Some M24LR chips may transiently NACK reads (e.g., during internal write
- * cycles), so this function retries with a short sleep until the timeout
- * expires.
- *
- * Returns:
- * Number of bytes read on success,
- * -ETIMEDOUT if the read fails within the timeout window.
- */
-static ssize_t m24lr_regmap_read(struct regmap *regmap, u8 *buf,
- size_t size, unsigned int offset)
-{
- int err;
- unsigned long timeout, read_time;
- ssize_t ret = -ETIMEDOUT;
-
- timeout = jiffies + msecs_to_jiffies(M24LR_READ_TIMEOUT);
- do {
- read_time = jiffies;
-
- err = regmap_bulk_read(regmap, offset, buf, size);
- if (!err) {
- ret = size;
- break;
- }
-
- usleep_range(1000, 2000);
- } while (time_before(read_time, timeout));
-
- return ret;
-}
-
-/**
- * m24lr_regmap_write - write data using regmap with retry on failure
- * @regmap: regmap instance for the device
- * @buf: buffer containing the data to write
- * @size: number of bytes to write
- * @offset: starting register address
- *
- * Attempts to write a block of data to the device with retries and a timeout.
- * Some M24LR devices may NACK I2C writes while an internal write operation
- * is in progress. This function retries the write operation with a short delay
- * until it succeeds or the timeout is reached.
- *
- * Returns:
- * Number of bytes written on success,
- * -ETIMEDOUT if the write fails within the timeout window.
- */
-static ssize_t m24lr_regmap_write(struct regmap *regmap, const u8 *buf,
- size_t size, unsigned int offset)
-{
- int err;
- unsigned long timeout, write_time;
- ssize_t ret = -ETIMEDOUT;
-
- timeout = jiffies + msecs_to_jiffies(M24LR_WRITE_TIMEOUT);
-
- do {
- write_time = jiffies;
-
- err = regmap_bulk_write(regmap, offset, buf, size);
- if (!err) {
- ret = size;
- break;
- }
-
- usleep_range(1000, 2000);
- } while (time_before(write_time, timeout));
-
- return ret;
-}
-
-static ssize_t m24lr_read(struct m24lr *m24lr, u8 *buf, size_t size,
- unsigned int offset, bool is_eeprom)
-{
- struct regmap *regmap;
- ssize_t ret;
-
- if (is_eeprom)
- regmap = m24lr->eeprom_regmap;
- else
- regmap = m24lr->ctl_regmap;
-
- mutex_lock(&m24lr->lock);
- ret = m24lr_regmap_read(regmap, buf, size, offset);
- mutex_unlock(&m24lr->lock);
-
- return ret;
-}
-
-/**
- * m24lr_write - write buffer to M24LR device with page alignment handling
- * @m24lr: pointer to driver context
- * @buf: data buffer to write
- * @size: number of bytes to write
- * @offset: target register address in the device
- * @is_eeprom: true if the write should target the EEPROM,
- * false if it should target the system parameters sector.
- *
- * Writes data to the M24LR device using regmap, split into chunks no larger
- * than page_size to respect device-specific write limitations (e.g., page
- * size or I2C hold-time concerns). Each chunk is aligned to the page boundary
- * defined by page_size.
- *
- * Returns:
- * Total number of bytes written on success,
- * A negative error code if any write fails.
- */
-static ssize_t m24lr_write(struct m24lr *m24lr, const u8 *buf, size_t size,
- unsigned int offset, bool is_eeprom)
-{
- unsigned int n, next_sector;
- struct regmap *regmap;
- ssize_t ret = 0;
- ssize_t err;
-
- if (is_eeprom)
- regmap = m24lr->eeprom_regmap;
- else
- regmap = m24lr->ctl_regmap;
-
- n = min_t(unsigned int, size, m24lr->page_size);
- next_sector = roundup(offset + 1, m24lr->page_size);
- if (offset + n > next_sector)
- n = next_sector - offset;
-
- mutex_lock(&m24lr->lock);
- while (n) {
- err = m24lr_regmap_write(regmap, buf + offset, n, offset);
- if (IS_ERR_VALUE(err)) {
- if (!ret)
- ret = err;
-
- break;
- }
-
- offset += n;
- size -= n;
- ret += n;
- n = min_t(unsigned int, size, m24lr->page_size);
- }
- mutex_unlock(&m24lr->lock);
-
- return ret;
-}
-
-/**
- * m24lr_write_pass - Write password to M24LR043-R using secure format
- * @m24lr: Pointer to device control structure
- * @buf: Input buffer containing hex-encoded password
- * @count: Number of bytes in @buf
- * @code: Operation code to embed between password copies
- *
- * This function parses a 4-byte password, encodes it in big-endian format,
- * and constructs a 9-byte sequence of the form:
- *
- * [BE(password), code, BE(password)]
- *
- * The result is written to register 0x0900 (2304), which is the password
- * register in M24LR04E-R chip.
- *
- * Return: Number of bytes written on success, or negative error code on failure
- */
-static ssize_t m24lr_write_pass(struct m24lr *m24lr, const char *buf,
- size_t count, u8 code)
-{
- __be32 be_pass;
- u8 output[9];
- ssize_t ret;
- u32 pass;
- int err;
-
- if (!count)
- return -EINVAL;
-
- if (count > 8)
- return -EINVAL;
-
- err = kstrtou32(buf, 16, &pass);
- if (err)
- return err;
-
- be_pass = cpu_to_be32(pass);
-
- memcpy(output, &be_pass, sizeof(be_pass));
- output[4] = code;
- memcpy(output + 5, &be_pass, sizeof(be_pass));
-
- mutex_lock(&m24lr->lock);
- ret = m24lr_regmap_write(m24lr->ctl_regmap, output, 9, 2304);
- mutex_unlock(&m24lr->lock);
-
- return ret;
-}
-
-static ssize_t m24lr_read_reg_le(struct m24lr *m24lr, u64 *val,
- unsigned int reg_addr,
- unsigned int reg_size)
-{
- ssize_t ret;
- __le64 input = 0;
-
- ret = m24lr_read(m24lr, (u8 *)&input, reg_size, reg_addr, false);
- if (IS_ERR_VALUE(ret))
- return ret;
-
- if (ret != reg_size)
- return -EINVAL;
-
- switch (reg_size) {
- case 1:
- *val = *(u8 *)&input;
- break;
- case 2:
- *val = le16_to_cpu((__le16)input);
- break;
- case 4:
- *val = le32_to_cpu((__le32)input);
- break;
- case 8:
- *val = le64_to_cpu((__le64)input);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int m24lr_nvmem_read(void *priv, unsigned int offset, void *val,
- size_t bytes)
-{
- ssize_t err;
- struct m24lr *m24lr = priv;
-
- if (!bytes)
- return bytes;
-
- if (offset + bytes > m24lr->eeprom_size)
- return -EINVAL;
-
- err = m24lr_read(m24lr, val, bytes, offset, true);
- if (IS_ERR_VALUE(err))
- return err;
-
- return 0;
-}
-
-static int m24lr_nvmem_write(void *priv, unsigned int offset, void *val,
- size_t bytes)
-{
- ssize_t err;
- struct m24lr *m24lr = priv;
-
- if (!bytes)
- return -EINVAL;
-
- if (offset + bytes > m24lr->eeprom_size)
- return -EINVAL;
-
- err = m24lr_write(m24lr, val, bytes, offset, true);
- if (IS_ERR_VALUE(err))
- return err;
-
- return 0;
-}
-
-static ssize_t m24lr_ctl_sss_read(struct file *filep, struct kobject *kobj,
- const struct bin_attribute *attr, char *buf,
- loff_t offset, size_t count)
-{
- struct m24lr *m24lr = attr->private;
-
- if (!count)
- return count;
-
- if (size_add(offset, count) > m24lr->sss_len)
- return -EINVAL;
-
- return m24lr_read(m24lr, buf, count, offset, false);
-}
-
-static ssize_t m24lr_ctl_sss_write(struct file *filep, struct kobject *kobj,
- const struct bin_attribute *attr, char *buf,
- loff_t offset, size_t count)
-{
- struct m24lr *m24lr = attr->private;
-
- if (!count)
- return -EINVAL;
-
- if (size_add(offset, count) > m24lr->sss_len)
- return -EINVAL;
-
- return m24lr_write(m24lr, buf, count, offset, false);
-}
-static BIN_ATTR(sss, 0600, m24lr_ctl_sss_read, m24lr_ctl_sss_write, 0);
-
-static ssize_t new_pass_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
-
- return m24lr_write_pass(m24lr, buf, count, 7);
-}
-static DEVICE_ATTR_WO(new_pass);
-
-static ssize_t unlock_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
-
- return m24lr_write_pass(m24lr, buf, count, 9);
-}
-static DEVICE_ATTR_WO(unlock);
-
-static ssize_t uid_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
-
- return sysfs_emit(buf, "%llx\n", m24lr->uid);
-}
-static DEVICE_ATTR_RO(uid);
-
-static ssize_t total_sectors_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
-
- return sysfs_emit(buf, "%x\n", m24lr->sss_len);
-}
-static DEVICE_ATTR_RO(total_sectors);
-
-static struct attribute *m24lr_ctl_dev_attrs[] = {
- &dev_attr_unlock.attr,
- &dev_attr_new_pass.attr,
- &dev_attr_uid.attr,
- &dev_attr_total_sectors.attr,
- NULL,
-};
-
-static const struct m24lr_chip *m24lr_get_chip(struct device *dev)
-{
- const struct m24lr_chip *ret;
- const struct i2c_device_id *id;
-
- id = i2c_match_id(m24lr_ids, to_i2c_client(dev));
-
- if (dev->of_node && of_match_device(m24lr_of_match, dev))
- ret = of_device_get_match_data(dev);
- else if (id)
- ret = (void *)id->driver_data;
- else
- ret = acpi_device_get_match_data(dev);
-
- return ret;
-}
-
-static int m24lr_probe(struct i2c_client *client)
-{
- struct regmap_config eeprom_regmap_conf = {0};
- struct nvmem_config nvmem_conf = {0};
- struct device *dev = &client->dev;
- struct i2c_client *eeprom_client;
- const struct m24lr_chip *chip;
- struct regmap *eeprom_regmap;
- struct nvmem_device *nvmem;
- struct regmap *ctl_regmap;
- struct m24lr *m24lr;
- u32 regs[2];
- long err;
-
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
- return -EOPNOTSUPP;
-
- chip = m24lr_get_chip(dev);
- if (!chip)
- return -ENODEV;
-
- m24lr = devm_kzalloc(dev, sizeof(struct m24lr), GFP_KERNEL);
- if (!m24lr)
- return -ENOMEM;
-
- err = device_property_read_u32_array(dev, "reg", regs, ARRAY_SIZE(regs));
- if (err)
- return dev_err_probe(dev, err, "Failed to read 'reg' property\n");
-
- /* Create a second I2C client for the eeprom interface */
- eeprom_client = devm_i2c_new_dummy_device(dev, client->adapter, regs[1]);
- if (IS_ERR(eeprom_client))
- return dev_err_probe(dev, PTR_ERR(eeprom_client),
- "Failed to create dummy I2C client for the EEPROM\n");
-
- ctl_regmap = devm_regmap_init_i2c(client, &m24lr_ctl_regmap_conf);
- if (IS_ERR(ctl_regmap))
- return dev_err_probe(dev, PTR_ERR(ctl_regmap),
- "Failed to init regmap\n");
-
- eeprom_regmap_conf.name = "m24lr_eeprom";
- eeprom_regmap_conf.reg_bits = 16;
- eeprom_regmap_conf.val_bits = 8;
- eeprom_regmap_conf.disable_locking = true;
- eeprom_regmap_conf.max_register = chip->eeprom_size - 1;
-
- eeprom_regmap = devm_regmap_init_i2c(eeprom_client,
- &eeprom_regmap_conf);
- if (IS_ERR(eeprom_regmap))
- return dev_err_probe(dev, PTR_ERR(eeprom_regmap),
- "Failed to init regmap\n");
-
- mutex_init(&m24lr->lock);
- m24lr->sss_len = chip->sss_len;
- m24lr->page_size = chip->page_size;
- m24lr->eeprom_size = chip->eeprom_size;
- m24lr->eeprom_regmap = eeprom_regmap;
- m24lr->ctl_regmap = ctl_regmap;
-
- nvmem_conf.dev = &eeprom_client->dev;
- nvmem_conf.owner = THIS_MODULE;
- nvmem_conf.type = NVMEM_TYPE_EEPROM;
- nvmem_conf.reg_read = m24lr_nvmem_read;
- nvmem_conf.reg_write = m24lr_nvmem_write;
- nvmem_conf.size = chip->eeprom_size;
- nvmem_conf.word_size = 1;
- nvmem_conf.stride = 1;
- nvmem_conf.priv = m24lr;
-
- nvmem = devm_nvmem_register(dev, &nvmem_conf);
- if (IS_ERR(nvmem))
- return dev_err_probe(dev, PTR_ERR(nvmem),
- "Failed to register nvmem\n");
-
- i2c_set_clientdata(client, m24lr);
- i2c_set_clientdata(eeprom_client, m24lr);
-
- bin_attr_sss.size = chip->sss_len;
- bin_attr_sss.private = m24lr;
- err = sysfs_create_bin_file(&dev->kobj, &bin_attr_sss);
- if (err)
- return dev_err_probe(dev, err,
- "Failed to create sss bin file\n");
-
- /* test by reading the uid, if success store it */
- err = m24lr_read_reg_le(m24lr, &m24lr->uid, 2324, sizeof(m24lr->uid));
- if (IS_ERR_VALUE(err))
- goto remove_bin_file;
-
- return 0;
-
-remove_bin_file:
- sysfs_remove_bin_file(&dev->kobj, &bin_attr_sss);
-
- return err;
-}
-
-static void m24lr_remove(struct i2c_client *client)
-{
- sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_sss);
-}
-
-ATTRIBUTE_GROUPS(m24lr_ctl_dev);
-
-static struct i2c_driver m24lr_driver = {
- .driver = {
- .name = "m24lr",
- .of_match_table = m24lr_of_match,
- .dev_groups = m24lr_ctl_dev_groups,
- },
- .probe = m24lr_probe,
- .remove = m24lr_remove,
- .id_table = m24lr_ids,
-};
-module_i2c_driver(m24lr_driver);
-
-MODULE_AUTHOR("Abd-Alrhman Masalkhi");
-MODULE_DESCRIPTION("st m24lr control driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c
index 5731d2dc8a57..be5e6d19be10 100644
--- a/drivers/misc/eeprom/max6875.c
+++ b/drivers/misc/eeprom/max6875.c
@@ -183,7 +183,7 @@ static void max6875_remove(struct i2c_client *client)
}
static const struct i2c_device_id max6875_id[] = {
- { "max6875" },
+ { .name = "max6875" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6875_id);
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
index cf6382981777..de457378c501 100644
--- a/drivers/misc/enclosure.c
+++ b/drivers/misc/enclosure.c
@@ -184,8 +184,8 @@ EXPORT_SYMBOL_GPL(enclosure_unregister);
static void enclosure_link_name(struct enclosure_component *cdev, char *name)
{
- strcpy(name, "enclosure_device:");
- strcat(name, dev_name(&cdev->cdev));
+ snprintf(name, ENCLOSURE_NAME_SIZE, "enclosure_device:%s",
+ dev_name(&cdev->cdev));
}
static void enclosure_remove_links(struct enclosure_component *cdev)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index eb6c2a78d3c7..90fd669636ec 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -22,7 +22,11 @@
#include <linux/firmware/qcom/qcom_scm.h>
#include <uapi/misc/fastrpc.h>
#include <linux/of_reserved_mem.h>
+#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/iopoll.h>
#define ADSP_DOMAIN_ID (0)
#define MDSP_DOMAIN_ID (1)
@@ -37,7 +41,27 @@
#define FASTRPC_CTX_MAX (256)
#define FASTRPC_INIT_HANDLE 1
#define FASTRPC_DSP_UTILITIES_HANDLE 2
-#define FASTRPC_CTXID_MASK (0xFF0)
+/*
+ * Maximum handle value for static handles.
+ * Static handles are pre-defined, fixed numeric values statically assigned
+ * in the IDL file or FastRPC framework.
+ */
+#define FASTRPC_MAX_STATIC_HANDLE (20)
+#define FASTRPC_CTXID_MASK GENMASK(15, 8)
+/* Sequence number occupies bits 63:16 of the ctxid / message context */
+#define FASTRPC_CTXID_SEQ_SHIFT 16
+#define FASTRPC_CTXID_SEQ_MASK GENMASK_ULL(63, 16)
+
+/*
+ * Newer DSP firmware implements a PD (Protection Domain) notification
+ * framework that sends PD state notifications upon request. The PD exit
+ * notification is unconditionally sent by the DSP with this fixed sentinel
+ * in the context field rather than the context of an outstanding invocation.
+ * Since the fastrpc driver does not support the DSP PD notification framework,
+ * this message must be dropped rather than matched against the context idr.
+ */
+#define FASTRPC_DSP_PD_NOTIFY_CTX 0xABCDABCD
+
#define INIT_FILELEN_MAX (2 * 1024 * 1024)
#define INIT_FILE_NAMELEN_MAX (128)
#define FASTRPC_DEVICE_NAME "fastrpc"
@@ -105,6 +129,12 @@
#define miscdev_to_fdevice(d) container_of(d, struct fastrpc_device, miscdev)
+/* Poll response number from remote processor for call completion */
+#define FASTRPC_POLL_RESPONSE (0xdecaf)
+
+/* Polling mode timeout limit */
+#define FASTRPC_POLL_MAX_TIMEOUT_US (10000)
+
struct fastrpc_phy_page {
dma_addr_t addr; /* dma address */
u64 size; /* size of contiguous region */
@@ -233,9 +263,16 @@ struct fastrpc_invoke_ctx {
int pid;
int client_id;
u32 sc;
+ u64 *fdlist;
u32 *crc;
+ /* Poll memory that DSP updates */
+ u32 *poll_addr;
u64 ctxid;
u64 msg_sz;
+ /* work done status flag */
+ bool is_work_done;
+ /* process updates poll memory instead of glink response */
+ bool is_polled;
struct kref refcount;
struct list_head node; /* list of ctxs */
struct completion work;
@@ -261,6 +298,7 @@ struct fastrpc_soc_data {
u32 sid_pos;
u32 dma_addr_bits_cdsp;
u32 dma_addr_bits_default;
+ bool poll_mode_supported;
};
struct fastrpc_channel_ctx {
@@ -283,6 +321,9 @@ struct fastrpc_channel_ctx {
struct list_head invoke_interrupted_mmaps;
bool secure;
bool unsigned_support;
+ bool poll_mode_supported;
+ /* Per-channel sequence counter; incremented on every context allocation */
+ atomic_t ctx_seq;
u64 dma_mask;
const struct fastrpc_soc_data *soc_data;
};
@@ -306,6 +347,8 @@ struct fastrpc_user {
int client_id;
int pd;
bool is_secure_dev;
+ /* Flags poll mode state */
+ bool poll_mode;
/* Lock for lists */
spinlock_t lock;
/* lock for allocations */
@@ -422,6 +465,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
static void fastrpc_buf_free(struct fastrpc_buf *buf)
{
+ if (!buf)
+ return;
+
dma_free_coherent(buf->dev, buf->size, buf->virt,
fastrpc_ipa_to_dma_addr(buf->fl->cctx, buf->dma_addr));
kfree(buf);
@@ -515,8 +561,7 @@ static void fastrpc_user_free(struct kref *ref)
struct fastrpc_map *map, *m;
struct fastrpc_buf *buf, *b;
- if (fl->init_mem)
- fastrpc_buf_free(fl->init_mem);
+ fastrpc_buf_free(fl->init_mem);
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
list_del(&ctx->node);
@@ -561,11 +606,10 @@ static void fastrpc_context_free(struct kref *ref)
for (i = 0; i < ctx->nbufs; i++)
fastrpc_map_put(ctx->maps[i]);
- if (ctx->buf)
- fastrpc_buf_free(ctx->buf);
+ fastrpc_buf_free(ctx->buf);
spin_lock_irqsave(&cctx->lock, flags);
- idr_remove(&cctx->ctx_idr, ctx->ctxid >> 4);
+ idr_remove(&cctx->ctx_idr, FIELD_GET(FASTRPC_CTXID_MASK, ctx->ctxid));
spin_unlock_irqrestore(&cctx->lock, flags);
kfree(ctx->maps);
@@ -703,7 +747,9 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
spin_unlock_irqrestore(&cctx->lock, flags);
goto err_idr;
}
- ctx->ctxid = ret << 4;
+ ctx->ctxid = FIELD_PREP(FASTRPC_CTXID_MASK, ret) |
+ FIELD_PREP(FASTRPC_CTXID_SEQ_MASK,
+ (u64)atomic_inc_return(&cctx->ctx_seq));
spin_unlock_irqrestore(&cctx->lock, flags);
kref_init(&ctx->refcount);
@@ -968,7 +1014,8 @@ static int fastrpc_get_meta_size(struct fastrpc_invoke_ctx *ctx)
sizeof(struct fastrpc_invoke_buf) +
sizeof(struct fastrpc_phy_page)) * ctx->nscalars +
sizeof(u64) * FASTRPC_MAX_FDLIST +
- sizeof(u32) * FASTRPC_MAX_CRCLIST;
+ sizeof(u32) * FASTRPC_MAX_CRCLIST +
+ sizeof(u32);
return size;
}
@@ -1063,6 +1110,10 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
rpra = ctx->buf->virt;
list = fastrpc_invoke_buf_start(rpra, ctx->nscalars);
pages = fastrpc_phy_page_start(list, ctx->nscalars);
+ ctx->fdlist = (u64 *)(pages + ctx->nscalars);
+ ctx->poll_addr = (u32 *)((uintptr_t)ctx->fdlist + sizeof(u64) * FASTRPC_MAX_FDLIST +
+ sizeof(u32) * FASTRPC_MAX_CRCLIST);
+
args = (uintptr_t)ctx->buf->virt + metalen;
rlen = pkt_size - metalen;
ctx->rpra = rpra;
@@ -1165,18 +1216,11 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx,
union fastrpc_remote_arg *rpra = ctx->rpra;
struct fastrpc_user *fl = ctx->fl;
struct fastrpc_map *mmap = NULL;
- struct fastrpc_invoke_buf *list;
- struct fastrpc_phy_page *pages;
- u64 *fdlist;
- int i, inbufs, outbufs, handles;
+ u64 *fdlist = ctx->fdlist;
+ int i, inbufs;
int ret = 0;
inbufs = REMOTE_SCALARS_INBUFS(ctx->sc);
- outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc);
- handles = REMOTE_SCALARS_INHANDLES(ctx->sc) + REMOTE_SCALARS_OUTHANDLES(ctx->sc);
- list = fastrpc_invoke_buf_start(rpra, ctx->nscalars);
- pages = fastrpc_phy_page_start(list, ctx->nscalars);
- fdlist = (uint64_t *)(pages + inbufs + outbufs + handles);
for (i = inbufs; i < ctx->nbufs; ++i) {
if (!ctx->maps[i]) {
@@ -1239,6 +1283,71 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx,
}
+static u32 fastrpc_read_poll_addr(struct fastrpc_invoke_ctx *ctx)
+{
+ dma_rmb();
+ return READ_ONCE(*ctx->poll_addr);
+}
+
+static int poll_for_remote_response(struct fastrpc_invoke_ctx *ctx)
+{
+ u32 val;
+ int ret;
+
+ /*
+ * Poll until DSP writes FASTRPC_POLL_RESPONSE into *ctx->poll_addr
+ * or until another path marks the work done.
+ */
+ ret = read_poll_timeout_atomic(fastrpc_read_poll_addr, val,
+ (val == FASTRPC_POLL_RESPONSE) || ctx->is_work_done, 1,
+ FASTRPC_POLL_MAX_TIMEOUT_US, false, ctx);
+
+ if (!ret && val == FASTRPC_POLL_RESPONSE) {
+ /*
+ * DSP writes FASTRPC_POLL_RESPONSE to signal successful
+ * completion via the poll path.
+ */
+ ctx->is_work_done = true;
+ ctx->retval = 0;
+ }
+
+ if (ret == -ETIMEDOUT)
+ ret = -EIO;
+
+ return ret;
+}
+
+static inline int fastrpc_wait_for_response(struct fastrpc_invoke_ctx *ctx,
+ u32 kernel)
+{
+ int err = 0;
+
+ if (kernel) {
+ if (!wait_for_completion_timeout(&ctx->work, 10 * HZ))
+ err = -ETIMEDOUT;
+ } else {
+ err = wait_for_completion_interruptible(&ctx->work);
+ }
+
+ return err;
+}
+
+static int fastrpc_wait_for_completion(struct fastrpc_invoke_ctx *ctx,
+ u32 kernel)
+{
+ int err;
+
+ if (ctx->is_polled) {
+ err = poll_for_remote_response(ctx);
+ if (!err)
+ return 0;
+ /* If polling timed out or failed, move to normal response mode */
+ ctx->is_polled = false;
+ }
+
+ return fastrpc_wait_for_response(ctx, kernel);
+}
+
static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
u32 handle, u32 sc,
struct fastrpc_invoke_args *args)
@@ -1274,13 +1383,14 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
if (err)
goto bail;
- if (kernel) {
- if (!wait_for_completion_timeout(&ctx->work, 10 * HZ))
- err = -ETIMEDOUT;
- } else {
- err = wait_for_completion_interruptible(&ctx->work);
- }
+ /*
+ * Set message context as polled if the call is for a user PD
+ * dynamic module and user has enabled poll mode.
+ */
+ if (handle > FASTRPC_MAX_STATIC_HANDLE && fl->pd == USER_PD && fl->poll_mode)
+ ctx->is_polled = true;
+ err = fastrpc_wait_for_completion(ctx, kernel);
if (err)
goto bail;
@@ -1846,6 +1956,35 @@ done:
return 0;
}
+static int fastrpc_set_option(struct fastrpc_user *fl, char __user *argp)
+{
+ struct fastrpc_ioctl_set_option opt = {0};
+ int i;
+
+ if (copy_from_user(&opt, argp, sizeof(opt)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(opt.reserved); i++) {
+ if (opt.reserved[i] != 0)
+ return -EINVAL;
+ }
+
+ if (opt.request_id != FASTRPC_POLL_MODE)
+ return -EINVAL;
+
+ if (!fl->cctx->poll_mode_supported)
+ return -EOPNOTSUPP;
+
+ if (opt.value == FASTRPC_POLL_MODE_ENABLE)
+ fl->poll_mode = true;
+ else if (opt.value == FASTRPC_POLL_MODE_DISABLE)
+ fl->poll_mode = false;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static int fastrpc_get_dsp_info(struct fastrpc_user *fl, char __user *argp)
{
struct fastrpc_ioctl_capability cap = {0};
@@ -2207,6 +2346,9 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
case FASTRPC_IOCTL_MEM_UNMAP:
err = fastrpc_req_mem_unmap(fl, argp);
break;
+ case FASTRPC_IOCTL_SET_OPTION:
+ err = fastrpc_set_option(fl, argp);
+ break;
case FASTRPC_IOCTL_GET_DSP_INFO:
err = fastrpc_get_dsp_info(fl, argp);
break;
@@ -2232,19 +2374,22 @@ static int fastrpc_cb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
int i, sessions = 0;
unsigned long flags;
- int rc;
u32 dma_bits;
+ u32 sid = 0;
+ int rc;
cctx = dev_get_drvdata(dev->parent);
if (!cctx)
return -EINVAL;
of_property_read_u32(dev->of_node, "qcom,nsessions", &sessions);
+ if (of_property_read_u32(dev->of_node, "reg", &sid))
+ dev_info(dev, "FastRPC Session ID not specified in DT\n");
spin_lock_irqsave(&cctx->lock, flags);
if (cctx->sesscount >= FASTRPC_MAX_SESSIONS) {
- dev_err(&pdev->dev, "too many sessions\n");
spin_unlock_irqrestore(&cctx->lock, flags);
+ dev_err(&pdev->dev, "too many sessions\n");
return -ENOSPC;
}
dma_bits = cctx->soc_data->dma_addr_bits_default;
@@ -2253,13 +2398,11 @@ static int fastrpc_cb_probe(struct platform_device *pdev)
sess->valid = true;
sess->dev = dev;
dev_set_drvdata(dev, sess);
+ sess->sid = sid;
if (cctx->domain_id == CDSP_DOMAIN_ID)
dma_bits = cctx->soc_data->dma_addr_bits_cdsp;
- if (of_property_read_u32(dev->of_node, "reg", &sess->sid))
- dev_info(dev, "FastRPC Session ID not specified in DT\n");
-
if (sessions > 0) {
struct fastrpc_session_ctx *dup_sess;
@@ -2298,8 +2441,8 @@ static void fastrpc_cb_remove(struct platform_device *pdev)
}
static const struct of_device_id fastrpc_match_table[] = {
- { .compatible = "qcom,fastrpc-compute-cb", },
- {}
+ { .compatible = "qcom,fastrpc-compute-cb" },
+ { }
};
static struct platform_driver fastrpc_cb_driver = {
@@ -2362,6 +2505,7 @@ static const struct fastrpc_soc_data kaanapali_soc_data = {
.sid_pos = 56,
.dma_addr_bits_cdsp = 34,
.dma_addr_bits_default = 32,
+ .poll_mode_supported = true,
};
static const struct fastrpc_soc_data default_soc_data = {
@@ -2370,6 +2514,29 @@ static const struct fastrpc_soc_data default_soc_data = {
.dma_addr_bits_default = 32,
};
+/*
+ * Exception list for older platforms that use default_soc_data but whose
+ * DSP firmware supports FastRPC polling mode.
+ *
+ * NOTE: This list is intentionally closed.
+ * Do NOT add new platforms here. New SoCs must advertise polling mode
+ * support via their soc_data.
+ */
+
+static const struct of_device_id fastrpc_poll_supported_machines[] __maybe_unused = {
+ { .compatible = "qcom,milos" },
+ { .compatible = "qcom,qcs8300" },
+ { .compatible = "qcom,sa8775p" },
+ { .compatible = "qcom,sar2130p" },
+ { .compatible = "qcom,sm8450" },
+ { .compatible = "qcom,sm8550" },
+ { .compatible = "qcom,sm8650" },
+ { .compatible = "qcom,sm8750" },
+ { .compatible = "qcom,x1e80100" },
+ { .compatible = "qcom,x1p42100" },
+ {},
+};
+
static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct device *rdev = &rpdev->dev;
@@ -2436,6 +2603,8 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain"));
data->secure = secure_dsp;
data->soc_data = soc_data;
+ data->poll_mode_supported = soc_data->poll_mode_supported ||
+ of_machine_get_match(fastrpc_poll_supported_machines);
switch (domain_id) {
case ADSP_DOMAIN_ID:
@@ -2465,6 +2634,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
}
kref_init(&data->refcount);
+ atomic_set(&data->ctx_seq, 0);
rdev->dma_mask = &data->dma_mask;
dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
@@ -2551,7 +2721,15 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
if (!cctx)
return -ENODEV;
- ctxid = ((rsp->ctx & FASTRPC_CTXID_MASK) >> 4);
+ /*
+ * A PD exit notification from the DSP PD notification framework carries
+ * this sentinel rather than a real context. Drop it: a real context is
+ * (idr_index << 4) | pd and can never collide with this value.
+ */
+ if (rsp->ctx == FASTRPC_DSP_PD_NOTIFY_CTX)
+ return 0;
+
+ ctxid = FIELD_GET(FASTRPC_CTXID_MASK, rsp->ctx);
spin_lock_irqsave(&cctx->lock, flags);
ctx = idr_find(&cctx->ctx_idr, ctxid);
@@ -2562,7 +2740,25 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
return -ENOENT;
}
+ /*
+ * Validate the sequence number embedded in the upper bits of the
+ * context ID. Under high concurrency the IDR slot can be recycled
+ * for a new request before a late (or duplicate) glink COMPLETE
+ * response for the previous request arrives. Without this check the
+ * driver would call complete() on the wrong context, waking a thread
+ * whose buffers are still being accessed by the DSP.
+ */
+ if (FIELD_GET(FASTRPC_CTXID_SEQ_MASK, rsp->ctx) !=
+ FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid)) {
+ dev_dbg(&rpdev->dev,
+ "Stale glink response ctx 0x%llx (expected seq 0x%llx), dropping\n",
+ rsp->ctx,
+ FIELD_GET(FASTRPC_CTXID_SEQ_MASK, ctx->ctxid));
+ return 0;
+ }
+
ctx->retval = rsp->retval;
+ ctx->is_work_done = true;
complete(&ctx->work);
/*
@@ -2578,7 +2774,7 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
static const struct of_device_id fastrpc_rpmsg_of_match[] = {
{ .compatible = "qcom,kaanapali-fastrpc", .data = &kaanapali_soc_data },
{ .compatible = "qcom,fastrpc", .data = &default_soc_data },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, fastrpc_rpmsg_of_match);
diff --git a/drivers/misc/gehc-achc.c b/drivers/misc/gehc-achc.c
index b8fca4d393c6..9792189c0923 100644
--- a/drivers/misc/gehc-achc.c
+++ b/drivers/misc/gehc-achc.c
@@ -538,8 +538,8 @@ static int gehc_achc_probe(struct spi_device *spi)
}
static const struct spi_device_id gehc_achc_id[] = {
- { "ge,achc", 0 },
- { "achc", 0 },
+ { .name = "ge,achc" },
+ { .name = "achc" },
{ }
};
MODULE_DEVICE_TABLE(spi, gehc_achc_id);
diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c
index 86bfa82723ff..e602eb3c51fa 100644
--- a/drivers/misc/genwqe/card_base.c
+++ b/drivers/misc/genwqe/card_base.c
@@ -48,60 +48,42 @@ static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX];
/* PCI structure for identifying device by PCI vendor and device ID */
static const struct pci_device_id genwqe_device_table[] = {
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5,
- .class = (PCI_CLASSCODE_GENWQE5 << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- /* Initial SR-IOV bring-up image */
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */
- .device = 0x0000, /* VF Device ID */
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_SRIOV,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- /* Fixed up image */
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- { .vendor = PCI_VENDOR_ID_IBM, /* VF Vendor ID */
- .device = 0x0000, /* VF Device ID */
- .subvendor = PCI_SUBVENDOR_ID_IBM_SRIOV,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5,
- .class = (PCI_CLASSCODE_GENWQE5_SRIOV << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- /* Even one more ... */
- { .vendor = PCI_VENDOR_ID_IBM,
- .device = PCI_DEVICE_GENWQE,
- .subvendor = PCI_SUBVENDOR_ID_IBM,
- .subdevice = PCI_SUBSYSTEM_ID_GENWQE5_NEW,
- .class = (PCI_CLASSCODE_GENWQE5 << 8),
- .class_mask = ~0,
- .driver_data = 0 },
-
- { 0, } /* 0 terminated list. */
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5),
+ .class = PCI_CLASSCODE_GENWQE5 << 8,
+ .class_mask = ~0,
+ }, {
+ /* Initial SR-IOV bring-up image */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM_SRIOV),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000,
+ PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5_SRIOV),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ /* Fixed up image */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, 0x0000,
+ PCI_SUBVENDOR_ID_IBM_SRIOV, PCI_SUBSYSTEM_ID_GENWQE5),
+ .class = PCI_CLASSCODE_GENWQE5_SRIOV << 8,
+ .class_mask = ~0,
+ }, {
+ /* Even one more ... */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_IBM, PCI_DEVICE_GENWQE,
+ PCI_SUBVENDOR_ID_IBM, PCI_SUBSYSTEM_ID_GENWQE5_NEW),
+ .class = (PCI_CLASSCODE_GENWQE5 << 8),
+ .class_mask = ~0,
+ },
+ { } /* 0 terminated list. */
};
-
MODULE_DEVICE_TABLE(pci, genwqe_device_table);
/**
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index a2c4a9b4f871..e3b5fb337182 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -413,7 +413,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
size -= size_to_map;
map_offs = 0;
- if (prev_daddr == daddr) {
+ if (last_s && prev_daddr == daddr) {
u32 prev_len = be32_to_cpu(last_s->len);
/* pr_info("daddr combining: "
diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c
index 5ba40222eb12..033f30014aa6 100644
--- a/drivers/misc/hi6421v600-irq.c
+++ b/drivers/misc/hi6421v600-irq.c
@@ -274,18 +274,15 @@ static int hi6421v600_irq_probe(struct platform_device *pdev)
NULL,
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
"pmic", priv);
- if (ret < 0) {
- dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
- ret);
+ if (ret < 0)
return ret;
- }
return 0;
}
static const struct platform_device_id hi6421v600_irq_table[] = {
{ .name = "hi6421v600-irq" },
- {},
+ { }
};
MODULE_DEVICE_TABLE(platform, hi6421v600_irq_table);
diff --git a/drivers/misc/hisi_hikey_usb.c b/drivers/misc/hisi_hikey_usb.c
deleted file mode 100644
index 79f06001259b..000000000000
--- a/drivers/misc/hisi_hikey_usb.c
+++ /dev/null
@@ -1,274 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Support for usb functionality of Hikey series boards
- * based on Hisilicon Kirin Soc.
- *
- * Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd.
- * http://www.huawei.com
- *
- * Authors: Yu Chen <chenyu56@huawei.com>
- */
-
-#include <linux/gpio/consumer.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/notifier.h>
-#include <linux/platform_device.h>
-#include <linux/property.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/string_choices.h>
-#include <linux/usb/role.h>
-
-#define DEVICE_DRIVER_NAME "hisi_hikey_usb"
-
-#define HUB_VBUS_POWER_ON 1
-#define HUB_VBUS_POWER_OFF 0
-#define USB_SWITCH_TO_HUB 1
-#define USB_SWITCH_TO_TYPEC 0
-#define TYPEC_VBUS_POWER_ON 1
-#define TYPEC_VBUS_POWER_OFF 0
-
-struct hisi_hikey_usb {
- struct device *dev;
- struct gpio_desc *otg_switch;
- struct gpio_desc *typec_vbus;
- struct gpio_desc *reset;
-
- struct regulator *regulator;
-
- struct usb_role_switch *hub_role_sw;
-
- struct usb_role_switch *dev_role_sw;
- enum usb_role role;
-
- struct mutex lock;
- struct work_struct work;
-
- struct notifier_block nb;
-};
-
-static void hub_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, int value)
-{
- int ret, status;
-
- if (!hisi_hikey_usb->regulator)
- return;
-
- status = regulator_is_enabled(hisi_hikey_usb->regulator);
- if (status == !!value)
- return;
-
- if (value)
- ret = regulator_enable(hisi_hikey_usb->regulator);
- else
- ret = regulator_disable(hisi_hikey_usb->regulator);
-
- if (ret)
- dev_err(hisi_hikey_usb->dev,
- "Can't switch regulator state to %s\n",
- str_enabled_disabled(value));
-}
-
-static void usb_switch_ctrl(struct hisi_hikey_usb *hisi_hikey_usb,
- int switch_to)
-{
- if (!hisi_hikey_usb->otg_switch)
- return;
-
- gpiod_set_value_cansleep(hisi_hikey_usb->otg_switch, switch_to);
-}
-
-static void usb_typec_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb,
- int value)
-{
- if (!hisi_hikey_usb->typec_vbus)
- return;
-
- gpiod_set_value_cansleep(hisi_hikey_usb->typec_vbus, value);
-}
-
-static void relay_set_role_switch(struct work_struct *work)
-{
- struct hisi_hikey_usb *hisi_hikey_usb = container_of(work,
- struct hisi_hikey_usb,
- work);
- struct usb_role_switch *sw;
- enum usb_role role;
-
- if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw)
- return;
-
- mutex_lock(&hisi_hikey_usb->lock);
- switch (hisi_hikey_usb->role) {
- case USB_ROLE_NONE:
- usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF);
- usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_HUB);
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_ON);
- break;
- case USB_ROLE_HOST:
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF);
- usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC);
- usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_ON);
- break;
- case USB_ROLE_DEVICE:
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF);
- usb_typec_power_ctrl(hisi_hikey_usb, TYPEC_VBUS_POWER_OFF);
- usb_switch_ctrl(hisi_hikey_usb, USB_SWITCH_TO_TYPEC);
- break;
- default:
- break;
- }
- sw = hisi_hikey_usb->dev_role_sw;
- role = hisi_hikey_usb->role;
- mutex_unlock(&hisi_hikey_usb->lock);
-
- usb_role_switch_set_role(sw, role);
-}
-
-static int hub_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
-{
- struct hisi_hikey_usb *hisi_hikey_usb = usb_role_switch_get_drvdata(sw);
-
- if (!hisi_hikey_usb || !hisi_hikey_usb->dev_role_sw)
- return -EINVAL;
-
- mutex_lock(&hisi_hikey_usb->lock);
- hisi_hikey_usb->role = role;
- mutex_unlock(&hisi_hikey_usb->lock);
-
- schedule_work(&hisi_hikey_usb->work);
-
- return 0;
-}
-
-static int hisi_hikey_usb_of_role_switch(struct platform_device *pdev,
- struct hisi_hikey_usb *hisi_hikey_usb)
-{
- struct device *dev = &pdev->dev;
- struct usb_role_switch_desc hub_role_switch = {NULL};
-
- if (!device_property_read_bool(dev, "usb-role-switch"))
- return 0;
-
- hisi_hikey_usb->otg_switch = devm_gpiod_get(dev, "otg-switch",
- GPIOD_OUT_HIGH);
- if (IS_ERR(hisi_hikey_usb->otg_switch)) {
- dev_err(dev, "get otg-switch failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->otg_switch));
- return PTR_ERR(hisi_hikey_usb->otg_switch);
- }
-
- hisi_hikey_usb->typec_vbus = devm_gpiod_get(dev, "typec-vbus",
- GPIOD_OUT_LOW);
- if (IS_ERR(hisi_hikey_usb->typec_vbus)) {
- dev_err(dev, "get typec-vbus failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->typec_vbus));
- return PTR_ERR(hisi_hikey_usb->typec_vbus);
- }
-
- hisi_hikey_usb->reset = devm_gpiod_get_optional(dev,
- "hub-reset-en",
- GPIOD_OUT_HIGH);
- if (IS_ERR(hisi_hikey_usb->reset)) {
- dev_err(dev, "get hub-reset-en failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->reset));
- return PTR_ERR(hisi_hikey_usb->reset);
- }
-
- hisi_hikey_usb->dev_role_sw = usb_role_switch_get(dev);
- if (!hisi_hikey_usb->dev_role_sw)
- return -EPROBE_DEFER;
- if (IS_ERR(hisi_hikey_usb->dev_role_sw)) {
- dev_err(dev, "get device role switch failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->dev_role_sw));
- return PTR_ERR(hisi_hikey_usb->dev_role_sw);
- }
-
- INIT_WORK(&hisi_hikey_usb->work, relay_set_role_switch);
-
- hub_role_switch.fwnode = dev_fwnode(dev);
- hub_role_switch.set = hub_usb_role_switch_set;
- hub_role_switch.driver_data = hisi_hikey_usb;
-
- hisi_hikey_usb->hub_role_sw = usb_role_switch_register(dev,
- &hub_role_switch);
-
- if (IS_ERR(hisi_hikey_usb->hub_role_sw)) {
- dev_err(dev,
- "failed to register hub role with error %ld\n",
- PTR_ERR(hisi_hikey_usb->hub_role_sw));
- usb_role_switch_put(hisi_hikey_usb->dev_role_sw);
- return PTR_ERR(hisi_hikey_usb->hub_role_sw);
- }
-
- return 0;
-}
-
-static int hisi_hikey_usb_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hisi_hikey_usb *hisi_hikey_usb;
- int ret;
-
- hisi_hikey_usb = devm_kzalloc(dev, sizeof(*hisi_hikey_usb), GFP_KERNEL);
- if (!hisi_hikey_usb)
- return -ENOMEM;
-
- hisi_hikey_usb->dev = &pdev->dev;
- mutex_init(&hisi_hikey_usb->lock);
-
- hisi_hikey_usb->regulator = devm_regulator_get(dev, "hub-vdd");
- if (IS_ERR(hisi_hikey_usb->regulator)) {
- if (PTR_ERR(hisi_hikey_usb->regulator) == -EPROBE_DEFER) {
- dev_info(dev, "waiting for hub-vdd-supply\n");
- return PTR_ERR(hisi_hikey_usb->regulator);
- }
- dev_err(dev, "get hub-vdd-supply failed with error %ld\n",
- PTR_ERR(hisi_hikey_usb->regulator));
- return PTR_ERR(hisi_hikey_usb->regulator);
- }
-
- ret = hisi_hikey_usb_of_role_switch(pdev, hisi_hikey_usb);
- if (ret)
- return ret;
-
- platform_set_drvdata(pdev, hisi_hikey_usb);
-
- return 0;
-}
-
-static void hisi_hikey_usb_remove(struct platform_device *pdev)
-{
- struct hisi_hikey_usb *hisi_hikey_usb = platform_get_drvdata(pdev);
-
- if (hisi_hikey_usb->hub_role_sw) {
- usb_role_switch_unregister(hisi_hikey_usb->hub_role_sw);
-
- if (hisi_hikey_usb->dev_role_sw)
- usb_role_switch_put(hisi_hikey_usb->dev_role_sw);
- } else {
- hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF);
- }
-}
-
-static const struct of_device_id id_table_hisi_hikey_usb[] = {
- { .compatible = "hisilicon,usbhub" },
- {}
-};
-MODULE_DEVICE_TABLE(of, id_table_hisi_hikey_usb);
-
-static struct platform_driver hisi_hikey_usb_driver = {
- .probe = hisi_hikey_usb_probe,
- .remove = hisi_hikey_usb_remove,
- .driver = {
- .name = DEVICE_DRIVER_NAME,
- .of_match_table = id_table_hisi_hikey_usb,
- },
-};
-
-module_platform_driver(hisi_hikey_usb_driver);
-
-MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>");
-MODULE_DESCRIPTION("Driver Support for USB functionality of Hikey");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c
index ff92c6edff6b..275dc054ca04 100644
--- a/drivers/misc/hmc6352.c
+++ b/drivers/misc/hmc6352.c
@@ -121,7 +121,7 @@ static void hmc6352_remove(struct i2c_client *client)
}
static const struct i2c_device_id hmc6352_id[] = {
- { "hmc6352" },
+ { .name = "hmc6352" },
{ }
};
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c
index ff3f03ea577e..8988858c2f31 100644
--- a/drivers/misc/hpilo.c
+++ b/drivers/misc/hpilo.c
@@ -33,10 +33,10 @@ static unsigned int max_ccb = 16;
static char ilo_hwdev[MAX_ILO_DEV];
static const struct pci_device_id ilo_blacklist[] = {
/* auxiliary iLO */
- {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979)},
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979) },
/* CL */
- {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289)},
- {}
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289) },
+ { }
};
static inline int get_entry_id(int entry)
@@ -160,11 +160,16 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb,
ret = fifo_dequeue(hw, fifobar, &entry);
if (ret) {
+ int pkt_len;
+
pkt_id = get_entry_id(entry);
+ pkt_len = get_entry_len(entry);
+ if (pkt_id >= NR_QENTRY || pkt_len > desc_mem_sz(1))
+ return 0;
if (id)
*id = pkt_id;
if (len)
- *len = get_entry_len(entry);
+ *len = pkt_len;
if (pkt)
*pkt = (void *)(desc + desc_mem_sz(pkt_id));
}
diff --git a/drivers/misc/ibmasm/Makefile b/drivers/misc/ibmasm/Makefile
deleted file mode 100644
index 1b9dd0f44411..000000000000
--- a/drivers/misc/ibmasm/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-
-obj-$(CONFIG_IBM_ASM) := ibmasm.o
-
-ibmasm-y := module.o \
- ibmasmfs.o \
- event.o \
- command.o \
- remote.o \
- heartbeat.o \
- r_heartbeat.o \
- dot_command.o \
- lowlevel.o
-
-ibmasm-$(CONFIG_SERIAL_8250) += uart.o
-
diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c
deleted file mode 100644
index 6b037675ff45..000000000000
--- a/drivers/misc/ibmasm/command.c
+++ /dev/null
@@ -1,173 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-
-static void exec_next_command(struct service_processor *sp);
-
-static atomic_t command_count = ATOMIC_INIT(0);
-
-struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size)
-{
- struct command *cmd;
-
- if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE)
- return NULL;
-
- cmd = kzalloc_obj(struct command);
- if (cmd == NULL)
- return NULL;
-
-
- cmd->buffer = kzalloc(buffer_size, GFP_KERNEL);
- if (cmd->buffer == NULL) {
- kfree(cmd);
- return NULL;
- }
- cmd->buffer_size = buffer_size;
-
- kref_init(&cmd->kref);
- cmd->lock = &sp->lock;
-
- cmd->status = IBMASM_CMD_PENDING;
- init_waitqueue_head(&cmd->wait);
- INIT_LIST_HEAD(&cmd->queue_node);
-
- atomic_inc(&command_count);
- dbg("command count: %d\n", atomic_read(&command_count));
-
- return cmd;
-}
-
-void ibmasm_free_command(struct kref *kref)
-{
- struct command *cmd = to_command(kref);
-
- list_del(&cmd->queue_node);
- atomic_dec(&command_count);
- dbg("command count: %d\n", atomic_read(&command_count));
- kfree(cmd->buffer);
- kfree(cmd);
-}
-
-static void enqueue_command(struct service_processor *sp, struct command *cmd)
-{
- list_add_tail(&cmd->queue_node, &sp->command_queue);
-}
-
-static struct command *dequeue_command(struct service_processor *sp)
-{
- struct command *cmd;
- struct list_head *next;
-
- if (list_empty(&sp->command_queue))
- return NULL;
-
- next = sp->command_queue.next;
- list_del_init(next);
- cmd = list_entry(next, struct command, queue_node);
-
- return cmd;
-}
-
-static inline void do_exec_command(struct service_processor *sp)
-{
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
-
- if (ibmasm_send_i2o_message(sp)) {
- sp->current_command->status = IBMASM_CMD_FAILED;
- wake_up(&sp->current_command->wait);
- command_put(sp->current_command);
- exec_next_command(sp);
- }
-}
-
-/*
- * exec_command
- * send a command to a service processor
- * Commands are executed sequentially. One command (sp->current_command)
- * is sent to the service processor. Once the interrupt handler gets a
- * message of type command_response, the message is copied into
- * the current commands buffer,
- */
-void ibmasm_exec_command(struct service_processor *sp, struct command *cmd)
-{
- unsigned long flags;
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
-
- spin_lock_irqsave(&sp->lock, flags);
-
- if (!sp->current_command) {
- sp->current_command = cmd;
- command_get(sp->current_command);
- spin_unlock_irqrestore(&sp->lock, flags);
- do_exec_command(sp);
- } else {
- enqueue_command(sp, cmd);
- spin_unlock_irqrestore(&sp->lock, flags);
- }
-}
-
-static void exec_next_command(struct service_processor *sp)
-{
- unsigned long flags;
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
-
- spin_lock_irqsave(&sp->lock, flags);
- sp->current_command = dequeue_command(sp);
- if (sp->current_command) {
- command_get(sp->current_command);
- spin_unlock_irqrestore(&sp->lock, flags);
- do_exec_command(sp);
- } else {
- spin_unlock_irqrestore(&sp->lock, flags);
- }
-}
-
-/*
- * Sleep until a command has failed or a response has been received
- * and the command status been updated by the interrupt handler.
- * (see receive_response).
- */
-void ibmasm_wait_for_response(struct command *cmd, int timeout)
-{
- wait_event_interruptible_timeout(cmd->wait,
- cmd->status == IBMASM_CMD_COMPLETE ||
- cmd->status == IBMASM_CMD_FAILED,
- timeout * HZ);
-}
-
-/*
- * receive_command_response
- * called by the interrupt handler when a dot command of type command_response
- * was received.
- */
-void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size)
-{
- struct command *cmd = sp->current_command;
-
- if (!sp->current_command)
- return;
-
- memcpy_fromio(cmd->buffer, response, min(size, cmd->buffer_size));
- cmd->status = IBMASM_CMD_COMPLETE;
- wake_up(&sp->current_command->wait);
- command_put(sp->current_command);
- exec_next_command(sp);
-}
diff --git a/drivers/misc/ibmasm/dot_command.c b/drivers/misc/ibmasm/dot_command.c
deleted file mode 100644
index df389bd4c9df..000000000000
--- a/drivers/misc/ibmasm/dot_command.c
+++ /dev/null
@@ -1,138 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include "ibmasm.h"
-#include "dot_command.h"
-
-/*
- * Dispatch an incoming message to the specific handler for the message.
- * Called from interrupt context.
- */
-void ibmasm_receive_message(struct service_processor *sp, void *message, int message_size)
-{
- u32 size;
- struct dot_command_header *header = (struct dot_command_header *)message;
-
- if (message_size == 0)
- return;
-
- size = get_dot_command_size(message);
- if (size == 0)
- return;
-
- if (size > message_size)
- size = message_size;
-
- switch (header->type) {
- case sp_event:
- ibmasm_receive_event(sp, message, size);
- break;
- case sp_command_response:
- ibmasm_receive_command_response(sp, message, size);
- break;
- case sp_heartbeat:
- ibmasm_receive_heartbeat(sp, message, size);
- break;
- default:
- dev_err(sp->dev, "Received unknown message from service processor\n");
- }
-}
-
-
-#define INIT_BUFFER_SIZE 32
-
-
-/*
- * send the 4.3.5.10 dot command (driver VPD) to the service processor
- */
-int ibmasm_send_driver_vpd(struct service_processor *sp)
-{
- struct command *command;
- struct dot_command_header *header;
- u8 *vpd_command;
- u8 *vpd_data;
- int result = 0;
-
- command = ibmasm_new_command(sp, INIT_BUFFER_SIZE);
- if (command == NULL)
- return -ENOMEM;
-
- header = (struct dot_command_header *)command->buffer;
- header->type = sp_write;
- header->command_size = 4;
- header->data_size = 16;
- header->status = 0;
- header->reserved = 0;
-
- vpd_command = command->buffer + sizeof(struct dot_command_header);
- vpd_command[0] = 0x4;
- vpd_command[1] = 0x3;
- vpd_command[2] = 0x5;
- vpd_command[3] = 0xa;
-
- vpd_data = vpd_command + header->command_size;
- vpd_data[0] = 0;
- strcat(vpd_data, IBMASM_DRIVER_VPD);
- vpd_data[10] = 0;
- vpd_data[15] = 0;
-
- ibmasm_exec_command(sp, command);
- ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL);
-
- if (command->status != IBMASM_CMD_COMPLETE)
- result = -ENODEV;
-
- command_put(command);
-
- return result;
-}
-
-struct os_state_command {
- struct dot_command_header header;
- unsigned char command[3];
- unsigned char data;
-};
-
-/*
- * send the 4.3.6 dot command (os state) to the service processor
- * During driver init this function is called with os state "up".
- * This causes the service processor to start sending heartbeats the
- * driver.
- * During driver exit the function is called with os state "down",
- * causing the service processor to stop the heartbeats.
- */
-int ibmasm_send_os_state(struct service_processor *sp, int os_state)
-{
- struct command *cmd;
- struct os_state_command *os_state_cmd;
- int result = 0;
-
- cmd = ibmasm_new_command(sp, sizeof(struct os_state_command));
- if (cmd == NULL)
- return -ENOMEM;
-
- os_state_cmd = (struct os_state_command *)cmd->buffer;
- os_state_cmd->header.type = sp_write;
- os_state_cmd->header.command_size = 3;
- os_state_cmd->header.data_size = 1;
- os_state_cmd->header.status = 0;
- os_state_cmd->command[0] = 4;
- os_state_cmd->command[1] = 3;
- os_state_cmd->command[2] = 6;
- os_state_cmd->data = os_state;
-
- ibmasm_exec_command(sp, cmd);
- ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
-
- if (cmd->status != IBMASM_CMD_COMPLETE)
- result = -ENODEV;
-
- command_put(cmd);
- return result;
-}
diff --git a/drivers/misc/ibmasm/dot_command.h b/drivers/misc/ibmasm/dot_command.h
deleted file mode 100644
index e03399ebe239..000000000000
--- a/drivers/misc/ibmasm/dot_command.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#ifndef __DOT_COMMAND_H__
-#define __DOT_COMMAND_H__
-
-/*
- * dot commands are the protocol used to communicate with the service
- * processor.
- * They consist of header, a command of variable length and data of
- * variable length.
- */
-
-/* dot command types */
-#define sp_write 0
-#define sp_write_next 1
-#define sp_read 2
-#define sp_read_next 3
-#define sp_command_response 4
-#define sp_event 5
-#define sp_heartbeat 6
-
-#pragma pack(1)
-struct dot_command_header {
- u8 type;
- u8 command_size;
- u16 data_size;
- u8 status;
- u8 reserved;
-};
-#pragma pack()
-
-static inline size_t get_dot_command_size(void *buffer)
-{
- struct dot_command_header *cmd = (struct dot_command_header *)buffer;
- return sizeof(struct dot_command_header) + cmd->command_size + cmd->data_size;
-}
-
-static inline unsigned int get_dot_command_timeout(void *buffer)
-{
- struct dot_command_header *header = (struct dot_command_header *)buffer;
- unsigned char *cmd = buffer + sizeof(struct dot_command_header);
-
- /* dot commands 6.3.1, 7.1 and 8.x need a longer timeout */
-
- if (header->command_size == 3) {
- if ((cmd[0] == 6) && (cmd[1] == 3) && (cmd[2] == 1))
- return IBMASM_CMD_TIMEOUT_EXTRA;
- } else if (header->command_size == 2) {
- if ((cmd[0] == 7) && (cmd[1] == 1))
- return IBMASM_CMD_TIMEOUT_EXTRA;
- if (cmd[0] == 8)
- return IBMASM_CMD_TIMEOUT_EXTRA;
- }
- return IBMASM_CMD_TIMEOUT_NORMAL;
-}
-
-#endif /* __DOT_COMMAND_H__ */
diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c
deleted file mode 100644
index 3990bf2b1728..000000000000
--- a/drivers/misc/ibmasm/event.c
+++ /dev/null
@@ -1,163 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-
-/*
- * ASM service processor event handling routines.
- *
- * Events are signalled to the device drivers through interrupts.
- * They have the format of dot commands, with the type field set to
- * sp_event.
- * The driver does not interpret the events, it simply stores them in a
- * circular buffer.
- */
-
-static void wake_up_event_readers(struct service_processor *sp)
-{
- struct event_reader *reader;
-
- list_for_each_entry(reader, &sp->event_buffer->readers, node)
- wake_up_interruptible(&reader->wait);
-}
-
-/*
- * receive_event
- * Called by the interrupt handler when a dot command of type sp_event is
- * received.
- * Store the event in the circular event buffer, wake up any sleeping
- * event readers.
- * There is no reader marker in the buffer, therefore readers are
- * responsible for keeping up with the writer, or they will lose events.
- */
-void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size)
-{
- struct event_buffer *buffer = sp->event_buffer;
- struct ibmasm_event *event;
- unsigned long flags;
-
- data_size = min(data_size, IBMASM_EVENT_MAX_SIZE);
-
- spin_lock_irqsave(&sp->lock, flags);
- /* copy the event into the next slot in the circular buffer */
- event = &buffer->events[buffer->next_index];
- memcpy_fromio(event->data, data, data_size);
- event->data_size = data_size;
- event->serial_number = buffer->next_serial_number;
-
- /* advance indices in the buffer */
- buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS;
- buffer->next_serial_number++;
- spin_unlock_irqrestore(&sp->lock, flags);
-
- wake_up_event_readers(sp);
-}
-
-static inline int event_available(struct event_buffer *b, struct event_reader *r)
-{
- return (r->next_serial_number < b->next_serial_number);
-}
-
-/*
- * get_next_event
- * Called by event readers (initiated from user space through the file
- * system).
- * Sleeps until a new event is available.
- */
-int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader)
-{
- struct event_buffer *buffer = sp->event_buffer;
- struct ibmasm_event *event;
- unsigned int index;
- unsigned long flags;
-
- reader->cancelled = 0;
-
- if (wait_event_interruptible(reader->wait,
- event_available(buffer, reader) || reader->cancelled))
- return -ERESTARTSYS;
-
- if (!event_available(buffer, reader))
- return 0;
-
- spin_lock_irqsave(&sp->lock, flags);
-
- index = buffer->next_index;
- event = &buffer->events[index];
- while (event->serial_number < reader->next_serial_number) {
- index = (index + 1) % IBMASM_NUM_EVENTS;
- event = &buffer->events[index];
- }
- memcpy(reader->data, event->data, event->data_size);
- reader->data_size = event->data_size;
- reader->next_serial_number = event->serial_number + 1;
-
- spin_unlock_irqrestore(&sp->lock, flags);
-
- return event->data_size;
-}
-
-void ibmasm_cancel_next_event(struct event_reader *reader)
-{
- reader->cancelled = 1;
- wake_up_interruptible(&reader->wait);
-}
-
-void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader)
-{
- unsigned long flags;
-
- reader->next_serial_number = sp->event_buffer->next_serial_number;
- init_waitqueue_head(&reader->wait);
- spin_lock_irqsave(&sp->lock, flags);
- list_add(&reader->node, &sp->event_buffer->readers);
- spin_unlock_irqrestore(&sp->lock, flags);
-}
-
-void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sp->lock, flags);
- list_del(&reader->node);
- spin_unlock_irqrestore(&sp->lock, flags);
-}
-
-int ibmasm_event_buffer_init(struct service_processor *sp)
-{
- struct event_buffer *buffer;
- struct ibmasm_event *event;
- int i;
-
- buffer = kmalloc_obj(struct event_buffer);
- if (!buffer)
- return -ENOMEM;
-
- buffer->next_index = 0;
- buffer->next_serial_number = 1;
-
- event = buffer->events;
- for (i=0; i<IBMASM_NUM_EVENTS; i++, event++)
- event->serial_number = 0;
-
- INIT_LIST_HEAD(&buffer->readers);
-
- sp->event_buffer = buffer;
-
- return 0;
-}
-
-void ibmasm_event_buffer_exit(struct service_processor *sp)
-{
- kfree(sp->event_buffer);
-}
diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c
deleted file mode 100644
index 59c9a0d95659..000000000000
--- a/drivers/misc/ibmasm/heartbeat.c
+++ /dev/null
@@ -1,88 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/notifier.h>
-#include <linux/panic_notifier.h>
-#include "ibmasm.h"
-#include "dot_command.h"
-#include "lowlevel.h"
-
-static int suspend_heartbeats = 0;
-
-/*
- * Once the driver indicates to the service processor that it is running
- * - see send_os_state() - the service processor sends periodic heartbeats
- * to the driver. The driver must respond to the heartbeats or else the OS
- * will be rebooted.
- * In the case of a panic the interrupt handler continues to work and thus
- * continues to respond to heartbeats, making the service processor believe
- * the OS is still running and thus preventing a reboot.
- * To prevent this from happening a callback is added the panic_notifier_list.
- * Before responding to a heartbeat the driver checks if a panic has happened,
- * if yes it suspends heartbeat, causing the service processor to reboot as
- * expected.
- */
-static int panic_happened(struct notifier_block *n, unsigned long val, void *v)
-{
- suspend_heartbeats = 1;
- return 0;
-}
-
-static struct notifier_block panic_notifier = { panic_happened, NULL, 1 };
-
-void ibmasm_register_panic_notifier(void)
-{
- atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier);
-}
-
-void ibmasm_unregister_panic_notifier(void)
-{
- atomic_notifier_chain_unregister(&panic_notifier_list,
- &panic_notifier);
-}
-
-
-int ibmasm_heartbeat_init(struct service_processor *sp)
-{
- sp->heartbeat = ibmasm_new_command(sp, HEARTBEAT_BUFFER_SIZE);
- if (sp->heartbeat == NULL)
- return -ENOMEM;
-
- return 0;
-}
-
-void ibmasm_heartbeat_exit(struct service_processor *sp)
-{
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
- ibmasm_wait_for_response(sp->heartbeat, IBMASM_CMD_TIMEOUT_NORMAL);
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
- suspend_heartbeats = 1;
- command_put(sp->heartbeat);
-}
-
-void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size)
-{
- struct command *cmd = sp->heartbeat;
- struct dot_command_header *header = (struct dot_command_header *)cmd->buffer;
- char tsbuf[32];
-
- dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf));
- if (suspend_heartbeats)
- return;
-
- /* return the received dot command to sender */
- cmd->status = IBMASM_CMD_PENDING;
- size = min(size, cmd->buffer_size);
- memcpy_fromio(cmd->buffer, message, size);
- header->type = sp_write;
- ibmasm_exec_command(sp, cmd);
-}
diff --git a/drivers/misc/ibmasm/i2o.h b/drivers/misc/ibmasm/i2o.h
deleted file mode 100644
index 468fa84a3257..000000000000
--- a/drivers/misc/ibmasm/i2o.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#pragma pack(1)
-struct i2o_header {
- u8 version;
- u8 message_flags;
- u16 message_size;
- u8 target;
- u8 initiator_and_target;
- u8 initiator;
- u8 function;
- u32 initiator_context;
-};
-#pragma pack()
-
-#define I2O_HEADER_TEMPLATE \
- { .version = 0x01, \
- .message_flags = 0x00, \
- .function = 0xFF, \
- .initiator = 0x00, \
- .initiator_and_target = 0x40, \
- .target = 0x00, \
- .initiator_context = 0x0 }
-
-#define I2O_MESSAGE_SIZE 0x1000
-#define I2O_COMMAND_SIZE (I2O_MESSAGE_SIZE - sizeof(struct i2o_header))
-
-#pragma pack(1)
-struct i2o_message {
- struct i2o_header header;
- void *data;
-};
-#pragma pack()
-
-static inline unsigned short outgoing_message_size(unsigned int data_size)
-{
- unsigned int size;
- unsigned short i2o_size;
-
- if (data_size > I2O_COMMAND_SIZE)
- data_size = I2O_COMMAND_SIZE;
-
- size = sizeof(struct i2o_header) + data_size;
-
- i2o_size = size / sizeof(u32);
-
- if (size % sizeof(u32))
- i2o_size++;
-
- return i2o_size;
-}
-
-static inline u32 incoming_data_size(struct i2o_message *i2o_message)
-{
- return (sizeof(u32) * i2o_message->header.message_size);
-}
diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h
deleted file mode 100644
index a5ced88ca923..000000000000
--- a/drivers/misc/ibmasm/ibmasm.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/wait.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/kref.h>
-#include <linux/device.h>
-#include <linux/input.h>
-#include <linux/time64.h>
-
-/* Driver identification */
-#define DRIVER_NAME "ibmasm"
-#define DRIVER_VERSION "1.0"
-#define DRIVER_AUTHOR "Max Asbock <masbock@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
-#define DRIVER_DESC "IBM ASM Service Processor Driver"
-
-#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
-#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME)
-
-extern int ibmasm_debug;
-#define dbg(STR, ARGS...) \
- do { \
- if (ibmasm_debug) \
- printk(KERN_DEBUG STR , ##ARGS); \
- } while (0)
-
-static inline char *get_timestamp(char *buf)
-{
- struct timespec64 now;
-
- ktime_get_real_ts64(&now);
- sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec,
- now.tv_nsec / NSEC_PER_USEC);
- return buf;
-}
-
-#define IBMASM_CMD_PENDING 0
-#define IBMASM_CMD_COMPLETE 1
-#define IBMASM_CMD_FAILED 2
-
-#define IBMASM_CMD_TIMEOUT_NORMAL 45
-#define IBMASM_CMD_TIMEOUT_EXTRA 240
-
-#define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000
-
-#define REVERSE_HEARTBEAT_TIMEOUT 120
-
-#define HEARTBEAT_BUFFER_SIZE 0x400
-
-#ifdef IA64
-#define IBMASM_DRIVER_VPD "Lin64 6.08 "
-#else
-#define IBMASM_DRIVER_VPD "Lin32 6.08 "
-#endif
-
-#define SYSTEM_STATE_OS_UP 5
-#define SYSTEM_STATE_OS_DOWN 4
-
-#define IBMASM_NAME_SIZE 16
-
-#define IBMASM_NUM_EVENTS 10
-#define IBMASM_EVENT_MAX_SIZE 2048u
-
-
-struct command {
- struct list_head queue_node;
- wait_queue_head_t wait;
- unsigned char *buffer;
- size_t buffer_size;
- int status;
- struct kref kref;
- spinlock_t *lock;
-};
-#define to_command(c) container_of(c, struct command, kref)
-
-void ibmasm_free_command(struct kref *kref);
-static inline void command_put(struct command *cmd)
-{
- unsigned long flags;
- spinlock_t *lock = cmd->lock;
-
- spin_lock_irqsave(lock, flags);
- kref_put(&cmd->kref, ibmasm_free_command);
- spin_unlock_irqrestore(lock, flags);
-}
-
-static inline void command_get(struct command *cmd)
-{
- kref_get(&cmd->kref);
-}
-
-
-struct ibmasm_event {
- unsigned int serial_number;
- unsigned int data_size;
- unsigned char data[IBMASM_EVENT_MAX_SIZE];
-};
-
-struct event_buffer {
- struct ibmasm_event events[IBMASM_NUM_EVENTS];
- unsigned int next_serial_number;
- unsigned int next_index;
- struct list_head readers;
-};
-
-struct event_reader {
- int cancelled;
- unsigned int next_serial_number;
- wait_queue_head_t wait;
- struct list_head node;
- unsigned int data_size;
- unsigned char data[IBMASM_EVENT_MAX_SIZE];
-};
-
-struct reverse_heartbeat {
- wait_queue_head_t wait;
- unsigned int stopped;
-};
-
-struct ibmasm_remote {
- struct input_dev *keybd_dev;
- struct input_dev *mouse_dev;
-};
-
-struct service_processor {
- struct list_head node;
- spinlock_t lock;
- void __iomem *base_address;
- unsigned int irq;
- struct command *current_command;
- struct command *heartbeat;
- struct list_head command_queue;
- struct event_buffer *event_buffer;
- char dirname[IBMASM_NAME_SIZE];
- char devname[IBMASM_NAME_SIZE];
- unsigned int number;
- struct ibmasm_remote remote;
- int serial_line;
- struct device *dev;
-};
-
-/* command processing */
-struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size);
-void ibmasm_exec_command(struct service_processor *sp, struct command *cmd);
-void ibmasm_wait_for_response(struct command *cmd, int timeout);
-void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size);
-
-/* event processing */
-int ibmasm_event_buffer_init(struct service_processor *sp);
-void ibmasm_event_buffer_exit(struct service_processor *sp);
-void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size);
-void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader);
-void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader);
-int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader);
-void ibmasm_cancel_next_event(struct event_reader *reader);
-
-/* heartbeat - from SP to OS */
-void ibmasm_register_panic_notifier(void);
-void ibmasm_unregister_panic_notifier(void);
-int ibmasm_heartbeat_init(struct service_processor *sp);
-void ibmasm_heartbeat_exit(struct service_processor *sp);
-void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size);
-
-/* reverse heartbeat - from OS to SP */
-void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
-int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
-void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb);
-
-/* dot commands */
-void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size);
-int ibmasm_send_driver_vpd(struct service_processor *sp);
-int ibmasm_send_os_state(struct service_processor *sp, int os_state);
-
-/* low level message processing */
-int ibmasm_send_i2o_message(struct service_processor *sp);
-irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id);
-
-/* remote console */
-void ibmasm_handle_mouse_interrupt(struct service_processor *sp);
-int ibmasm_init_remote_input_dev(struct service_processor *sp);
-void ibmasm_free_remote_input_dev(struct service_processor *sp);
-
-/* file system */
-int ibmasmfs_register(void);
-void ibmasmfs_unregister(void);
-void ibmasmfs_add_sp(struct service_processor *sp);
-
-/* uart */
-#if IS_ENABLED(CONFIG_SERIAL_8250)
-void ibmasm_register_uart(struct service_processor *sp);
-void ibmasm_unregister_uart(struct service_processor *sp);
-#else
-#define ibmasm_register_uart(sp) do { } while(0)
-#define ibmasm_unregister_uart(sp) do { } while(0)
-#endif
diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c
deleted file mode 100644
index dfdfa9ba4747..000000000000
--- a/drivers/misc/ibmasm/ibmasmfs.c
+++ /dev/null
@@ -1,604 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-/*
- * Parts of this code are based on an article by Jonathan Corbet
- * that appeared in Linux Weekly News.
- */
-
-
-/*
- * The IBMASM file virtual filesystem. It creates the following hierarchy
- * dynamically when mounted from user space:
- *
- * /ibmasm
- * |-- 0
- * | |-- command
- * | |-- event
- * | |-- reverse_heartbeat
- * | `-- remote_video
- * | |-- depth
- * | |-- height
- * | `-- width
- * .
- * .
- * .
- * `-- n
- * |-- command
- * |-- event
- * |-- reverse_heartbeat
- * `-- remote_video
- * |-- depth
- * |-- height
- * `-- width
- *
- * For each service processor the following files are created:
- *
- * command: execute dot commands
- * write: execute a dot command on the service processor
- * read: return the result of a previously executed dot command
- *
- * events: listen for service processor events
- * read: sleep (interruptible) until an event occurs
- * write: wakeup sleeping event listener
- *
- * reverse_heartbeat: send a heartbeat to the service processor
- * read: sleep (interruptible) until the reverse heartbeat fails
- * write: wakeup sleeping heartbeat listener
- *
- * remote_video/width
- * remote_video/height
- * remote_video/width: control remote display settings
- * write: set value
- * read: read value
- */
-
-#include <linux/fs.h>
-#include <linux/fs_context.h>
-#include <linux/pagemap.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <asm/io.h>
-#include "ibmasm.h"
-#include "remote.h"
-#include "dot_command.h"
-
-#define IBMASMFS_MAGIC 0x66726f67
-
-static LIST_HEAD(service_processors);
-
-static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
-static void ibmasmfs_create_files (struct super_block *sb);
-static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc);
-
-static int ibmasmfs_get_tree(struct fs_context *fc)
-{
- return get_tree_single(fc, ibmasmfs_fill_super);
-}
-
-static const struct fs_context_operations ibmasmfs_context_ops = {
- .get_tree = ibmasmfs_get_tree,
-};
-
-static int ibmasmfs_init_fs_context(struct fs_context *fc)
-{
- fc->ops = &ibmasmfs_context_ops;
- return 0;
-}
-
-static const struct super_operations ibmasmfs_s_ops = {
- .statfs = simple_statfs,
- .drop_inode = inode_just_drop,
-};
-
-static struct file_system_type ibmasmfs_type = {
- .owner = THIS_MODULE,
- .name = "ibmasmfs",
- .init_fs_context = ibmasmfs_init_fs_context,
- .kill_sb = kill_anon_super,
-};
-MODULE_ALIAS_FS("ibmasmfs");
-
-static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc)
-{
- struct inode *root;
-
- sb->s_blocksize = PAGE_SIZE;
- sb->s_blocksize_bits = PAGE_SHIFT;
- sb->s_magic = IBMASMFS_MAGIC;
- sb->s_op = &ibmasmfs_s_ops;
- sb->s_time_gran = 1;
-
- root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
- if (!root)
- return -ENOMEM;
-
- root->i_op = &simple_dir_inode_operations;
- root->i_fop = &simple_dir_operations;
-
- sb->s_root = d_make_root(root);
- if (!sb->s_root)
- return -ENOMEM;
-
- ibmasmfs_create_files(sb);
- return 0;
-}
-
-static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
-{
- struct inode *ret = new_inode(sb);
-
- if (ret) {
- ret->i_ino = get_next_ino();
- ret->i_mode = mode;
- simple_inode_init_ts(ret);
- }
- return ret;
-}
-
-static int ibmasmfs_create_file(struct dentry *parent,
- const char *name,
- const struct file_operations *fops,
- void *data,
- int mode)
-{
- struct dentry *dentry;
- struct inode *inode;
-
- dentry = d_alloc_name(parent, name);
- if (!dentry)
- return -ENOMEM;
-
- inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode);
- if (!inode) {
- dput(dentry);
- return -ENOMEM;
- }
-
- inode->i_fop = fops;
- inode->i_private = data;
-
- d_make_persistent(dentry, inode);
- dput(dentry);
- return 0;
-}
-
-static struct dentry *ibmasmfs_create_dir(struct dentry *parent,
- const char *name)
-{
- struct dentry *dentry;
- struct inode *inode;
-
- dentry = d_alloc_name(parent, name);
- if (!dentry)
- return NULL;
-
- inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500);
- if (!inode) {
- dput(dentry);
- return NULL;
- }
-
- inode->i_op = &simple_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
-
- d_make_persistent(dentry, inode);
- dput(dentry);
- return dentry; // borrowed
-}
-
-int ibmasmfs_register(void)
-{
- return register_filesystem(&ibmasmfs_type);
-}
-
-void ibmasmfs_unregister(void)
-{
- unregister_filesystem(&ibmasmfs_type);
-}
-
-void ibmasmfs_add_sp(struct service_processor *sp)
-{
- list_add(&sp->node, &service_processors);
-}
-
-/* struct to save state between command file operations */
-struct ibmasmfs_command_data {
- struct service_processor *sp;
- struct command *command;
-};
-
-/* struct to save state between event file operations */
-struct ibmasmfs_event_data {
- struct service_processor *sp;
- struct event_reader reader;
- int active;
-};
-
-/* struct to save state between reverse heartbeat file operations */
-struct ibmasmfs_heartbeat_data {
- struct service_processor *sp;
- struct reverse_heartbeat heartbeat;
- int active;
-};
-
-static int command_file_open(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_command_data *command_data;
-
- if (!inode->i_private)
- return -ENODEV;
-
- command_data = kmalloc_obj(struct ibmasmfs_command_data);
- if (!command_data)
- return -ENOMEM;
-
- command_data->command = NULL;
- command_data->sp = inode->i_private;
- file->private_data = command_data;
- return 0;
-}
-
-static int command_file_close(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_command_data *command_data = file->private_data;
-
- if (command_data->command)
- command_put(command_data->command);
-
- kfree(command_data);
- return 0;
-}
-
-static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_command_data *command_data = file->private_data;
- struct command *cmd;
- int len;
- unsigned long flags;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
- return 0;
- if (*offset != 0)
- return 0;
-
- spin_lock_irqsave(&command_data->sp->lock, flags);
- cmd = command_data->command;
- if (cmd == NULL) {
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
- return 0;
- }
- command_data->command = NULL;
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
-
- if (cmd->status != IBMASM_CMD_COMPLETE) {
- command_put(cmd);
- return -EIO;
- }
- len = min(count, cmd->buffer_size);
- if (copy_to_user(buf, cmd->buffer, len)) {
- command_put(cmd);
- return -EFAULT;
- }
- command_put(cmd);
-
- return len;
-}
-
-static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
-{
- struct ibmasmfs_command_data *command_data = file->private_data;
- struct command *cmd;
- unsigned long flags;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
- return 0;
- if (count < sizeof(struct dot_command_header))
- return -EINVAL;
- if (*offset != 0)
- return 0;
-
- /* commands are executed sequentially, only one command at a time */
- if (command_data->command)
- return -EAGAIN;
-
- cmd = ibmasm_new_command(command_data->sp, count);
- if (!cmd)
- return -ENOMEM;
-
- if (copy_from_user(cmd->buffer, ubuff, count)) {
- command_put(cmd);
- return -EFAULT;
- }
-
- if (count < get_dot_command_size(cmd->buffer)) {
- command_put(cmd);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&command_data->sp->lock, flags);
- if (command_data->command) {
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
- command_put(cmd);
- return -EAGAIN;
- }
- command_data->command = cmd;
- spin_unlock_irqrestore(&command_data->sp->lock, flags);
-
- ibmasm_exec_command(command_data->sp, cmd);
- ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
-
- return count;
-}
-
-static int event_file_open(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_event_data *event_data;
- struct service_processor *sp;
-
- if (!inode->i_private)
- return -ENODEV;
-
- sp = inode->i_private;
-
- event_data = kmalloc_obj(struct ibmasmfs_event_data);
- if (!event_data)
- return -ENOMEM;
-
- ibmasm_event_reader_register(sp, &event_data->reader);
-
- event_data->sp = sp;
- event_data->active = 0;
- file->private_data = event_data;
- return 0;
-}
-
-static int event_file_close(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_event_data *event_data = file->private_data;
-
- ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
- kfree(event_data);
- return 0;
-}
-
-static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_event_data *event_data = file->private_data;
- struct event_reader *reader = &event_data->reader;
- struct service_processor *sp = event_data->sp;
- int ret;
- unsigned long flags;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
- return 0;
- if (*offset != 0)
- return 0;
-
- spin_lock_irqsave(&sp->lock, flags);
- if (event_data->active) {
- spin_unlock_irqrestore(&sp->lock, flags);
- return -EBUSY;
- }
- event_data->active = 1;
- spin_unlock_irqrestore(&sp->lock, flags);
-
- ret = ibmasm_get_next_event(sp, reader);
- if (ret <= 0)
- goto out;
-
- if (count < reader->data_size) {
- ret = -EINVAL;
- goto out;
- }
-
- if (copy_to_user(buf, reader->data, reader->data_size)) {
- ret = -EFAULT;
- goto out;
- }
- ret = reader->data_size;
-
-out:
- event_data->active = 0;
- return ret;
-}
-
-static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_event_data *event_data = file->private_data;
-
- if (*offset < 0)
- return -EINVAL;
- if (count != 1)
- return 0;
- if (*offset != 0)
- return 0;
-
- ibmasm_cancel_next_event(&event_data->reader);
- return 0;
-}
-
-static int r_heartbeat_file_open(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_heartbeat_data *rhbeat;
-
- if (!inode->i_private)
- return -ENODEV;
-
- rhbeat = kmalloc_obj(struct ibmasmfs_heartbeat_data);
- if (!rhbeat)
- return -ENOMEM;
-
- rhbeat->sp = inode->i_private;
- rhbeat->active = 0;
- ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
- file->private_data = rhbeat;
- return 0;
-}
-
-static int r_heartbeat_file_close(struct inode *inode, struct file *file)
-{
- struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
-
- kfree(rhbeat);
- return 0;
-}
-
-static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
- unsigned long flags;
- int result;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > 1024)
- return 0;
- if (*offset != 0)
- return 0;
-
- /* allow only one reverse heartbeat per process */
- spin_lock_irqsave(&rhbeat->sp->lock, flags);
- if (rhbeat->active) {
- spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
- return -EBUSY;
- }
- rhbeat->active = 1;
- spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
-
- result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
- rhbeat->active = 0;
-
- return result;
-}
-
-static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
-{
- struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
-
- if (*offset < 0)
- return -EINVAL;
- if (count != 1)
- return 0;
- if (*offset != 0)
- return 0;
-
- if (rhbeat->active)
- ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
-
- return 1;
-}
-
-static int remote_settings_file_close(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
- void __iomem *address = (void __iomem *)file->private_data;
- int len = 0;
- unsigned int value;
- char lbuf[20];
-
- value = readl(address);
- len = snprintf(lbuf, sizeof(lbuf), "%d\n", value);
-
- return simple_read_from_buffer(buf, count, offset, lbuf, len);
-}
-
-static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
-{
- void __iomem *address = (void __iomem *)file->private_data;
- char *buff;
- unsigned int value;
-
- if (*offset < 0)
- return -EINVAL;
- if (count == 0 || count > 1024)
- return 0;
- if (*offset != 0)
- return 0;
-
- buff = memdup_user_nul(ubuff, count);
- if (IS_ERR(buff))
- return PTR_ERR(buff);
-
- value = simple_strtoul(buff, NULL, 10);
- writel(value, address);
- kfree(buff);
-
- return count;
-}
-
-static const struct file_operations command_fops = {
- .open = command_file_open,
- .release = command_file_close,
- .read = command_file_read,
- .write = command_file_write,
- .llseek = generic_file_llseek,
-};
-
-static const struct file_operations event_fops = {
- .open = event_file_open,
- .release = event_file_close,
- .read = event_file_read,
- .write = event_file_write,
- .llseek = generic_file_llseek,
-};
-
-static const struct file_operations r_heartbeat_fops = {
- .open = r_heartbeat_file_open,
- .release = r_heartbeat_file_close,
- .read = r_heartbeat_file_read,
- .write = r_heartbeat_file_write,
- .llseek = generic_file_llseek,
-};
-
-static const struct file_operations remote_settings_fops = {
- .open = simple_open,
- .release = remote_settings_file_close,
- .read = remote_settings_file_read,
- .write = remote_settings_file_write,
- .llseek = generic_file_llseek,
-};
-
-
-static void ibmasmfs_create_files (struct super_block *sb)
-{
- struct list_head *entry;
- struct service_processor *sp;
-
- list_for_each(entry, &service_processors) {
- struct dentry *dir;
- struct dentry *remote_dir;
- sp = list_entry(entry, struct service_processor, node);
- dir = ibmasmfs_create_dir(sb->s_root, sp->dirname);
- if (!dir)
- continue;
-
- ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR);
-
- remote_dir = ibmasmfs_create_dir(dir, "remote_video");
- if (!remote_dir)
- continue;
-
- ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR);
- ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR);
- }
-}
diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c
deleted file mode 100644
index 5313230f36ad..000000000000
--- a/drivers/misc/ibmasm/lowlevel.c
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include "ibmasm.h"
-#include "lowlevel.h"
-#include "i2o.h"
-#include "dot_command.h"
-#include "remote.h"
-
-static struct i2o_header header = I2O_HEADER_TEMPLATE;
-
-
-int ibmasm_send_i2o_message(struct service_processor *sp)
-{
- u32 mfa;
- size_t command_size;
- struct i2o_message *message;
- struct command *command = sp->current_command;
-
- command_size = get_dot_command_size(command->buffer);
- if (command_size > command->buffer_size)
- return 1;
- if (command_size > I2O_COMMAND_SIZE)
- command_size = I2O_COMMAND_SIZE;
-
- mfa = get_mfa_inbound(sp->base_address);
- if (!mfa)
- return 1;
-
- header.message_size = outgoing_message_size((unsigned int)command_size);
- message = get_i2o_message(sp->base_address, mfa);
-
- memcpy_toio(&message->header, &header, sizeof(struct i2o_header));
- memcpy_toio(&message->data, command->buffer, command_size);
-
- set_mfa_inbound(sp->base_address, mfa);
-
- return 0;
-}
-
-irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id)
-{
- u32 mfa;
- struct service_processor *sp = (struct service_processor *)dev_id;
- void __iomem *base_address = sp->base_address;
- char tsbuf[32];
-
- if (!sp_interrupt_pending(base_address))
- return IRQ_NONE;
-
- dbg("respond to interrupt at %s\n", get_timestamp(tsbuf));
-
- if (mouse_interrupt_pending(sp)) {
- ibmasm_handle_mouse_interrupt(sp);
- clear_mouse_interrupt(sp);
- }
-
- mfa = get_mfa_outbound(base_address);
- if (valid_mfa(mfa)) {
- struct i2o_message *msg = get_i2o_message(base_address, mfa);
- ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg));
- } else
- dbg("didn't get a valid MFA\n");
-
- set_mfa_outbound(base_address, mfa);
- dbg("finished interrupt at %s\n", get_timestamp(tsbuf));
-
- return IRQ_HANDLED;
-}
diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h
deleted file mode 100644
index 25f1ed07c3c5..000000000000
--- a/drivers/misc/ibmasm/lowlevel.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-/* Condor service processor specific hardware definitions */
-
-#ifndef __IBMASM_CONDOR_H__
-#define __IBMASM_CONDOR_H__
-
-#include <asm/io.h>
-
-#define VENDORID_IBM 0x1014
-#define DEVICEID_RSA 0x010F
-
-#define GET_MFA_ADDR(x) (x & 0xFFFFFF00)
-
-#define MAILBOX_FULL(x) (x & 0x00000001)
-
-#define NO_MFAS_AVAILABLE 0xFFFFFFFF
-
-
-#define INBOUND_QUEUE_PORT 0x40 /* contains address of next free MFA */
-#define OUTBOUND_QUEUE_PORT 0x44 /* contains address of posted MFA */
-
-#define SP_INTR_MASK 0x00000008
-#define UART_INTR_MASK 0x00000010
-
-#define INTR_STATUS_REGISTER 0x13A0
-#define INTR_CONTROL_REGISTER 0x13A4
-
-#define SCOUT_COM_A_BASE 0x0000
-#define SCOUT_COM_B_BASE 0x0100
-#define SCOUT_COM_C_BASE 0x0200
-#define SCOUT_COM_D_BASE 0x0300
-
-static inline int sp_interrupt_pending(void __iomem *base_address)
-{
- return SP_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER);
-}
-
-static inline int uart_interrupt_pending(void __iomem *base_address)
-{
- return UART_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER);
-}
-
-static inline void ibmasm_enable_interrupts(void __iomem *base_address, int mask)
-{
- void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER;
- writel( readl(ctrl_reg) & ~mask, ctrl_reg);
-}
-
-static inline void ibmasm_disable_interrupts(void __iomem *base_address, int mask)
-{
- void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER;
- writel( readl(ctrl_reg) | mask, ctrl_reg);
-}
-
-static inline void enable_sp_interrupts(void __iomem *base_address)
-{
- ibmasm_enable_interrupts(base_address, SP_INTR_MASK);
-}
-
-static inline void disable_sp_interrupts(void __iomem *base_address)
-{
- ibmasm_disable_interrupts(base_address, SP_INTR_MASK);
-}
-
-static inline void enable_uart_interrupts(void __iomem *base_address)
-{
- ibmasm_enable_interrupts(base_address, UART_INTR_MASK);
-}
-
-static inline void disable_uart_interrupts(void __iomem *base_address)
-{
- ibmasm_disable_interrupts(base_address, UART_INTR_MASK);
-}
-
-#define valid_mfa(mfa) ( (mfa) != NO_MFAS_AVAILABLE )
-
-static inline u32 get_mfa_outbound(void __iomem *base_address)
-{
- int retry;
- u32 mfa;
-
- for (retry=0; retry<=10; retry++) {
- mfa = readl(base_address + OUTBOUND_QUEUE_PORT);
- if (valid_mfa(mfa))
- break;
- }
- return mfa;
-}
-
-static inline void set_mfa_outbound(void __iomem *base_address, u32 mfa)
-{
- writel(mfa, base_address + OUTBOUND_QUEUE_PORT);
-}
-
-static inline u32 get_mfa_inbound(void __iomem *base_address)
-{
- u32 mfa = readl(base_address + INBOUND_QUEUE_PORT);
-
- if (MAILBOX_FULL(mfa))
- return 0;
-
- return mfa;
-}
-
-static inline void set_mfa_inbound(void __iomem *base_address, u32 mfa)
-{
- writel(mfa, base_address + INBOUND_QUEUE_PORT);
-}
-
-static inline struct i2o_message *get_i2o_message(void __iomem *base_address, u32 mfa)
-{
- return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address);
-}
-
-#endif /* __IBMASM_CONDOR_H__ */
diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c
deleted file mode 100644
index 4509c15a76a8..000000000000
--- a/drivers/misc/ibmasm/module.c
+++ /dev/null
@@ -1,224 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- *
- * This driver is based on code originally written by Pete Reynolds
- * and others.
- */
-
-/*
- * The ASM device driver does the following things:
- *
- * 1) When loaded it sends a message to the service processor,
- * indicating that an OS is * running. This causes the service processor
- * to send periodic heartbeats to the OS.
- *
- * 2) Answers the periodic heartbeats sent by the service processor.
- * Failure to do so would result in system reboot.
- *
- * 3) Acts as a pass through for dot commands sent from user applications.
- * The interface for this is the ibmasmfs file system.
- *
- * 4) Allows user applications to register for event notification. Events
- * are sent to the driver through interrupts. They can be read from user
- * space through the ibmasmfs file system.
- *
- * 5) Allows user space applications to send heartbeats to the service
- * processor (aka reverse heartbeats). Again this happens through ibmasmfs.
- *
- * 6) Handles remote mouse and keyboard event interrupts and makes them
- * available to user applications through ibmasmfs.
- *
- */
-
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-#include "remote.h"
-
-int ibmasm_debug = 0;
-module_param(ibmasm_debug, int , S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(ibmasm_debug, " Set debug mode on or off");
-
-
-static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- int result;
- struct service_processor *sp;
-
- if ((result = pci_enable_device(pdev))) {
- dev_err(&pdev->dev, "Failed to enable PCI device\n");
- return result;
- }
- if ((result = pci_request_regions(pdev, DRIVER_NAME))) {
- dev_err(&pdev->dev, "Failed to allocate PCI resources\n");
- goto error_resources;
- }
- /* vnc client won't work without bus-mastering */
- pci_set_master(pdev);
-
- sp = kzalloc_obj(struct service_processor);
- if (sp == NULL) {
- dev_err(&pdev->dev, "Failed to allocate memory\n");
- result = -ENOMEM;
- goto error_kmalloc;
- }
-
- spin_lock_init(&sp->lock);
- INIT_LIST_HEAD(&sp->command_queue);
-
- pci_set_drvdata(pdev, (void *)sp);
- sp->dev = &pdev->dev;
- sp->number = pdev->bus->number;
- snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number);
- snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number);
-
- result = ibmasm_event_buffer_init(sp);
- if (result) {
- dev_err(sp->dev, "Failed to allocate event buffer\n");
- goto error_eventbuffer;
- }
-
- result = ibmasm_heartbeat_init(sp);
- if (result) {
- dev_err(sp->dev, "Failed to allocate heartbeat command\n");
- goto error_heartbeat;
- }
-
- sp->irq = pdev->irq;
- sp->base_address = pci_ioremap_bar(pdev, 0);
- if (!sp->base_address) {
- dev_err(sp->dev, "Failed to ioremap pci memory\n");
- result = -ENODEV;
- goto error_ioremap;
- }
-
- result = request_irq(sp->irq, ibmasm_interrupt_handler, IRQF_SHARED, sp->devname, (void*)sp);
- if (result) {
- dev_err(sp->dev, "Failed to register interrupt handler\n");
- goto error_request_irq;
- }
-
- enable_sp_interrupts(sp->base_address);
-
- result = ibmasm_init_remote_input_dev(sp);
- if (result) {
- dev_err(sp->dev, "Failed to initialize remote queue\n");
- goto error_init_remote;
- }
-
- result = ibmasm_send_driver_vpd(sp);
- if (result) {
- dev_err(sp->dev, "Failed to send driver VPD to service processor\n");
- goto error_send_message;
- }
- result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP);
- if (result) {
- dev_err(sp->dev, "Failed to send OS state to service processor\n");
- goto error_send_message;
- }
- ibmasmfs_add_sp(sp);
-
- ibmasm_register_uart(sp);
-
- return 0;
-
-error_send_message:
- ibmasm_free_remote_input_dev(sp);
-error_init_remote:
- disable_sp_interrupts(sp->base_address);
- free_irq(sp->irq, (void *)sp);
-error_request_irq:
- iounmap(sp->base_address);
-error_ioremap:
- ibmasm_heartbeat_exit(sp);
-error_heartbeat:
- ibmasm_event_buffer_exit(sp);
-error_eventbuffer:
- kfree(sp);
-error_kmalloc:
- pci_release_regions(pdev);
-error_resources:
- pci_disable_device(pdev);
-
- return result;
-}
-
-static void ibmasm_remove_one(struct pci_dev *pdev)
-{
- struct service_processor *sp = pci_get_drvdata(pdev);
-
- dbg("Unregistering UART\n");
- ibmasm_unregister_uart(sp);
- dbg("Sending OS down message\n");
- if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN))
- err("failed to get response to 'Send OS State' command\n");
- dbg("Disabling heartbeats\n");
- ibmasm_heartbeat_exit(sp);
- dbg("Disabling interrupts\n");
- disable_sp_interrupts(sp->base_address);
- dbg("Freeing SP irq\n");
- free_irq(sp->irq, (void *)sp);
- dbg("Cleaning up\n");
- ibmasm_free_remote_input_dev(sp);
- iounmap(sp->base_address);
- ibmasm_event_buffer_exit(sp);
- kfree(sp);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
-}
-
-static struct pci_device_id ibmasm_pci_table[] =
-{
- { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) },
- {},
-};
-
-static struct pci_driver ibmasm_driver = {
- .name = DRIVER_NAME,
- .id_table = ibmasm_pci_table,
- .probe = ibmasm_init_one,
- .remove = ibmasm_remove_one,
-};
-
-static void __exit ibmasm_exit (void)
-{
- ibmasm_unregister_panic_notifier();
- ibmasmfs_unregister();
- pci_unregister_driver(&ibmasm_driver);
- info(DRIVER_DESC " version " DRIVER_VERSION " unloaded");
-}
-
-static int __init ibmasm_init(void)
-{
- int result = pci_register_driver(&ibmasm_driver);
- if (result)
- return result;
-
- result = ibmasmfs_register();
- if (result) {
- pci_unregister_driver(&ibmasm_driver);
- err("Failed to register ibmasmfs file system");
- return result;
- }
-
- ibmasm_register_panic_notifier();
- info(DRIVER_DESC " version " DRIVER_VERSION " loaded");
- return 0;
-}
-
-module_init(ibmasm_init);
-module_exit(ibmasm_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(pci, ibmasm_pci_table);
-
diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c
deleted file mode 100644
index 21c9b6a6f2c3..000000000000
--- a/drivers/misc/ibmasm/r_heartbeat.c
+++ /dev/null
@@ -1,86 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/sched/signal.h>
-#include "ibmasm.h"
-#include "dot_command.h"
-
-/*
- * Reverse Heartbeat, i.e. heartbeats sent from the driver to the
- * service processor.
- * These heartbeats are initiated by user level programs.
- */
-
-/* the reverse heartbeat dot command */
-#pragma pack(1)
-static struct {
- struct dot_command_header header;
- unsigned char command[3];
-} rhb_dot_cmd = {
- .header = {
- .type = sp_read,
- .command_size = 3,
- .data_size = 0,
- .status = 0
- },
- .command = { 4, 3, 6 }
-};
-#pragma pack()
-
-void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
-{
- init_waitqueue_head(&rhb->wait);
- rhb->stopped = 0;
-}
-
-/*
- * start_reverse_heartbeat
- * Loop forever, sending a reverse heartbeat dot command to the service
- * processor, then sleeping. The loop comes to an end if the service
- * processor fails to respond 3 times or we were interrupted.
- */
-int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
-{
- struct command *cmd;
- int times_failed = 0;
- int result = 1;
-
- cmd = ibmasm_new_command(sp, sizeof rhb_dot_cmd);
- if (!cmd)
- return -ENOMEM;
-
- while (times_failed < 3) {
- memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd);
- cmd->status = IBMASM_CMD_PENDING;
- ibmasm_exec_command(sp, cmd);
- ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
-
- if (cmd->status != IBMASM_CMD_COMPLETE)
- times_failed++;
-
- wait_event_interruptible_timeout(rhb->wait,
- rhb->stopped,
- REVERSE_HEARTBEAT_TIMEOUT * HZ);
-
- if (signal_pending(current) || rhb->stopped) {
- result = -EINTR;
- break;
- }
- }
- command_put(cmd);
- rhb->stopped = 0;
-
- return result;
-}
-
-void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb)
-{
- rhb->stopped = 1;
- wake_up_interruptible(&rhb->wait);
-}
diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c
deleted file mode 100644
index 521531738c9a..000000000000
--- a/drivers/misc/ibmasm/remote.c
+++ /dev/null
@@ -1,273 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Authors: Max Asböck <amax@us.ibm.com>
- * Vernon Mauery <vernux@us.ibm.com>
- */
-
-/* Remote mouse and keyboard event handling functions */
-
-#include <linux/pci.h>
-#include "ibmasm.h"
-#include "remote.h"
-
-#define MOUSE_X_MAX 1600
-#define MOUSE_Y_MAX 1200
-
-static const unsigned short xlate_high[XLATE_SIZE] = {
- [KEY_SYM_ENTER & 0xff] = KEY_ENTER,
- [KEY_SYM_KPSLASH & 0xff] = KEY_KPSLASH,
- [KEY_SYM_KPSTAR & 0xff] = KEY_KPASTERISK,
- [KEY_SYM_KPMINUS & 0xff] = KEY_KPMINUS,
- [KEY_SYM_KPDOT & 0xff] = KEY_KPDOT,
- [KEY_SYM_KPPLUS & 0xff] = KEY_KPPLUS,
- [KEY_SYM_KP0 & 0xff] = KEY_KP0,
- [KEY_SYM_KP1 & 0xff] = KEY_KP1,
- [KEY_SYM_KP2 & 0xff] = KEY_KP2, [KEY_SYM_KPDOWN & 0xff] = KEY_KP2,
- [KEY_SYM_KP3 & 0xff] = KEY_KP3,
- [KEY_SYM_KP4 & 0xff] = KEY_KP4, [KEY_SYM_KPLEFT & 0xff] = KEY_KP4,
- [KEY_SYM_KP5 & 0xff] = KEY_KP5,
- [KEY_SYM_KP6 & 0xff] = KEY_KP6, [KEY_SYM_KPRIGHT & 0xff] = KEY_KP6,
- [KEY_SYM_KP7 & 0xff] = KEY_KP7,
- [KEY_SYM_KP8 & 0xff] = KEY_KP8, [KEY_SYM_KPUP & 0xff] = KEY_KP8,
- [KEY_SYM_KP9 & 0xff] = KEY_KP9,
- [KEY_SYM_BK_SPC & 0xff] = KEY_BACKSPACE,
- [KEY_SYM_TAB & 0xff] = KEY_TAB,
- [KEY_SYM_CTRL & 0xff] = KEY_LEFTCTRL,
- [KEY_SYM_ALT & 0xff] = KEY_LEFTALT,
- [KEY_SYM_INSERT & 0xff] = KEY_INSERT,
- [KEY_SYM_DELETE & 0xff] = KEY_DELETE,
- [KEY_SYM_SHIFT & 0xff] = KEY_LEFTSHIFT,
- [KEY_SYM_UARROW & 0xff] = KEY_UP,
- [KEY_SYM_DARROW & 0xff] = KEY_DOWN,
- [KEY_SYM_LARROW & 0xff] = KEY_LEFT,
- [KEY_SYM_RARROW & 0xff] = KEY_RIGHT,
- [KEY_SYM_ESCAPE & 0xff] = KEY_ESC,
- [KEY_SYM_PAGEUP & 0xff] = KEY_PAGEUP,
- [KEY_SYM_PAGEDOWN & 0xff] = KEY_PAGEDOWN,
- [KEY_SYM_HOME & 0xff] = KEY_HOME,
- [KEY_SYM_END & 0xff] = KEY_END,
- [KEY_SYM_F1 & 0xff] = KEY_F1,
- [KEY_SYM_F2 & 0xff] = KEY_F2,
- [KEY_SYM_F3 & 0xff] = KEY_F3,
- [KEY_SYM_F4 & 0xff] = KEY_F4,
- [KEY_SYM_F5 & 0xff] = KEY_F5,
- [KEY_SYM_F6 & 0xff] = KEY_F6,
- [KEY_SYM_F7 & 0xff] = KEY_F7,
- [KEY_SYM_F8 & 0xff] = KEY_F8,
- [KEY_SYM_F9 & 0xff] = KEY_F9,
- [KEY_SYM_F10 & 0xff] = KEY_F10,
- [KEY_SYM_F11 & 0xff] = KEY_F11,
- [KEY_SYM_F12 & 0xff] = KEY_F12,
- [KEY_SYM_CAP_LOCK & 0xff] = KEY_CAPSLOCK,
- [KEY_SYM_NUM_LOCK & 0xff] = KEY_NUMLOCK,
- [KEY_SYM_SCR_LOCK & 0xff] = KEY_SCROLLLOCK,
-};
-
-static const unsigned short xlate[XLATE_SIZE] = {
- [NO_KEYCODE] = KEY_RESERVED,
- [KEY_SYM_SPACE] = KEY_SPACE,
- [KEY_SYM_TILDE] = KEY_GRAVE, [KEY_SYM_BKTIC] = KEY_GRAVE,
- [KEY_SYM_ONE] = KEY_1, [KEY_SYM_BANG] = KEY_1,
- [KEY_SYM_TWO] = KEY_2, [KEY_SYM_AT] = KEY_2,
- [KEY_SYM_THREE] = KEY_3, [KEY_SYM_POUND] = KEY_3,
- [KEY_SYM_FOUR] = KEY_4, [KEY_SYM_DOLLAR] = KEY_4,
- [KEY_SYM_FIVE] = KEY_5, [KEY_SYM_PERCENT] = KEY_5,
- [KEY_SYM_SIX] = KEY_6, [KEY_SYM_CARAT] = KEY_6,
- [KEY_SYM_SEVEN] = KEY_7, [KEY_SYM_AMPER] = KEY_7,
- [KEY_SYM_EIGHT] = KEY_8, [KEY_SYM_STAR] = KEY_8,
- [KEY_SYM_NINE] = KEY_9, [KEY_SYM_LPAREN] = KEY_9,
- [KEY_SYM_ZERO] = KEY_0, [KEY_SYM_RPAREN] = KEY_0,
- [KEY_SYM_MINUS] = KEY_MINUS, [KEY_SYM_USCORE] = KEY_MINUS,
- [KEY_SYM_EQUAL] = KEY_EQUAL, [KEY_SYM_PLUS] = KEY_EQUAL,
- [KEY_SYM_LBRKT] = KEY_LEFTBRACE, [KEY_SYM_LCURLY] = KEY_LEFTBRACE,
- [KEY_SYM_RBRKT] = KEY_RIGHTBRACE, [KEY_SYM_RCURLY] = KEY_RIGHTBRACE,
- [KEY_SYM_SLASH] = KEY_BACKSLASH, [KEY_SYM_PIPE] = KEY_BACKSLASH,
- [KEY_SYM_TIC] = KEY_APOSTROPHE, [KEY_SYM_QUOTE] = KEY_APOSTROPHE,
- [KEY_SYM_SEMIC] = KEY_SEMICOLON, [KEY_SYM_COLON] = KEY_SEMICOLON,
- [KEY_SYM_COMMA] = KEY_COMMA, [KEY_SYM_LT] = KEY_COMMA,
- [KEY_SYM_PERIOD] = KEY_DOT, [KEY_SYM_GT] = KEY_DOT,
- [KEY_SYM_BSLASH] = KEY_SLASH, [KEY_SYM_QMARK] = KEY_SLASH,
- [KEY_SYM_A] = KEY_A, [KEY_SYM_a] = KEY_A,
- [KEY_SYM_B] = KEY_B, [KEY_SYM_b] = KEY_B,
- [KEY_SYM_C] = KEY_C, [KEY_SYM_c] = KEY_C,
- [KEY_SYM_D] = KEY_D, [KEY_SYM_d] = KEY_D,
- [KEY_SYM_E] = KEY_E, [KEY_SYM_e] = KEY_E,
- [KEY_SYM_F] = KEY_F, [KEY_SYM_f] = KEY_F,
- [KEY_SYM_G] = KEY_G, [KEY_SYM_g] = KEY_G,
- [KEY_SYM_H] = KEY_H, [KEY_SYM_h] = KEY_H,
- [KEY_SYM_I] = KEY_I, [KEY_SYM_i] = KEY_I,
- [KEY_SYM_J] = KEY_J, [KEY_SYM_j] = KEY_J,
- [KEY_SYM_K] = KEY_K, [KEY_SYM_k] = KEY_K,
- [KEY_SYM_L] = KEY_L, [KEY_SYM_l] = KEY_L,
- [KEY_SYM_M] = KEY_M, [KEY_SYM_m] = KEY_M,
- [KEY_SYM_N] = KEY_N, [KEY_SYM_n] = KEY_N,
- [KEY_SYM_O] = KEY_O, [KEY_SYM_o] = KEY_O,
- [KEY_SYM_P] = KEY_P, [KEY_SYM_p] = KEY_P,
- [KEY_SYM_Q] = KEY_Q, [KEY_SYM_q] = KEY_Q,
- [KEY_SYM_R] = KEY_R, [KEY_SYM_r] = KEY_R,
- [KEY_SYM_S] = KEY_S, [KEY_SYM_s] = KEY_S,
- [KEY_SYM_T] = KEY_T, [KEY_SYM_t] = KEY_T,
- [KEY_SYM_U] = KEY_U, [KEY_SYM_u] = KEY_U,
- [KEY_SYM_V] = KEY_V, [KEY_SYM_v] = KEY_V,
- [KEY_SYM_W] = KEY_W, [KEY_SYM_w] = KEY_W,
- [KEY_SYM_X] = KEY_X, [KEY_SYM_x] = KEY_X,
- [KEY_SYM_Y] = KEY_Y, [KEY_SYM_y] = KEY_Y,
- [KEY_SYM_Z] = KEY_Z, [KEY_SYM_z] = KEY_Z,
-};
-
-static void print_input(struct remote_input *input)
-{
- if (input->type == INPUT_TYPE_MOUSE) {
- unsigned char buttons = input->mouse_buttons;
- dbg("remote mouse movement: (x,y)=(%d,%d)%s%s%s%s\n",
- input->data.mouse.x, input->data.mouse.y,
- (buttons) ? " -- buttons:" : "",
- (buttons & REMOTE_BUTTON_LEFT) ? "left " : "",
- (buttons & REMOTE_BUTTON_MIDDLE) ? "middle " : "",
- (buttons & REMOTE_BUTTON_RIGHT) ? "right" : ""
- );
- } else {
- dbg("remote keypress (code, flag, down):"
- "%d (0x%x) [0x%x] [0x%x]\n",
- input->data.keyboard.key_code,
- input->data.keyboard.key_code,
- input->data.keyboard.key_flag,
- input->data.keyboard.key_down
- );
- }
-}
-
-static void send_mouse_event(struct input_dev *dev, struct remote_input *input)
-{
- unsigned char buttons = input->mouse_buttons;
-
- input_report_abs(dev, ABS_X, input->data.mouse.x);
- input_report_abs(dev, ABS_Y, input->data.mouse.y);
- input_report_key(dev, BTN_LEFT, buttons & REMOTE_BUTTON_LEFT);
- input_report_key(dev, BTN_MIDDLE, buttons & REMOTE_BUTTON_MIDDLE);
- input_report_key(dev, BTN_RIGHT, buttons & REMOTE_BUTTON_RIGHT);
- input_sync(dev);
-}
-
-static void send_keyboard_event(struct input_dev *dev,
- struct remote_input *input)
-{
- unsigned int key;
- unsigned short code = input->data.keyboard.key_code;
-
- if (code & 0xff00)
- key = xlate_high[code & 0xff];
- else
- key = xlate[code];
- input_report_key(dev, key, input->data.keyboard.key_down);
- input_sync(dev);
-}
-
-void ibmasm_handle_mouse_interrupt(struct service_processor *sp)
-{
- unsigned long reader;
- unsigned long writer;
- struct remote_input input;
-
- reader = get_queue_reader(sp);
- writer = get_queue_writer(sp);
-
- while (reader != writer) {
- if (reader >= REMOTE_QUEUE_SIZE || writer >= REMOTE_QUEUE_SIZE) {
- set_queue_reader(sp, 0);
- break;
- }
-
- memcpy_fromio(&input, get_queue_entry(sp, reader),
- sizeof(struct remote_input));
-
- print_input(&input);
- if (input.type == INPUT_TYPE_MOUSE) {
- send_mouse_event(sp->remote.mouse_dev, &input);
- } else if (input.type == INPUT_TYPE_KEYBOARD) {
- send_keyboard_event(sp->remote.keybd_dev, &input);
- } else
- break;
-
- reader = advance_queue_reader(sp, reader);
- writer = get_queue_writer(sp);
- }
-}
-
-int ibmasm_init_remote_input_dev(struct service_processor *sp)
-{
- /* set up the mouse input device */
- struct input_dev *mouse_dev, *keybd_dev;
- struct pci_dev *pdev = to_pci_dev(sp->dev);
- int error = -ENOMEM;
- int i;
-
- sp->remote.mouse_dev = mouse_dev = input_allocate_device();
- sp->remote.keybd_dev = keybd_dev = input_allocate_device();
-
- if (!mouse_dev || !keybd_dev)
- goto err_free_devices;
-
- mouse_dev->id.bustype = BUS_PCI;
- mouse_dev->id.vendor = pdev->vendor;
- mouse_dev->id.product = pdev->device;
- mouse_dev->id.version = 1;
- mouse_dev->dev.parent = sp->dev;
- mouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- mouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
- BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
- set_bit(BTN_TOUCH, mouse_dev->keybit);
- mouse_dev->name = "ibmasm RSA I remote mouse";
- input_set_abs_params(mouse_dev, ABS_X, 0, MOUSE_X_MAX, 0, 0);
- input_set_abs_params(mouse_dev, ABS_Y, 0, MOUSE_Y_MAX, 0, 0);
-
- keybd_dev->id.bustype = BUS_PCI;
- keybd_dev->id.vendor = pdev->vendor;
- keybd_dev->id.product = pdev->device;
- keybd_dev->id.version = 2;
- keybd_dev->dev.parent = sp->dev;
- keybd_dev->evbit[0] = BIT_MASK(EV_KEY);
- keybd_dev->name = "ibmasm RSA I remote keyboard";
-
- for (i = 0; i < XLATE_SIZE; i++) {
- if (xlate_high[i])
- set_bit(xlate_high[i], keybd_dev->keybit);
- if (xlate[i])
- set_bit(xlate[i], keybd_dev->keybit);
- }
-
- error = input_register_device(mouse_dev);
- if (error)
- goto err_free_devices;
-
- error = input_register_device(keybd_dev);
- if (error)
- goto err_unregister_mouse_dev;
-
- enable_mouse_interrupts(sp);
-
- printk(KERN_INFO "ibmasm remote responding to events on RSA card %d\n", sp->number);
-
- return 0;
-
- err_unregister_mouse_dev:
- input_unregister_device(mouse_dev);
- mouse_dev = NULL; /* so we don't try to free it again below */
- err_free_devices:
- input_free_device(mouse_dev);
- input_free_device(keybd_dev);
-
- return error;
-}
-
-void ibmasm_free_remote_input_dev(struct service_processor *sp)
-{
- disable_mouse_interrupts(sp);
- input_unregister_device(sp->remote.mouse_dev);
- input_unregister_device(sp->remote.keybd_dev);
-}
-
diff --git a/drivers/misc/ibmasm/remote.h b/drivers/misc/ibmasm/remote.h
deleted file mode 100644
index ec4e78ec5a67..000000000000
--- a/drivers/misc/ibmasm/remote.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- *
- * Originally written by Pete Reynolds
- */
-
-#ifndef _IBMASM_REMOTE_H_
-#define _IBMASM_REMOTE_H_
-
-#include <asm/io.h>
-
-/* pci offsets */
-#define CONDOR_MOUSE_DATA 0x000AC000
-#define CONDOR_MOUSE_ISR_CONTROL 0x00
-#define CONDOR_MOUSE_ISR_STATUS 0x04
-#define CONDOR_MOUSE_Q_READER 0x08
-#define CONDOR_MOUSE_Q_WRITER 0x0C
-#define CONDOR_MOUSE_Q_BEGIN 0x10
-#define CONDOR_MOUSE_MAX_X 0x14
-#define CONDOR_MOUSE_MAX_Y 0x18
-
-#define CONDOR_INPUT_DESKTOP_INFO 0x1F0
-#define CONDOR_INPUT_DISPLAY_RESX 0x1F4
-#define CONDOR_INPUT_DISPLAY_RESY 0x1F8
-#define CONDOR_INPUT_DISPLAY_BITS 0x1FC
-#define CONDOR_OUTPUT_VNC_STATUS 0x200
-
-#define CONDOR_MOUSE_INTR_STATUS_MASK 0x00000001
-
-#define INPUT_TYPE_MOUSE 0x1
-#define INPUT_TYPE_KEYBOARD 0x2
-
-
-/* mouse button states received from SP */
-#define REMOTE_DOUBLE_CLICK 0xF0
-#define REMOTE_BUTTON_LEFT 0x01
-#define REMOTE_BUTTON_MIDDLE 0x02
-#define REMOTE_BUTTON_RIGHT 0x04
-
-/* size of keysym/keycode translation matrices */
-#define XLATE_SIZE 256
-
-struct mouse_input {
- unsigned short y;
- unsigned short x;
-};
-
-
-struct keyboard_input {
- unsigned short key_code;
- unsigned char key_flag;
- unsigned char key_down;
-};
-
-
-
-struct remote_input {
- union {
- struct mouse_input mouse;
- struct keyboard_input keyboard;
- } data;
-
- unsigned char type;
- unsigned char pad1;
- unsigned char mouse_buttons;
- unsigned char pad3;
-};
-
-#define mouse_addr(sp) (sp->base_address + CONDOR_MOUSE_DATA)
-#define display_width(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESX)
-#define display_height(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESY)
-#define display_depth(sp) (mouse_addr(sp) + CONDOR_INPUT_DISPLAY_BITS)
-#define desktop_info(sp) (mouse_addr(sp) + CONDOR_INPUT_DESKTOP_INFO)
-#define vnc_status(sp) (mouse_addr(sp) + CONDOR_OUTPUT_VNC_STATUS)
-#define isr_control(sp) (mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
-
-#define mouse_interrupt_pending(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS)
-#define clear_mouse_interrupt(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS)
-#define enable_mouse_interrupts(sp) writel(1, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
-#define disable_mouse_interrupts(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
-
-/* remote input queue operations */
-#define REMOTE_QUEUE_SIZE 60
-
-#define get_queue_writer(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_WRITER)
-#define get_queue_reader(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_READER)
-#define set_queue_reader(sp, reader) writel(reader, mouse_addr(sp) + CONDOR_MOUSE_Q_READER)
-
-#define queue_begin (mouse_addr(sp) + CONDOR_MOUSE_Q_BEGIN)
-
-#define get_queue_entry(sp, read_index) \
- ((void*)(queue_begin + read_index * sizeof(struct remote_input)))
-
-static inline int advance_queue_reader(struct service_processor *sp, unsigned long reader)
-{
- reader++;
- if (reader == REMOTE_QUEUE_SIZE)
- reader = 0;
-
- set_queue_reader(sp, reader);
- return reader;
-}
-
-#define NO_KEYCODE 0
-#define KEY_SYM_BK_SPC 0xFF08
-#define KEY_SYM_TAB 0xFF09
-#define KEY_SYM_ENTER 0xFF0D
-#define KEY_SYM_SCR_LOCK 0xFF14
-#define KEY_SYM_ESCAPE 0xFF1B
-#define KEY_SYM_HOME 0xFF50
-#define KEY_SYM_LARROW 0xFF51
-#define KEY_SYM_UARROW 0xFF52
-#define KEY_SYM_RARROW 0xFF53
-#define KEY_SYM_DARROW 0xFF54
-#define KEY_SYM_PAGEUP 0xFF55
-#define KEY_SYM_PAGEDOWN 0xFF56
-#define KEY_SYM_END 0xFF57
-#define KEY_SYM_INSERT 0xFF63
-#define KEY_SYM_NUM_LOCK 0xFF7F
-#define KEY_SYM_KPSTAR 0xFFAA
-#define KEY_SYM_KPPLUS 0xFFAB
-#define KEY_SYM_KPMINUS 0xFFAD
-#define KEY_SYM_KPDOT 0xFFAE
-#define KEY_SYM_KPSLASH 0xFFAF
-#define KEY_SYM_KPRIGHT 0xFF96
-#define KEY_SYM_KPUP 0xFF97
-#define KEY_SYM_KPLEFT 0xFF98
-#define KEY_SYM_KPDOWN 0xFF99
-#define KEY_SYM_KP0 0xFFB0
-#define KEY_SYM_KP1 0xFFB1
-#define KEY_SYM_KP2 0xFFB2
-#define KEY_SYM_KP3 0xFFB3
-#define KEY_SYM_KP4 0xFFB4
-#define KEY_SYM_KP5 0xFFB5
-#define KEY_SYM_KP6 0xFFB6
-#define KEY_SYM_KP7 0xFFB7
-#define KEY_SYM_KP8 0xFFB8
-#define KEY_SYM_KP9 0xFFB9
-#define KEY_SYM_F1 0xFFBE // 1B 5B 5B 41
-#define KEY_SYM_F2 0xFFBF // 1B 5B 5B 42
-#define KEY_SYM_F3 0xFFC0 // 1B 5B 5B 43
-#define KEY_SYM_F4 0xFFC1 // 1B 5B 5B 44
-#define KEY_SYM_F5 0xFFC2 // 1B 5B 5B 45
-#define KEY_SYM_F6 0xFFC3 // 1B 5B 31 37 7E
-#define KEY_SYM_F7 0xFFC4 // 1B 5B 31 38 7E
-#define KEY_SYM_F8 0xFFC5 // 1B 5B 31 39 7E
-#define KEY_SYM_F9 0xFFC6 // 1B 5B 32 30 7E
-#define KEY_SYM_F10 0xFFC7 // 1B 5B 32 31 7E
-#define KEY_SYM_F11 0xFFC8 // 1B 5B 32 33 7E
-#define KEY_SYM_F12 0xFFC9 // 1B 5B 32 34 7E
-#define KEY_SYM_SHIFT 0xFFE1
-#define KEY_SYM_CTRL 0xFFE3
-#define KEY_SYM_ALT 0xFFE9
-#define KEY_SYM_CAP_LOCK 0xFFE5
-#define KEY_SYM_DELETE 0xFFFF
-#define KEY_SYM_TILDE 0x60
-#define KEY_SYM_BKTIC 0x7E
-#define KEY_SYM_ONE 0x31
-#define KEY_SYM_BANG 0x21
-#define KEY_SYM_TWO 0x32
-#define KEY_SYM_AT 0x40
-#define KEY_SYM_THREE 0x33
-#define KEY_SYM_POUND 0x23
-#define KEY_SYM_FOUR 0x34
-#define KEY_SYM_DOLLAR 0x24
-#define KEY_SYM_FIVE 0x35
-#define KEY_SYM_PERCENT 0x25
-#define KEY_SYM_SIX 0x36
-#define KEY_SYM_CARAT 0x5E
-#define KEY_SYM_SEVEN 0x37
-#define KEY_SYM_AMPER 0x26
-#define KEY_SYM_EIGHT 0x38
-#define KEY_SYM_STAR 0x2A
-#define KEY_SYM_NINE 0x39
-#define KEY_SYM_LPAREN 0x28
-#define KEY_SYM_ZERO 0x30
-#define KEY_SYM_RPAREN 0x29
-#define KEY_SYM_MINUS 0x2D
-#define KEY_SYM_USCORE 0x5F
-#define KEY_SYM_EQUAL 0x2B
-#define KEY_SYM_PLUS 0x3D
-#define KEY_SYM_LBRKT 0x5B
-#define KEY_SYM_LCURLY 0x7B
-#define KEY_SYM_RBRKT 0x5D
-#define KEY_SYM_RCURLY 0x7D
-#define KEY_SYM_SLASH 0x5C
-#define KEY_SYM_PIPE 0x7C
-#define KEY_SYM_TIC 0x27
-#define KEY_SYM_QUOTE 0x22
-#define KEY_SYM_SEMIC 0x3B
-#define KEY_SYM_COLON 0x3A
-#define KEY_SYM_COMMA 0x2C
-#define KEY_SYM_LT 0x3C
-#define KEY_SYM_PERIOD 0x2E
-#define KEY_SYM_GT 0x3E
-#define KEY_SYM_BSLASH 0x2F
-#define KEY_SYM_QMARK 0x3F
-#define KEY_SYM_A 0x41
-#define KEY_SYM_B 0x42
-#define KEY_SYM_C 0x43
-#define KEY_SYM_D 0x44
-#define KEY_SYM_E 0x45
-#define KEY_SYM_F 0x46
-#define KEY_SYM_G 0x47
-#define KEY_SYM_H 0x48
-#define KEY_SYM_I 0x49
-#define KEY_SYM_J 0x4A
-#define KEY_SYM_K 0x4B
-#define KEY_SYM_L 0x4C
-#define KEY_SYM_M 0x4D
-#define KEY_SYM_N 0x4E
-#define KEY_SYM_O 0x4F
-#define KEY_SYM_P 0x50
-#define KEY_SYM_Q 0x51
-#define KEY_SYM_R 0x52
-#define KEY_SYM_S 0x53
-#define KEY_SYM_T 0x54
-#define KEY_SYM_U 0x55
-#define KEY_SYM_V 0x56
-#define KEY_SYM_W 0x57
-#define KEY_SYM_X 0x58
-#define KEY_SYM_Y 0x59
-#define KEY_SYM_Z 0x5A
-#define KEY_SYM_a 0x61
-#define KEY_SYM_b 0x62
-#define KEY_SYM_c 0x63
-#define KEY_SYM_d 0x64
-#define KEY_SYM_e 0x65
-#define KEY_SYM_f 0x66
-#define KEY_SYM_g 0x67
-#define KEY_SYM_h 0x68
-#define KEY_SYM_i 0x69
-#define KEY_SYM_j 0x6A
-#define KEY_SYM_k 0x6B
-#define KEY_SYM_l 0x6C
-#define KEY_SYM_m 0x6D
-#define KEY_SYM_n 0x6E
-#define KEY_SYM_o 0x6F
-#define KEY_SYM_p 0x70
-#define KEY_SYM_q 0x71
-#define KEY_SYM_r 0x72
-#define KEY_SYM_s 0x73
-#define KEY_SYM_t 0x74
-#define KEY_SYM_u 0x75
-#define KEY_SYM_v 0x76
-#define KEY_SYM_w 0x77
-#define KEY_SYM_x 0x78
-#define KEY_SYM_y 0x79
-#define KEY_SYM_z 0x7A
-#define KEY_SYM_SPACE 0x20
-#endif /* _IBMASM_REMOTE_H_ */
diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c
deleted file mode 100644
index a5d4c8e010ee..000000000000
--- a/drivers/misc/ibmasm/uart.c
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * IBM ASM Service Processor Device Driver
- *
- * Copyright (C) IBM Corporation, 2004
- *
- * Author: Max Asböck <amax@us.ibm.com>
- */
-
-#include <linux/termios.h>
-#include <linux/tty.h>
-#include <linux/serial_core.h>
-#include <linux/serial_reg.h>
-#include <linux/serial_8250.h>
-#include "ibmasm.h"
-#include "lowlevel.h"
-
-
-void ibmasm_register_uart(struct service_processor *sp)
-{
- struct uart_8250_port uart;
- void __iomem *iomem_base;
-
- iomem_base = sp->base_address + SCOUT_COM_B_BASE;
-
- /* read the uart scratch register to determine if the UART
- * is dedicated to the service processor or if the OS can use it
- */
- if (0 == readl(iomem_base + UART_SCR)) {
- dev_info(sp->dev, "IBM SP UART not registered, owned by service processor\n");
- sp->serial_line = -1;
- return;
- }
-
- memset(&uart, 0, sizeof(uart));
- uart.port.irq = sp->irq;
- uart.port.uartclk = 3686400;
- uart.port.flags = UPF_SHARE_IRQ;
- uart.port.iotype = UPIO_MEM;
- uart.port.membase = iomem_base;
-
- sp->serial_line = serial8250_register_8250_port(&uart);
- if (sp->serial_line < 0) {
- dev_err(sp->dev, "Failed to register serial port\n");
- return;
- }
- enable_uart_interrupts(sp->base_address);
-}
-
-void ibmasm_unregister_uart(struct service_processor *sp)
-{
- if (sp->serial_line < 0)
- return;
-
- disable_uart_interrupts(sp->base_address);
- serial8250_unregister_port(sp->serial_line);
-}
diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c
index beb18c34f20d..da82b1edb7c9 100644
--- a/drivers/misc/ibmvmc.c
+++ b/drivers/misc/ibmvmc.c
@@ -1040,7 +1040,7 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer,
size_t count, loff_t *ppos)
{
struct inode *inode;
- struct ibmvmc_buffer *vmc_buffer;
+ struct ibmvmc_buffer *vmc_buffer = NULL;
struct ibmvmc_file_session *session;
struct crq_server_adapter *adapter;
struct ibmvmc_hmc *hmc;
@@ -1130,9 +1130,15 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer,
dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n",
(unsigned long)file, (unsigned long)count);
- ibmvmc_send_msg(adapter, vmc_buffer, hmc, count);
+ if (ibmvmc_send_msg(adapter, vmc_buffer, hmc, count)) {
+ ret = -EIO;
+ goto out;
+ }
+ vmc_buffer = NULL;
ret = p - buffer;
out:
+ if (vmc_buffer)
+ vmc_buffer->free = 1;
spin_unlock_irqrestore(&hmc->lock, flags);
return (ssize_t)(ret);
}
@@ -1653,6 +1659,13 @@ static int ibmvmc_recv_msg(struct crq_server_adapter *adapter,
return -1;
}
+ if (msg_len > buffer->size) {
+ dev_err(adapter->dev, "Recv_msg: msg_len 0x%lx exceeds buffer size 0x%x\n",
+ msg_len, buffer->size);
+ spin_unlock_irqrestore(&hmc->lock, flags);
+ return -1;
+ }
+
/* RDMA the data into the partition. */
rc = h_copy_rdma(msg_len,
adapter->riobn,
@@ -2298,8 +2311,8 @@ static void ibmvmc_remove(struct vio_dev *vdev)
}
static struct vio_device_id ibmvmc_device_table[] = {
- { "ibm,vmc", "IBM,vmc" },
- { "", "" }
+ { .type = "ibm,vmc", .compat = "IBM,vmc" },
+ { }
};
MODULE_DEVICE_TABLE(vio, ibmvmc_device_table);
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c
index 015710762a65..e8bcf0df2da8 100644
--- a/drivers/misc/ics932s401.c
+++ b/drivers/misc/ics932s401.c
@@ -95,7 +95,7 @@ static int ics932s401_detect(struct i2c_client *client,
static void ics932s401_remove(struct i2c_client *client);
static const struct i2c_device_id ics932s401_id[] = {
- { "ics932s401" },
+ { .name = "ics932s401" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ics932s401_id);
diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c
index 95480e16ae5f..c9b526a212d0 100644
--- a/drivers/misc/isl29003.c
+++ b/drivers/misc/isl29003.c
@@ -449,8 +449,8 @@ static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id isl29003_id[] = {
- { "isl29003" },
- {}
+ { .name = "isl29003" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, isl29003_id);
diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c
index c288aeec16c0..dd966db28ec4 100644
--- a/drivers/misc/isl29020.c
+++ b/drivers/misc/isl29020.c
@@ -177,7 +177,7 @@ static void isl29020_remove(struct i2c_client *client)
}
static const struct i2c_device_id isl29020_id[] = {
- { "isl29020" },
+ { .name = "isl29020" },
{ }
};
diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig
new file mode 100644
index 000000000000..12a3fd809969
--- /dev/null
+++ b/drivers/misc/issei/Kconfig
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023-2026 Intel Corporation
+config INTEL_SSEI
+ tristate "Intel Silicon Security Engine Interface"
+ help
+ The ISSEI (Intel Silicon Security Engine Interface)
+ subsystem provides a communication channel between the host and the
+ Silicon Security Engine.
+ Enable this driver to get SPDM and other features on Intel client CPUs
+ released in 2024 (Lunar Lake) or later.
+
+ If selected, the /dev/isseiX device will be created.
+ If in doubt, select N.
+
+if INTEL_SSEI
+
+config INTEL_SSEI_HW_HECI
+ tristate "Intel Silicon Security Engine Interface Hardware"
+ depends on X86 && PCI
+ help
+ HECI interface of communication channel between
+ the host and the Silicon Security Engine.
+
+ Implementation of ISSEI communication channel over
+ the HECI hardware PCI device.
+ This device is available on Intel client CPUs released in 2024
+ (Lunar Lake) or later.
+
+endif
diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile
new file mode 100644
index 000000000000..8fac360c3342
--- /dev/null
+++ b/drivers/misc/issei/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023-2026 Intel Corporation
+ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"'
+
+obj-$(CONFIG_INTEL_SSEI) += issei.o
+issei-objs += cdev.o
+issei-objs += dma.o
+issei-objs += fw_client.o
+issei-objs += host_client.o
+issei-objs += ham.o
+issei-objs += main.o
+
+obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o
+issei-heci-objs := pci_heci.o
+issei-heci-objs += hw_heci.o
diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c
new file mode 100644
index 000000000000..b7d65e2d0813
--- /dev/null
+++ b/drivers/misc/issei/cdev.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/atomic.h>
+#include <linux/cdev.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/issei.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/xarray.h>
+
+#include "issei_dev.h"
+#include "host_client.h"
+#include "cdev.h"
+
+struct class *issei_class;
+static dev_t issei_devt;
+
+#define ISSEI_MAX_DEVS MINORMASK
+
+static DEFINE_XARRAY_ALLOC(issei_minor_xa);
+
+static int issei_open(struct inode *inode, struct file *fp)
+{
+ struct issei_host_client *cl;
+ struct issei_device *idev;
+
+ xa_lock(&issei_minor_xa);
+ idev = xa_load(&issei_minor_xa, iminor(inode));
+ if (idev)
+ get_device(&idev->dev);
+ xa_unlock(&issei_minor_xa);
+ if (!idev)
+ return -ENODEV;
+
+ cl = issei_cl_create(idev, fp);
+ if (IS_ERR(cl)) {
+ put_device(&idev->dev);
+ return PTR_ERR(cl);
+ }
+ fp->private_data = cl;
+
+ return nonseekable_open(inode, fp);
+}
+
+static int issei_release(struct inode *inode, struct file *fp)
+{
+ struct issei_host_client *cl = fp->private_data;
+ struct issei_device *idev = cl->idev;
+
+ issei_cl_remove(cl);
+ put_device(&idev->dev);
+
+ return 0;
+}
+
+static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
+{
+ struct issei_host_client *cl = file->private_data;
+ struct issei_connect_client_data conn;
+ struct issei_device *idev = cl->idev;
+ int ret;
+
+ switch (cmd) {
+ case IOCTL_ISSEI_CONNECT_CLIENT:
+ dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n");
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -ENODEV;
+ }
+
+ if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) {
+ dev_dbg(&idev->dev, "failed to copy data from userland\n");
+ return -EFAULT;
+ }
+
+ ret = issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid,
+ &conn.out_client_properties.max_msg_length,
+ &conn.out_client_properties.protocol_version,
+ &conn.out_client_properties.flags);
+ if (ret)
+ return ret;
+
+ if (copy_to_user((char __user *)data, &conn, sizeof(conn))) {
+ dev_dbg(&idev->dev, "failed to copy data to userland\n");
+ issei_cl_disconnect(cl);
+ return -EFAULT;
+ }
+ return 0;
+
+ case IOCTL_ISSEI_DISCONNECT_CLIENT:
+ dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n");
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -ENODEV;
+ }
+
+ return issei_cl_disconnect(cl);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static ssize_t issei_write(struct file *file, const char __user *ubuf,
+ size_t length, loff_t *offset)
+{
+ struct issei_host_client *cl = file->private_data;
+ struct issei_device *idev = cl->idev;
+ ssize_t ret;
+
+ if (!length)
+ return 0;
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -EBUSY;
+ }
+
+ /* sanity check */
+ if (length > idev->dma.length.h2f) {
+ dev_dbg(&idev->dev, "Write is too big %zu > %zu\n",
+ length, idev->dma.length.h2f);
+ return -EFBIG;
+ }
+
+ u8 *buf __free(kfree) = memdup_user(ubuf, length);
+ if (IS_ERR(buf)) {
+ dev_dbg(&idev->dev, "failed to copy data from userland\n");
+ return PTR_ERR(buf);
+ }
+
+ do {
+ ret = issei_cl_write(cl, buf, length);
+ if (ret < 0 && ret != -EAGAIN)
+ return ret;
+ /* buf is consumed by issei_cl_write on success */
+ if (ret >= 0)
+ retain_and_null_ptr(buf);
+ if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != 1)) {
+ issei_cl_clean_all_wbuf(cl);
+ if (signal_pending(current))
+ return -EINTR;
+ return -ERESTARTSYS;
+ }
+ } while (ret == -EAGAIN);
+
+ return ret;
+}
+
+static ssize_t issei_read(struct file *file, char __user *ubuf,
+ size_t length, loff_t *offset)
+{
+ struct issei_host_client *cl = file->private_data;
+ struct issei_device *idev = cl->idev;
+ u8 *data = NULL;
+ ssize_t ret;
+
+ if (!length)
+ return 0;
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return -EBUSY;
+ }
+
+ /* sanity check */
+ if (length > idev->dma.length.f2h) {
+ dev_dbg(&idev->dev, "Read is too big %zu > %zu\n",
+ length, idev->dma.length.f2h);
+ return -EFBIG;
+ }
+
+ ret = issei_cl_read(cl, &data, length);
+ if (ret < 0) {
+ if (ret != -ENOENT)
+ return ret;
+
+ if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) != 0)) {
+ if (signal_pending(current))
+ return -EINTR;
+ return -ERESTARTSYS;
+ }
+
+ ret = issei_cl_read(cl, &data, length);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (copy_to_user(ubuf, data, ret)) {
+ dev_dbg(&idev->dev, "failed to copy data to userland\n");
+ ret = -EFAULT;
+ } else {
+ *offset = 0;
+ }
+
+ kfree(data);
+
+ return ret;
+}
+
+static __poll_t issei_poll(struct file *file, poll_table *wait)
+{
+ __poll_t req_events = poll_requested_events(wait);
+ struct issei_host_client *cl = file->private_data;
+ struct issei_device *idev = cl->idev;
+ __poll_t mask = 0;
+ int ret;
+
+ if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+ dev_dbg(&idev->dev, "Device is in transition\n");
+ return EPOLLERR;
+ }
+
+ if (req_events & (EPOLLIN | EPOLLRDNORM)) {
+ poll_wait(file, &cl->read_wait, wait);
+ ret = issei_cl_check_read(cl);
+ if (ret == 1)
+ mask |= EPOLLIN | EPOLLRDNORM;
+ else if (ret < 0)
+ mask |= EPOLLERR;
+ }
+
+ if (req_events & (EPOLLOUT | EPOLLWRNORM)) {
+ poll_wait(file, &cl->write_wait, wait);
+ ret = issei_cl_check_write(cl);
+ if (ret == 0)
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ else if (ret < 0)
+ mask |= EPOLLERR;
+ }
+
+ return mask;
+}
+
+static ssize_t fw_ver_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct issei_device *idev = dev_get_drvdata(device);
+
+ return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1],
+ idev->fw_version[2], idev->fw_version[3]);
+}
+static DEVICE_ATTR_RO(fw_ver);
+
+static struct attribute *issei_attrs[] = {
+ &dev_attr_fw_ver.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(issei);
+
+static const struct file_operations issei_fops = {
+ .owner = THIS_MODULE,
+ .open = issei_open,
+ .unlocked_ioctl = issei_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .write = issei_write,
+ .read = issei_read,
+ .release = issei_release,
+ .poll = issei_poll,
+};
+
+static void issei_device_release(struct device *dev)
+{
+ kfree(dev_get_drvdata(dev));
+}
+
+static void issei_device_init(struct issei_device *idev, struct device *parent,
+ const struct issei_dma_length *dma_length,
+ const struct issei_hw_ops *ops)
+{
+ idev->parent = parent;
+ idev->power_down = false;
+ init_waitqueue_head(&idev->wait_has_data);
+ idev->has_data = false;
+ init_waitqueue_head(&idev->wait_rst_state);
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+
+ mutex_init(&idev->client_lock);
+ INIT_LIST_HEAD(&idev->host_client_list);
+ idev->host_client_last_id = 0;
+ idev->host_client_count = 0;
+ INIT_LIST_HEAD(&idev->fw_client_list);
+ INIT_LIST_HEAD(&idev->write_queue);
+ idev->last_write_ts = 0;
+
+ idev->dma.length = *dma_length;
+
+ idev->ops = ops;
+}
+
+/**
+ * issei_register: register issei character device
+ * @hw_size: size of the hardware structure to allocate
+ * @parent: parent device
+ * @dma_length: structure with DMA sizes
+ * @ops: hardware-related operations
+ *
+ * Return: pointer allocated to issei_device structure, error on failure
+ */
+struct issei_device *issei_register(size_t hw_size, struct device *parent,
+ const struct issei_dma_length *dma_length,
+ const struct issei_hw_ops *ops)
+{
+ struct issei_device *idev;
+ u32 minor;
+ int ret, devno;
+
+ idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL);
+ if (!idev)
+ return ERR_PTR(-ENOMEM);
+
+ issei_device_init(idev, parent, dma_length, ops);
+
+ ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL);
+ if (ret < 0) {
+ dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret);
+ kfree(idev);
+ return ERR_PTR(ret);
+ }
+
+ idev->minor = minor;
+ devno = MKDEV(MAJOR(issei_devt), idev->minor);
+
+ device_initialize(&idev->dev);
+ idev->dev.devt = devno;
+ idev->dev.class = issei_class;
+ idev->dev.parent = parent;
+ idev->dev.groups = issei_groups;
+ idev->dev.release = issei_device_release;
+ dev_set_drvdata(&idev->dev, idev);
+
+ idev->cdev = cdev_alloc();
+ if (!idev->cdev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ idev->cdev->ops = &issei_fops;
+ if (parent->driver)
+ idev->cdev->owner = parent->driver->owner;
+ cdev_set_parent(idev->cdev, &idev->dev.kobj);
+
+ ret = cdev_add(idev->cdev, devno, 1);
+ if (ret) {
+ dev_err(parent, "unable to add device %d:%u ret = %d\n",
+ MAJOR(issei_devt), idev->minor, ret);
+ goto err_del_cdev;
+ }
+
+ ret = dev_set_name(&idev->dev, "issei%u", idev->minor);
+ if (ret) {
+ dev_err(parent, "unable to set name to device %d:%u ret = %d\n",
+ MAJOR(issei_devt), idev->minor, ret);
+ goto err_del_cdev;
+ }
+
+ ret = device_add(&idev->dev);
+ if (ret) {
+ dev_err(parent, "unable to add device %d:%u ret = %d\n",
+ MAJOR(issei_devt), idev->minor, ret);
+ goto err_del_cdev;
+ }
+
+ idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj);
+ if (!idev->fw_clients) {
+ ret = -ENOMEM;
+ goto err_del_dev;
+ }
+
+ return idev;
+
+err_del_dev:
+ device_del(&idev->dev);
+err_del_cdev:
+ cdev_del(idev->cdev);
+err:
+ put_device(&idev->dev);
+ xa_erase(&issei_minor_xa, minor);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(issei_register);
+
+/**
+ * issei_deregister: remove issei character device
+ * @idev: the device structure
+ */
+void issei_deregister(struct issei_device *idev)
+{
+ u32 minor = idev->minor;
+
+ cdev_del(idev->cdev);
+
+ kset_unregister(idev->fw_clients);
+
+ device_del(&idev->dev);
+
+ put_device(&idev->dev);
+
+ xa_erase(&issei_minor_xa, minor);
+}
+EXPORT_SYMBOL_GPL(issei_deregister);
+
+static int __init issei_cdev_init(void)
+{
+ int ret;
+
+ issei_class = class_create("issei");
+ if (IS_ERR(issei_class)) {
+ pr_err("couldn't create class\n");
+ return PTR_ERR(issei_class);
+ }
+
+ ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei");
+ if (ret < 0) {
+ pr_err("unable to allocate char dev region\n");
+ class_destroy(issei_class);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit issei_cdev_exit(void)
+{
+ unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS);
+ class_destroy(issei_class);
+}
+
+module_init(issei_cdev_init);
+module_exit(issei_cdev_exit);
+
+MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h
new file mode 100644
index 000000000000..30075a624f2d
--- /dev/null
+++ b/drivers/misc/issei/cdev.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_CDEV_H_
+#define _ISSEI_CDEV_H_
+
+struct device;
+struct issei_device;
+struct issei_dma_length;
+struct issei_hw_ops;
+
+struct issei_device *issei_register(size_t hw_size, struct device *parent,
+ const struct issei_dma_length *dma_length,
+ const struct issei_hw_ops *ops);
+void issei_deregister(struct issei_device *idev);
+
+#endif /* _ISSEI_CDEV_H_ */
diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c
new file mode 100644
index 000000000000..457d28f31c06
--- /dev/null
+++ b/drivers/misc/issei/dma.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/dev_printk.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timekeeping.h>
+
+#include "issei_dev.h"
+#include "hw_msg.h"
+
+static inline size_t __issei_dma_size(const struct issei_dma *dma)
+{
+ return dma->length.h2f + dma->length.f2h + dma->length.ctl;
+}
+
+/**
+ * issei_dmam_setup - setup DMA buffer and clean it
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_dmam_setup(struct issei_device *idev)
+{
+ struct issei_dma *dma = &idev->dma;
+ size_t size;
+
+ size = __issei_dma_size(dma);
+ if (!size)
+ return -EINVAL;
+
+ if (!dma->vaddr)
+ dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr,
+ GFP_KERNEL | __GFP_ZERO);
+ if (dma->vaddr)
+ memset(dma->vaddr, 0, size);
+ return dma->vaddr ? 0 : -ENOMEM;
+}
+
+static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma)
+{
+ return dma->vaddr + dma->length.h2f + dma->length.f2h;
+}
+
+static bool __issei_dma_is_read_busy(struct issei_dma *dma)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(dma);
+
+ return ctl->f2h_counter_wr != ctl->f2h_counter_rd;
+}
+
+static bool __issei_dma_is_write_busy(struct issei_dma *dma)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(dma);
+
+ return ctl->h2f_counter_wr != ctl->h2f_counter_rd;
+}
+
+static void __issei_dma_read_finalize(struct issei_device *idev)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
+
+ dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd);
+ /* No need to check overflow - the firmware counters overflow the same way */
+ ctl->f2h_counter_rd++;
+}
+
+static void __issei_dma_write_finalize(struct issei_device *idev)
+{
+ struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
+
+ dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr);
+ /* No need to check overflow - the firmware counters overflow the same way */
+ ctl->h2f_counter_wr++;
+}
+
+/**
+ * issei_dma_write - write data package to DMA
+ * @idev: issei device object
+ * @data: data atructure
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data)
+{
+ u8 *write_buf = idev->dma.vaddr;
+ struct ham_message_header *hdr = (struct ham_message_header *)write_buf;
+
+ if (data->length > idev->dma.length.h2f - sizeof(*hdr)) {
+ dev_err(&idev->dev, "Message is too big\n");
+ return -EMSGSIZE;
+ }
+
+ if (__issei_dma_is_write_busy(&idev->dma)) {
+ if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) {
+ dev_err(&idev->dev, "Write stuck in queue\n");
+ return -EIO;
+ }
+ dev_info(&idev->dev, "Write is busy\n");
+ return -EBUSY;
+ }
+
+ hdr->length = data->length;
+ hdr->fw_id = data->fw_id;
+ hdr->host_id = data->host_id;
+ hdr->flags = data->flags;
+ hdr->status = data->status;
+ hdr->reserved = 0;
+
+ memcpy(write_buf + sizeof(*hdr), data->buf, data->length);
+
+ __issei_dma_write_finalize(idev);
+ idev->last_write_ts = ktime_get();
+ return 0;
+}
+
+/**
+ * issei_dma_read - read data package from DMA
+ * @idev: issei device object
+ * @data: data atructure
+ *
+ * Return: %0 on success, <0 on failures
+ */
+int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data)
+{
+ u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f;
+ struct ham_message_header *hdr = (struct ham_message_header *)read_buf;
+
+ if (!__issei_dma_is_read_busy(&idev->dma)) {
+ dev_dbg(&idev->dev, "Nothing to read\n");
+ return -ENODATA;
+ }
+
+ dev_dbg(&idev->dev, "Reading header\n");
+ data->length = hdr->length;
+ data->fw_id = hdr->fw_id;
+ data->host_id = hdr->host_id;
+ data->flags = hdr->flags;
+ data->status = hdr->status;
+
+ if (data->length > idev->dma.length.f2h - sizeof(*hdr)) {
+ dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n",
+ data->length, idev->dma.length.f2h - sizeof(*hdr));
+ return -EIO;
+ }
+
+ dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length);
+ data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL);
+ if (!data->buf)
+ return -ENOMEM;
+ __issei_dma_read_finalize(idev);
+ return 0;
+}
diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h
new file mode 100644
index 000000000000..e6b7aeb50ae6
--- /dev/null
+++ b/drivers/misc/issei/dma.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_DMA_H_
+#define _ISSEI_DMA_H_
+
+#include <linux/types.h>
+
+struct issei_device;
+
+/**
+ * struct issei_dma_length - sizes of DMA memory portions
+ * @h2f: host to firmware buffer size
+ * @f2h: firmware to host buffer size
+ * @ctl: control buffer size
+ */
+struct issei_dma_length {
+ size_t h2f;
+ size_t f2h;
+ size_t ctl;
+};
+
+/**
+ * struct issei_dma - DMA memory structure
+ * @vaddr: virtual address
+ * @daddr: physical address
+ * @length: memory sizes structure
+ */
+struct issei_dma {
+ void *vaddr;
+ dma_addr_t daddr;
+ struct issei_dma_length length;
+};
+
+/* Operation statuses */
+#define HAMS_SUCCESS 0x00
+#define HAMS_PROTOCOL_NOT_SUPPORTED 0x01
+#define HAMS_DEPRECATED_BUS_MSG 0x02
+#define HAMS_CLIENT_NOT_EXISTS 0x03
+#define HAMS_MSG_TOO_BIG 0x04
+#define HAMS_MSG_NOT_CONSUMED 0x05
+#define HAMS_CORRUPTED_BUS_MSG 0x06
+#define HAMS_CORRUPTED_HEADER 0x07
+#define HAMS_INVALID_LENGTH 0x08
+#define HAMS_SHARED_MEMORY_SIZE_UNSUPPORTED 0x09
+#define HAMS_GENERAL_FATAL_ERROR 0xff
+
+/**
+ * struct issei_dma_data - data passed through channel
+ * @fw_id: firmware client id
+ * @host_id: host client id
+ * @flags: flags bitmap
+ * @status: operation status
+ * @length: data length
+ * @buf: pointer to data buffer
+ */
+struct issei_dma_data {
+ u16 fw_id;
+ u16 host_id;
+ u32 flags;
+ u32 status;
+ u32 length;
+ void *buf;
+};
+
+int issei_dmam_setup(struct issei_device *idev);
+int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data);
+int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data);
+
+#endif /*_ISSEI_DMA_H_*/
diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c
new file mode 100644
index 000000000000..b8e48dbf1c9c
--- /dev/null
+++ b/drivers/misc/issei/fw_client.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/bug.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/sprintf.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+#include "issei_dev.h"
+#include "fw_client.h"
+
+/*
+ * Specific attribute handlers for fw_clients kset.
+ * Provide only show function as all fw_client attributes are read-only.
+ */
+
+struct issei_fw_cl_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct issei_fw_client *fw_cl, const struct issei_fw_cl_attr *attr,
+ char *buf);
+};
+#define to_issei_fw_cl_attr(x) container_of_const(x, struct issei_fw_cl_attr, attr)
+
+static ssize_t fw_cl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ const struct issei_fw_cl_attr *issei_attr;
+ struct issei_fw_client *fw_cl;
+
+ issei_attr = to_issei_fw_cl_attr(attr);
+ fw_cl = to_issei_fw_client(kobj);
+
+ if (!issei_attr->show)
+ return -EIO;
+
+ return issei_attr->show(fw_cl, issei_attr, buf);
+}
+
+static const struct sysfs_ops fw_cl_sysfs_ops = {
+ .show = fw_cl_attr_show,
+};
+
+#define FW_CL_ATTR_RO(_name) \
+ struct issei_fw_cl_attr fw_cl_attr_##_name = __ATTR_RO(_name)
+
+/* fw_client attributes */
+
+static ssize_t id_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", fw_cl->id);
+}
+static FW_CL_ATTR_RO(id);
+
+static ssize_t ver_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", fw_cl->ver);
+}
+static FW_CL_ATTR_RO(ver);
+
+static ssize_t uuid_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%pUb\n", &fw_cl->uuid);
+}
+static FW_CL_ATTR_RO(uuid);
+
+static ssize_t mtu_show(struct issei_fw_client *fw_cl,
+ const struct issei_fw_cl_attr *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", fw_cl->mtu);
+}
+static FW_CL_ATTR_RO(mtu);
+
+static const struct attribute *const fw_cl_attrs[] = {
+ &fw_cl_attr_id.attr,
+ &fw_cl_attr_ver.attr,
+ &fw_cl_attr_uuid.attr,
+ &fw_cl_attr_mtu.attr,
+ NULL,
+};
+
+static const struct attribute_group fw_cl_group = {
+ .attrs_const = fw_cl_attrs,
+};
+__ATTRIBUTE_GROUPS(fw_cl);
+
+static void issei_fw_cl_init(struct issei_fw_client *fw_cl, u16 id, u8 ver, const uuid_t *uuid,
+ u32 mtu, u32 flags)
+{
+ INIT_LIST_HEAD(&fw_cl->list);
+ fw_cl->id = id;
+ fw_cl->ver = ver;
+ fw_cl->uuid = *uuid;
+ fw_cl->mtu = mtu;
+ fw_cl->flags = flags;
+}
+
+static void fw_cl_release(struct kobject *kobj)
+{
+ struct issei_fw_client *fw_cl = to_issei_fw_client(kobj);
+
+ kfree(fw_cl);
+}
+
+static const struct kobj_type fw_client_ktype = {
+ .sysfs_ops = &fw_cl_sysfs_ops,
+ .release = fw_cl_release,
+ .default_groups = fw_cl_groups,
+};
+
+/**
+ * issei_fw_cl_create - create firmware client object and add to list
+ * @idev: issei device object
+ * @id: firmware client id
+ * @ver: firmware client version
+ * @uuid: firmware client unique id
+ * @mtu: firmware client maximum message size
+ * @flags: firmware client flags
+ *
+ * Should be called under idev->client_lock
+ *
+ * Return: pointer to newly created object on success, ERR_PTR on failure
+ */
+struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver,
+ const uuid_t *uuid, u32 mtu, u32 flags)
+{
+ int ret;
+ struct issei_fw_client *fw_cl = kzalloc_obj(*fw_cl);
+
+ if (!fw_cl)
+ return ERR_PTR(-ENOMEM);
+
+ WARN_ON(!mutex_is_locked(&idev->client_lock));
+
+ issei_fw_cl_init(fw_cl, id, ver, uuid, mtu, flags);
+ fw_cl->kobj.kset = idev->fw_clients;
+
+ ret = kobject_init_and_add(&fw_cl->kobj, &fw_client_ktype, NULL, "%u", id);
+ if (ret) {
+ kobject_put(&fw_cl->kobj);
+ return ERR_PTR(ret);
+ }
+
+ list_add_tail(&fw_cl->list, &idev->fw_client_list);
+
+ dev_dbg(&idev->dev, "FW client %pUb created\n", uuid);
+
+ kobject_uevent(&fw_cl->kobj, KOBJ_ADD);
+
+ return fw_cl;
+}
+
+static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl)
+{
+ WARN(fw_cl->cl, "Removing connected client!\n");
+
+ dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid);
+
+ list_del(&fw_cl->list);
+ kobject_put(&fw_cl->kobj);
+}
+
+/**
+ * issei_fw_cl_remove_all - remove all firmware client objects
+ * @idev: issei device object
+ */
+void issei_fw_cl_remove_all(struct issei_device *idev)
+{
+ struct issei_fw_client *fw_cl, *next;
+
+ guard(mutex)(&idev->client_lock);
+
+ list_for_each_entry_safe(fw_cl, next, &idev->fw_client_list, list)
+ __issei_fw_cl_remove(idev, fw_cl);
+}
+
+/**
+ * issei_fw_cl_find_by_uuid - find firmware client by uuid
+ * @idev: issei device object
+ * @uuid: uuid to search by it
+ *
+ * Should be called under idev->client_lock
+ *
+ * Return: pointer to firmware client object if found, NULL on failure
+ */
+struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid)
+{
+ struct issei_fw_client *fw_cl;
+
+ WARN_ON(!mutex_is_locked(&idev->client_lock));
+
+ list_for_each_entry(fw_cl, &idev->fw_client_list, list) {
+ if (uuid_equal(&fw_cl->uuid, uuid)) {
+ kobject_get(&fw_cl->kobj);
+ return fw_cl;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * issei_fw_cl_connect - connect firmware and host client
+ * @fw_cl: firmware client
+ * @cl: host client
+ *
+ * Should be called under idev->client_lock
+ *
+ * Return: 0 on success, -EBUSY if already connected
+ */
+int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl)
+{
+ if (fw_cl->cl)
+ return -EBUSY;
+
+ kobject_get(&fw_cl->kobj);
+ fw_cl->cl = cl;
+ return 0;
+}
+
+/**
+ * issei_fw_cl_disconnect - disconnect firmware and host client
+ * @fw_cl: firmware client
+ *
+ * Should be called under idev->client_lock
+ */
+void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl)
+{
+ WARN_ON(!fw_cl->cl);
+
+ fw_cl->cl = NULL;
+ kobject_put(&fw_cl->kobj);
+}
diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h
new file mode 100644
index 000000000000..377f733d7e91
--- /dev/null
+++ b/drivers/misc/issei/fw_client.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_FW_CLIENT_H_
+#define _ISSEI_FW_CLIENT_H_
+
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+struct issei_device;
+struct issei_host_client;
+
+/**
+ * struct issei_fw_client - represents firmware queue
+ * @kobj: associated kobject
+ * @list: link in firmware clients list
+ * @id: firmware client id
+ * @ver: firmware client version
+ * @uuid: firmware client protocol id
+ * @mtu: firmware client maximum buffer size
+ * @flags: firmware client flags
+ * @cl: pointer to host client, if connected
+ */
+struct issei_fw_client {
+ struct kobject kobj;
+ struct list_head list;
+ u16 id;
+ u8 ver;
+ uuid_t uuid;
+ u32 mtu;
+ u32 flags;
+ struct issei_host_client *cl;
+};
+#define to_issei_fw_client(x) container_of(x, struct issei_fw_client, kobj)
+
+struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver,
+ const uuid_t *uuid, u32 mtu, u32 flags);
+void issei_fw_cl_remove_all(struct issei_device *idev);
+struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid);
+
+int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl);
+void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl);
+
+#endif /* _ISSEI_FW_CLIENT_H_ */
diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c
new file mode 100644
index 000000000000..674d9733d16c
--- /dev/null
+++ b/drivers/misc/issei/ham.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dma.h"
+#include "fw_client.h"
+#include "ham.h"
+#include "hw_msg.h"
+#include "issei_dev.h"
+
+static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf)
+{
+ struct issei_dma_data data = { };
+ int ret;
+
+ data.length = length;
+ data.buf = buf;
+ ret = issei_dma_write(idev, &data);
+ if (ret)
+ return ret;
+ return idev->ops->irq_write_generate(idev);
+}
+
+/**
+ * issei_ham_send_start_req - send start request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_start_req(struct issei_device *idev)
+{
+ struct ham_start_message_req req;
+
+ req.header.cmd = HAM_BUS_CMD_START_REQ;
+ req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER;
+ req.heci_capabilities_length = 0;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+/**
+ * issei_ham_send_clients_req - send clients request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_clients_req(struct issei_device *idev)
+{
+ struct ham_get_clients_req req;
+
+ req.header.cmd = HAM_BUS_CMD_CLIENT_REQ;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_start_message_res *res = (struct ham_start_message_res *)buf;
+ int ret;
+
+ if (idev->rst_state != ISSEI_RST_STATE_START) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_START);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small start response size %zu < %zu\n",
+ length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->heci_capabilities_length) {
+ dev_err(&idev->dev, "Wrong start response size %zu != %u\n",
+ length - sizeof(*res), res->heci_capabilities_length);
+ return -EPROTO;
+ }
+
+ memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version));
+ idev->fw_protocol_ver = res->supported_version;
+ dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver,
+ idev->fw_version[0], idev->fw_version[1],
+ idev->fw_version[2], idev->fw_version[3]);
+
+ ret = issei_ham_send_clients_req(idev);
+ if (ret == -EBUSY)
+ ret = 0;
+
+ return ret;
+}
+
+static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf;
+ struct ham_client_properties *client;
+
+ if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) {
+ dev_err(&idev->dev, "Wrong response size %zu < %zu\n",
+ length - sizeof(*res),
+ res->client_count * sizeof(struct ham_client_properties));
+ return -EPROTO;
+ }
+
+ guard(mutex)(&idev->client_lock);
+
+ for (size_t i = 0; i < res->client_count; i++) {
+ client = &res->clients_props[i];
+ dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u",
+ client->client_number, client->protocol_ver, &client->client_uuid,
+ client->client_mtu, client->flags);
+ issei_fw_cl_create(idev, client->client_number, client->protocol_ver,
+ &client->client_uuid, client->client_mtu, client->flags);
+ }
+ return 0;
+}
+
+static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_bus_message *hdr = (struct ham_bus_message *)buf;
+
+ if (length < sizeof(*hdr)) {
+ dev_err(&idev->dev, "Small bus message size %zu < %zu\n",
+ length, sizeof(*hdr));
+ return -EPROTO;
+ }
+
+ switch (hdr->cmd) {
+ case HAM_BUS_CMD_START_RSP:
+ return issei_ham_start_rsp(idev, buf, length);
+
+ case HAM_BUS_CMD_CLIENT_RSP:
+ return issei_ham_client_rsp(idev, buf, length);
+
+ default:
+ dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd);
+ return -EPROTO;
+ }
+}
+
+/**
+ * issei_ham_process_ham_rsp - process response from firmware and release buffer
+ * @idev: issei device object
+ * @buf: response buffer
+ * @length: response buffer length
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ int ret;
+
+ ret = __issei_ham_process_ham_rsp(idev, buf, length);
+ kfree(buf);
+ return ret;
+}
diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h
new file mode 100644
index 000000000000..37ea64bb2920
--- /dev/null
+++ b/drivers/misc/issei/ham.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HAM_H_
+#define _ISSEI_HAM_H_
+
+#include <linux/types.h>
+
+struct issei_device;
+
+int issei_ham_send_start_req(struct issei_device *idev);
+int issei_ham_send_clients_req(struct issei_device *idev);
+
+static inline bool issei_is_ham_rsp(u16 fw_id, u16 host_id)
+{
+ return fw_id == 0 && host_id == 0;
+}
+
+int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length);
+
+#endif /* _ISSEI_HAM_H_ */
diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c
new file mode 100644
index 000000000000..8f36d1c319ec
--- /dev/null
+++ b/drivers/misc/issei/host_client.c
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/uuid.h>
+
+#include "fw_client.h"
+#include "host_client.h"
+#include "issei_dev.h"
+
+static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl)
+{
+ return cl->fw_cl ? cl->fw_cl->id : 0;
+}
+
+#define ISSEI_CL_FMT "cl:host=%02d fw=%02d "
+
+#define cl_dbg(_dev_, _cl_, format, arg...) do { \
+ struct issei_host_client *_l_cl_ = _cl_; \
+ dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
+ __issei_cl_fw_id(_l_cl_), ##arg); \
+} while (0)
+
+#define cl_warn(_dev_, _cl_, format, arg...) do { \
+ struct issei_host_client *_l_cl_ = _cl_; \
+ dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
+ __issei_cl_fw_id(_l_cl_), ##arg); \
+} while (0)
+
+#define cl_err(_dev_, _cl_, format, arg...) do { \
+ struct issei_host_client *_l_cl_ = _cl_; \
+ dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
+ __issei_cl_fw_id(_l_cl_), ##arg); \
+} while (0)
+
+static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf)
+{
+ list_del(&wbuf->list);
+ kfree(wbuf->data);
+ kfree(wbuf);
+}
+
+static void __issei_cl_release_rbuf(struct issei_host_client *cl)
+{
+ cl->read_data = NULL;
+ cl->read_data_size = 0;
+}
+
+static void __issei_cl_clean_rbuf(struct issei_host_client *cl)
+{
+ kfree(cl->read_data);
+ __issei_cl_release_rbuf(cl);
+}
+
+static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct issei_host_client *cl)
+{
+ struct issei_write_buf *wbuf, *next;
+
+ if (!cl->write_in_progress)
+ return;
+ list_for_each_entry_safe(wbuf, next, &idev->write_queue, list) {
+ if (wbuf->cl == cl) {
+ __issei_cl_clean_wbuf(wbuf);
+ break;
+ }
+ }
+ cl->write_in_progress = false;
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->write_wait))
+ wake_up_interruptible(&cl->write_wait);
+}
+
+static struct issei_host_client *__issei_cl_by_id(struct issei_device *idev, u16 id)
+{
+ struct issei_host_client *cl;
+
+ list_for_each_entry(cl, &idev->host_client_list, list) {
+ if (cl->id == id)
+ return cl;
+ }
+ return NULL;
+}
+
+static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_client *cl)
+{
+ if (cl->state == ISSEI_HOST_CL_STATE_DISCONNECTED)
+ return;
+
+ __issei_cl_clean_all_wbuf(idev, cl);
+
+ if (!WARN_ON(!cl->fw_cl)) {
+ issei_fw_cl_disconnect(cl->fw_cl);
+ cl->fw_cl = NULL;
+ }
+ cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED;
+
+ if (cl->read_data)
+ __issei_cl_clean_rbuf(cl);
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->read_wait))
+ wake_up_interruptible(&cl->read_wait);
+ cl_dbg(idev, cl, "Disconnected\n");
+}
+
+static void __issei_cl_init(struct issei_host_client *cl, struct issei_device *idev,
+ u16 id, struct file *fp)
+{
+ INIT_LIST_HEAD(&cl->list);
+ cl->idev = idev;
+ cl->id = id;
+ cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED;
+ cl->fp = fp;
+ init_waitqueue_head(&cl->write_wait);
+ init_waitqueue_head(&cl->read_wait);
+ __issei_cl_release_rbuf(cl);
+}
+
+/**
+ * issei_cl_create - create the host client
+ * @idev: issei device
+ * @fp: file pointer to associate with host client
+ *
+ * Return: client pointer on success, ERR_PTR on error
+ */
+struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp)
+{
+ struct issei_host_client *cl;
+ u16 id;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) {
+ dev_err(&idev->dev, "Maximum open clients %d is reached.\n",
+ ISSEI_HOST_CLIENTS_MAX);
+ return ERR_PTR(-EMFILE);
+ }
+
+ do {
+ if (check_add_overflow(idev->host_client_last_id, 1, &id)) /* overflow */
+ id = 1;
+ idev->host_client_last_id = id;
+ /* Not an endless loop as we have less clients then id's */
+ } while (__issei_cl_by_id(idev, id));
+
+ cl = kzalloc_obj(*cl);
+ if (!cl)
+ return ERR_PTR(-ENOMEM);
+
+ __issei_cl_init(cl, idev, id, fp);
+ list_add_tail(&cl->list, &idev->host_client_list);
+ idev->host_client_count++;
+
+ cl_dbg(idev, cl, "Created\n");
+ return cl;
+}
+
+/**
+ * issei_cl_remove - disconnect and free the host client
+ * @cl: host client
+ */
+void issei_cl_remove(struct issei_host_client *cl)
+{
+ struct issei_device *idev;
+
+ /* don't shout on error exit path */
+ if (!cl)
+ return;
+
+ idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ idev->host_client_count--;
+ list_del(&cl->list);
+
+ __issei_cl_disconnect(idev, cl);
+
+ cl_dbg(idev, cl, "Removed\n");
+ kfree(cl);
+}
+
+/**
+ * issei_cl_connect - connect between FW and host client
+ * @cl: host client
+ * @uuid: FW client unique ID
+ * @mtu: memory for FW client max message size
+ * @ver: memory for FW client version
+ * @flags: memory for FW client flags
+ *
+ * Search for firmware client by UUID and connect it to provided
+ * host client, if not already connected to some client.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver,
+ u32 *flags)
+{
+ struct issei_device *idev = cl->idev;
+ struct issei_fw_client *fw_cl;
+ int ret;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state == ISSEI_HOST_CL_STATE_CONNECTED) {
+ cl_err(idev, cl, "Already connected\n");
+ return -EISCONN;
+ }
+
+ fw_cl = issei_fw_cl_find_by_uuid(idev, uuid);
+ if (!fw_cl) {
+ cl_dbg(idev, cl, "FW client %pUb not found\n", uuid);
+ return -ENOTTY;
+ }
+
+ ret = issei_fw_cl_connect(fw_cl, cl); /* calls kobject_get for fw_cl on success */
+ kobject_put(&fw_cl->kobj);
+ if (ret) {
+ cl_err(idev, cl, "FW client is already connected ret = %d\n", ret);
+ return ret;
+ }
+
+ cl->fw_cl = fw_cl;
+ cl->state = ISSEI_HOST_CL_STATE_CONNECTED;
+
+ *mtu = fw_cl->mtu;
+ *ver = fw_cl->ver;
+ *flags = fw_cl->flags;
+ cl_dbg(idev, cl, "Connected\n");
+ return 0;
+}
+
+/**
+ * issei_cl_disconnect - disconnect between FW and host client
+ * @cl: host client
+ *
+ * Return: 0 on success, -ENOTCONN if not connected
+ */
+int issei_cl_disconnect(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+ __issei_cl_disconnect(idev, cl);
+ return 0;
+}
+
+/**
+ * issei_cl_all_disconnect - disconnect all FW clients
+ * @idev: issei device
+ */
+void issei_cl_all_disconnect(struct issei_device *idev)
+{
+ struct issei_host_client *cl;
+
+ guard(mutex)(&idev->client_lock);
+
+ list_for_each_entry(cl, &idev->host_client_list, list)
+ __issei_cl_disconnect(idev, cl);
+}
+
+/**
+ * issei_cl_write - enqueue write request
+ * @cl: host client
+ * @buf: buffer to write
+ * @buf_size: buffer size
+ *
+ * Add write request to the write queue and wakes working thread.
+ * This call takes ownership of buf memory, if succeeded.
+ *
+ * Return: size of data on success, <0 on error
+ */
+ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size)
+{
+ struct issei_device *idev = cl->idev;
+ struct issei_write_buf *wbuf;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+
+ if (cl->write_in_progress) {
+ cl_dbg(idev, cl, "Another write is in progress\n");
+ return -EAGAIN;
+ }
+
+ if (buf_size > cl->fw_cl->mtu) {
+ cl_err(idev, cl, "Write is too big %zu > %u\n", buf_size, cl->fw_cl->mtu);
+ return -EFBIG;
+ }
+
+ wbuf = kmalloc_obj(*wbuf);
+ if (!wbuf)
+ return -ENOMEM;
+ wbuf->cl = cl;
+ wbuf->data = buf;
+ wbuf->data_size = buf_size;
+ list_add_tail(&wbuf->list, &idev->write_queue);
+ cl->write_in_progress = true;
+ cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size);
+
+ issei_poke_process_thread(idev);
+
+ return buf_size;
+}
+
+/**
+ * issei_cl_write_from_queue - writes first request from queue to firmware
+ * @idev: issei device
+ *
+ * Tries to write first request from the write queue to firmware.
+ * Releases buf memory, if succeeded.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_cl_write_from_queue(struct issei_device *idev)
+{
+ struct issei_write_buf *wbuf;
+ struct issei_dma_data data;
+ struct issei_host_client *cl;
+ int ret;
+
+ guard(mutex)(&idev->client_lock);
+
+ wbuf = list_first_entry_or_null(&idev->write_queue, struct issei_write_buf, list);
+ if (!wbuf)
+ return 0;
+
+ cl = wbuf->cl;
+
+ data.fw_id = cl->fw_cl->id;
+ data.host_id = cl->id;
+ data.flags = 0;
+ data.status = 0;
+ data.length = wbuf->data_size;
+ data.buf = (void *)wbuf->data;
+ ret = issei_dma_write(idev, &data);
+ if (ret == -EBUSY)
+ return 0;
+ if (ret == -EIO)
+ return ret;
+ if (ret >= 0)
+ idev->ops->irq_write_generate(idev);
+ cl->write_in_progress = false;
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->write_wait))
+ wake_up_interruptible(&cl->write_wait);
+ cl_dbg(idev, cl, "Write %zu bytes\n", wbuf->data_size);
+ __issei_cl_clean_wbuf(wbuf);
+ return 0;
+}
+
+static struct issei_host_client *__issei_cl_read_buf_check(struct issei_device *idev, u16 fw_id,
+ u16 host_id, size_t buf_size)
+{
+ struct issei_host_client *cl;
+
+ cl = __issei_cl_by_id(idev, host_id);
+ if (!cl) {
+ dev_dbg(&idev->dev, "No client %u\n", host_id);
+ return ERR_PTR(-ENOTTY);
+ }
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) {
+ cl_dbg(idev, cl, "Not connected\n");
+ return ERR_PTR(-ENODEV);
+ }
+ if (cl->fw_cl->id != fw_id) {
+ cl_dbg(idev, cl, "Wrong firmware client %u ?= %u\n", cl->fw_cl->id, fw_id);
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (buf_size > cl->fw_cl->mtu) {
+ cl_err(idev, cl, "Read is too big %zu > %u\n", buf_size, cl->fw_cl->mtu);
+ __issei_cl_disconnect(idev, cl);
+ return NULL;
+ }
+
+ if (cl->read_data) {
+ cl_err(idev, cl, "Previous data was not read by user-space, disconnecting\n");
+ __issei_cl_disconnect(idev, cl);
+ return NULL;
+ }
+
+ return cl;
+}
+
+/**
+ * issei_cl_read_buf - process data from firmware
+ * @idev: issei device
+ * @fw_id: firmware client id
+ * @host_id: host client id
+ * @buf: buffer with data
+ * @buf_size: buffer size
+ *
+ * Puts data from firmware into provided host client storage.
+ * Free buffer or consume it.
+ *
+ * Return: 0 on success or recoverable error, <0 on unrecoverable error
+ */
+int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size)
+{
+ struct issei_host_client *cl;
+
+ guard(mutex)(&idev->client_lock);
+
+ cl = __issei_cl_read_buf_check(idev, fw_id, host_id, buf_size);
+ if (IS_ERR_OR_NULL(cl)) {
+ kfree(buf);
+ return PTR_ERR(cl);
+ }
+
+ cl->read_data = buf;
+ cl->read_data_size = buf_size;
+
+ /* synchronized under host client mutex */
+ if (waitqueue_active(&cl->read_wait))
+ wake_up_interruptible(&cl->read_wait);
+ cl_dbg(idev, cl, "Read %zu bytes\n", buf_size);
+
+ return 0;
+}
+
+/**
+ * issei_cl_read - read data from queue to provided buffer
+ * @cl: host client
+ * @buf: buffer to store data
+ * @buf_size: buffer size
+ *
+ * Tries to take data buffer from client and return it to caller.
+ * The caller receives ownership of the data buffer.
+ *
+ * Return: read data size on success, <0 on error
+ */
+ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size)
+{
+ struct issei_device *idev = cl->idev;
+ size_t read_data_size;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+
+ if (!cl->read_data)
+ return -ENOENT;
+
+ if (cl->read_data_size > buf_size) {
+ cl_err(idev, cl, "Buffer is too small %zu > %zu\n",
+ cl->read_data_size, buf_size);
+ return -EFBIG;
+ }
+
+ *buf = cl->read_data;
+ read_data_size = cl->read_data_size;
+ cl_dbg(idev, cl, "Read by client %zu bytes\n", read_data_size);
+ __issei_cl_release_rbuf(cl);
+ return read_data_size;
+}
+
+/**
+ * issei_cl_check_read - check if client has data to read
+ *
+ * @cl: host client
+ *
+ * Return: 1 - data available, 0 - no data, < 0 on error
+ */
+int issei_cl_check_read(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+ if (!cl->read_data)
+ return 0;
+ return 1;
+}
+
+/**
+ * issei_cl_check_write - check if client is ready to write
+ *
+ * @cl: host client
+ *
+ * Return: 1 - can not write, 0 - can write, < 0 on error
+ */
+int issei_cl_check_write(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
+ return -ENOTCONN;
+ if (cl->write_in_progress)
+ return 1;
+ return 0;
+}
+
+void issei_cl_clean_all_wbuf(struct issei_host_client *cl)
+{
+ struct issei_device *idev = cl->idev;
+
+ guard(mutex)(&idev->client_lock);
+
+ __issei_cl_clean_all_wbuf(idev, cl);
+}
diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h
new file mode 100644
index 000000000000..05e2f4ade9c3
--- /dev/null
+++ b/drivers/misc/issei/host_client.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HOST_CLIENT_H_
+#define _ISSEI_HOST_CLIENT_H_
+
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/wait.h>
+
+struct file;
+
+struct issei_device;
+struct issei_fw_client;
+
+/**
+ * enum issei_host_client_state - host client states
+ * @ISSEI_HOST_CL_STATE_DISCONNECTED: host client is disconnected
+ * @ISSEI_HOST_CL_STATE_CONNECTED: host client is connected
+ */
+enum issei_host_client_state {
+ ISSEI_HOST_CL_STATE_DISCONNECTED,
+ ISSEI_HOST_CL_STATE_CONNECTED,
+};
+
+/**
+ * struct issei_host_client - represents host client
+ * @list: link in host clients list
+ * @idev: issei parent device
+ * @id: host client id
+ * @fp: file associated with client
+ *
+ * @write_wait: waitqueue for pending write data
+ * @write_in_progress: indicator for write in process
+ *
+ * @state: host client state
+ * @fw_cl: pointer to firmware client, if connected
+ *
+ * @read_wait: waitqueue for read object
+ * @read_data: received data pointer
+ * @read_data_size: received data size
+ */
+struct issei_host_client {
+ struct list_head list;
+ struct issei_device *idev;
+ u16 id;
+ const struct file *fp;
+
+ wait_queue_head_t write_wait;
+ bool write_in_progress;
+
+ enum issei_host_client_state state;
+ struct issei_fw_client *fw_cl;
+
+ wait_queue_head_t read_wait;
+ u8 *read_data;
+ size_t read_data_size;
+};
+
+struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp);
+void issei_cl_remove(struct issei_host_client *cl);
+
+int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver,
+ u32 *flags);
+int issei_cl_disconnect(struct issei_host_client *cl);
+void issei_cl_all_disconnect(struct issei_device *idev);
+
+ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size);
+int issei_cl_write_from_queue(struct issei_device *idev);
+int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size);
+ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size);
+int issei_cl_check_read(struct issei_host_client *cl);
+int issei_cl_check_write(struct issei_host_client *cl);
+void issei_cl_clean_all_wbuf(struct issei_host_client *cl);
+
+#endif /* ISSEI_HOST_CLIENT_H_ */
diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c
new file mode 100644
index 000000000000..501774aa95ff
--- /dev/null
+++ b/drivers/misc/issei/hw_heci.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/align.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "hw_heci.h"
+#include "hw_heci_regs.h"
+#include "hw_msg.h"
+
+/**
+ * heci_reg_read - Reads 32bit data from the issei heci device
+ * @hw: the heci hardware structure
+ * @offset: offset from which to read the data
+ *
+ * Return: register value (u32)
+ */
+static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned long offset)
+{
+ return ioread32(hw->mem_addr + offset);
+}
+
+/**
+ * heci_reg_write - Writes 32bit data to the issei heci device
+ *
+ * @hw: the heci hardware structure
+ * @offset: offset from which to write the data
+ * @value: register value to write (u32)
+ */
+static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned long offset, u32 value)
+{
+ iowrite32(value, hw->mem_addr + offset);
+}
+
+/**
+ * heci_fwcbrw_read - Reads 32bit data from heci circular buffer
+ * @hw: the heci hardware structure
+ *
+ * Return: FW_CB_RW register value (u32)
+ */
+static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw)
+{
+ return heci_reg_read(hw, FW_CB_RW);
+}
+
+/**
+ * heci_hcbww_write - write 32bit data to the host circular buffer
+ * @hw: the heci hardware structure
+ * @data: 32bit data to be written to the host circular buffer
+ */
+static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 data)
+{
+ heci_reg_write(hw, H_CB_WW, data);
+}
+
+/**
+ * heci_irq_src - Filters IRQ source bits from the host CSR
+ * @hcsr: host CSR register value
+ *
+ * Return: interrupt source bits of host CSR
+ */
+static inline u32 heci_irq_src(u32 hcsr)
+{
+ return hcsr & H_CSR_IS;
+}
+
+/**
+ * heci_hcsr_read - Reads 32bit data from the host CSR
+ * @idev: the device structure
+ *
+ * Return: H_CSR register value (u32)
+ */
+static inline u32 heci_hcsr_read(const struct issei_device *idev)
+{
+ return heci_reg_read(to_heci_hw(idev), H_CSR);
+}
+
+/**
+ * heci_hcsr_write - writes H_CSR register to device
+ * @idev: the device structure
+ * @reg: new register value
+ */
+static inline void heci_hcsr_write(struct issei_device *idev, u32 reg)
+{
+ heci_reg_write(to_heci_hw(idev), H_CSR, reg);
+}
+
+/**
+ * heci_hcsr_set - writes H_CSR register to the heci device
+ * @idev: the device structure
+ * @reg: new register value
+ *
+ * Writes H_CSR register to the heci device
+ * and ignores the H_IS bit for it is write-one-to-zero.
+ *
+ */
+static inline void heci_hcsr_set(struct issei_device *idev, u32 reg)
+{
+ reg &= ~H_CSR_IS;
+ heci_hcsr_write(idev, reg);
+}
+
+/**
+ * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG)
+ * @idev: the device structure
+ */
+static inline void heci_hcsr_set_hig(struct issei_device *idev)
+{
+ u32 reg;
+
+ reg = heci_hcsr_read(idev) | H_CSR_IG;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * heci_fwcsr_read - Reads 32bit data from the FW CSR
+ * @idev: the device structure
+ *
+ * Return: FW_CSR_HA register value (u32)
+ */
+static inline u32 heci_fwcsr_read(const struct issei_device *idev)
+{
+ return heci_reg_read(to_heci_hw(idev), FW_CSR_HA);
+}
+
+/**
+ * heci_count_full_read_slots - counts read full slots.
+ * @idev: the device structure
+ *
+ * Return: -EOVERFLOW if overflow, otherwise filled slots count
+ */
+static int heci_count_full_read_slots(struct issei_device *idev)
+{
+ u8 buffer_depth, filled_slots;
+ u8 read_ptr, write_ptr;
+ u32 reg;
+
+ reg = heci_fwcsr_read(idev);
+ buffer_depth = (u8)FIELD_GET(FW_CSR_CBD, reg);
+ read_ptr = (u8)FIELD_GET(FW_CSR_CBRP, reg);
+ write_ptr = (u8)FIELD_GET(FW_CSR_CBWP, reg);
+ filled_slots = write_ptr - read_ptr;
+
+ /* check for overflow */
+ if (filled_slots > buffer_depth)
+ return -EOVERFLOW;
+
+ dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots);
+ return filled_slots;
+}
+
+/**
+ * heci_irq_disable - disables heci device interrupts
+ * @idev: the device structure
+ * @reg: supplied hcsr register value
+ *
+ * disables heci device interrupts using supplied hcsr register value.
+ */
+static inline void heci_irq_disable(struct issei_device *idev, u32 reg)
+{
+ reg &= ~H_CSR_IE;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * heci_irq_clear - clear and stop interrupts
+ * @idev: the device structure
+ * @reg: supplied hcsr register value
+ */
+static inline void heci_irq_clear(struct issei_device *idev, u32 reg)
+{
+ if (heci_irq_src(reg))
+ heci_hcsr_write(idev, reg);
+}
+
+/**
+ * issei_heci_irq_clear - clear and stop interrupts
+ * @idev: the device structure
+ */
+static void issei_heci_irq_clear(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ heci_irq_clear(idev, reg);
+}
+
+/**
+ * issei_heci_irq_enable - enables heci device interrupts
+ * @idev: the device structure
+ */
+static void issei_heci_irq_enable(struct issei_device *idev)
+{
+ u32 reg;
+
+ reg = heci_hcsr_read(idev) | H_CSR_IE;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * issei_heci_irq_disable - disables heci device interrupts
+ * @idev: the device structure
+ */
+static void issei_heci_irq_disable(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ heci_irq_disable(idev, reg);
+}
+
+static void issei_heci_irq_sync(struct issei_device *idev)
+{
+ synchronize_irq(to_heci_hw(idev)->irq);
+}
+
+/**
+ * issei_heci_hw_reset_release - release device from the reset
+ * @idev: the device structure
+ */
+static void issei_heci_hw_reset_release(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ reg |= H_CSR_IG;
+ reg &= ~H_CSR_RST;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * heci_hw_is_ready - check whether the hw has turned ready
+ * @idev: the device structure
+ *
+ * Return: bool
+ */
+static bool heci_hw_is_ready(struct issei_device *idev)
+{
+ u32 reg = heci_fwcsr_read(idev);
+
+ return reg & FW_CSR_RDY;
+}
+
+/**
+ * heci_hw_is_resetting - check whether the hw is in reset
+ * @idev: the device structure
+ *
+ * Return: bool
+ */
+static bool heci_hw_is_resetting(struct issei_device *idev)
+{
+ u32 reg = heci_fwcsr_read(idev);
+
+ return reg & FW_CSR_RST;
+}
+
+/**
+ * issei_heci_host_set_ready - enable device
+ * @idev: the device structure
+ */
+static void issei_heci_host_set_ready(struct issei_device *idev)
+{
+ u32 reg = heci_hcsr_read(idev);
+
+ reg |= H_CSR_IE | H_CSR_IG | H_CSR_RDY;
+ heci_hcsr_set(idev, reg);
+}
+
+/**
+ * issei_heci_hw_reset - resets fw via heci csr register.
+ * @idev: the device structure
+ * @enable: if interrupt should be enabled after reset.
+ *
+ * Return: 0 on success an error code otherwise
+ */
+static int issei_heci_hw_reset(struct issei_device *idev, bool enable)
+{
+ u32 reg;
+
+ if (enable)
+ issei_heci_irq_enable(idev);
+
+ reg = heci_hcsr_read(idev);
+ /*
+ * H_CSR_RST may be found lit before reset is started,
+ * for example if preceding reset flow hasn't completed.
+ * In that case asserting H_CSR_RST will be ignored, therefore
+ * we need to clean H_CSR_RST bit to start a successful reset sequence.
+ */
+ if (reg & H_CSR_RST) {
+ dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg);
+ reg &= ~H_CSR_RST;
+ heci_hcsr_set(idev, reg);
+ reg = heci_hcsr_read(idev);
+ }
+
+ reg |= H_CSR_RST | H_CSR_IG | H_CSR_IS;
+
+ if (!enable)
+ reg &= ~H_CSR_IE;
+
+ heci_hcsr_write(idev, reg);
+
+ /*
+ * Host reads the H_CSR once to ensure that the
+ * posted write to H_CSR completes.
+ */
+ reg = heci_hcsr_read(idev);
+
+ if (!(reg & H_CSR_RST))
+ dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg);
+
+ if (reg & H_CSR_RDY)
+ dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg);
+
+ if (!enable)
+ issei_heci_hw_reset_release(idev);
+ return 0;
+}
+
+/**
+ * issei_heci_irq_write_generate - generate interrupt to signal write completion.
+ * @idev: the device structure
+ *
+ * Return: 0 on success, -EIO if hardware is not ready and requires reset
+ */
+static int issei_heci_irq_write_generate(struct issei_device *idev)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock)
+ heci_hcsr_set_hig(idev);
+ if (!heci_hw_is_ready(idev))
+ return -EIO;
+ return 0;
+}
+
+/**
+ * issei_heci_hw_config - initial hardware configuration.
+ * @idev: the device structure
+ *
+ * Return: 0 always
+ */
+static int issei_heci_hw_config(struct issei_device *idev)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+ u32 reg;
+
+ /* Doesn't change in runtime */
+ reg = heci_hcsr_read(idev);
+ hw->hbuf_depth = FIELD_GET(H_CSR_CBD, reg);
+
+ return 0;
+}
+
+/**
+ * heci_write_hbuf - write to hardware buffer
+ * @idev: the device structure
+ * @data: data to write
+ * @data_len: data size
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int heci_write_hbuf(struct issei_device *idev, const void *data, size_t data_len)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
+ dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
+ data_len, CB_SLOT_SIZE);
+ return -EINVAL;
+ }
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock) {
+ const u32 *reg_buf = data;
+ size_t i;
+
+ for (i = 0; i < data_len / CB_SLOT_SIZE; i++)
+ heci_hcbww_write(hw, reg_buf[i]);
+ }
+
+ return issei_heci_irq_write_generate(idev);
+}
+
+/**
+ * heci_read_hbuf - read data from hardware buffer
+ * @idev: the device structure
+ * @data: buffer to store read data
+ * @data_len: buffer size
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int heci_read_hbuf(struct issei_device *idev, void *data, size_t data_len)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+ u32 *reg_buf = data;
+
+ if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) {
+ dev_err(&idev->dev, "Data size %zu not aligned to slot size %zu\n",
+ data_len, CB_SLOT_SIZE);
+ return -EINVAL;
+ }
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock) {
+ for (; data_len >= CB_SLOT_SIZE; data_len -= CB_SLOT_SIZE)
+ *reg_buf++ = heci_fwcbrw_read(hw);
+ }
+ return 0;
+}
+
+/**
+ * issei_heci_setup_message_send - send setup message to firmware
+ * @idev: the device structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int issei_heci_setup_message_send(struct issei_device *idev)
+{
+ struct ham_setup_shared_memory_req req = {
+ .msg_id = HAM_CB_MESSAGE_ID_REQ,
+ .ver = HAM_CB_MESSAGE_VER,
+ .reserved = 0,
+ .buffer_physical_address = idev->dma.daddr,
+ .host_to_fw_section_length = idev->dma.length.h2f,
+ .fw_to_host_section_length = idev->dma.length.f2h,
+ .control_length = idev->dma.length.ctl
+ };
+ int ret;
+
+ ret = heci_write_hbuf(idev, &req, sizeof(req));
+ if (ret)
+ dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * issei_heci_setup_message_recv - receive setup message response from firmware
+ * @idev: the device structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int issei_heci_setup_message_recv(struct issei_device *idev)
+{
+ struct ham_setup_shared_memory_res res;
+ int ret;
+
+ if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) {
+ dev_dbg(&idev->dev, "Setup response is not fully received\n");
+ return -ENODATA;
+ }
+
+ ret = heci_read_hbuf(idev, &res, sizeof(res));
+ if (ret) {
+ dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret);
+ return ret;
+ }
+
+ if (res.msg_id != HAM_CB_MESSAGE_ID_RES) {
+ dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n",
+ res.msg_id, HAM_CB_MESSAGE_ID_RES);
+ return -EPROTO;
+ }
+ if (res.status != 0) {
+ dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static const struct issei_hw_ops hw_heci_ops = {
+ .irq_clear = issei_heci_irq_clear,
+ .irq_enable = issei_heci_irq_enable,
+ .irq_disable = issei_heci_irq_disable,
+ .irq_sync = issei_heci_irq_sync,
+
+ .hw_reset = issei_heci_hw_reset,
+ .hw_config = issei_heci_hw_config,
+ .hw_reset_release = issei_heci_hw_reset_release,
+ .host_set_ready = issei_heci_host_set_ready,
+
+ .hw_is_ready = heci_hw_is_ready,
+
+ .setup_message_send = issei_heci_setup_message_send,
+ .setup_message_recv = issei_heci_setup_message_recv,
+ .irq_write_generate = issei_heci_irq_write_generate,
+};
+
+const struct issei_hw_ops *issei_heci_get_ops(void)
+{
+ return &hw_heci_ops;
+}
+
+irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id)
+{
+ struct issei_device *idev = dev_id;
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+ u32 reg;
+
+ reg = heci_hcsr_read(idev);
+ if (!heci_irq_src(reg))
+ return IRQ_NONE;
+
+ scoped_guard(spinlock_irqsave, &hw->access_lock) {
+ reg = heci_hcsr_read(idev);
+ heci_irq_clear(idev, reg);
+
+ if (heci_hw_is_resetting(idev))
+ heci_hcsr_set_hig(idev);
+ }
+
+ dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg));
+
+ issei_poke_process_thread(idev);
+
+ return IRQ_HANDLED;
+}
+
+static const struct hw_heci_cfg hw_heci_pch_cfg = {
+ .dma_length.h2f = SZ_64K,
+ .dma_length.f2h = SZ_64K,
+ .dma_length.ctl = SZ_4K,
+};
+
+const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx)
+{
+ return &hw_heci_pch_cfg;
+}
+
+/**
+ * issei_heci_dev_init - initializes the issei device structure with hw_heci
+ * @idev: device structure
+ * @mem_addr: memory address on bar
+ * @cfg: per device generation config
+ */
+void issei_heci_dev_init(struct issei_device *idev,
+ void __iomem *mem_addr, const struct hw_heci_cfg *cfg)
+{
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ spin_lock_init(&hw->access_lock);
+ hw->mem_addr = mem_addr;
+ hw->cfg = cfg;
+}
diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h
new file mode 100644
index 000000000000..0ae2faeedf12
--- /dev/null
+++ b/drivers/misc/issei/hw_heci.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HW_HECI_H_
+#define _ISSEI_HW_HECI_H_
+#include <linux/irqreturn.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "issei_dev.h"
+
+/*
+ * hw_heci_cfg - issei heci device configuration
+ *
+ * @dma_length: DMA area length
+ */
+struct hw_heci_cfg {
+ const struct issei_dma_length dma_length;
+};
+
+/**
+ * struct issei_heci_hw - issei heci hw specific data
+ *
+ * @cfg: per device generation config and ops
+ * @mem_addr: io memory address
+ * @irq: device irq number
+ * @access_lock: spinlock to protect hw access
+ * @hbuf_depth: depth of hardware host/write buffer in slots
+ */
+struct issei_heci_hw {
+ const struct hw_heci_cfg *cfg;
+ void __iomem *mem_addr;
+ int irq;
+ spinlock_t access_lock;
+ u8 hbuf_depth;
+};
+
+#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw)
+
+const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx);
+const struct issei_hw_ops *issei_heci_get_ops(void);
+
+void issei_heci_dev_init(struct issei_device *idev,
+ void __iomem *mem_addr, const struct hw_heci_cfg *cfg);
+
+irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id);
+
+#endif /* _ISSEI_HW_HECI_H_ */
diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci_regs.h
new file mode 100644
index 000000000000..a991b670a9d3
--- /dev/null
+++ b/drivers/misc/issei/hw_heci_regs.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HW_HECI_REGS_H_
+#define _ISSEI_HW_HECI_REGS_H_
+
+#include <linux/bits.h>
+
+/* H_CB_WW - Host Circular Buffer (CB) Write Window register */
+#define H_CB_WW 0x0
+/* H_CSR - Host Control Status register */
+#define H_CSR 0x4
+#define H_CSR_CBD GENMASK(31, 24) /* Host Circular Buffer Depth */
+#define H_CSR_CBWP GENMASK(23, 16) /* Host Circular Buffer Write Pointer */
+#define H_CSR_CBRP GENMASK(15, 8) /* Host Circular Buffer Read Pointer */
+#define H_CSR_RST BIT(4) /* Host Reset */
+#define H_CSR_RDY BIT(3) /* Host Ready */
+#define H_CSR_IG BIT(2) /* Host Interrupt Generate */
+#define H_CSR_IS BIT(1) /* Host Interrupt Status */
+#define H_CSR_IE BIT(0) /* Host Interrupt Enable */
+/* FW_CB_RW - FW Circular Buffer Read Window register (read only) */
+#define FW_CB_RW 0x8
+/* FW_CSR_HA - FW Control Status Host Access register (read only) */
+#define FW_CSR_HA 0xC
+#define FW_CSR_CBD GENMASK(31, 24) /* FW CB (Circular Buffer) Depth */
+#define FW_CSR_CBWP GENMASK(23, 16) /* FW CB Write Pointer */
+#define FW_CSR_CBRP GENMASK(15, 8) /* FW CB Read Pointer */
+#define FW_CSR_RST BIT(4) /* FW Reset */
+#define FW_CSR_RDY BIT(3) /* FW Ready */
+#define FW_CSR_IG BIT(2) /* FW Interrupt Generate */
+#define FW_CSR_IS BIT(1) /* FW Interrupt Status */
+#define FW_CSR_IE BIT(0) /* FW Interrupt Enable */
+
+#define CB_SLOT_SIZE sizeof(u32) /* Circular Buffer windows size */
+
+#endif /* _ISSEI_HW_HECI_REGS_H_ */
diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h
new file mode 100644
index 000000000000..28fd3775f64c
--- /dev/null
+++ b/drivers/misc/issei/hw_msg.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_HW_MSG_H_
+#define _ISSEI_HW_MSG_H_
+
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+#define HAM_CB_MESSAGE_ID_REQ 0x8086cafe
+#define HAM_CB_MESSAGE_ID_RES 0xcafe8086
+#define HAM_CB_MESSAGE_VER 0x1
+
+/**
+ * struct ham_setup_shared_memory_req - shared memory setup request
+ * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_REQ
+ * @ver: message version (%HAM_CB_MESSAGE_VER)
+ * @reserved: reserved
+ * @buffer_physical_address: physical address of DMA buffer
+ * @host_to_fw_section_length: memory size for host to fw communication
+ * @fw_to_host_section_length: memory size for fw to host communication
+ * @control_length: memory size for control buffer
+ */
+struct ham_setup_shared_memory_req {
+ u32 msg_id;
+ u16 ver;
+ u16 reserved;
+ u64 buffer_physical_address;
+ u32 host_to_fw_section_length;
+ u32 fw_to_host_section_length;
+ u32 control_length;
+} __packed __aligned(4);
+
+/**
+ * struct ham_setup_shared_memory_res - shared memory setup response
+ * @msg_id: message id, should be %HAM_CB_MESSAGE_ID_RES
+ * @status: operation status
+ */
+struct ham_setup_shared_memory_res {
+ u32 msg_id;
+ u32 status;
+};
+
+/**
+ * struct control_buffer - control buffer structure
+ * @h2f_counter_wr: write counter host to fw
+ * @h2f_counter_rd: read counter host to fw
+ * @f2h_counter_wr: write counter fw to host
+ * @f2h_counter_rd: read counter fw to host
+ */
+struct control_buffer {
+ u32 h2f_counter_wr;
+ u32 h2f_counter_rd;
+ u32 f2h_counter_wr;
+ u32 f2h_counter_rd;
+};
+
+/* HAM messages over DMA */
+
+/**
+ * struct ham_message_header - message header over DMA
+ * @length: message length (payload only, not including header)
+ * @fw_id: firmware client id (0 means Bus Message)
+ * @host_id: host client id (0 means Bus Message)
+ * @flags: message flags
+ * @status: operation status
+ * @reserved: reserved
+ */
+struct ham_message_header {
+ u32 length;
+ u16 fw_id;
+ u16 host_id;
+ u32 flags;
+ u32 status;
+ u32 reserved;
+};
+
+/* Bus Commands */
+#define HAM_BUS_CMD_START_REQ 0x00
+#define HAM_BUS_CMD_START_RSP 0x80
+#define HAM_BUS_CMD_CLIENT_REQ 0x01
+#define HAM_BUS_CMD_CLIENT_RSP 0x81
+
+/**
+ * struct ham_bus_message - bus message header
+ * @cmd: command code
+ */
+struct ham_bus_message {
+ u32 cmd;
+};
+
+#define HAM_SUPPORTED_VERSION 0x01
+
+/**
+ * struct ham_start_message_req - start message
+ * @header: bus message header (%HAM_BUS_CMD_START_REQ)
+ * @supported_version: supported protocol version
+ * @heci_capabilities_length: protocol capabilities length in bytes
+ * @heci_capabilities: protocol capabilities data
+ */
+struct ham_start_message_req {
+ struct ham_bus_message header;
+ u16 supported_version;
+ u8 heci_capabilities_length;
+ u8 heci_capabilities[] __counted_by(heci_capabilities_length);
+} __packed;
+
+/**
+ * struct ham_start_message_res - start message response
+ * @header: bus message header (%HAM_BUS_CMD_START_RSP)
+ * @fw_version: firmware version (four u16 blocks)
+ * @supported_version: supported protocol version
+ * @heci_capabilities_length: protocol capabilities length in bytes
+ * @heci_capabilities: protocol capabilities data
+ */
+struct ham_start_message_res {
+ struct ham_bus_message header;
+ u16 fw_version[4];
+ u16 supported_version;
+ u8 heci_capabilities_length;
+ u8 heci_capabilities[] __counted_by(heci_capabilities_length);
+} __packed;
+
+/**
+ * struct ham_get_clients_req - clients list request
+ * @header: bus message header (%HAM_BUS_CMD_CLIENT_REQ)
+ */
+struct ham_get_clients_req {
+ struct ham_bus_message header;
+};
+
+/**
+ * struct ham_client_properties - single client properties
+ * @client_number: client id in firmware
+ * @protocol_ver: client protocol version
+ * @reserved: reserved
+ * @client_uuid: protocol name (UUID)
+ * @client_mtu: max message length supported by client
+ * @flags: client flags
+ */
+struct ham_client_properties {
+ u16 client_number;
+ u8 protocol_ver;
+ u8 reserved;
+ uuid_t client_uuid;
+ u32 client_mtu;
+ u32 flags;
+};
+
+/**
+ * struct ham_get_clients_res - client properties response
+ * @header: bus message header (%HAM_BUS_CMD_CLIENT_RSP)
+ * @client_count: number of clients in firmware
+ * @reserved: reserved
+ * @clients_props: list of client properties
+ */
+struct ham_get_clients_res {
+ struct ham_bus_message header;
+ u16 client_count;
+ u16 reserved;
+ struct ham_client_properties clients_props[] __counted_by(client_count);
+};
+
+#endif /* _ISSEI_HW_MSG_H_ */
diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h
new file mode 100644
index 000000000000..b47c4a7c2da4
--- /dev/null
+++ b/drivers/misc/issei/issei_dev.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023-2026 Intel Corporation */
+#ifndef _ISSEI_DEV_H_
+#define _ISSEI_DEV_H_
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include "dma.h"
+
+struct cdev;
+struct kset;
+
+struct issei_device;
+struct issei_host_client;
+
+extern struct class *issei_class;
+
+#define ISSEI_HOST_CLIENTS_MAX 255
+
+#define ISSEI_SUPPORTED_PROTOCOL_VER 1
+
+#define ISSEI_MAX_CONSEC_RESET 3
+
+#define ISSEI_RST_HW_READY_TIMEOUT_MSEC (2 * MSEC_PER_SEC)
+#define ISSEI_RST_STEP_TIMEOUT_MSEC (2 * MSEC_PER_SEC)
+#define ISSEI_STOP_TIMEOUT_MSEC 500
+#define ISSEI_WRITE_TIMEOUT_MSEC (MSEC_PER_SEC)
+
+/**
+ * struct issei_write_buf - write buffer object
+ * @list: linked list pointer
+ * @cl: host client that requested this write
+ * @data: data to write
+ * @data_size: data size
+ */
+struct issei_write_buf {
+ struct list_head list;
+ struct issei_host_client *cl;
+ const u8 *data;
+ size_t data_size;
+};
+
+/**
+ * struct issei_hw_ops - callbacks for hardware operations
+ * @irq_clear: clear irq
+ * @irq_enable: enable irq
+ * @irq_disable: disable irq
+ * @irq_sync: sync irq
+ * @hw_reset: initiate hardware reset
+ * @hw_config: initial hardware config
+ * @hw_is_ready: check if hardware is ready
+ * @hw_reset_release: release hardware from reset
+ * @host_set_ready: set host ready indicator
+ * @setup_message_send: send setup message
+ * @setup_message_recv: receive setup message
+ * @irq_write_generate: generate interrupt on write complete
+ */
+struct issei_hw_ops {
+ void (*irq_clear)(struct issei_device *idev);
+ void (*irq_enable)(struct issei_device *idev);
+ void (*irq_disable)(struct issei_device *idev);
+ void (*irq_sync)(struct issei_device *idev);
+ int (*hw_reset)(struct issei_device *idev, bool enable);
+ int (*hw_config)(struct issei_device *idev);
+ bool (*hw_is_ready)(struct issei_device *idev);
+ void (*hw_reset_release)(struct issei_device *idev);
+ void (*host_set_ready)(struct issei_device *idev);
+ int (*setup_message_send)(struct issei_device *idev);
+ int (*setup_message_recv)(struct issei_device *idev);
+ int (*irq_write_generate)(struct issei_device *idev);
+};
+
+/**
+ * enum issei_rst_state: driver reset flow states
+ * @ISSEI_RST_STATE_INIT: initial state
+ * @ISSEI_RST_STATE_HW_READY: waiting for HW to be ready
+ * @ISSEI_RST_STATE_SETUP: waiting for channel setup completion
+ * @ISSEI_RST_STATE_START: waiting for start handshake completion
+ * @ISSEI_RST_STATE_CLIENT_ENUM: waiting for client enumeration
+ * @ISSEI_RST_STATE_DONE: reset flow is done
+ * @ISSEI_RST_STATE_DISABLED: flow is disabled
+ */
+enum issei_rst_state {
+ ISSEI_RST_STATE_INIT,
+ ISSEI_RST_STATE_HW_READY,
+ ISSEI_RST_STATE_SETUP,
+ ISSEI_RST_STATE_START,
+ ISSEI_RST_STATE_CLIENT_ENUM,
+ ISSEI_RST_STATE_DONE,
+ ISSEI_RST_STATE_DISABLED,
+};
+
+/**
+ * struct issei_device - issei device
+ * @parent: parent device object
+ * @dev: associated device object
+ * @cdev: character device
+ * @minor: allocated minor number
+ * @wait_has_data: wait queue for data
+ * @has_data: there are data to process
+ * @power_down: device is powering down
+ * @wait_rst_state: waitqueue for reset state processing
+ * @rst_state: reset state
+ * @fw_protocol_ver: protocol version
+ * @fw_version: firmware version
+ * @process_thread: worker thread
+ * @reset_count: number of consecutive link reset attempts
+ * @all_reset_count: cumilative number of link reset attempts
+ * @client_lock: mutex to protect client lists and write queue
+ * @host_client_list: host clients list
+ * @host_client_last_id: last allocated host client id
+ * @host_client_count: number of active host clients
+ * @fw_client_list: firmware clients list
+ * @write_queue: write queue
+ * @last_write_ts: last write timestamp
+ * @dma: DMA memory configuration
+ * @ops: hardware operations
+ * @hw: hw-specific data
+ */
+struct issei_device {
+ struct device *parent;
+ struct device dev;
+ struct cdev *cdev;
+ u32 minor;
+ wait_queue_head_t wait_has_data;
+ bool has_data;
+ bool power_down;
+ wait_queue_head_t wait_rst_state;
+ enum issei_rst_state rst_state;
+ u16 fw_protocol_ver;
+ u16 fw_version[4];
+ /* reset flow */
+ struct task_struct *process_thread;
+ u8 reset_count;
+ u8 all_reset_count;
+ /* clients */
+ struct mutex client_lock;
+ struct list_head host_client_list;
+ u16 host_client_last_id;
+ u8 host_client_count;
+ struct kset *fw_clients;
+ struct list_head fw_client_list;
+ struct list_head write_queue;
+ ktime_t last_write_ts;
+ struct issei_dma dma;
+ const struct issei_hw_ops *ops;
+ char hw[];
+};
+
+static inline void issei_poke_process_thread(struct issei_device *idev)
+{
+ WRITE_ONCE(idev->has_data, true);
+ wake_up_interruptible(&idev->wait_has_data);
+}
+
+int issei_start(struct issei_device *idev);
+void issei_stop(struct issei_device *idev);
+#endif /* _ISSEI_DEV_H_ */
diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c
new file mode 100644
index 000000000000..5987fb340250
--- /dev/null
+++ b/drivers/misc/issei/main.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "cdev.h"
+#include "fw_client.h"
+#include "host_client.h"
+#include "ham.h"
+#include "issei_dev.h"
+
+static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_state state)
+{
+ idev->rst_state = state;
+ /* wake up the thread */
+ if (waitqueue_active(&idev->wait_rst_state))
+ wake_up(&idev->wait_rst_state);
+}
+
+static int issei_reset(struct issei_device *idev)
+{
+ int ret;
+
+ idev->ops->irq_clear(idev);
+
+ issei_cl_all_disconnect(idev);
+ issei_fw_cl_remove_all(idev);
+ /* No need to check for overflow here, the counter is used only for info */
+ idev->all_reset_count++;
+ ret = idev->ops->hw_reset(idev, !idev->power_down);
+ issei_dmam_setup(idev);
+ if (ret) {
+ dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret);
+ return ret;
+ }
+
+ if (idev->power_down) {
+ dev_dbg(&idev->dev, "powering down: end of reset\n");
+ issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int issei_process_read_msg(struct issei_device *idev)
+{
+ struct issei_dma_data data = {};
+ int ret;
+
+ ret = issei_dma_read(idev, &data);
+ if (ret)
+ return ret;
+
+ dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id,
+ data.status, data.length);
+ if (data.status != HAMS_SUCCESS) {
+ dev_err(&idev->dev, "Command failed with status 0x%02X", data.status);
+ kfree(data.buf);
+ ret = -EIO;
+ } else {
+ if (issei_is_ham_rsp(data.fw_id, data.host_id))
+ ret = issei_ham_process_ham_rsp(idev, data.buf, data.length);
+ else
+ ret = issei_cl_read_buf(idev, data.fw_id, data.host_id,
+ data.buf, data.length);
+ }
+ idev->ops->irq_write_generate(idev);
+ return ret;
+}
+
+static int issei_process_write_msg(struct issei_device *idev)
+{
+ if (idev->rst_state != ISSEI_RST_STATE_DONE)
+ return 0;
+
+ return issei_cl_write_from_queue(idev);
+}
+
+static int issei_process_thread(void *_dev)
+{
+ long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT;
+ struct issei_device *idev = _dev;
+ int ret;
+
+ while (!kthread_should_stop()) {
+ dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state);
+ if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) {
+ if (!idev->power_down)
+ dev_dbg(&idev->dev, "HW not ready, resetting\n");
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ }
+ if (idev->power_down)
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ WRITE_ONCE(idev->has_data, false);
+ dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state);
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ ret = 0;
+ switch (idev->rst_state) {
+ case ISSEI_RST_STATE_DISABLED:
+ if (idev->power_down) {
+ dev_dbg(&idev->dev, "Interrupt in power down?\n");
+ break;
+ }
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ fallthrough;
+
+ case ISSEI_RST_STATE_INIT:
+ idev->ops->irq_clear(idev);
+ idev->ops->irq_sync(idev);
+
+ if (!idev->power_down) {
+ idev->reset_count++;
+ if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) {
+ dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
+ issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED);
+ break;
+ }
+ }
+
+ ret = issei_reset(idev);
+ if (idev->power_down) {
+ dev_dbg(&idev->dev, "Powering down\n");
+ return 0;
+ }
+ if (ret)
+ break;
+
+ idev->rst_state = ISSEI_RST_STATE_HW_READY;
+ timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC);
+ break;
+
+ case ISSEI_RST_STATE_HW_READY:
+ if (!idev->ops->hw_is_ready(idev)) {
+ dev_dbg(&idev->dev, "HW is not ready?\n");
+ timeout = old_timeout;
+ break;
+ }
+
+ dev_dbg(&idev->dev, "HW is ready\n");
+ idev->ops->hw_reset_release(idev);
+ idev->ops->host_set_ready(idev);
+ ret = idev->ops->setup_message_send(idev);
+ if (ret)
+ break;
+
+ idev->rst_state = ISSEI_RST_STATE_SETUP;
+ timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+ break;
+
+ case ISSEI_RST_STATE_SETUP:
+ ret = idev->ops->setup_message_recv(idev);
+ if (ret) {
+ if (ret == -ENODATA) {
+ ret = 0;
+ timeout = old_timeout;
+ }
+ } else {
+ timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+ ret = issei_ham_send_start_req(idev);
+ idev->rst_state = ISSEI_RST_STATE_START;
+ }
+ break;
+
+ case ISSEI_RST_STATE_START:
+ ret = issei_process_read_msg(idev);
+ if (!ret) {
+ timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+ idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM;
+ } else if (ret == -ENODATA) {
+ ret = 0;
+ timeout = old_timeout;
+ }
+ break;
+
+ case ISSEI_RST_STATE_CLIENT_ENUM:
+ ret = issei_process_read_msg(idev);
+ if (ret) {
+ if (ret == -ENODATA) {
+ ret = 0;
+ timeout = old_timeout;
+ }
+ } else {
+ idev->reset_count = 0;
+ idev->rst_state = ISSEI_RST_STATE_DONE;
+ dev_dbg(&idev->dev, "Reset finished successfully\n");
+ }
+ break;
+
+ case ISSEI_RST_STATE_DONE:
+ ret = issei_process_read_msg(idev);
+ if (ret != 0 && ret != -ENODATA)
+ break;
+
+ ret = issei_process_write_msg(idev);
+ break;
+ }
+
+ if (ret) {
+ dev_warn(&idev->dev, "Process failed ret = %d\n", ret);
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ continue;
+ }
+
+ /*
+ * Every thread that has data to process sets the 'has_data' flag and
+ * triggers the wait queue.
+ * The processing thread, in each loop iteration, resets 'has_data'
+ * and processes all available data.
+ *
+ * After processing, the thread waits for 'has_data' to be set again.
+ *
+ * If the wait function times out but 'has_data' becomes 1 before
+ * the subsequent atomic read check, this is acceptable from a flow
+ * perspective - the thread will continue processing the data.
+ *
+ * The 'has_data' flag cannot become 0 between the wait function and
+ * the atomic read check, since only this thread is allowed to reset it to 0.
+ */
+
+ old_timeout = wait_event_interruptible_timeout(idev->wait_has_data,
+ READ_ONCE(idev->has_data),
+ timeout);
+ if (idev->rst_state == ISSEI_RST_STATE_DISABLED)
+ continue;
+
+ if (!READ_ONCE(idev->has_data)) {
+ dev_warn(&idev->dev, "Timed out at state %d, resetting\n",
+ idev->rst_state);
+ idev->rst_state = ISSEI_RST_STATE_INIT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * issei_start - configure HW device and start processing thread.
+ * @idev: the device structure
+ *
+ * Return: 0 on success, < 0 on failure
+ */
+int issei_start(struct issei_device *idev)
+{
+ int ret;
+
+ idev->power_down = false;
+
+ ret = issei_dmam_setup(idev);
+ if (ret)
+ return ret;
+
+ idev->ops->irq_clear(idev);
+
+ ret = idev->ops->hw_config(idev);
+ if (ret)
+ return ret;
+
+ idev->process_thread = kthread_run(issei_process_thread, idev,
+ "kisseiprocess/%s", dev_name(&idev->dev));
+ if (IS_ERR(idev->process_thread)) {
+ ret = PTR_ERR(idev->process_thread);
+ dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret);
+ return ret;
+ }
+
+ issei_poke_process_thread(idev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(issei_start);
+
+/**
+ * issei_stop - stop interrupts and processing thread.
+ * @idev: the device structure
+ */
+void issei_stop(struct issei_device *idev)
+{
+ idev->power_down = true;
+
+ idev->ops->irq_clear(idev);
+ idev->ops->irq_sync(idev);
+
+ issei_poke_process_thread(idev);
+
+ wait_event_timeout(idev->wait_rst_state,
+ (idev->rst_state == ISSEI_RST_STATE_DISABLED),
+ msecs_to_jiffies(ISSEI_STOP_TIMEOUT_MSEC));
+ kthread_stop(idev->process_thread);
+}
+EXPORT_SYMBOL_GPL(issei_stop);
diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c
new file mode 100644
index 000000000000..86c567a4bf0f
--- /dev/null
+++ b/drivers/misc/issei/pci_heci.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+
+#include "cdev.h"
+#include "hw_heci.h"
+#include "hw_heci_regs.h"
+
+static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct device *dev = &pdev->dev;
+ const struct hw_heci_cfg *cfg;
+ struct issei_device *idev;
+ struct issei_heci_hw *hw;
+ char __iomem *registers;
+ int err;
+
+ cfg = issei_heci_get_cfg(ent->driver_data);
+ if (!cfg)
+ return dev_err_probe(dev, -ENODEV, "no usable configuration.\n");
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return dev_err_probe(dev, err, "failed to enable pci device.\n");
+
+ pci_set_master(pdev);
+
+ registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME);
+ if (IS_ERR(registers))
+ return dev_err_probe(dev, PTR_ERR(registers), "failed to get pci region.\n");
+
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (err)
+ return dev_err_probe(dev, err, "no usable DMA configuration.\n");
+
+ idev = issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_get_ops());
+ if (IS_ERR(idev))
+ return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n");
+
+ issei_heci_dev_init(idev, registers, cfg);
+
+ pci_set_drvdata(pdev, idev);
+
+ err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
+ if (err < 0) {
+ dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n");
+ goto deregister;
+ }
+
+ hw = to_heci_hw(idev);
+ hw->irq = pci_irq_vector(pdev, 0);
+
+ err = request_threaded_irq(hw->irq,
+ issei_heci_irq_quick_handler,
+ NULL,
+ IRQF_SHARED, KBUILD_MODNAME, idev);
+ if (err)
+ goto release_irq;
+
+ err = issei_start(idev);
+ if (err) {
+ dev_err_probe(dev, err, "init hw failure.\n");
+ goto free_irq;
+ }
+
+ return 0;
+
+free_irq:
+ idev->ops->irq_disable(idev);
+ free_irq(hw->irq, idev);
+release_irq:
+ pci_free_irq_vectors(pdev);
+deregister:
+ issei_deregister(idev);
+ return err;
+}
+
+static void issei_heci_shutdown(struct pci_dev *pdev)
+{
+ struct issei_device *idev = pci_get_drvdata(pdev);
+ struct issei_heci_hw *hw = to_heci_hw(idev);
+
+ issei_stop(idev);
+
+ idev->ops->irq_disable(idev);
+ free_irq(hw->irq, idev);
+ pci_free_irq_vectors(pdev);
+}
+
+static void issei_heci_remove(struct pci_dev *pdev)
+{
+ issei_heci_shutdown(pdev);
+
+ issei_deregister(pci_get_drvdata(pdev));
+}
+
+static int issei_heci_pm_suspend(struct device *device)
+{
+ struct issei_device *idev = dev_get_drvdata(device);
+
+ issei_stop(idev);
+ idev->ops->irq_disable(idev);
+
+ return 0;
+}
+
+static int issei_heci_pm_resume(struct device *device)
+{
+ struct issei_device *idev = dev_get_drvdata(device);
+
+ return issei_start(idev);
+}
+
+static const struct dev_pm_ops issei_heci_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(issei_heci_pm_suspend, issei_heci_pm_resume)
+};
+
+static const struct pci_device_id heci_pci_tbl[] = {
+ {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */
+ {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panther Lake H */
+ {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panther Lake P */
+ {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */
+ {PCI_VDEVICE(INTEL, 0xD358)}, /* Nova Lake H */
+ {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */
+ {}
+};
+MODULE_DEVICE_TABLE(pci, heci_pci_tbl);
+
+static struct pci_driver issei_heci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = heci_pci_tbl,
+ .probe = issei_heci_probe,
+ .remove = issei_heci_remove,
+ .shutdown = issei_heci_shutdown,
+ .driver = {
+ .pm = &issei_heci_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+module_pci_driver(issei_heci_driver);
+
+MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface - HECI");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("INTEL_SSEI");
diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c
index 6c65fbf22e75..c2ca63e40814 100644
--- a/drivers/misc/keba/cp500.c
+++ b/drivers/misc/keba/cp500.c
@@ -887,7 +887,7 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
else
return -ENODEV;
- ret = pci_enable_device(pci_dev);
+ ret = pcim_enable_device(pci_dev);
if (ret)
return ret;
pci_set_master(pci_dev);
@@ -896,18 +896,15 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
startup.end = startup.start + cp500->devs->startup.size - 1;
cp500->system_startup_addr = devm_ioremap_resource(&pci_dev->dev,
&startup);
- if (IS_ERR(cp500->system_startup_addr)) {
- ret = PTR_ERR(cp500->system_startup_addr);
- goto out_disable;
- }
+ if (IS_ERR(cp500->system_startup_addr))
+ return PTR_ERR(cp500->system_startup_addr);
cp500->msix_num = pci_alloc_irq_vectors(pci_dev, CP500_NUM_MSIX_NO_MMI,
CP500_NUM_MSIX, PCI_IRQ_MSIX);
if (cp500->msix_num < CP500_NUM_MSIX_NO_MMI) {
dev_err(&pci_dev->dev,
"Hardware does not support enough MSI-X interrupts\n");
- ret = -ENODEV;
- goto out_disable;
+ return -ENODEV;
}
cp500_vers = ioread32(cp500->system_startup_addr + CP500_VERSION_REG);
@@ -937,9 +934,6 @@ out_unregister_nvmem:
nvmem_unregister_notifier(&cp500->nvmem_notifier);
out_free_irq:
pci_free_irq_vectors(pci_dev);
-out_disable:
- pci_clear_master(pci_dev);
- pci_disable_device(pci_dev);
return ret;
}
@@ -962,9 +956,6 @@ static void cp500_remove(struct pci_dev *pci_dev)
pci_set_drvdata(pci_dev, 0);
pci_free_irq_vectors(pci_dev);
-
- pci_clear_master(pci_dev);
- pci_disable_device(pci_dev);
}
static struct pci_device_id cp500_ids[] = {
diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c
index fc54afd1d05b..874dd50fac66 100644
--- a/drivers/misc/keba/lan9252.c
+++ b/drivers/misc/keba/lan9252.c
@@ -339,8 +339,8 @@ out:
}
static const struct spi_device_id lan9252_id[] = {
- {"lan9252"},
- {}
+ { .name = "lan9252" },
+ { }
};
MODULE_DEVICE_TABLE(spi, lan9252_id);
diff --git a/drivers/misc/lan966x_pci.c b/drivers/misc/lan966x_pci.c
index 0bb90c0943bf..b0949c653e5b 100644
--- a/drivers/misc/lan966x_pci.c
+++ b/drivers/misc/lan966x_pci.c
@@ -183,6 +183,7 @@ static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
return 0;
err_unload_overlay:
+ of_platform_depopulate(dev);
lan966x_pci_unload_overlay(data);
return ret;
}
diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c
index 93949df3bcff..740b5cf53842 100644
--- a/drivers/misc/lattice-ecp3-config.c
+++ b/drivers/misc/lattice-ecp3-config.c
@@ -219,8 +219,8 @@ static void lattice_ecp3_remove(struct spi_device *spi)
}
static const struct spi_device_id lattice_ecp3_id[] = {
- { "ecp3-17", 0 },
- { "ecp3-35", 0 },
+ { .name = "ecp3-17" },
+ { .name = "ecp3-35" },
{ }
};
MODULE_DEVICE_TABLE(spi, lattice_ecp3_id);
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
index 15119584473c..7c3ca6d604d1 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
@@ -95,7 +95,7 @@ static union axis_conversion lis3lv02d_axis_map =
#ifdef CONFIG_OF
static const struct of_device_id lis3lv02d_i2c_dt_ids[] = {
{ .compatible = "st,lis3lv02d" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids);
#endif
@@ -247,9 +247,9 @@ static int lis3_i2c_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct i2c_device_id lis3lv02d_id[] = {
- {"lis3lv02d", LIS3LV02D},
- {"lis331dlh", LIS331DLH},
- {}
+ { .name = "lis3lv02d", .driver_data = LIS3LV02D },
+ { .name = "lis331dlh", .driver_data = LIS331DLH },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lis3lv02d_id);
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
index 203a108b8883..5bf95176a00e 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
@@ -60,7 +60,7 @@ static union axis_conversion lis3lv02d_axis_normal =
#ifdef CONFIG_OF
static const struct of_device_id lis302dl_spi_dt_ids[] = {
{ .compatible = "st,lis302dl-spi" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
#endif
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
index 48bea1117771..b0b678e6b28d 100644
--- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
@@ -150,7 +150,7 @@ static const struct pci_device_id pci1xxxx_tbl[] = {
{ PCI_DEVICE(0x1055, 0xA035) },
{ PCI_DEVICE(0x1055, 0xA045) },
{ PCI_DEVICE(0x1055, 0xA055) },
- {0,}
+ { }
};
MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl);
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
index 8eddbaa1fccd..82635db04dc5 100644
--- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
@@ -513,8 +513,8 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev,
static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume);
static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = {
- {.name = "mchp_pci1xxxx_gp.gp_gpio"},
- {}
+ { .name = "mchp_pci1xxxx_gp.gp_gpio" },
+ { }
};
MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_gpio_auxiliary_id_table);
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
index a2ed477e0370..09841c4f1a06 100644
--- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
@@ -419,8 +419,8 @@ static void pci1xxxx_otp_eeprom_remove(struct auxiliary_device *aux_dev)
}
static const struct auxiliary_device_id pci1xxxx_otp_eeprom_auxiliary_id_table[] = {
- {.name = "mchp_pci1xxxx_gp.gp_otp_e2p"},
- {},
+ { .name = "mchp_pci1xxxx_gp.gp_otp_e2p" },
+ { }
};
MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_otp_eeprom_auxiliary_id_table);
diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c
index 73d5beeb9c34..376840b2c354 100644
--- a/drivers/misc/mei/gsc-me.c
+++ b/drivers/misc/mei/gsc-me.c
@@ -101,10 +101,8 @@ static int mei_gsc_probe(struct auxiliary_device *aux_dev,
mei_me_irq_quick_handler,
mei_me_irq_thread_handler,
IRQF_ONESHOT, KBUILD_MODNAME, dev);
- if (ret) {
- dev_err(device, "irq register failed %d\n", ret);
+ if (ret)
goto err;
- }
}
ret = mei_register(dev, device);
diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c
index f6a258c2b838..9fa69acf28d5 100644
--- a/drivers/misc/mei/mei_lb.c
+++ b/drivers/misc/mei/mei_lb.c
@@ -281,7 +281,7 @@ static int mei_lb_check_response_v2(const struct device *dev, ssize_t bytes,
bytes, sizeof(rsp->rheader));
return -ENOMSG;
}
- if (rsp->rheader.header.command_id != MEI_LB2_CMD) {
+ if (rsp->rheader.header.command_id != cpu_to_le32(MEI_LB2_CMD)) {
dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n",
rsp->rheader.header.command_id, MEI_LB2_CMD);
return -EPROTO;
@@ -327,7 +327,7 @@ static int mei_lb_push_payload_v2(struct device *dev, struct mei_cl_device *clde
if (sent_data + chunk_size == payload_size)
last_chunk = MEI_LB2_FLAG_LST_CHUNK;
- req->header.command_id = MEI_LB2_CMD;
+ req->header.command_id = cpu_to_le32(MEI_LB2_CMD);
req->type = cpu_to_le32(type);
req->flags = cpu_to_le32(flags | first_chunk | last_chunk);
req->reserved = 0;
diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c
index 70792bf9b3c0..6e1e8008f267 100644
--- a/drivers/misc/mei/pci-csc.c
+++ b/drivers/misc/mei/pci-csc.c
@@ -240,7 +240,7 @@ static const struct dev_pm_ops mei_csc_pm_ops = {
static const struct pci_device_id mei_csc_pci_tbl[] = {
{ PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) },
- {}
+ { }
};
MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl);
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 55e0b8a98827..790a4647811b 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -27,114 +27,114 @@
/* mei_pci_tbl - PCI Device ID Table */
static const struct pci_device_id mei_me_pci_tbl[] = {
- {PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)},
-
- {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG)},
- {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)},
+ { PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG) },
+
+ { PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG) },
+ { PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG) },
/* required last entry */
- {0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index f5441bc5efe2..815ca72877ce 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -24,10 +24,10 @@
#include "hw-txe.h"
static const struct pci_device_id mei_txe_pci_tbl[] = {
- {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */
- {PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */
+ { PCI_VDEVICE(INTEL, 0x0F18) }, /* Baytrail */
+ { PCI_VDEVICE(INTEL, 0x2298) }, /* Cherrytrail */
- {0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c
index 5100234ba5b6..ef6dcc043862 100644
--- a/drivers/misc/mei/platform-vsc.c
+++ b/drivers/misc/mei/platform-vsc.c
@@ -435,7 +435,7 @@ static int mei_vsc_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume);
static const struct platform_device_id mei_vsc_id_table[] = {
- { MEI_VSC_DRV_NAME },
+ { .name = MEI_VSC_DRV_NAME },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, mei_vsc_id_table);
diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c
index 5ecf99883996..432bc59f11f9 100644
--- a/drivers/misc/mei/vsc-tp.c
+++ b/drivers/misc/mei/vsc-tp.c
@@ -552,11 +552,11 @@ static void vsc_tp_remove(struct spi_device *spi)
}
static const struct acpi_device_id vsc_tp_acpi_ids[] = {
- { "INTC1009" }, /* Raptor Lake */
- { "INTC1058" }, /* Tiger Lake */
- { "INTC1094" }, /* Alder Lake */
- { "INTC10D0" }, /* Meteor Lake */
- {}
+ { .id = "INTC1009" }, /* Raptor Lake */
+ { .id = "INTC1058" }, /* Tiger Lake */
+ { .id = "INTC1094" }, /* Alder Lake */
+ { .id = "INTC10D0" }, /* Meteor Lake */
+ { }
};
MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids);
diff --git a/drivers/misc/mrvl_cn10k_dpi.c b/drivers/misc/mrvl_cn10k_dpi.c
index 7d5433121ff6..73ff0f198ec3 100644
--- a/drivers/misc/mrvl_cn10k_dpi.c
+++ b/drivers/misc/mrvl_cn10k_dpi.c
@@ -470,10 +470,8 @@ static int dpi_irq_init(struct dpipf *dpi)
ret = devm_request_irq(dev, pci_irq_vector(pdev, DPI_MBOX_PF_VF_INT_IDX),
dpi_mbox_intr_handler, 0, "dpi-mbox", dpi);
- if (ret) {
- dev_err(dev, "DPI: request_irq failed for mbox; err=%d\n", ret);
+ if (ret)
return ret;
- }
dpi_reg_write(dpi, DPI_MBOX_VF_PF_INT_ENA_W1S, GENMASK_ULL(31, 0));
@@ -658,7 +656,7 @@ static void dpi_remove(struct pci_dev *pdev)
static const struct pci_device_id dpi_id_table[] = {
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_MRVL_CN10K_DPI_PF,
PCI_VENDOR_ID_CAVIUM, PCI_SUBDEVID_MRVL_CN10K_DPI_PF) },
- { 0, } /* end of table */
+ { } /* end of table */
};
static struct pci_driver dpi_driver = {
diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c
index 3960506eb7ab..a3a9f9bfc6b7 100644
--- a/drivers/misc/nsm.c
+++ b/drivers/misc/nsm.c
@@ -61,7 +61,7 @@ struct nsm {
/* NSM device ID */
static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID },
- { 0 },
+ { }
};
static struct nsm *file_to_nsm(struct file *file)
@@ -243,7 +243,7 @@ static int nsm_sendrecv_msg_locked(struct nsm *nsm)
goto cleanup;
}
- msg->resp.len = len;
+ msg->resp.len = min_t(unsigned int, len, sizeof(msg->resp.data));
rc = 0;
@@ -365,9 +365,8 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
/* Copy user argument struct to kernel argument struct */
- r = -EFAULT;
if (copy_from_user(&raw, argp, _IOC_SIZE(cmd)))
- return r;
+ return -EFAULT;
mutex_lock(&nsm->lock);
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index 02c9d1192812..4a805919bb0c 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -875,6 +875,9 @@ static int setup_wait(struct ntsync_device *dev,
if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
return -EINVAL;
+ if (!args->owner)
+ return -EINVAL;
+
if (size >= sizeof(fds))
return -EINVAL;
diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
index cb920aa88d3a..0cf38bd482a9 100644
--- a/drivers/misc/ocxl/pci.c
+++ b/drivers/misc/ocxl/pci.c
@@ -9,7 +9,7 @@
* vendor/device ID to help differentiate devices.
*/
static const struct pci_device_id ocxl_pci_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), },
+ { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B) },
{ }
};
MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);
diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c
index 45060fb4ea27..b3cf59954111 100644
--- a/drivers/misc/open-dice.c
+++ b/drivers/misc/open-dice.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2021 - Google LLC
* Author: David Brazdil <dbrazdil@google.com>
+ * Author: Song Guo <songguo@google.com>
*
* Driver for Open Profile for DICE.
*
@@ -19,6 +20,7 @@
* close(fd);
*/
+#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
@@ -31,7 +33,8 @@
struct open_dice_drvdata {
struct mutex lock;
char name[16];
- struct reserved_mem *rmem;
+ phys_addr_t mem_base;
+ resource_size_t mem_size;
struct miscdevice misc;
};
@@ -45,14 +48,14 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata)
void *kaddr;
mutex_lock(&drvdata->lock);
- kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base,
- drvdata->rmem->size, MEMREMAP_WC);
+ kaddr = devm_memremap(drvdata->misc.this_device, drvdata->mem_base,
+ drvdata->mem_size, MEMREMAP_WC);
if (IS_ERR(kaddr)) {
mutex_unlock(&drvdata->lock);
return PTR_ERR(kaddr);
}
- memset(kaddr, 0, drvdata->rmem->size);
+ memset(kaddr, 0, drvdata->mem_size);
devm_memunmap(drvdata->misc.this_device, kaddr);
mutex_unlock(&drvdata->lock);
return 0;
@@ -64,7 +67,7 @@ static int open_dice_wipe(struct open_dice_drvdata *drvdata)
static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len,
loff_t *off)
{
- unsigned long val = to_open_dice_drvdata(filp)->rmem->size;
+ unsigned long val = to_open_dice_drvdata(filp)->mem_size;
return simple_read_from_buffer(ptr, len, off, &val, sizeof(val));
}
@@ -102,8 +105,8 @@ static int open_dice_mmap_prepare(struct vm_area_desc *desc)
/* Create write-combine mapping so all clients observe a wipe. */
desc->page_prot = pgprot_writecombine(desc->page_prot);
vma_desc_set_flags(desc, VMA_DONTCOPY_BIT, VMA_DONTDUMP_BIT);
- mmap_action_simple_ioremap(desc, drvdata->rmem->base,
- drvdata->rmem->size);
+ mmap_action_simple_ioremap(desc, drvdata->mem_base,
+ drvdata->mem_size);
return 0;
}
@@ -118,22 +121,40 @@ static int __init open_dice_probe(struct platform_device *pdev)
{
static unsigned int dev_idx;
struct device *dev = &pdev->dev;
- struct reserved_mem *rmem;
struct open_dice_drvdata *drvdata;
+ phys_addr_t mem_base;
+ resource_size_t mem_size;
int ret;
- rmem = of_reserved_mem_lookup(dev->of_node);
- if (!rmem) {
- dev_err(dev, "failed to lookup reserved memory\n");
+ if (dev->of_node) {
+ struct reserved_mem *rmem = of_reserved_mem_lookup(dev->of_node);
+
+ if (!rmem) {
+ dev_err(dev, "failed to lookup reserved memory\n");
+ return -EINVAL;
+ }
+ mem_base = rmem->base;
+ mem_size = rmem->size;
+ } else if (is_acpi_node(dev->fwnode)) {
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(dev, "failed to get MMIO resource\n");
+ return -EINVAL;
+ }
+ mem_base = res->start;
+ mem_size = resource_size(res);
+ } else {
+ dev_err(dev, "device not supported (no DT or ACPI node)\n");
return -EINVAL;
}
- if (!rmem->size || (rmem->size > ULONG_MAX)) {
+ if (!mem_size || (mem_size > ULONG_MAX)) {
dev_err(dev, "invalid memory region size\n");
return -EINVAL;
}
- if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) {
+ if (!PAGE_ALIGNED(mem_base) || !PAGE_ALIGNED(mem_size)) {
dev_err(dev, "memory region must be page-aligned\n");
return -EINVAL;
}
@@ -143,7 +164,8 @@ static int __init open_dice_probe(struct platform_device *pdev)
return -ENOMEM;
*drvdata = (struct open_dice_drvdata){
- .rmem = rmem,
+ .mem_base = mem_base,
+ .mem_size = mem_size,
.misc = (struct miscdevice){
.parent = dev,
.name = drvdata->name,
@@ -177,7 +199,7 @@ static void open_dice_remove(struct platform_device *pdev)
static const struct of_device_id open_dice_of_match[] = {
{ .compatible = "google,open-dice" },
- {},
+ { }
};
static struct platform_driver open_dice_driver = {
@@ -207,3 +229,4 @@ module_exit(open_dice_exit);
MODULE_DESCRIPTION("Driver for Open Profile for DICE.");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");
+MODULE_AUTHOR("Song Guo <songguo@google.com>");
diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c
index 19c4fa017f24..15785597da40 100644
--- a/drivers/misc/pch_phub.c
+++ b/drivers/misc/pch_phub.c
@@ -83,22 +83,16 @@
#define PCH_PHUB_OROM_SIZE 15360
+enum pch_phub_type {
+ PCH_EG20T,
+ PCH_ML7213,
+ PCH_ML7223M,
+ PCH_ML7223N,
+ PCH_ML7831,
+};
+
/**
* struct pch_phub_reg - PHUB register structure
- * @phub_id_reg: PHUB_ID register val
- * @q_pri_val_reg: QUEUE_PRI_VAL register val
- * @rc_q_maxsize_reg: RC_QUEUE_MAXSIZE register val
- * @bri_q_maxsize_reg: BRI_QUEUE_MAXSIZE register val
- * @comp_resp_timeout_reg: COMP_RESP_TIMEOUT register val
- * @bus_slave_control_reg: BUS_SLAVE_CONTROL_REG register val
- * @deadlock_avoid_type_reg: DEADLOCK_AVOID_TYPE register val
- * @intpin_reg_wpermit_reg0: INTPIN_REG_WPERMIT register 0 val
- * @intpin_reg_wpermit_reg1: INTPIN_REG_WPERMIT register 1 val
- * @intpin_reg_wpermit_reg2: INTPIN_REG_WPERMIT register 2 val
- * @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val
- * @int_reduce_control_reg: INT_REDUCE_CONTROL registers val
- * @clkcfg_reg: CLK CFG register val
- * @funcsel_reg: Function select register value
* @pch_phub_base_address: Register base address
* @pch_phub_extrom_base_address: external rom base address
* @pch_mac_start_address: MAC address area start address
@@ -107,25 +101,11 @@
* @pdev: pointer to pci device struct
*/
struct pch_phub_reg {
- u32 phub_id_reg;
- u32 q_pri_val_reg;
- u32 rc_q_maxsize_reg;
- u32 bri_q_maxsize_reg;
- u32 comp_resp_timeout_reg;
- u32 bus_slave_control_reg;
- u32 deadlock_avoid_type_reg;
- u32 intpin_reg_wpermit_reg0;
- u32 intpin_reg_wpermit_reg1;
- u32 intpin_reg_wpermit_reg2;
- u32 intpin_reg_wpermit_reg3;
- u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG];
- u32 clkcfg_reg;
- u32 funcsel_reg;
void __iomem *pch_phub_base_address;
void __iomem *pch_phub_extrom_base_address;
u32 pch_mac_start_address;
u32 pch_opt_rom_start_address;
- int ioh_type;
+ enum pch_phub_type ioh_type;
struct pci_dev *pdev;
};
@@ -344,10 +324,19 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data)
int retval;
int i;
- if ((chip->ioh_type == 1) || (chip->ioh_type == 5)) /* EG20T or ML7831*/
+ switch (chip->ioh_type) {
+ case PCH_EG20T:
+ case PCH_ML7831:
retval = pch_phub_gbe_serial_rom_conf(chip);
- else /* ML7223 */
+ break;
+
+ case PCH_ML7213:
+ case PCH_ML7223M:
+ case PCH_ML7223N:
retval = pch_phub_gbe_serial_rom_conf_mp(chip);
+ break;
+ }
+
if (retval)
return retval;
@@ -537,14 +526,6 @@ static const struct bin_attribute pch_bin_attr = {
.write = pch_phub_bin_write,
};
-enum {
- PCH_EG20T,
- PCH_ML7213,
- PCH_ML7223M,
- PCH_ML7223N,
- PCH_ML7831,
-};
-
static int pch_phub_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 7686d777a27e..6b18ea914689 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -1419,18 +1419,18 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0),
.driver_data = (kernel_ulong_t)&default_data,
},
- { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),},
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8) },
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A),
.driver_data = (kernel_ulong_t)&default_data,
},
{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654),
- .driver_data = (kernel_ulong_t)&am654_data
+ .driver_data = (kernel_ulong_t)&am654_data,
},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1),},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),},
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),},
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774A1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1) },
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0),
.driver_data = (kernel_ulong_t)&default_data,
},
@@ -1449,8 +1449,8 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ROCKCHIP, PCI_DEVICE_ID_ROCKCHIP_RK3588),
.driver_data = (kernel_ulong_t)&rk3588_data,
},
- { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP),},
- { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP),},
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP) },
{ }
};
MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index 34a5054a6b40..331cae539290 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -481,10 +481,10 @@ static int __maybe_unused phantom_resume(struct device *dev_d)
}
static struct pci_device_id phantom_pci_tbl[] = {
- { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050,
- .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050,
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
.class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
- { 0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c
index bedcda9b6ac5..724dfddf69e4 100644
--- a/drivers/misc/pvpanic/pvpanic-mmio.c
+++ b/drivers/misc/pvpanic/pvpanic-mmio.c
@@ -51,14 +51,14 @@ static int pvpanic_mmio_probe(struct platform_device *pdev)
}
static const struct of_device_id pvpanic_mmio_match[] = {
- { .compatible = "qemu,pvpanic-mmio", },
- {}
+ { .compatible = "qemu,pvpanic-mmio" },
+ { }
};
MODULE_DEVICE_TABLE(of, pvpanic_mmio_match);
static const struct acpi_device_id pvpanic_device_ids[] = {
- { "QEMU0001", 0 },
- { "", 0 }
+ { .id = "QEMU0001" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c
index b21598a18f6d..572c6148a297 100644
--- a/drivers/misc/pvpanic/pvpanic-pci.c
+++ b/drivers/misc/pvpanic/pvpanic-pci.c
@@ -35,8 +35,8 @@ static int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
}
static const struct pci_device_id pvpanic_pci_id_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)},
- {}
+ { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC) },
+ { }
};
MODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl);
diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c
index 3c57f7429147..b58bd34fc471 100644
--- a/drivers/misc/qcom-coincell.c
+++ b/drivers/misc/qcom-coincell.c
@@ -124,8 +124,8 @@ static int qcom_coincell_probe(struct platform_device *pdev)
}
static const struct of_device_id qcom_coincell_match_table[] = {
- { .compatible = "qcom,pm8941-coincell", },
- {}
+ { .compatible = "qcom,pm8941-coincell" },
+ { }
};
MODULE_DEVICE_TABLE(of, qcom_coincell_match_table);
diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c
index 81685e3f3296..207a676913a2 100644
--- a/drivers/misc/rp1/rp1_pci.c
+++ b/drivers/misc/rp1/rp1_pci.c
@@ -166,6 +166,9 @@ static void rp1_unregister_interrupts(struct pci_dev *pdev)
struct rp1_dev *rp1 = pci_get_drvdata(pdev);
int irq, i;
+ for (i = 0; i < RP1_INT_END; i++)
+ irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), NULL, NULL);
+
if (rp1->domain) {
for (i = 0; i < RP1_INT_END; i++) {
irq = irq_find_mapping(rp1->domain, i);
@@ -191,13 +194,13 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (!rp1_node) {
dev_err(dev, "Missing of_node for device\n");
err = -EINVAL;
- goto err_put_node;
+ goto err_out;
}
rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL);
if (!rp1) {
err = -ENOMEM;
- goto err_put_node;
+ goto err_out;
}
rp1->pdev = pdev;
@@ -206,21 +209,21 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_err(&pdev->dev,
"Not initialized - is the firmware running?\n");
err = -EINVAL;
- goto err_put_node;
+ goto err_out;
}
err = pcim_enable_device(pdev);
if (err < 0) {
err = dev_err_probe(&pdev->dev, err,
"Enabling PCI device has failed");
- goto err_put_node;
+ goto err_out;
}
rp1->bar1 = pcim_iomap(pdev, 1, 0);
if (!rp1->bar1) {
dev_err(&pdev->dev, "Cannot map PCI BAR\n");
err = -EIO;
- goto err_put_node;
+ goto err_out;
}
pci_set_master(pdev);
@@ -230,16 +233,16 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err < 0) {
err = dev_err_probe(&pdev->dev, err,
"Failed to allocate MSI-X vectors\n");
- goto err_put_node;
+ goto err_out;
} else if (err != RP1_INT_END) {
dev_err(&pdev->dev, "Cannot allocate enough interrupts\n");
err = -EINVAL;
- goto err_put_node;
+ goto err_out;
}
pci_set_drvdata(pdev, rp1);
- rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END,
- &rp1_domain_ops, rp1);
+ rp1->domain = irq_domain_create_linear(of_fwnode_handle(rp1_node), RP1_INT_END,
+ &rp1_domain_ops, rp1);
if (!rp1->domain) {
dev_err(&pdev->dev, "Error creating IRQ domain\n");
err = -ENOMEM;
@@ -267,15 +270,11 @@ static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_unregister_interrupts;
}
- of_node_put(rp1_node);
-
return 0;
err_unregister_interrupts:
rp1_unregister_interrupts(pdev);
-err_put_node:
- of_node_put(rp1_node);
-
+err_out:
return err;
}
@@ -288,7 +287,7 @@ static void rp1_remove(struct pci_dev *pdev)
}
static const struct pci_device_id dev_id_table[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), },
+ { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0) },
{ }
};
MODULE_DEVICE_TABLE(pci, dev_id_table);
diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile
deleted file mode 100644
index 8132116ec0f0..000000000000
--- a/drivers/misc/sgi-gru/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-ccflags-$(CONFIG_SGI_GRU_DEBUG) := -DDEBUG
-
-obj-$(CONFIG_SGI_GRU) := gru.o
-gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o gruhandles.o grukdump.o
-
diff --git a/drivers/misc/sgi-gru/gru.h b/drivers/misc/sgi-gru/gru.h
deleted file mode 100644
index 6ae045037219..000000000000
--- a/drivers/misc/sgi-gru/gru.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __GRU_H__
-#define __GRU_H__
-
-/*
- * GRU architectural definitions
- */
-#define GRU_CACHE_LINE_BYTES 64
-#define GRU_HANDLE_STRIDE 256
-#define GRU_CB_BASE 0
-#define GRU_DS_BASE 0x20000
-
-/*
- * Size used to map GRU GSeg
- */
-#if defined(CONFIG_X86_64)
-#define GRU_GSEG_PAGESIZE (256 * 1024UL) /* ZZZ 2MB ??? */
-#else
-#error "Unsupported architecture"
-#endif
-
-/*
- * Structure for obtaining GRU resource information
- */
-struct gru_chiplet_info {
- int node;
- int chiplet;
- int blade;
- int total_dsr_bytes;
- int total_cbr;
- int total_user_dsr_bytes;
- int total_user_cbr;
- int free_user_dsr_bytes;
- int free_user_cbr;
-};
-
-/*
- * Statictics kept for each context.
- */
-struct gru_gseg_statistics {
- unsigned long fmm_tlbmiss;
- unsigned long upm_tlbmiss;
- unsigned long tlbdropin;
- unsigned long context_stolen;
- unsigned long reserved[10];
-};
-
-/* Flags for GRU options on the gru_create_context() call */
-/* Select one of the follow 4 options to specify how TLB misses are handled */
-#define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */
-#define GRU_OPT_MISS_USER_POLL 0x0001 /* User will poll CB for faults */
-#define GRU_OPT_MISS_FMM_INTR 0x0002 /* Send interrupt to cpu to
- handle fault */
-#define GRU_OPT_MISS_FMM_POLL 0x0003 /* Use system polling thread */
-#define GRU_OPT_MISS_MASK 0x0003 /* Mask for TLB MISS option */
-
-
-
-#endif /* __GRU_H__ */
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h
deleted file mode 100644
index da5eb9edf9ec..000000000000
--- a/drivers/misc/sgi-gru/gru_instructions.h
+++ /dev/null
@@ -1,726 +0,0 @@
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __GRU_INSTRUCTIONS_H__
-#define __GRU_INSTRUCTIONS_H__
-
-extern int gru_check_status_proc(void *cb);
-extern int gru_wait_proc(void *cb);
-extern void gru_wait_abort_proc(void *cb);
-
-
-
-/*
- * Architecture dependent functions
- */
-
-#if defined(CONFIG_X86_64)
-#include <asm/cacheflush.h>
-#define __flush_cache(p) clflush(p)
-#define gru_ordered_store_ulong(p, v) \
- do { \
- barrier(); \
- *(unsigned long *)p = v; \
- } while (0)
-#else
-#error "Unsupported architecture"
-#endif
-
-/*
- * Control block status and exception codes
- */
-#define CBS_IDLE 0
-#define CBS_EXCEPTION 1
-#define CBS_ACTIVE 2
-#define CBS_CALL_OS 3
-
-/* CB substatus bitmasks */
-#define CBSS_MSG_QUEUE_MASK 7
-#define CBSS_IMPLICIT_ABORT_ACTIVE_MASK 8
-
-/* CB substatus message queue values (low 3 bits of substatus) */
-#define CBSS_NO_ERROR 0
-#define CBSS_LB_OVERFLOWED 1
-#define CBSS_QLIMIT_REACHED 2
-#define CBSS_PAGE_OVERFLOW 3
-#define CBSS_AMO_NACKED 4
-#define CBSS_PUT_NACKED 5
-
-/*
- * Structure used to fetch exception detail for CBs that terminate with
- * CBS_EXCEPTION
- */
-struct control_block_extended_exc_detail {
- unsigned long cb;
- int opc;
- int ecause;
- int exopc;
- long exceptdet0;
- int exceptdet1;
- int cbrstate;
- int cbrexecstatus;
-};
-
-/*
- * Instruction formats
- */
-
-/*
- * Generic instruction format.
- * This definition has precise bit field definitions.
- */
-struct gru_instruction_bits {
- /* DW 0 - low */
- unsigned int icmd: 1;
- unsigned char ima: 3; /* CB_DelRep, unmapped mode */
- unsigned char reserved0: 4;
- unsigned int xtype: 3;
- unsigned int iaa0: 2;
- unsigned int iaa1: 2;
- unsigned char reserved1: 1;
- unsigned char opc: 8; /* opcode */
- unsigned char exopc: 8; /* extended opcode */
- /* DW 0 - high */
- unsigned int idef2: 22; /* TRi0 */
- unsigned char reserved2: 2;
- unsigned char istatus: 2;
- unsigned char isubstatus:4;
- unsigned char reserved3: 1;
- unsigned char tlb_fault_color: 1;
- /* DW 1 */
- unsigned long idef4; /* 42 bits: TRi1, BufSize */
- /* DW 2-6 */
- unsigned long idef1; /* BAddr0 */
- unsigned long idef5; /* Nelem */
- unsigned long idef6; /* Stride, Operand1 */
- unsigned long idef3; /* BAddr1, Value, Operand2 */
- unsigned long reserved4;
- /* DW 7 */
- unsigned long avalue; /* AValue */
-};
-
-/*
- * Generic instruction with friendlier names. This format is used
- * for inline instructions.
- */
-struct gru_instruction {
- /* DW 0 */
- union {
- unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */
- struct {
- unsigned int op32;
- unsigned int tri0;
- };
- };
- unsigned long tri1_bufsize; /* DW 1 */
- unsigned long baddr0; /* DW 2 */
- unsigned long nelem; /* DW 3 */
- unsigned long op1_stride; /* DW 4 */
- unsigned long op2_value_baddr1; /* DW 5 */
- unsigned long reserved0; /* DW 6 */
- unsigned long avalue; /* DW 7 */
-};
-
-/* Some shifts and masks for the low 64 bits of a GRU command */
-#define GRU_CB_ICMD_SHFT 0
-#define GRU_CB_ICMD_MASK 0x1
-#define GRU_CB_XTYPE_SHFT 8
-#define GRU_CB_XTYPE_MASK 0x7
-#define GRU_CB_IAA0_SHFT 11
-#define GRU_CB_IAA0_MASK 0x3
-#define GRU_CB_IAA1_SHFT 13
-#define GRU_CB_IAA1_MASK 0x3
-#define GRU_CB_IMA_SHFT 1
-#define GRU_CB_IMA_MASK 0x3
-#define GRU_CB_OPC_SHFT 16
-#define GRU_CB_OPC_MASK 0xff
-#define GRU_CB_EXOPC_SHFT 24
-#define GRU_CB_EXOPC_MASK 0xff
-#define GRU_IDEF2_SHFT 32
-#define GRU_IDEF2_MASK 0x3ffff
-#define GRU_ISTATUS_SHFT 56
-#define GRU_ISTATUS_MASK 0x3
-
-/* GRU instruction opcodes (opc field) */
-#define OP_NOP 0x00
-#define OP_BCOPY 0x01
-#define OP_VLOAD 0x02
-#define OP_IVLOAD 0x03
-#define OP_VSTORE 0x04
-#define OP_IVSTORE 0x05
-#define OP_VSET 0x06
-#define OP_IVSET 0x07
-#define OP_MESQ 0x08
-#define OP_GAMXR 0x09
-#define OP_GAMIR 0x0a
-#define OP_GAMIRR 0x0b
-#define OP_GAMER 0x0c
-#define OP_GAMERR 0x0d
-#define OP_BSTORE 0x0e
-#define OP_VFLUSH 0x0f
-
-
-/* Extended opcodes values (exopc field) */
-
-/* GAMIR - AMOs with implicit operands */
-#define EOP_IR_FETCH 0x01 /* Plain fetch of memory */
-#define EOP_IR_CLR 0x02 /* Fetch and clear */
-#define EOP_IR_INC 0x05 /* Fetch and increment */
-#define EOP_IR_DEC 0x07 /* Fetch and decrement */
-#define EOP_IR_QCHK1 0x0d /* Queue check, 64 byte msg */
-#define EOP_IR_QCHK2 0x0e /* Queue check, 128 byte msg */
-
-/* GAMIRR - Registered AMOs with implicit operands */
-#define EOP_IRR_FETCH 0x01 /* Registered fetch of memory */
-#define EOP_IRR_CLR 0x02 /* Registered fetch and clear */
-#define EOP_IRR_INC 0x05 /* Registered fetch and increment */
-#define EOP_IRR_DEC 0x07 /* Registered fetch and decrement */
-#define EOP_IRR_DECZ 0x0f /* Registered fetch and decrement, update on zero*/
-
-/* GAMER - AMOs with explicit operands */
-#define EOP_ER_SWAP 0x00 /* Exchange argument and memory */
-#define EOP_ER_OR 0x01 /* Logical OR with memory */
-#define EOP_ER_AND 0x02 /* Logical AND with memory */
-#define EOP_ER_XOR 0x03 /* Logical XOR with memory */
-#define EOP_ER_ADD 0x04 /* Add value to memory */
-#define EOP_ER_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/
-#define EOP_ER_CADD 0x0c /* Queue check, operand1*64 byte msg */
-
-/* GAMERR - Registered AMOs with explicit operands */
-#define EOP_ERR_SWAP 0x00 /* Exchange argument and memory */
-#define EOP_ERR_OR 0x01 /* Logical OR with memory */
-#define EOP_ERR_AND 0x02 /* Logical AND with memory */
-#define EOP_ERR_XOR 0x03 /* Logical XOR with memory */
-#define EOP_ERR_ADD 0x04 /* Add value to memory */
-#define EOP_ERR_CSWAP 0x08 /* Compare with operand2, write operand1 if match*/
-#define EOP_ERR_EPOLL 0x09 /* Poll for equality */
-#define EOP_ERR_NPOLL 0x0a /* Poll for inequality */
-
-/* GAMXR - SGI Arithmetic unit */
-#define EOP_XR_CSWAP 0x0b /* Masked compare exchange */
-
-
-/* Transfer types (xtype field) */
-#define XTYPE_B 0x0 /* byte */
-#define XTYPE_S 0x1 /* short (2-byte) */
-#define XTYPE_W 0x2 /* word (4-byte) */
-#define XTYPE_DW 0x3 /* doubleword (8-byte) */
-#define XTYPE_CL 0x6 /* cacheline (64-byte) */
-
-
-/* Instruction access attributes (iaa0, iaa1 fields) */
-#define IAA_RAM 0x0 /* normal cached RAM access */
-#define IAA_NCRAM 0x2 /* noncoherent RAM access */
-#define IAA_MMIO 0x1 /* noncoherent memory-mapped I/O space */
-#define IAA_REGISTER 0x3 /* memory-mapped registers, etc. */
-
-
-/* Instruction mode attributes (ima field) */
-#define IMA_MAPPED 0x0 /* Virtual mode */
-#define IMA_CB_DELAY 0x1 /* hold read responses until status changes */
-#define IMA_UNMAPPED 0x2 /* bypass the TLBs (OS only) */
-#define IMA_INTERRUPT 0x4 /* Interrupt when instruction completes */
-
-/* CBE ecause bits */
-#define CBE_CAUSE_RI (1 << 0)
-#define CBE_CAUSE_INVALID_INSTRUCTION (1 << 1)
-#define CBE_CAUSE_UNMAPPED_MODE_FORBIDDEN (1 << 2)
-#define CBE_CAUSE_PE_CHECK_DATA_ERROR (1 << 3)
-#define CBE_CAUSE_IAA_GAA_MISMATCH (1 << 4)
-#define CBE_CAUSE_DATA_SEGMENT_LIMIT_EXCEPTION (1 << 5)
-#define CBE_CAUSE_OS_FATAL_TLB_FAULT (1 << 6)
-#define CBE_CAUSE_EXECUTION_HW_ERROR (1 << 7)
-#define CBE_CAUSE_TLBHW_ERROR (1 << 8)
-#define CBE_CAUSE_RA_REQUEST_TIMEOUT (1 << 9)
-#define CBE_CAUSE_HA_REQUEST_TIMEOUT (1 << 10)
-#define CBE_CAUSE_RA_RESPONSE_FATAL (1 << 11)
-#define CBE_CAUSE_RA_RESPONSE_NON_FATAL (1 << 12)
-#define CBE_CAUSE_HA_RESPONSE_FATAL (1 << 13)
-#define CBE_CAUSE_HA_RESPONSE_NON_FATAL (1 << 14)
-#define CBE_CAUSE_ADDRESS_SPACE_DECODE_ERROR (1 << 15)
-#define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16)
-#define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17)
-#define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18)
-#define CBE_CAUSE_FORCED_ERROR (1 << 19)
-
-/* CBE cbrexecstatus bits */
-#define CBR_EXS_ABORT_OCC_BIT 0
-#define CBR_EXS_INT_OCC_BIT 1
-#define CBR_EXS_PENDING_BIT 2
-#define CBR_EXS_QUEUED_BIT 3
-#define CBR_EXS_TLB_INVAL_BIT 4
-#define CBR_EXS_EXCEPTION_BIT 5
-#define CBR_EXS_CB_INT_PENDING_BIT 6
-
-#define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT)
-#define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT)
-#define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT)
-#define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT)
-#define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT)
-#define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT)
-#define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT)
-
-/*
- * Exceptions are retried for the following cases. If any OTHER bits are set
- * in ecause, the exception is not retryable.
- */
-#define EXCEPTION_RETRY_BITS (CBE_CAUSE_EXECUTION_HW_ERROR | \
- CBE_CAUSE_TLBHW_ERROR | \
- CBE_CAUSE_RA_REQUEST_TIMEOUT | \
- CBE_CAUSE_RA_RESPONSE_NON_FATAL | \
- CBE_CAUSE_HA_RESPONSE_NON_FATAL | \
- CBE_CAUSE_RA_RESPONSE_DATA_ERROR | \
- CBE_CAUSE_HA_RESPONSE_DATA_ERROR \
- )
-
-/* Message queue head structure */
-union gru_mesqhead {
- unsigned long val;
- struct {
- unsigned int head;
- unsigned int limit;
- };
-};
-
-
-/* Generate the low word of a GRU instruction */
-static inline unsigned long
-__opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype,
- unsigned char iaa0, unsigned char iaa1,
- unsigned long idef2, unsigned char ima)
-{
- return (1 << GRU_CB_ICMD_SHFT) |
- ((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) |
- (idef2<< GRU_IDEF2_SHFT) |
- (iaa0 << GRU_CB_IAA0_SHFT) |
- (iaa1 << GRU_CB_IAA1_SHFT) |
- (ima << GRU_CB_IMA_SHFT) |
- (xtype << GRU_CB_XTYPE_SHFT) |
- (opcode << GRU_CB_OPC_SHFT) |
- (exopc << GRU_CB_EXOPC_SHFT);
-}
-
-/*
- * Architecture specific intrinsics
- */
-static inline void gru_flush_cache(void *p)
-{
- __flush_cache(p);
-}
-
-/*
- * Store the lower 64 bits of the command including the "start" bit. Then
- * start the instruction executing.
- */
-static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64)
-{
- gru_ordered_store_ulong(ins, op64);
- mb();
- gru_flush_cache(ins);
-}
-
-
-/* Convert "hints" to IMA */
-#define CB_IMA(h) ((h) | IMA_UNMAPPED)
-
-/* Convert data segment cache line index into TRI0 / TRI1 value */
-#define GRU_DINDEX(i) ((i) * GRU_CACHE_LINE_BYTES)
-
-/* Inline functions for GRU instructions.
- * Note:
- * - nelem and stride are in elements
- * - tri0/tri1 is in bytes for the beginning of the data segment.
- */
-static inline void gru_vload_phys(void *cb, unsigned long gpa,
- unsigned int tri0, int iaa, unsigned long hints)
-{
- struct gru_instruction *ins = (struct gru_instruction *)cb;
-
- ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
- ins->nelem = 1;
- ins->op1_stride = 1;
- gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0,
- (unsigned long)tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vstore_phys(void *cb, unsigned long gpa,
- unsigned int tri0, int iaa, unsigned long hints)
-{
- struct gru_instruction *ins = (struct gru_instruction *)cb;
-
- ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
- ins->nelem = 1;
- ins->op1_stride = 1;
- gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0,
- (unsigned long)tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vload(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned char xtype, unsigned long nelem,
- unsigned long stride, unsigned long hints)
-{
- struct gru_instruction *ins = (struct gru_instruction *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->op1_stride = stride;
- gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0,
- (unsigned long)tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vstore(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned char xtype, unsigned long nelem,
- unsigned long stride, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->op1_stride = stride;
- gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_ivload(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned int tri1, unsigned char xtype,
- unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_ivstore(void *cb, unsigned long mem_addr,
- unsigned int tri0, unsigned int tri1,
- unsigned char xtype, unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->nelem = nelem;
- ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_vset(void *cb, unsigned long mem_addr,
- unsigned long value, unsigned char xtype, unsigned long nelem,
- unsigned long stride, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->op2_value_baddr1 = value;
- ins->nelem = nelem;
- ins->op1_stride = stride;
- gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_ivset(void *cb, unsigned long mem_addr,
- unsigned int tri1, unsigned long value, unsigned char xtype,
- unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->op2_value_baddr1 = value;
- ins->nelem = nelem;
- ins->tri1_bufsize = tri1;
- gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_vflush(void *cb, unsigned long mem_addr,
- unsigned long nelem, unsigned char xtype, unsigned long stride,
- unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)mem_addr;
- ins->op1_stride = stride;
- ins->nelem = nelem;
- gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_nop(void *cb, int hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints)));
-}
-
-
-static inline void gru_bcopy(void *cb, const unsigned long src,
- unsigned long dest,
- unsigned int tri0, unsigned int xtype, unsigned long nelem,
- unsigned int bufsize, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op2_value_baddr1 = (long)dest;
- ins->nelem = nelem;
- ins->tri1_bufsize = bufsize;
- gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM,
- IAA_RAM, tri0, CB_IMA(hints)));
-}
-
-static inline void gru_bstore(void *cb, const unsigned long src,
- unsigned long dest, unsigned int tri0, unsigned int xtype,
- unsigned long nelem, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op2_value_baddr1 = (long)dest;
- ins->nelem = nelem;
- gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM,
- tri0, CB_IMA(hints)));
-}
-
-static inline void gru_gamir(void *cb, int exopc, unsigned long src,
- unsigned int xtype, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamirr(void *cb, int exopc, unsigned long src,
- unsigned int xtype, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamer(void *cb, int exopc, unsigned long src,
- unsigned int xtype,
- unsigned long operand1, unsigned long operand2,
- unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op1_stride = operand1;
- ins->op2_value_baddr1 = operand2;
- gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamerr(void *cb, int exopc, unsigned long src,
- unsigned int xtype, unsigned long operand1,
- unsigned long operand2, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->op1_stride = operand1;
- ins->op2_value_baddr1 = operand2;
- gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0,
- 0, CB_IMA(hints)));
-}
-
-static inline void gru_gamxr(void *cb, unsigned long src,
- unsigned int tri0, unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)src;
- ins->nelem = 4;
- gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW,
- IAA_RAM, 0, 0, CB_IMA(hints)));
-}
-
-static inline void gru_mesq(void *cb, unsigned long queue,
- unsigned long tri0, unsigned long nelem,
- unsigned long hints)
-{
- struct gru_instruction *ins = (void *)cb;
-
- ins->baddr0 = (long)queue;
- ins->nelem = nelem;
- gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0,
- tri0, CB_IMA(hints)));
-}
-
-static inline unsigned long gru_get_amo_value(void *cb)
-{
- struct gru_instruction *ins = (void *)cb;
-
- return ins->avalue;
-}
-
-static inline int gru_get_amo_value_head(void *cb)
-{
- struct gru_instruction *ins = (void *)cb;
-
- return ins->avalue & 0xffffffff;
-}
-
-static inline int gru_get_amo_value_limit(void *cb)
-{
- struct gru_instruction *ins = (void *)cb;
-
- return ins->avalue >> 32;
-}
-
-static inline union gru_mesqhead gru_mesq_head(int head, int limit)
-{
- union gru_mesqhead mqh;
-
- mqh.head = head;
- mqh.limit = limit;
- return mqh;
-}
-
-/*
- * Get struct control_block_extended_exc_detail for CB.
- */
-extern int gru_get_cb_exception_detail(void *cb,
- struct control_block_extended_exc_detail *excdet);
-
-#define GRU_EXC_STR_SIZE 256
-
-
-/*
- * Control block definition for checking status
- */
-struct gru_control_block_status {
- unsigned int icmd :1;
- unsigned int ima :3;
- unsigned int reserved0 :4;
- unsigned int unused1 :24;
- unsigned int unused2 :24;
- unsigned int istatus :2;
- unsigned int isubstatus :4;
- unsigned int unused3 :2;
-};
-
-/* Get CB status */
-static inline int gru_get_cb_status(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
-
- return cbs->istatus;
-}
-
-/* Get CB message queue substatus */
-static inline int gru_get_cb_message_queue_substatus(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
-
- return cbs->isubstatus & CBSS_MSG_QUEUE_MASK;
-}
-
-/* Get CB substatus */
-static inline int gru_get_cb_substatus(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
-
- return cbs->isubstatus;
-}
-
-/*
- * User interface to check an instruction status. UPM and exceptions
- * are handled automatically. However, this function does NOT wait
- * for an active instruction to complete.
- *
- */
-static inline int gru_check_status(void *cb)
-{
- struct gru_control_block_status *cbs = (void *)cb;
- int ret;
-
- ret = cbs->istatus;
- if (ret != CBS_ACTIVE)
- ret = gru_check_status_proc(cb);
- return ret;
-}
-
-/*
- * User interface (via inline function) to wait for an instruction
- * to complete. Completion status (IDLE or EXCEPTION is returned
- * to the user. Exception due to hardware errors are automatically
- * retried before returning an exception.
- *
- */
-static inline int gru_wait(void *cb)
-{
- return gru_wait_proc(cb);
-}
-
-/*
- * Wait for CB to complete. Aborts program if error. (Note: error does NOT
- * mean TLB mis - only fatal errors such as memory parity error or user
- * bugs will cause termination.
- */
-static inline void gru_wait_abort(void *cb)
-{
- gru_wait_abort_proc(cb);
-}
-
-/*
- * Get a pointer to the start of a gseg
- * p - Any valid pointer within the gseg
- */
-static inline void *gru_get_gseg_pointer (void *p)
-{
- return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1));
-}
-
-/*
- * Get a pointer to a control block
- * gseg - GSeg address returned from gru_get_thread_gru_segment()
- * index - index of desired CB
- */
-static inline void *gru_get_cb_pointer(void *gseg,
- int index)
-{
- return gseg + GRU_CB_BASE + index * GRU_HANDLE_STRIDE;
-}
-
-/*
- * Get a pointer to a cacheline in the data segment portion of a GSeg
- * gseg - GSeg address returned from gru_get_thread_gru_segment()
- * index - index of desired cache line
- */
-static inline void *gru_get_data_pointer(void *gseg, int index)
-{
- return gseg + GRU_DS_BASE + index * GRU_CACHE_LINE_BYTES;
-}
-
-/*
- * Convert a vaddr into the tri index within the GSEG
- * vaddr - virtual address of within gseg
- */
-static inline int gru_get_tri(void *vaddr)
-{
- return ((unsigned long)vaddr & (GRU_GSEG_PAGESIZE - 1)) - GRU_DS_BASE;
-}
-#endif /* __GRU_INSTRUCTIONS_H__ */
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
deleted file mode 100644
index 3557d78ee47a..000000000000
--- a/drivers/misc/sgi-gru/grufault.c
+++ /dev/null
@@ -1,903 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * FAULT HANDLER FOR GRU DETECTED TLB MISSES
- *
- * This file contains code that handles TLB misses within the GRU.
- * These misses are reported either via interrupts or user polling of
- * the user CB.
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/security.h>
-#include <linux/sync_core.h>
-#include <linux/prefetch.h>
-#include "gru.h"
-#include "grutables.h"
-#include "grulib.h"
-#include "gru_instructions.h"
-#include <asm/uv/uv_hub.h>
-
-/* Return codes for vtop functions */
-#define VTOP_SUCCESS 0
-#define VTOP_INVALID -1
-#define VTOP_RETRY -2
-
-
-/*
- * Test if a physical address is a valid GRU GSEG address
- */
-static inline int is_gru_paddr(unsigned long paddr)
-{
- return paddr >= gru_start_paddr && paddr < gru_end_paddr;
-}
-
-/*
- * Find the vma of a GRU segment. Caller must hold mmap_lock.
- */
-struct vm_area_struct *gru_find_vma(unsigned long vaddr)
-{
- struct vm_area_struct *vma;
-
- vma = vma_lookup(current->mm, vaddr);
- if (vma && vma->vm_ops == &gru_vm_ops)
- return vma;
- return NULL;
-}
-
-/*
- * Find and lock the gts that contains the specified user vaddr.
- *
- * Returns:
- * - *gts with the mmap_lock locked for read and the GTS locked.
- * - NULL if vaddr invalid OR is not a valid GSEG vaddr.
- */
-
-static struct gru_thread_state *gru_find_lock_gts(unsigned long vaddr)
-{
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- struct gru_thread_state *gts = NULL;
-
- mmap_read_lock(mm);
- vma = gru_find_vma(vaddr);
- if (vma)
- gts = gru_find_thread_state(vma, TSID(vaddr, vma));
- if (gts)
- mutex_lock(&gts->ts_ctxlock);
- else
- mmap_read_unlock(mm);
- return gts;
-}
-
-static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr)
-{
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- struct gru_thread_state *gts = ERR_PTR(-EINVAL);
-
- mmap_write_lock(mm);
- vma = gru_find_vma(vaddr);
- if (!vma)
- goto err;
-
- gts = gru_alloc_thread_state(vma, TSID(vaddr, vma));
- if (IS_ERR(gts))
- goto err;
- mutex_lock(&gts->ts_ctxlock);
- mmap_write_downgrade(mm);
- return gts;
-
-err:
- mmap_write_unlock(mm);
- return gts;
-}
-
-/*
- * Unlock a GTS that was previously locked with gru_find_lock_gts().
- */
-static void gru_unlock_gts(struct gru_thread_state *gts)
-{
- mutex_unlock(&gts->ts_ctxlock);
- mmap_read_unlock(current->mm);
-}
-
-/*
- * Set a CB.istatus to active using a user virtual address. This must be done
- * just prior to a TFH RESTART. The new cb.istatus is an in-cache status ONLY.
- * If the line is evicted, the status may be lost. The in-cache update
- * is necessary to prevent the user from seeing a stale cb.istatus that will
- * change as soon as the TFH restart is complete. Races may cause an
- * occasional failure to clear the cb.istatus, but that is ok.
- */
-static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk)
-{
- if (cbk) {
- cbk->istatus = CBS_ACTIVE;
- }
-}
-
-/*
- * Read & clear a TFM
- *
- * The GRU has an array of fault maps. A map is private to a cpu
- * Only one cpu will be accessing a cpu's fault map.
- *
- * This function scans the cpu-private fault map & clears all bits that
- * are set. The function returns a bitmap that indicates the bits that
- * were cleared. Note that sense the maps may be updated asynchronously by
- * the GRU, atomic operations must be used to clear bits.
- */
-static void get_clear_fault_map(struct gru_state *gru,
- struct gru_tlb_fault_map *imap,
- struct gru_tlb_fault_map *dmap)
-{
- unsigned long i, k;
- struct gru_tlb_fault_map *tfm;
-
- tfm = get_tfm_for_cpu(gru, gru_cpu_fault_map_id());
- prefetchw(tfm); /* Helps on hardware, required for emulator */
- for (i = 0; i < BITS_TO_LONGS(GRU_NUM_CBE); i++) {
- k = tfm->fault_bits[i];
- if (k)
- k = xchg(&tfm->fault_bits[i], 0UL);
- imap->fault_bits[i] = k;
- k = tfm->done_bits[i];
- if (k)
- k = xchg(&tfm->done_bits[i], 0UL);
- dmap->fault_bits[i] = k;
- }
-
- /*
- * Not functionally required but helps performance. (Required
- * on emulator)
- */
- gru_flush_cache(tfm);
-}
-
-/*
- * Atomic (interrupt context) & non-atomic (user context) functions to
- * convert a vaddr into a physical address. The size of the page
- * is returned in pageshift.
- * returns:
- * 0 - successful
- * < 0 - error code
- * 1 - (atomic only) try again in non-atomic context
- */
-static int non_atomic_pte_lookup(struct vm_area_struct *vma,
- unsigned long vaddr, int write,
- unsigned long *paddr, int *pageshift)
-{
- struct page *page;
-
-#ifdef CONFIG_HUGETLB_PAGE
- *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT;
-#else
- *pageshift = PAGE_SHIFT;
-#endif
- if (get_user_pages(vaddr, 1, write ? FOLL_WRITE : 0, &page) <= 0)
- return -EFAULT;
- *paddr = page_to_phys(page);
- put_page(page);
- return 0;
-}
-
-/*
- * atomic_pte_lookup
- *
- * Convert a user virtual address to a physical address
- * Only supports Intel large pages (2MB only) on x86_64.
- * ZZZ - hugepage support is incomplete
- *
- * NOTE: mmap_lock is already held on entry to this function. This
- * guarantees existence of the page tables.
- */
-static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr,
- int write, unsigned long *paddr, int *pageshift)
-{
- pgd_t *pgdp;
- p4d_t *p4dp;
- pud_t *pudp;
- pmd_t *pmdp;
- pte_t pte;
-
- pgdp = pgd_offset(vma->vm_mm, vaddr);
- if (unlikely(pgd_none(*pgdp)))
- goto err;
-
- p4dp = p4d_offset(pgdp, vaddr);
- if (unlikely(p4d_none(*p4dp)))
- goto err;
-
- pudp = pud_offset(p4dp, vaddr);
- if (unlikely(pud_none(*pudp)))
- goto err;
-
- pmdp = pmd_offset(pudp, vaddr);
- if (unlikely(pmd_none(*pmdp)))
- goto err;
-#ifdef CONFIG_X86_64
- if (unlikely(pmd_leaf(*pmdp)))
- pte = ptep_get((pte_t *)pmdp);
- else
-#endif
- pte = *pte_offset_kernel(pmdp, vaddr);
-
- if (unlikely(!pte_present(pte) ||
- (write && (!pte_write(pte) || !pte_dirty(pte)))))
- return 1;
-
- *paddr = pte_pfn(pte) << PAGE_SHIFT;
-#ifdef CONFIG_HUGETLB_PAGE
- *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT;
-#else
- *pageshift = PAGE_SHIFT;
-#endif
- return 0;
-
-err:
- return 1;
-}
-
-static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
- int write, int atomic, unsigned long *gpa, int *pageshift)
-{
- struct mm_struct *mm = gts->ts_mm;
- struct vm_area_struct *vma;
- unsigned long paddr;
- int ret, ps;
-
- vma = find_vma(mm, vaddr);
- if (!vma)
- goto inval;
-
- /*
- * Atomic lookup is faster & usually works even if called in non-atomic
- * context.
- */
- rmb(); /* Must/check ms_range_active before loading PTEs */
- ret = atomic_pte_lookup(vma, vaddr, write, &paddr, &ps);
- if (ret) {
- if (atomic)
- goto upm;
- if (non_atomic_pte_lookup(vma, vaddr, write, &paddr, &ps))
- goto inval;
- }
- if (is_gru_paddr(paddr))
- goto inval;
- paddr = paddr & ~((1UL << ps) - 1);
- *gpa = uv_soc_phys_ram_to_gpa(paddr);
- *pageshift = ps;
- return VTOP_SUCCESS;
-
-inval:
- return VTOP_INVALID;
-upm:
- return VTOP_RETRY;
-}
-
-
-/*
- * Flush a CBE from cache. The CBE is clean in the cache. Dirty the
- * CBE cacheline so that the line will be written back to home agent.
- * Otherwise the line may be silently dropped. This has no impact
- * except on performance.
- */
-static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe)
-{
- if (unlikely(cbe)) {
- cbe->cbrexecstatus = 0; /* make CL dirty */
- gru_flush_cache(cbe);
- }
-}
-
-/*
- * Preload the TLB with entries that may be required. Currently, preloading
- * is implemented only for BCOPY. Preload <tlb_preload_count> pages OR to
- * the end of the bcopy tranfer, whichever is smaller.
- */
-static void gru_preload_tlb(struct gru_state *gru,
- struct gru_thread_state *gts, int atomic,
- unsigned long fault_vaddr, int asid, int write,
- unsigned char tlb_preload_count,
- struct gru_tlb_fault_handle *tfh,
- struct gru_control_block_extended *cbe)
-{
- unsigned long vaddr = 0, gpa;
- int ret, pageshift;
-
- if (cbe->opccpy != OP_BCOPY)
- return;
-
- if (fault_vaddr == cbe->cbe_baddr0)
- vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1;
- else if (fault_vaddr == cbe->cbe_baddr1)
- vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1;
-
- fault_vaddr &= PAGE_MASK;
- vaddr &= PAGE_MASK;
- vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE);
-
- while (vaddr > fault_vaddr) {
- ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
- if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write,
- GRU_PAGESIZE(pageshift)))
- return;
- gru_dbg(grudev,
- "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n",
- atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh,
- vaddr, asid, write, pageshift, gpa);
- vaddr -= PAGE_SIZE;
- STAT(tlb_preload_page);
- }
-}
-
-/*
- * Drop a TLB entry into the GRU. The fault is described by info in an TFH.
- * Input:
- * cb Address of user CBR. Null if not running in user context
- * Return:
- * 0 = dropin, exception, or switch to UPM successful
- * 1 = range invalidate active
- * < 0 = error code
- *
- */
-static int gru_try_dropin(struct gru_state *gru,
- struct gru_thread_state *gts,
- struct gru_tlb_fault_handle *tfh,
- struct gru_instruction_bits *cbk)
-{
- struct gru_control_block_extended *cbe = NULL;
- unsigned char tlb_preload_count = gts->ts_tlb_preload_count;
- int pageshift = 0, asid, write, ret, atomic = !cbk, indexway;
- unsigned long gpa = 0, vaddr = 0;
-
- /*
- * NOTE: The GRU contains magic hardware that eliminates races between
- * TLB invalidates and TLB dropins. If an invalidate occurs
- * in the window between reading the TFH and the subsequent TLB dropin,
- * the dropin is ignored. This eliminates the need for additional locks.
- */
-
- /*
- * Prefetch the CBE if doing TLB preloading
- */
- if (unlikely(tlb_preload_count)) {
- cbe = gru_tfh_to_cbe(tfh);
- prefetchw(cbe);
- }
-
- /*
- * Error if TFH state is IDLE or FMM mode & the user issuing a UPM call.
- * Might be a hardware race OR a stupid user. Ignore FMM because FMM
- * is a transient state.
- */
- if (tfh->status != TFHSTATUS_EXCEPTION) {
- gru_flush_cache(tfh);
- sync_core();
- if (tfh->status != TFHSTATUS_EXCEPTION)
- goto failnoexception;
- STAT(tfh_stale_on_fault);
- }
- if (tfh->state == TFHSTATE_IDLE)
- goto failidle;
- if (tfh->state == TFHSTATE_MISS_FMM && cbk)
- goto failfmm;
-
- write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0;
- vaddr = tfh->missvaddr;
- asid = tfh->missasid;
- indexway = tfh->indexway;
- if (asid == 0)
- goto failnoasid;
-
- rmb(); /* TFH must be cache resident before reading ms_range_active */
-
- /*
- * TFH is cache resident - at least briefly. Fail the dropin
- * if a range invalidate is active.
- */
- if (atomic_read(&gts->ts_gms->ms_range_active))
- goto failactive;
-
- ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
- if (ret == VTOP_INVALID)
- goto failinval;
- if (ret == VTOP_RETRY)
- goto failupm;
-
- if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) {
- gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift);
- if (atomic || !gru_update_cch(gts)) {
- gts->ts_force_cch_reload = 1;
- goto failupm;
- }
- }
-
- if (unlikely(cbe) && pageshift == PAGE_SHIFT) {
- gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe);
- gru_flush_cache_cbe(cbe);
- }
-
- gru_cb_set_istatus_active(cbk);
- gts->ustats.tlbdropin++;
- tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write,
- GRU_PAGESIZE(pageshift));
- gru_dbg(grudev,
- "%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x,"
- " rw %d, ps %d, gpa 0x%lx\n",
- atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid,
- indexway, write, pageshift, gpa);
- STAT(tlb_dropin);
- return 0;
-
-failnoasid:
- /* No asid (delayed unload). */
- STAT(tlb_dropin_fail_no_asid);
- gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- if (!cbk)
- tfh_user_polling_mode(tfh);
- else
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- return -EAGAIN;
-
-failupm:
- /* Atomic failure switch CBR to UPM */
- tfh_user_polling_mode(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_upm);
- gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- return 1;
-
-failfmm:
- /* FMM state on UPM call */
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_fmm);
- gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state);
- return 0;
-
-failnoexception:
- /* TFH status did not show exception pending */
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- if (cbk)
- gru_flush_cache(cbk);
- STAT(tlb_dropin_fail_no_exception);
- gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n",
- tfh, tfh->status, tfh->state);
- return 0;
-
-failidle:
- /* TFH state was idle - no miss pending */
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- if (cbk)
- gru_flush_cache(cbk);
- STAT(tlb_dropin_fail_idle);
- gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state);
- return 0;
-
-failinval:
- /* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */
- tfh_exception(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_invalid);
- gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
- return -EFAULT;
-
-failactive:
- /* Range invalidate active. Switch to UPM iff atomic */
- if (!cbk)
- tfh_user_polling_mode(tfh);
- else
- gru_flush_cache(tfh);
- gru_flush_cache_cbe(cbe);
- STAT(tlb_dropin_fail_range_active);
- gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n",
- tfh, vaddr);
- return 1;
-}
-
-/*
- * Process an external interrupt from the GRU. This interrupt is
- * caused by a TLB miss.
- * Note that this is the interrupt handler that is registered with linux
- * interrupt handlers.
- */
-static irqreturn_t gru_intr(int chiplet, int blade)
-{
- struct gru_state *gru;
- struct gru_tlb_fault_map imap, dmap;
- struct gru_thread_state *gts;
- struct gru_tlb_fault_handle *tfh = NULL;
- struct completion *cmp;
- int cbrnum, ctxnum;
-
- STAT(intr);
-
- gru = &gru_base[blade]->bs_grus[chiplet];
- if (!gru) {
- dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n",
- raw_smp_processor_id(), chiplet);
- return IRQ_NONE;
- }
- get_clear_fault_map(gru, &imap, &dmap);
- gru_dbg(grudev,
- "cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n",
- smp_processor_id(), chiplet, gru->gs_gid,
- imap.fault_bits[0], imap.fault_bits[1],
- dmap.fault_bits[0], dmap.fault_bits[1]);
-
- for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) {
- STAT(intr_cbr);
- cmp = gru->gs_blade->bs_async_wq;
- if (cmp)
- complete(cmp);
- gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n",
- gru->gs_gid, cbrnum, cmp ? cmp->done : -1);
- }
-
- for_each_cbr_in_tfm(cbrnum, imap.fault_bits) {
- STAT(intr_tfh);
- tfh = get_tfh_by_index(gru, cbrnum);
- prefetchw(tfh); /* Helps on hdw, required for emulator */
-
- /*
- * When hardware sets a bit in the faultmap, it implicitly
- * locks the GRU context so that it cannot be unloaded.
- * The gts cannot change until a TFH start/writestart command
- * is issued.
- */
- ctxnum = tfh->ctxnum;
- gts = gru->gs_gts[ctxnum];
-
- /* Spurious interrupts can cause this. Ignore. */
- if (!gts) {
- STAT(intr_spurious);
- continue;
- }
-
- /*
- * This is running in interrupt context. Trylock the mmap_lock.
- * If it fails, retry the fault in user context.
- */
- gts->ustats.fmm_tlbmiss++;
- if (!gts->ts_force_cch_reload &&
- mmap_read_trylock(gts->ts_mm)) {
- gru_try_dropin(gru, gts, tfh, NULL);
- mmap_read_unlock(gts->ts_mm);
- } else {
- tfh_user_polling_mode(tfh);
- STAT(intr_mm_lock_failed);
- }
- }
- return IRQ_HANDLED;
-}
-
-irqreturn_t gru0_intr(int irq, void *dev_id)
-{
- return gru_intr(0, uv_numa_blade_id());
-}
-
-irqreturn_t gru1_intr(int irq, void *dev_id)
-{
- return gru_intr(1, uv_numa_blade_id());
-}
-
-irqreturn_t gru_intr_mblade(int irq, void *dev_id)
-{
- int blade;
-
- for_each_possible_blade(blade) {
- if (uv_blade_nr_possible_cpus(blade))
- continue;
- gru_intr(0, blade);
- gru_intr(1, blade);
- }
- return IRQ_HANDLED;
-}
-
-
-static int gru_user_dropin(struct gru_thread_state *gts,
- struct gru_tlb_fault_handle *tfh,
- void *cb)
-{
- struct gru_mm_struct *gms = gts->ts_gms;
- int ret;
-
- gts->ustats.upm_tlbmiss++;
- while (1) {
- wait_event(gms->ms_wait_queue,
- atomic_read(&gms->ms_range_active) == 0);
- prefetchw(tfh); /* Helps on hdw, required for emulator */
- ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb);
- if (ret <= 0)
- return ret;
- STAT(call_os_wait_queue);
- }
-}
-
-/*
- * This interface is called as a result of a user detecting a "call OS" bit
- * in a user CB. Normally means that a TLB fault has occurred.
- * cb - user virtual address of the CB
- */
-int gru_handle_user_call_os(unsigned long cb)
-{
- struct gru_tlb_fault_handle *tfh;
- struct gru_thread_state *gts;
- void *cbk;
- int ucbnum, cbrnum, ret = -EINVAL;
-
- STAT(call_os);
-
- /* sanity check the cb pointer */
- ucbnum = get_cb_number((void *)cb);
- if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB)
- return -EINVAL;
-
-again:
- gts = gru_find_lock_gts(cb);
- if (!gts)
- return -EINVAL;
- gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
-
- if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE)
- goto exit;
-
- if (gru_check_context_placement(gts)) {
- gru_unlock_gts(gts);
- gru_unload_context(gts, 1);
- goto again;
- }
-
- /*
- * CCH may contain stale data if ts_force_cch_reload is set.
- */
- if (gts->ts_gru && gts->ts_force_cch_reload) {
- gts->ts_force_cch_reload = 0;
- gru_update_cch(gts);
- }
-
- ret = -EAGAIN;
- cbrnum = thread_cbr_number(gts, ucbnum);
- if (gts->ts_gru) {
- tfh = get_tfh_by_index(gts->ts_gru, cbrnum);
- cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr,
- gts->ts_ctxnum, ucbnum);
- ret = gru_user_dropin(gts, tfh, cbk);
- }
-exit:
- gru_unlock_gts(gts);
- return ret;
-}
-
-/*
- * Fetch the exception detail information for a CB that terminated with
- * an exception.
- */
-int gru_get_exception_detail(unsigned long arg)
-{
- struct control_block_extended_exc_detail excdet;
- struct gru_control_block_extended *cbe;
- struct gru_thread_state *gts;
- int ucbnum, cbrnum, ret;
-
- STAT(user_exception);
- if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet)))
- return -EFAULT;
-
- gts = gru_find_lock_gts(excdet.cb);
- if (!gts)
- return -EINVAL;
-
- gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
- ucbnum = get_cb_number((void *)excdet.cb);
- if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) {
- ret = -EINVAL;
- } else if (gts->ts_gru) {
- cbrnum = thread_cbr_number(gts, ucbnum);
- cbe = get_cbe_by_index(gts->ts_gru, cbrnum);
- gru_flush_cache(cbe); /* CBE not coherent */
- sync_core(); /* make sure we are have current data */
- excdet.opc = cbe->opccpy;
- excdet.exopc = cbe->exopccpy;
- excdet.ecause = cbe->ecause;
- excdet.exceptdet0 = cbe->idef1upd;
- excdet.exceptdet1 = cbe->idef3upd;
- excdet.cbrstate = cbe->cbrstate;
- excdet.cbrexecstatus = cbe->cbrexecstatus;
- gru_flush_cache_cbe(cbe);
- ret = 0;
- } else {
- ret = -EAGAIN;
- }
- gru_unlock_gts(gts);
-
- gru_dbg(grudev,
- "cb 0x%lx, op %d, exopc %d, cbrstate %d, cbrexecstatus 0x%x, ecause 0x%x, "
- "exdet0 0x%lx, exdet1 0x%x\n",
- excdet.cb, excdet.opc, excdet.exopc, excdet.cbrstate, excdet.cbrexecstatus,
- excdet.ecause, excdet.exceptdet0, excdet.exceptdet1);
- if (!ret && copy_to_user((void __user *)arg, &excdet, sizeof(excdet)))
- ret = -EFAULT;
- return ret;
-}
-
-/*
- * User request to unload a context. Content is saved for possible reload.
- */
-static int gru_unload_all_contexts(void)
-{
- struct gru_thread_state *gts;
- struct gru_state *gru;
- int gid, ctxnum;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- foreach_gid(gid) {
- gru = GID_TO_GRU(gid);
- spin_lock(&gru->gs_lock);
- for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
- gts = gru->gs_gts[ctxnum];
- if (gts && mutex_trylock(&gts->ts_ctxlock)) {
- spin_unlock(&gru->gs_lock);
- gru_unload_context(gts, 1);
- mutex_unlock(&gts->ts_ctxlock);
- spin_lock(&gru->gs_lock);
- }
- }
- spin_unlock(&gru->gs_lock);
- }
- return 0;
-}
-
-int gru_user_unload_context(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_unload_context_req req;
-
- STAT(user_unload_context);
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- gru_dbg(grudev, "gseg 0x%lx\n", req.gseg);
-
- if (!req.gseg)
- return gru_unload_all_contexts();
-
- gts = gru_find_lock_gts(req.gseg);
- if (!gts)
- return -EINVAL;
-
- if (gts->ts_gru)
- gru_unload_context(gts, 1);
- gru_unlock_gts(gts);
-
- return 0;
-}
-
-/*
- * User request to flush a range of virtual addresses from the GRU TLB
- * (Mainly for testing).
- */
-int gru_user_flush_tlb(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_flush_tlb_req req;
- struct gru_mm_struct *gms;
-
- STAT(user_flush_tlb);
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- gru_dbg(grudev, "gseg 0x%lx, vaddr 0x%lx, len 0x%lx\n", req.gseg,
- req.vaddr, req.len);
-
- gts = gru_find_lock_gts(req.gseg);
- if (!gts)
- return -EINVAL;
-
- gms = gts->ts_gms;
- gru_unlock_gts(gts);
- gru_flush_tlb_range(gms, req.vaddr, req.len);
-
- return 0;
-}
-
-/*
- * Fetch GSEG statisticss
- */
-long gru_get_gseg_statistics(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_get_gseg_statistics_req req;
-
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- /*
- * The library creates arrays of contexts for threaded programs.
- * If no gts exists in the array, the context has never been used & all
- * statistics are implicitly 0.
- */
- gts = gru_find_lock_gts(req.gseg);
- if (gts) {
- memcpy(&req.stats, &gts->ustats, sizeof(gts->ustats));
- gru_unlock_gts(gts);
- } else {
- memset(&req.stats, 0, sizeof(gts->ustats));
- }
-
- if (copy_to_user((void __user *)arg, &req, sizeof(req)))
- return -EFAULT;
-
- return 0;
-}
-
-/*
- * Register the current task as the user of the GSEG slice.
- * Needed for TLB fault interrupt targeting.
- */
-int gru_set_context_option(unsigned long arg)
-{
- struct gru_thread_state *gts;
- struct gru_set_context_option_req req;
- int ret = 0;
-
- STAT(set_context_option);
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
- gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1);
-
- gts = gru_find_lock_gts(req.gseg);
- if (!gts) {
- gts = gru_alloc_locked_gts(req.gseg);
- if (IS_ERR(gts))
- return PTR_ERR(gts);
- }
-
- switch (req.op) {
- case sco_blade_chiplet:
- /* Select blade/chiplet for GRU context */
- if (req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB ||
- req.val1 < -1 || req.val1 >= GRU_MAX_BLADES ||
- (req.val1 >= 0 && !gru_base[req.val1])) {
- ret = -EINVAL;
- } else {
- gts->ts_user_blade_id = req.val1;
- gts->ts_user_chiplet_id = req.val0;
- if (gru_check_context_placement(gts)) {
- gru_unlock_gts(gts);
- gru_unload_context(gts, 1);
- return ret;
- }
- }
- break;
- case sco_gseg_owner:
- /* Register the current task as the GSEG owner */
- gts->ts_tgid_owner = current->tgid;
- break;
- case sco_cch_req_slice:
- /* Set the CCH slice option */
- gts->ts_cch_req_slice = req.val1 & 3;
- break;
- default:
- ret = -EINVAL;
- }
- gru_unlock_gts(gts);
-
- return ret;
-}
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c
deleted file mode 100644
index e755690c9805..000000000000
--- a/drivers/misc/sgi-gru/grufile.c
+++ /dev/null
@@ -1,540 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * FILE OPERATIONS & DRIVER INITIALIZATION
- *
- * This file supports the user system call for file open, close, mmap, etc.
- * This also incudes the driver initialization code.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/miscdevice.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/uaccess.h>
-#ifdef CONFIG_X86_64
-#include <asm/uv/uv_irq.h>
-#endif
-#include <asm/uv/uv.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-
-#include <asm/uv/uv_hub.h>
-#include <asm/uv/uv_mmrs.h>
-
-struct gru_blade_state *gru_base[GRU_MAX_BLADES] __read_mostly;
-unsigned long gru_start_paddr __read_mostly;
-void *gru_start_vaddr __read_mostly;
-unsigned long gru_end_paddr __read_mostly;
-unsigned int gru_max_gids __read_mostly;
-struct gru_stats_s gru_stats;
-
-/* Guaranteed user available resources on each node */
-static int max_user_cbrs, max_user_dsr_bytes;
-
-static struct miscdevice gru_miscdev;
-
-static int gru_supported(void)
-{
- return is_uv_system() &&
- (uv_hub_info->hub_revision < UV3_HUB_REVISION_BASE);
-}
-
-/*
- * gru_vma_close
- *
- * Called when unmapping a device mapping. Frees all gru resources
- * and tables belonging to the vma.
- */
-static void gru_vma_close(struct vm_area_struct *vma)
-{
- struct gru_vma_data *vdata;
- struct gru_thread_state *gts;
- struct list_head *entry, *next;
-
- if (!vma->vm_private_data)
- return;
-
- vdata = vma->vm_private_data;
- vma->vm_private_data = NULL;
- gru_dbg(grudev, "vma %p, file %p, vdata %p\n", vma, vma->vm_file,
- vdata);
- list_for_each_safe(entry, next, &vdata->vd_head) {
- gts =
- list_entry(entry, struct gru_thread_state, ts_next);
- list_del(&gts->ts_next);
- mutex_lock(&gts->ts_ctxlock);
- if (gts->ts_gru)
- gru_unload_context(gts, 0);
- mutex_unlock(&gts->ts_ctxlock);
- gts_drop(gts);
- }
- kfree(vdata);
- STAT(vdata_free);
-}
-
-/*
- * gru_file_mmap
- *
- * Called when mmapping the device. Initializes the vma with a fault handler
- * and private data structure necessary to allocate, track, and free the
- * underlying pages.
- */
-static int gru_file_mmap(struct file *file, struct vm_area_struct *vma)
-{
- if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) != (VM_SHARED | VM_WRITE))
- return -EPERM;
-
- if (vma->vm_start & (GRU_GSEG_PAGESIZE - 1) ||
- vma->vm_end & (GRU_GSEG_PAGESIZE - 1))
- return -EINVAL;
-
- vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_LOCKED |
- VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
- vma->vm_page_prot = PAGE_SHARED;
- vma->vm_ops = &gru_vm_ops;
-
- vma->vm_private_data = gru_alloc_vma_data(vma, 0);
- if (!vma->vm_private_data)
- return -ENOMEM;
-
- gru_dbg(grudev, "file %p, vaddr 0x%lx, vma %p, vdata %p\n",
- file, vma->vm_start, vma, vma->vm_private_data);
- return 0;
-}
-
-/*
- * Create a new GRU context
- */
-static int gru_create_new_context(unsigned long arg)
-{
- struct gru_create_context_req req;
- struct vm_area_struct *vma;
- struct gru_vma_data *vdata;
- int ret = -EINVAL;
-
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- if (req.data_segment_bytes > max_user_dsr_bytes)
- return -EINVAL;
- if (req.control_blocks > max_user_cbrs || !req.maximum_thread_count)
- return -EINVAL;
-
- if (!(req.options & GRU_OPT_MISS_MASK))
- req.options |= GRU_OPT_MISS_FMM_INTR;
-
- mmap_write_lock(current->mm);
- vma = gru_find_vma(req.gseg);
- if (vma) {
- vdata = vma->vm_private_data;
- vdata->vd_user_options = req.options;
- vdata->vd_dsr_au_count =
- GRU_DS_BYTES_TO_AU(req.data_segment_bytes);
- vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks);
- vdata->vd_tlb_preload_count = req.tlb_preload_count;
- ret = 0;
- }
- mmap_write_unlock(current->mm);
-
- return ret;
-}
-
-/*
- * Get GRU configuration info (temp - for emulator testing)
- */
-static long gru_get_config_info(unsigned long arg)
-{
- struct gru_config_info info;
- int nodesperblade;
-
- if (num_online_nodes() > 1 &&
- (uv_node_to_blade_id(1) == uv_node_to_blade_id(0)))
- nodesperblade = 2;
- else
- nodesperblade = 1;
- memset(&info, 0, sizeof(info));
- info.cpus = num_online_cpus();
- info.nodes = num_online_nodes();
- info.blades = info.nodes / nodesperblade;
- info.chiplets = GRU_CHIPLETS_PER_BLADE * info.blades;
-
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-/*
- * gru_file_unlocked_ioctl
- *
- * Called to update file attributes via IOCTL calls.
- */
-static long gru_file_unlocked_ioctl(struct file *file, unsigned int req,
- unsigned long arg)
-{
- int err = -EBADRQC;
-
- gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg);
-
- switch (req) {
- case GRU_CREATE_CONTEXT:
- err = gru_create_new_context(arg);
- break;
- case GRU_SET_CONTEXT_OPTION:
- err = gru_set_context_option(arg);
- break;
- case GRU_USER_GET_EXCEPTION_DETAIL:
- err = gru_get_exception_detail(arg);
- break;
- case GRU_USER_UNLOAD_CONTEXT:
- err = gru_user_unload_context(arg);
- break;
- case GRU_USER_FLUSH_TLB:
- err = gru_user_flush_tlb(arg);
- break;
- case GRU_USER_CALL_OS:
- err = gru_handle_user_call_os(arg);
- break;
- case GRU_GET_GSEG_STATISTICS:
- err = gru_get_gseg_statistics(arg);
- break;
- case GRU_KTEST:
- err = gru_ktest(arg);
- break;
- case GRU_GET_CONFIG_INFO:
- err = gru_get_config_info(arg);
- break;
- case GRU_DUMP_CHIPLET_STATE:
- err = gru_dump_chiplet_request(arg);
- break;
- }
- return err;
-}
-
-/*
- * Called at init time to build tables for all GRUs that are present in the
- * system.
- */
-static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr,
- void *vaddr, int blade_id, int chiplet_id)
-{
- spin_lock_init(&gru->gs_lock);
- spin_lock_init(&gru->gs_asid_lock);
- gru->gs_gru_base_paddr = paddr;
- gru->gs_gru_base_vaddr = vaddr;
- gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id;
- gru->gs_blade = gru_base[blade_id];
- gru->gs_blade_id = blade_id;
- gru->gs_chiplet_id = chiplet_id;
- gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1;
- gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1;
- gru->gs_asid_limit = MAX_ASID;
- gru_tgh_flush_init(gru);
- if (gru->gs_gid >= gru_max_gids)
- gru_max_gids = gru->gs_gid + 1;
- gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n",
- blade_id, gru->gs_gid, gru->gs_gru_base_vaddr,
- gru->gs_gru_base_paddr);
-}
-
-static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
-{
- int pnode, nid, bid, chip;
- int cbrs, dsrbytes, n;
- int order = get_order(sizeof(struct gru_blade_state));
- struct page *page;
- struct gru_state *gru;
- unsigned long paddr;
- void *vaddr;
-
- max_user_cbrs = GRU_NUM_CB;
- max_user_dsr_bytes = GRU_NUM_DSR_BYTES;
- for_each_possible_blade(bid) {
- pnode = uv_blade_to_pnode(bid);
- nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */
- page = alloc_pages_node(nid, GFP_KERNEL, order);
- if (!page)
- goto fail;
- gru_base[bid] = page_address(page);
- memset(gru_base[bid], 0, sizeof(struct gru_blade_state));
- gru_base[bid]->bs_lru_gru = &gru_base[bid]->bs_grus[0];
- spin_lock_init(&gru_base[bid]->bs_lock);
- init_rwsem(&gru_base[bid]->bs_kgts_sema);
-
- dsrbytes = 0;
- cbrs = 0;
- for (gru = gru_base[bid]->bs_grus, chip = 0;
- chip < GRU_CHIPLETS_PER_BLADE;
- chip++, gru++) {
- paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip);
- vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip);
- gru_init_chiplet(gru, paddr, vaddr, bid, chip);
- n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE;
- cbrs = max(cbrs, n);
- n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES;
- dsrbytes = max(dsrbytes, n);
- }
- max_user_cbrs = min(max_user_cbrs, cbrs);
- max_user_dsr_bytes = min(max_user_dsr_bytes, dsrbytes);
- }
-
- return 0;
-
-fail:
- for (bid--; bid >= 0; bid--)
- free_pages((unsigned long)gru_base[bid], order);
- return -ENOMEM;
-}
-
-static void gru_free_tables(void)
-{
- int bid;
- int order = get_order(sizeof(struct gru_state) *
- GRU_CHIPLETS_PER_BLADE);
-
- for (bid = 0; bid < GRU_MAX_BLADES; bid++)
- free_pages((unsigned long)gru_base[bid], order);
-}
-
-static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep)
-{
- unsigned long mmr = 0;
- int core;
-
- /*
- * We target the cores of a blade and not the hyperthreads themselves.
- * There is a max of 8 cores per socket and 2 sockets per blade,
- * making for a max total of 16 cores (i.e., 16 CPUs without
- * hyperthreading and 32 CPUs with hyperthreading).
- */
- core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
- if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu))
- return 0;
-
- if (chiplet == 0) {
- mmr = UVH_GR0_TLB_INT0_CONFIG +
- core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG);
- } else if (chiplet == 1) {
- mmr = UVH_GR1_TLB_INT0_CONFIG +
- core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG);
- } else {
- BUG();
- }
-
- *corep = core;
- return mmr;
-}
-
-static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name,
- irq_handler_t irq_handler, int cpu, int blade)
-{
- unsigned long mmr;
- int irq, core;
- int ret;
-
- mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
- if (mmr == 0)
- return 0;
-
- irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU);
- if (irq < 0) {
- printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n",
- GRU_DRIVER_ID_STR, -irq);
- return irq;
- }
-
- ret = request_irq(irq, irq_handler, 0, irq_name, NULL);
- if (ret) {
- uv_teardown_irq(irq);
- printk(KERN_ERR "%s: request_irq failed, errno=%d\n",
- GRU_DRIVER_ID_STR, -ret);
- return ret;
- }
- gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq;
- return 0;
-}
-
-static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade)
-{
- int irq, core;
- unsigned long mmr;
-
- mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
- if (mmr) {
- irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core];
- if (irq) {
- free_irq(irq, NULL);
- uv_teardown_irq(irq);
- }
- }
-}
-
-static void gru_teardown_tlb_irqs(void)
-{
- int blade;
- int cpu;
-
- for_each_online_cpu(cpu) {
- blade = uv_cpu_to_blade_id(cpu);
- gru_chiplet_teardown_tlb_irq(0, cpu, blade);
- gru_chiplet_teardown_tlb_irq(1, cpu, blade);
- }
- for_each_possible_blade(blade) {
- if (uv_blade_nr_possible_cpus(blade))
- continue;
- gru_chiplet_teardown_tlb_irq(0, 0, blade);
- gru_chiplet_teardown_tlb_irq(1, 0, blade);
- }
-}
-
-static int gru_setup_tlb_irqs(void)
-{
- int blade;
- int cpu;
- int ret;
-
- for_each_online_cpu(cpu) {
- blade = uv_cpu_to_blade_id(cpu);
- ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade);
- if (ret != 0)
- goto exit1;
-
- ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade);
- if (ret != 0)
- goto exit1;
- }
- for_each_possible_blade(blade) {
- if (uv_blade_nr_possible_cpus(blade))
- continue;
- ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade);
- if (ret != 0)
- goto exit1;
-
- ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade);
- if (ret != 0)
- goto exit1;
- }
-
- return 0;
-
-exit1:
- gru_teardown_tlb_irqs();
- return ret;
-}
-
-/*
- * gru_init
- *
- * Called at boot or module load time to initialize the GRUs.
- */
-static int __init gru_init(void)
-{
- int ret;
-
- if (!gru_supported())
- return 0;
-
- gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG) &
- 0x7fffffffffffUL;
- gru_start_vaddr = __va(gru_start_paddr);
- gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE;
- printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n",
- gru_start_paddr, gru_end_paddr);
- ret = misc_register(&gru_miscdev);
- if (ret) {
- printk(KERN_ERR "%s: misc_register failed\n",
- GRU_DRIVER_ID_STR);
- goto exit0;
- }
-
- ret = gru_proc_init();
- if (ret) {
- printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR);
- goto exit1;
- }
-
- ret = gru_init_tables(gru_start_paddr, gru_start_vaddr);
- if (ret) {
- printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR);
- goto exit2;
- }
-
- ret = gru_setup_tlb_irqs();
- if (ret != 0)
- goto exit3;
-
- gru_kservices_init();
-
- printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR,
- GRU_DRIVER_VERSION_STR);
- return 0;
-
-exit3:
- gru_free_tables();
-exit2:
- gru_proc_exit();
-exit1:
- misc_deregister(&gru_miscdev);
-exit0:
- return ret;
-
-}
-
-static void __exit gru_exit(void)
-{
- if (!gru_supported())
- return;
-
- gru_teardown_tlb_irqs();
- gru_kservices_exit();
- gru_free_tables();
- misc_deregister(&gru_miscdev);
- gru_proc_exit();
- mmu_notifier_synchronize();
-}
-
-static const struct file_operations gru_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = gru_file_unlocked_ioctl,
- .mmap = gru_file_mmap,
- .llseek = noop_llseek,
-};
-
-static struct miscdevice gru_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "gru",
- .fops = &gru_fops,
-};
-
-const struct vm_operations_struct gru_vm_ops = {
- .close = gru_vma_close,
- .fault = gru_fault,
-};
-
-#ifndef MODULE
-fs_initcall(gru_init);
-#else
-module_init(gru_init);
-#endif
-module_exit(gru_exit);
-
-module_param(gru_options, ulong, 0644);
-MODULE_PARM_DESC(gru_options, "Various debug options");
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(GRU_DRIVER_ID_STR GRU_DRIVER_VERSION_STR);
-MODULE_VERSION(GRU_DRIVER_VERSION_STR);
-
diff --git a/drivers/misc/sgi-gru/gruhandles.c b/drivers/misc/sgi-gru/gruhandles.c
deleted file mode 100644
index 695316a83b01..000000000000
--- a/drivers/misc/sgi-gru/gruhandles.c
+++ /dev/null
@@ -1,192 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * GRU KERNEL MCS INSTRUCTIONS
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-
-/* 10 sec */
-#include <linux/sync_core.h>
-#include <asm/tsc.h>
-#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
-#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz)
-
-/* Extract the status field from a kernel handle */
-#define GET_MSEG_HANDLE_STATUS(h) (((*(unsigned long *)(h)) >> 16) & 3)
-
-struct mcs_op_statistic mcs_op_statistics[mcsop_last];
-
-static void update_mcs_stats(enum mcs_op op, unsigned long clks)
-{
- unsigned long nsec;
-
- nsec = CLKS2NSEC(clks);
- atomic_long_inc(&mcs_op_statistics[op].count);
- atomic_long_add(nsec, &mcs_op_statistics[op].total);
- if (mcs_op_statistics[op].max < nsec)
- mcs_op_statistics[op].max = nsec;
-}
-
-static void start_instruction(void *h)
-{
- unsigned long *w0 = h;
-
- wmb(); /* setting CMD/STATUS bits must be last */
- *w0 = *w0 | 0x20001;
- gru_flush_cache(h);
-}
-
-static void report_instruction_timeout(void *h)
-{
- unsigned long goff = GSEGPOFF((unsigned long)h);
- char *id = "???";
-
- if (TYPE_IS(CCH, goff))
- id = "CCH";
- else if (TYPE_IS(TGH, goff))
- id = "TGH";
- else if (TYPE_IS(TFH, goff))
- id = "TFH";
-
- panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id);
-}
-
-static int wait_instruction_complete(void *h, enum mcs_op opc)
-{
- int status;
- unsigned long start_time = get_cycles();
-
- while (1) {
- cpu_relax();
- status = GET_MSEG_HANDLE_STATUS(h);
- if (status != CCHSTATUS_ACTIVE)
- break;
- if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) {
- report_instruction_timeout(h);
- start_time = get_cycles();
- }
- }
- if (gru_options & OPT_STATS)
- update_mcs_stats(opc, get_cycles() - start_time);
- return status;
-}
-
-int cch_allocate(struct gru_context_configuration_handle *cch)
-{
- int ret;
-
- cch->opc = CCHOP_ALLOCATE;
- start_instruction(cch);
- ret = wait_instruction_complete(cch, cchop_allocate);
-
- /*
- * Stop speculation into the GSEG being mapped by the previous ALLOCATE.
- * The GSEG memory does not exist until the ALLOCATE completes.
- */
- sync_core();
- return ret;
-}
-
-int cch_start(struct gru_context_configuration_handle *cch)
-{
- cch->opc = CCHOP_START;
- start_instruction(cch);
- return wait_instruction_complete(cch, cchop_start);
-}
-
-int cch_interrupt(struct gru_context_configuration_handle *cch)
-{
- cch->opc = CCHOP_INTERRUPT;
- start_instruction(cch);
- return wait_instruction_complete(cch, cchop_interrupt);
-}
-
-int cch_deallocate(struct gru_context_configuration_handle *cch)
-{
- int ret;
-
- cch->opc = CCHOP_DEALLOCATE;
- start_instruction(cch);
- ret = wait_instruction_complete(cch, cchop_deallocate);
-
- /*
- * Stop speculation into the GSEG being unmapped by the previous
- * DEALLOCATE.
- */
- sync_core();
- return ret;
-}
-
-int cch_interrupt_sync(struct gru_context_configuration_handle
- *cch)
-{
- cch->opc = CCHOP_INTERRUPT_SYNC;
- start_instruction(cch);
- return wait_instruction_complete(cch, cchop_interrupt_sync);
-}
-
-int tgh_invalidate(struct gru_tlb_global_handle *tgh,
- unsigned long vaddr, unsigned long vaddrmask,
- int asid, int pagesize, int global, int n,
- unsigned short ctxbitmap)
-{
- tgh->vaddr = vaddr;
- tgh->asid = asid;
- tgh->pagesize = pagesize;
- tgh->n = n;
- tgh->global = global;
- tgh->vaddrmask = vaddrmask;
- tgh->ctxbitmap = ctxbitmap;
- tgh->opc = TGHOP_TLBINV;
- start_instruction(tgh);
- return wait_instruction_complete(tgh, tghop_invalidate);
-}
-
-int tfh_write_only(struct gru_tlb_fault_handle *tfh,
- unsigned long paddr, int gaa,
- unsigned long vaddr, int asid, int dirty,
- int pagesize)
-{
- tfh->fillasid = asid;
- tfh->fillvaddr = vaddr;
- tfh->pfn = paddr >> GRU_PADDR_SHIFT;
- tfh->gaa = gaa;
- tfh->dirty = dirty;
- tfh->pagesize = pagesize;
- tfh->opc = TFHOP_WRITE_ONLY;
- start_instruction(tfh);
- return wait_instruction_complete(tfh, tfhop_write_only);
-}
-
-void tfh_write_restart(struct gru_tlb_fault_handle *tfh,
- unsigned long paddr, int gaa,
- unsigned long vaddr, int asid, int dirty,
- int pagesize)
-{
- tfh->fillasid = asid;
- tfh->fillvaddr = vaddr;
- tfh->pfn = paddr >> GRU_PADDR_SHIFT;
- tfh->gaa = gaa;
- tfh->dirty = dirty;
- tfh->pagesize = pagesize;
- tfh->opc = TFHOP_WRITE_RESTART;
- start_instruction(tfh);
-}
-
-void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh)
-{
- tfh->opc = TFHOP_USER_POLLING_MODE;
- start_instruction(tfh);
-}
-
-void tfh_exception(struct gru_tlb_fault_handle *tfh)
-{
- tfh->opc = TFHOP_EXCEPTION;
- start_instruction(tfh);
-}
-
diff --git a/drivers/misc/sgi-gru/gruhandles.h b/drivers/misc/sgi-gru/gruhandles.h
deleted file mode 100644
index 5a498bf8d003..000000000000
--- a/drivers/misc/sgi-gru/gruhandles.h
+++ /dev/null
@@ -1,517 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * SN Platform GRU Driver
- *
- * GRU HANDLE DEFINITION
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#ifndef __GRUHANDLES_H__
-#define __GRUHANDLES_H__
-#include "gru_instructions.h"
-
-/*
- * Manifest constants for GRU Memory Map
- */
-#define GRU_GSEG0_BASE 0
-#define GRU_MCS_BASE (64 * 1024 * 1024)
-#define GRU_SIZE (128UL * 1024 * 1024)
-
-/* Handle & resource counts */
-#define GRU_NUM_CB 128
-#define GRU_NUM_DSR_BYTES (32 * 1024)
-#define GRU_NUM_TFM 16
-#define GRU_NUM_TGH 24
-#define GRU_NUM_CBE 128
-#define GRU_NUM_TFH 128
-#define GRU_NUM_CCH 16
-
-/* Maximum resource counts that can be reserved by user programs */
-#define GRU_NUM_USER_CBR GRU_NUM_CBE
-#define GRU_NUM_USER_DSR_BYTES GRU_NUM_DSR_BYTES
-
-/* Bytes per handle & handle stride. Code assumes all cb, tfh, cbe handles
- * are the same */
-#define GRU_HANDLE_BYTES 64
-#define GRU_HANDLE_STRIDE 256
-
-/* Base addresses of handles */
-#define GRU_TFM_BASE (GRU_MCS_BASE + 0x00000)
-#define GRU_TGH_BASE (GRU_MCS_BASE + 0x08000)
-#define GRU_CBE_BASE (GRU_MCS_BASE + 0x10000)
-#define GRU_TFH_BASE (GRU_MCS_BASE + 0x18000)
-#define GRU_CCH_BASE (GRU_MCS_BASE + 0x20000)
-
-/* User gseg constants */
-#define GRU_GSEG_STRIDE (4 * 1024 * 1024)
-#define GSEG_BASE(a) ((a) & ~(GRU_GSEG_PAGESIZE - 1))
-
-/* Data segment constants */
-#define GRU_DSR_AU_BYTES 1024
-#define GRU_DSR_CL (GRU_NUM_DSR_BYTES / GRU_CACHE_LINE_BYTES)
-#define GRU_DSR_AU_CL (GRU_DSR_AU_BYTES / GRU_CACHE_LINE_BYTES)
-#define GRU_DSR_AU (GRU_NUM_DSR_BYTES / GRU_DSR_AU_BYTES)
-
-/* Control block constants */
-#define GRU_CBR_AU_SIZE 2
-#define GRU_CBR_AU (GRU_NUM_CBE / GRU_CBR_AU_SIZE)
-
-/* Convert resource counts to the number of AU */
-#define GRU_DS_BYTES_TO_AU(n) DIV_ROUND_UP(n, GRU_DSR_AU_BYTES)
-#define GRU_CB_COUNT_TO_AU(n) DIV_ROUND_UP(n, GRU_CBR_AU_SIZE)
-
-/* UV limits */
-#define GRU_CHIPLETS_PER_HUB 2
-#define GRU_HUBS_PER_BLADE 1
-#define GRU_CHIPLETS_PER_BLADE (GRU_HUBS_PER_BLADE * GRU_CHIPLETS_PER_HUB)
-
-/* User GRU Gseg offsets */
-#define GRU_CB_BASE 0
-#define GRU_CB_LIMIT (GRU_CB_BASE + GRU_HANDLE_STRIDE * GRU_NUM_CBE)
-#define GRU_DS_BASE 0x20000
-#define GRU_DS_LIMIT (GRU_DS_BASE + GRU_NUM_DSR_BYTES)
-
-/* Convert a GRU physical address to the chiplet offset */
-#define GSEGPOFF(h) ((h) & (GRU_SIZE - 1))
-
-/* Convert an arbitrary handle address to the beginning of the GRU segment */
-#define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1)))
-
-/* Test a valid handle address to determine the type */
-#define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \
- GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \
- (((h) & (GRU_HANDLE_STRIDE - 1)) == 0))
-
-
-/* General addressing macros. */
-static inline void *get_gseg_base_address(void *base, int ctxnum)
-{
- return (void *)(base + GRU_GSEG0_BASE + GRU_GSEG_STRIDE * ctxnum);
-}
-
-static inline void *get_gseg_base_address_cb(void *base, int ctxnum, int line)
-{
- return (void *)(get_gseg_base_address(base, ctxnum) +
- GRU_CB_BASE + GRU_HANDLE_STRIDE * line);
-}
-
-static inline void *get_gseg_base_address_ds(void *base, int ctxnum, int line)
-{
- return (void *)(get_gseg_base_address(base, ctxnum) + GRU_DS_BASE +
- GRU_CACHE_LINE_BYTES * line);
-}
-
-static inline struct gru_tlb_fault_map *get_tfm(void *base, int ctxnum)
-{
- return (struct gru_tlb_fault_map *)(base + GRU_TFM_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_tlb_global_handle *get_tgh(void *base, int ctxnum)
-{
- return (struct gru_tlb_global_handle *)(base + GRU_TGH_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_control_block_extended *get_cbe(void *base, int ctxnum)
-{
- return (struct gru_control_block_extended *)(base + GRU_CBE_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_tlb_fault_handle *get_tfh(void *base, int ctxnum)
-{
- return (struct gru_tlb_fault_handle *)(base + GRU_TFH_BASE +
- ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline struct gru_context_configuration_handle *get_cch(void *base,
- int ctxnum)
-{
- return (struct gru_context_configuration_handle *)(base +
- GRU_CCH_BASE + ctxnum * GRU_HANDLE_STRIDE);
-}
-
-static inline unsigned long get_cb_number(void *cb)
-{
- return (((unsigned long)cb - GRU_CB_BASE) % GRU_GSEG_PAGESIZE) /
- GRU_HANDLE_STRIDE;
-}
-
-/* byte offset to a specific GRU chiplet. (p=pnode, c=chiplet (0 or 1)*/
-static inline unsigned long gru_chiplet_paddr(unsigned long paddr, int pnode,
- int chiplet)
-{
- return paddr + GRU_SIZE * (2 * pnode + chiplet);
-}
-
-static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet)
-{
- return vaddr + GRU_SIZE * (2 * pnode + chiplet);
-}
-
-static inline struct gru_control_block_extended *gru_tfh_to_cbe(
- struct gru_tlb_fault_handle *tfh)
-{
- unsigned long cbe;
-
- cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE;
- return (struct gru_control_block_extended*)cbe;
-}
-
-
-
-
-/*
- * Global TLB Fault Map
- * Bitmap of outstanding TLB misses needing interrupt/polling service.
- *
- */
-struct gru_tlb_fault_map {
- unsigned long fault_bits[BITS_TO_LONGS(GRU_NUM_CBE)];
- unsigned long fill0[2];
- unsigned long done_bits[BITS_TO_LONGS(GRU_NUM_CBE)];
- unsigned long fill1[2];
-};
-
-/*
- * TGH - TLB Global Handle
- * Used for TLB flushing.
- *
- */
-struct gru_tlb_global_handle {
- unsigned int cmd:1; /* DW 0 */
- unsigned int delresp:1;
- unsigned int opc:1;
- unsigned int fill1:5;
-
- unsigned int fill2:8;
-
- unsigned int status:2;
- unsigned long fill3:2;
- unsigned int state:3;
- unsigned long fill4:1;
-
- unsigned int cause:3;
- unsigned long fill5:37;
-
- unsigned long vaddr:64; /* DW 1 */
-
- unsigned int asid:24; /* DW 2 */
- unsigned int fill6:8;
-
- unsigned int pagesize:5;
- unsigned int fill7:11;
-
- unsigned int global:1;
- unsigned int fill8:15;
-
- unsigned long vaddrmask:39; /* DW 3 */
- unsigned int fill9:9;
- unsigned int n:10;
- unsigned int fill10:6;
-
- unsigned int ctxbitmap:16; /* DW4 */
- unsigned long fill11[3];
-};
-
-enum gru_tgh_cmd {
- TGHCMD_START
-};
-
-enum gru_tgh_opc {
- TGHOP_TLBNOP,
- TGHOP_TLBINV
-};
-
-enum gru_tgh_status {
- TGHSTATUS_IDLE,
- TGHSTATUS_EXCEPTION,
- TGHSTATUS_ACTIVE
-};
-
-enum gru_tgh_state {
- TGHSTATE_IDLE,
- TGHSTATE_PE_INVAL,
- TGHSTATE_INTERRUPT_INVAL,
- TGHSTATE_WAITDONE,
- TGHSTATE_RESTART_CTX,
-};
-
-enum gru_tgh_cause {
- TGHCAUSE_RR_ECC,
- TGHCAUSE_TLB_ECC,
- TGHCAUSE_LRU_ECC,
- TGHCAUSE_PS_ECC,
- TGHCAUSE_MUL_ERR,
- TGHCAUSE_DATA_ERR,
- TGHCAUSE_SW_FORCE
-};
-
-
-/*
- * TFH - TLB Global Handle
- * Used for TLB dropins into the GRU TLB.
- *
- */
-struct gru_tlb_fault_handle {
- unsigned int cmd:1; /* DW 0 - low 32*/
- unsigned int delresp:1;
- unsigned int fill0:2;
- unsigned int opc:3;
- unsigned int fill1:9;
-
- unsigned int status:2;
- unsigned int fill2:2;
- unsigned int state:3;
- unsigned int fill3:1;
-
- unsigned int cause:6;
- unsigned int cb_int:1;
- unsigned int fill4:1;
-
- unsigned int indexway:12; /* DW 0 - high 32 */
- unsigned int fill5:4;
-
- unsigned int ctxnum:4;
- unsigned int fill6:12;
-
- unsigned long missvaddr:64; /* DW 1 */
-
- unsigned int missasid:24; /* DW 2 */
- unsigned int fill7:8;
- unsigned int fillasid:24;
- unsigned int dirty:1;
- unsigned int gaa:2;
- unsigned long fill8:5;
-
- unsigned long pfn:41; /* DW 3 */
- unsigned int fill9:7;
- unsigned int pagesize:5;
- unsigned int fill10:11;
-
- unsigned long fillvaddr:64; /* DW 4 */
-
- unsigned long fill11[3];
-};
-
-enum gru_tfh_opc {
- TFHOP_NOOP,
- TFHOP_RESTART,
- TFHOP_WRITE_ONLY,
- TFHOP_WRITE_RESTART,
- TFHOP_EXCEPTION,
- TFHOP_USER_POLLING_MODE = 7,
-};
-
-enum tfh_status {
- TFHSTATUS_IDLE,
- TFHSTATUS_EXCEPTION,
- TFHSTATUS_ACTIVE,
-};
-
-enum tfh_state {
- TFHSTATE_INACTIVE,
- TFHSTATE_IDLE,
- TFHSTATE_MISS_UPM,
- TFHSTATE_MISS_FMM,
- TFHSTATE_HW_ERR,
- TFHSTATE_WRITE_TLB,
- TFHSTATE_RESTART_CBR,
-};
-
-/* TFH cause bits */
-enum tfh_cause {
- TFHCAUSE_NONE,
- TFHCAUSE_TLB_MISS,
- TFHCAUSE_TLB_MOD,
- TFHCAUSE_HW_ERROR_RR,
- TFHCAUSE_HW_ERROR_MAIN_ARRAY,
- TFHCAUSE_HW_ERROR_VALID,
- TFHCAUSE_HW_ERROR_PAGESIZE,
- TFHCAUSE_INSTRUCTION_EXCEPTION,
- TFHCAUSE_UNCORRECTIBLE_ERROR,
-};
-
-/* GAA values */
-#define GAA_RAM 0x0
-#define GAA_NCRAM 0x2
-#define GAA_MMIO 0x1
-#define GAA_REGISTER 0x3
-
-/* GRU paddr shift for pfn. (NOTE: shift is NOT by actual pagesize) */
-#define GRU_PADDR_SHIFT 12
-
-/*
- * Context Configuration handle
- * Used to allocate resources to a GSEG context.
- *
- */
-struct gru_context_configuration_handle {
- unsigned int cmd:1; /* DW0 */
- unsigned int delresp:1;
- unsigned int opc:3;
- unsigned int unmap_enable:1;
- unsigned int req_slice_set_enable:1;
- unsigned int req_slice:2;
- unsigned int cb_int_enable:1;
- unsigned int tlb_int_enable:1;
- unsigned int tfm_fault_bit_enable:1;
- unsigned int tlb_int_select:4;
-
- unsigned int status:2;
- unsigned int state:2;
- unsigned int reserved2:4;
-
- unsigned int cause:4;
- unsigned int tfm_done_bit_enable:1;
- unsigned int unused:3;
-
- unsigned int dsr_allocation_map;
-
- unsigned long cbr_allocation_map; /* DW1 */
-
- unsigned int asid[8]; /* DW 2 - 5 */
- unsigned short sizeavail[8]; /* DW 6 - 7 */
-} __attribute__ ((packed));
-
-enum gru_cch_opc {
- CCHOP_START = 1,
- CCHOP_ALLOCATE,
- CCHOP_INTERRUPT,
- CCHOP_DEALLOCATE,
- CCHOP_INTERRUPT_SYNC,
-};
-
-enum gru_cch_status {
- CCHSTATUS_IDLE,
- CCHSTATUS_EXCEPTION,
- CCHSTATUS_ACTIVE,
-};
-
-enum gru_cch_state {
- CCHSTATE_INACTIVE,
- CCHSTATE_MAPPED,
- CCHSTATE_ACTIVE,
- CCHSTATE_INTERRUPTED,
-};
-
-/* CCH Exception cause */
-enum gru_cch_cause {
- CCHCAUSE_REGION_REGISTER_WRITE_ERROR = 1,
- CCHCAUSE_ILLEGAL_OPCODE = 2,
- CCHCAUSE_INVALID_START_REQUEST = 3,
- CCHCAUSE_INVALID_ALLOCATION_REQUEST = 4,
- CCHCAUSE_INVALID_DEALLOCATION_REQUEST = 5,
- CCHCAUSE_INVALID_INTERRUPT_REQUEST = 6,
- CCHCAUSE_CCH_BUSY = 7,
- CCHCAUSE_NO_CBRS_TO_ALLOCATE = 8,
- CCHCAUSE_BAD_TFM_CONFIG = 9,
- CCHCAUSE_CBR_RESOURCES_OVERSUBSCRIPED = 10,
- CCHCAUSE_DSR_RESOURCES_OVERSUBSCRIPED = 11,
- CCHCAUSE_CBR_DEALLOCATION_ERROR = 12,
-};
-/*
- * CBE - Control Block Extended
- * Maintains internal GRU state for active CBs.
- *
- */
-struct gru_control_block_extended {
- unsigned int reserved0:1; /* DW 0 - low */
- unsigned int imacpy:3;
- unsigned int reserved1:4;
- unsigned int xtypecpy:3;
- unsigned int iaa0cpy:2;
- unsigned int iaa1cpy:2;
- unsigned int reserved2:1;
- unsigned int opccpy:8;
- unsigned int exopccpy:8;
-
- unsigned int idef2cpy:22; /* DW 0 - high */
- unsigned int reserved3:10;
-
- unsigned int idef4cpy:22; /* DW 1 */
- unsigned int reserved4:10;
- unsigned int idef4upd:22;
- unsigned int reserved5:10;
-
- unsigned long idef1upd:64; /* DW 2 */
-
- unsigned long idef5cpy:64; /* DW 3 */
-
- unsigned long idef6cpy:64; /* DW 4 */
-
- unsigned long idef3upd:64; /* DW 5 */
-
- unsigned long idef5upd:64; /* DW 6 */
-
- unsigned int idef2upd:22; /* DW 7 */
- unsigned int reserved6:10;
-
- unsigned int ecause:20;
- unsigned int cbrstate:4;
- unsigned int cbrexecstatus:8;
-};
-
-/* CBE fields for active BCOPY instructions */
-#define cbe_baddr0 idef1upd
-#define cbe_baddr1 idef3upd
-#define cbe_src_cl idef6cpy
-#define cbe_nelemcur idef5upd
-
-enum gru_cbr_state {
- CBRSTATE_INACTIVE,
- CBRSTATE_IDLE,
- CBRSTATE_PE_CHECK,
- CBRSTATE_QUEUED,
- CBRSTATE_WAIT_RESPONSE,
- CBRSTATE_INTERRUPTED,
- CBRSTATE_INTERRUPTED_MISS_FMM,
- CBRSTATE_BUSY_INTERRUPT_MISS_FMM,
- CBRSTATE_INTERRUPTED_MISS_UPM,
- CBRSTATE_BUSY_INTERRUPTED_MISS_UPM,
- CBRSTATE_REQUEST_ISSUE,
- CBRSTATE_BUSY_INTERRUPT,
-};
-
-/* CBE cbrexecstatus bits - defined in gru_instructions.h*/
-/* CBE ecause bits - defined in gru_instructions.h */
-
-/*
- * Convert a processor pagesize into the strange encoded pagesize used by the
- * GRU. Processor pagesize is encoded as log of bytes per page. (or PAGE_SHIFT)
- * pagesize log pagesize grupagesize
- * 4k 12 0
- * 16k 14 1
- * 64k 16 2
- * 256k 18 3
- * 1m 20 4
- * 2m 21 5
- * 4m 22 6
- * 16m 24 7
- * 64m 26 8
- * ...
- */
-#define GRU_PAGESIZE(sh) ((((sh) > 20 ? (sh) + 2 : (sh)) >> 1) - 6)
-#define GRU_SIZEAVAIL(sh) (1UL << GRU_PAGESIZE(sh))
-
-/* minimum TLB purge count to ensure a full purge */
-#define GRUMAXINVAL 1024UL
-
-int cch_allocate(struct gru_context_configuration_handle *cch);
-int cch_start(struct gru_context_configuration_handle *cch);
-int cch_interrupt(struct gru_context_configuration_handle *cch);
-int cch_deallocate(struct gru_context_configuration_handle *cch);
-int cch_interrupt_sync(struct gru_context_configuration_handle *cch);
-int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr,
- unsigned long vaddrmask, int asid, int pagesize, int global, int n,
- unsigned short ctxbitmap);
-int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
- int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
-void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
- int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
-void tfh_user_polling_mode(struct gru_tlb_fault_handle *tfh);
-void tfh_exception(struct gru_tlb_fault_handle *tfh);
-
-#endif /* __GRUHANDLES_H__ */
diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c
deleted file mode 100644
index 9869f4f2f476..000000000000
--- a/drivers/misc/sgi-gru/grukdump.c
+++ /dev/null
@@ -1,223 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * Dump GRU State
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-#include <asm/uv/uv_hub.h>
-
-#include <linux/nospec.h>
-
-#include "gru.h"
-#include "grutables.h"
-#include "gruhandles.h"
-#include "grulib.h"
-
-#define CCH_LOCK_ATTEMPTS 10
-
-static int gru_user_copy_handle(void __user **dp, void *s)
-{
- if (copy_to_user(*dp, s, GRU_HANDLE_BYTES))
- return -1;
- *dp += GRU_HANDLE_BYTES;
- return 0;
-}
-
-static int gru_dump_context_data(void *grubase,
- struct gru_context_configuration_handle *cch,
- void __user *ubuf, int ctxnum, int dsrcnt,
- int flush_cbrs)
-{
- void *cb, *cbe, *tfh, *gseg;
- int i, scr;
-
- gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
- cb = gseg + GRU_CB_BASE;
- cbe = grubase + GRU_CBE_BASE;
- tfh = grubase + GRU_TFH_BASE;
-
- for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) {
- if (flush_cbrs)
- gru_flush_cache(cb);
- if (gru_user_copy_handle(&ubuf, cb))
- goto fail;
- if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE))
- goto fail;
- if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE))
- goto fail;
- cb += GRU_HANDLE_STRIDE;
- }
- if (dsrcnt)
- memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE);
- return 0;
-
-fail:
- return -EFAULT;
-}
-
-static int gru_dump_tfm(struct gru_state *gru,
- void __user *ubuf, void __user *ubufend)
-{
- struct gru_tlb_fault_map *tfm;
- int i;
-
- if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
- return -EFBIG;
-
- for (i = 0; i < GRU_NUM_TFM; i++) {
- tfm = get_tfm(gru->gs_gru_base_vaddr, i);
- if (gru_user_copy_handle(&ubuf, tfm))
- goto fail;
- }
- return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES;
-
-fail:
- return -EFAULT;
-}
-
-static int gru_dump_tgh(struct gru_state *gru,
- void __user *ubuf, void __user *ubufend)
-{
- struct gru_tlb_global_handle *tgh;
- int i;
-
- if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
- return -EFBIG;
-
- for (i = 0; i < GRU_NUM_TGH; i++) {
- tgh = get_tgh(gru->gs_gru_base_vaddr, i);
- if (gru_user_copy_handle(&ubuf, tgh))
- goto fail;
- }
- return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES;
-
-fail:
- return -EFAULT;
-}
-
-static int gru_dump_context(struct gru_state *gru, int ctxnum,
- void __user *ubuf, void __user *ubufend, char data_opt,
- char lock_cch, char flush_cbrs)
-{
- struct gru_dump_context_header hdr;
- struct gru_dump_context_header __user *uhdr = ubuf;
- struct gru_context_configuration_handle *cch, *ubufcch;
- struct gru_thread_state *gts;
- int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0;
- void *grubase;
-
- memset(&hdr, 0, sizeof(hdr));
- grubase = gru->gs_gru_base_vaddr;
- cch = get_cch(grubase, ctxnum);
- for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) {
- cch_locked = trylock_cch_handle(cch);
- if (cch_locked)
- break;
- msleep(1);
- }
-
- ubuf += sizeof(hdr);
- ubufcch = ubuf;
- if (gru_user_copy_handle(&ubuf, cch)) {
- if (cch_locked)
- unlock_cch_handle(cch);
- return -EFAULT;
- }
- if (cch_locked)
- ubufcch->delresp = 0;
- bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES;
-
- if (cch_locked || !lock_cch) {
- gts = gru->gs_gts[ctxnum];
- if (gts && gts->ts_vma) {
- hdr.pid = gts->ts_tgid_owner;
- hdr.vaddr = gts->ts_vma->vm_start;
- }
- if (cch->state != CCHSTATE_INACTIVE) {
- cbrcnt = hweight64(cch->cbr_allocation_map) *
- GRU_CBR_AU_SIZE;
- dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) *
- GRU_DSR_AU_CL : 0;
- }
- bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES;
- if (bytes > ubufend - ubuf)
- ret = -EFBIG;
- else
- ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum,
- dsrcnt, flush_cbrs);
- }
- if (cch_locked)
- unlock_cch_handle(cch);
- if (ret)
- return ret;
-
- hdr.magic = GRU_DUMP_MAGIC;
- hdr.gid = gru->gs_gid;
- hdr.ctxnum = ctxnum;
- hdr.cbrcnt = cbrcnt;
- hdr.dsrcnt = dsrcnt;
- hdr.cch_locked = cch_locked;
- if (copy_to_user(uhdr, &hdr, sizeof(hdr)))
- return -EFAULT;
-
- return bytes;
-}
-
-int gru_dump_chiplet_request(unsigned long arg)
-{
- struct gru_state *gru;
- struct gru_dump_chiplet_state_req req;
- void __user *ubuf;
- void __user *ubufend;
- int ctxnum, ret, cnt = 0;
-
- if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
- return -EFAULT;
-
- /* Currently, only dump by gid is implemented */
- if (req.gid >= gru_max_gids)
- return -EINVAL;
- req.gid = array_index_nospec(req.gid, gru_max_gids);
-
- gru = GID_TO_GRU(req.gid);
- ubuf = req.buf;
- ubufend = req.buf + req.buflen;
-
- ret = gru_dump_tfm(gru, ubuf, ubufend);
- if (ret < 0)
- goto fail;
- ubuf += ret;
-
- ret = gru_dump_tgh(gru, ubuf, ubufend);
- if (ret < 0)
- goto fail;
- ubuf += ret;
-
- for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
- if (req.ctxnum == ctxnum || req.ctxnum < 0) {
- ret = gru_dump_context(gru, ctxnum, ubuf, ubufend,
- req.data_opt, req.lock_cch,
- req.flush_cbrs);
- if (ret < 0)
- goto fail;
- ubuf += ret;
- cnt++;
- }
- }
-
- if (copy_to_user((void __user *)arg, &req, sizeof(req)))
- return -EFAULT;
- return cnt;
-
-fail:
- return ret;
-}
diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c
deleted file mode 100644
index 205945ce9e86..000000000000
--- a/drivers/misc/sgi-gru/grukservices.c
+++ /dev/null
@@ -1,1157 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * KERNEL SERVICES THAT USE THE GRU
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/miscdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
-#include <linux/sync_core.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <asm/io_apic.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-#include "grukservices.h"
-#include "gru_instructions.h"
-#include <asm/uv/uv_hub.h>
-
-/*
- * Kernel GRU Usage
- *
- * The following is an interim algorithm for management of kernel GRU
- * resources. This will likely be replaced when we better understand the
- * kernel/user requirements.
- *
- * Blade percpu resources reserved for kernel use. These resources are
- * reserved whenever the kernel context for the blade is loaded. Note
- * that the kernel context is not guaranteed to be always available. It is
- * loaded on demand & can be stolen by a user if the user demand exceeds the
- * kernel demand. The kernel can always reload the kernel context but
- * a SLEEP may be required!!!.
- *
- * Async Overview:
- *
- * Each blade has one "kernel context" that owns GRU kernel resources
- * located on the blade. Kernel drivers use GRU resources in this context
- * for sending messages, zeroing memory, etc.
- *
- * The kernel context is dynamically loaded on demand. If it is not in
- * use by the kernel, the kernel context can be unloaded & given to a user.
- * The kernel context will be reloaded when needed. This may require that
- * a context be stolen from a user.
- * NOTE: frequent unloading/reloading of the kernel context is
- * expensive. We are depending on batch schedulers, cpusets, sane
- * drivers or some other mechanism to prevent the need for frequent
- * stealing/reloading.
- *
- * The kernel context consists of two parts:
- * - 1 CB & a few DSRs that are reserved for each cpu on the blade.
- * Each cpu has it's own private resources & does not share them
- * with other cpus. These resources are used serially, ie,
- * locked, used & unlocked on each call to a function in
- * grukservices.
- * (Now that we have dynamic loading of kernel contexts, I
- * may rethink this & allow sharing between cpus....)
- *
- * - Additional resources can be reserved long term & used directly
- * by UV drivers located in the kernel. Drivers using these GRU
- * resources can use asynchronous GRU instructions that send
- * interrupts on completion.
- * - these resources must be explicitly locked/unlocked
- * - locked resources prevent (obviously) the kernel
- * context from being unloaded.
- * - drivers using these resource directly issue their own
- * GRU instruction and must wait/check completion.
- *
- * When these resources are reserved, the caller can optionally
- * associate a wait_queue with the resources and use asynchronous
- * GRU instructions. When an async GRU instruction completes, the
- * driver will do a wakeup on the event.
- *
- */
-
-
-#define ASYNC_HAN_TO_BID(h) ((h) - 1)
-#define ASYNC_BID_TO_HAN(b) ((b) + 1)
-#define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)]
-
-#define GRU_NUM_KERNEL_CBR 1
-#define GRU_NUM_KERNEL_DSR_BYTES 256
-#define GRU_NUM_KERNEL_DSR_CL (GRU_NUM_KERNEL_DSR_BYTES / \
- GRU_CACHE_LINE_BYTES)
-
-/* GRU instruction attributes for all instructions */
-#define IMA IMA_CB_DELAY
-
-/* GRU cacheline size is always 64 bytes - even on arches with 128 byte lines */
-#define __gru_cacheline_aligned__ \
- __attribute__((__aligned__(GRU_CACHE_LINE_BYTES)))
-
-#define MAGIC 0x1234567887654321UL
-
-/* Default retry count for GRU errors on kernel instructions */
-#define EXCEPTION_RETRY_LIMIT 3
-
-/* Status of message queue sections */
-#define MQS_EMPTY 0
-#define MQS_FULL 1
-#define MQS_NOOP 2
-
-/*----------------- RESOURCE MANAGEMENT -------------------------------------*/
-/* optimized for x86_64 */
-struct message_queue {
- union gru_mesqhead head __gru_cacheline_aligned__; /* CL 0 */
- int qlines; /* DW 1 */
- long hstatus[2];
- void *next __gru_cacheline_aligned__;/* CL 1 */
- void *limit;
- void *start;
- void *start2;
- char data ____cacheline_aligned; /* CL 2 */
-};
-
-/* First word in every message - used by mesq interface */
-struct message_header {
- char present;
- char present2;
- char lines;
- char fill;
-};
-
-#define HSTATUS(mq, h) ((mq) + offsetof(struct message_queue, hstatus[h]))
-
-/*
- * Reload the blade's kernel context into a GRU chiplet. Called holding
- * the bs_kgts_sema for READ. Will steal user contexts if necessary.
- */
-static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id)
-{
- struct gru_state *gru;
- struct gru_thread_state *kgts;
- void *vaddr;
- int ctxnum, ncpus;
-
- up_read(&bs->bs_kgts_sema);
- down_write(&bs->bs_kgts_sema);
-
- if (!bs->bs_kgts) {
- do {
- bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0);
- if (!IS_ERR(bs->bs_kgts))
- break;
- msleep(1);
- } while (true);
- bs->bs_kgts->ts_user_blade_id = blade_id;
- }
- kgts = bs->bs_kgts;
-
- if (!kgts->ts_gru) {
- STAT(load_kernel_context);
- ncpus = uv_blade_nr_possible_cpus(blade_id);
- kgts->ts_cbr_au_count = GRU_CB_COUNT_TO_AU(
- GRU_NUM_KERNEL_CBR * ncpus + bs->bs_async_cbrs);
- kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU(
- GRU_NUM_KERNEL_DSR_BYTES * ncpus +
- bs->bs_async_dsr_bytes);
- while (!gru_assign_gru_context(kgts)) {
- msleep(1);
- gru_steal_context(kgts);
- }
- gru_load_context(kgts);
- gru = bs->bs_kgts->ts_gru;
- vaddr = gru->gs_gru_base_vaddr;
- ctxnum = kgts->ts_ctxnum;
- bs->kernel_cb = get_gseg_base_address_cb(vaddr, ctxnum, 0);
- bs->kernel_dsr = get_gseg_base_address_ds(vaddr, ctxnum, 0);
- }
- downgrade_write(&bs->bs_kgts_sema);
-}
-
-/*
- * Free all kernel contexts that are not currently in use.
- * Returns 0 if all freed, else number of inuse context.
- */
-static int gru_free_kernel_contexts(void)
-{
- struct gru_blade_state *bs;
- struct gru_thread_state *kgts;
- int bid, ret = 0;
-
- for (bid = 0; bid < GRU_MAX_BLADES; bid++) {
- bs = gru_base[bid];
- if (!bs)
- continue;
-
- /* Ignore busy contexts. Don't want to block here. */
- if (down_write_trylock(&bs->bs_kgts_sema)) {
- kgts = bs->bs_kgts;
- if (kgts && kgts->ts_gru)
- gru_unload_context(kgts, 0);
- bs->bs_kgts = NULL;
- up_write(&bs->bs_kgts_sema);
- kfree(kgts);
- } else {
- ret++;
- }
- }
- return ret;
-}
-
-/*
- * Lock & load the kernel context for the specified blade.
- */
-static struct gru_blade_state *gru_lock_kernel_context(int blade_id)
-{
- struct gru_blade_state *bs;
- int bid;
-
- STAT(lock_kernel_context);
-again:
- bid = blade_id < 0 ? uv_numa_blade_id() : blade_id;
- bs = gru_base[bid];
-
- /* Handle the case where migration occurred while waiting for the sema */
- down_read(&bs->bs_kgts_sema);
- if (blade_id < 0 && bid != uv_numa_blade_id()) {
- up_read(&bs->bs_kgts_sema);
- goto again;
- }
- if (!bs->bs_kgts || !bs->bs_kgts->ts_gru)
- gru_load_kernel_context(bs, bid);
- return bs;
-
-}
-
-/*
- * Unlock the kernel context for the specified blade. Context is not
- * unloaded but may be stolen before next use.
- */
-static void gru_unlock_kernel_context(int blade_id)
-{
- struct gru_blade_state *bs;
-
- bs = gru_base[blade_id];
- up_read(&bs->bs_kgts_sema);
- STAT(unlock_kernel_context);
-}
-
-/*
- * Reserve & get pointers to the DSR/CBRs reserved for the current cpu.
- * - returns with preemption disabled
- */
-static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr)
-{
- struct gru_blade_state *bs;
- int lcpu;
-
- BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES);
- bs = gru_lock_kernel_context(-1);
- lcpu = uv_blade_processor_id();
- *cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE;
- *dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES;
- return 0;
-}
-
-/*
- * Free the current cpus reserved DSR/CBR resources.
- */
-static void gru_free_cpu_resources(void *cb, void *dsr)
-{
- gru_unlock_kernel_context(uv_numa_blade_id());
-}
-
-/*
- * Reserve GRU resources to be used asynchronously.
- * Note: currently supports only 1 reservation per blade.
- *
- * input:
- * blade_id - blade on which resources should be reserved
- * cbrs - number of CBRs
- * dsr_bytes - number of DSR bytes needed
- * output:
- * handle to identify resource
- * (0 = async resources already reserved)
- */
-unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes,
- struct completion *cmp)
-{
- struct gru_blade_state *bs;
- struct gru_thread_state *kgts;
- int ret = 0;
-
- bs = gru_base[blade_id];
-
- down_write(&bs->bs_kgts_sema);
-
- /* Verify no resources already reserved */
- if (bs->bs_async_dsr_bytes + bs->bs_async_cbrs)
- goto done;
- bs->bs_async_dsr_bytes = dsr_bytes;
- bs->bs_async_cbrs = cbrs;
- bs->bs_async_wq = cmp;
- kgts = bs->bs_kgts;
-
- /* Resources changed. Unload context if already loaded */
- if (kgts && kgts->ts_gru)
- gru_unload_context(kgts, 0);
- ret = ASYNC_BID_TO_HAN(blade_id);
-
-done:
- up_write(&bs->bs_kgts_sema);
- return ret;
-}
-
-/*
- * Release async resources previously reserved.
- *
- * input:
- * han - handle to identify resources
- */
-void gru_release_async_resources(unsigned long han)
-{
- struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
-
- down_write(&bs->bs_kgts_sema);
- bs->bs_async_dsr_bytes = 0;
- bs->bs_async_cbrs = 0;
- bs->bs_async_wq = NULL;
- up_write(&bs->bs_kgts_sema);
-}
-
-/*
- * Wait for async GRU instructions to complete.
- *
- * input:
- * han - handle to identify resources
- */
-void gru_wait_async_cbr(unsigned long han)
-{
- struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
-
- wait_for_completion(bs->bs_async_wq);
- mb();
-}
-
-/*
- * Lock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- * output:
- * cb - pointer to first CBR
- * dsr - pointer to first DSR
- */
-void gru_lock_async_resource(unsigned long han, void **cb, void **dsr)
-{
- struct gru_blade_state *bs = ASYNC_HAN_TO_BS(han);
- int blade_id = ASYNC_HAN_TO_BID(han);
- int ncpus;
-
- gru_lock_kernel_context(blade_id);
- ncpus = uv_blade_nr_possible_cpus(blade_id);
- if (cb)
- *cb = bs->kernel_cb + ncpus * GRU_HANDLE_STRIDE;
- if (dsr)
- *dsr = bs->kernel_dsr + ncpus * GRU_NUM_KERNEL_DSR_BYTES;
-}
-
-/*
- * Unlock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- */
-void gru_unlock_async_resource(unsigned long han)
-{
- int blade_id = ASYNC_HAN_TO_BID(han);
-
- gru_unlock_kernel_context(blade_id);
-}
-
-/*----------------------------------------------------------------------*/
-int gru_get_cb_exception_detail(void *cb,
- struct control_block_extended_exc_detail *excdet)
-{
- struct gru_control_block_extended *cbe;
- struct gru_thread_state *kgts = NULL;
- unsigned long off;
- int cbrnum, bid;
-
- /*
- * Locate kgts for cb. This algorithm is SLOW but
- * this function is rarely called (ie., almost never).
- * Performance does not matter.
- */
- for_each_possible_blade(bid) {
- if (!gru_base[bid])
- break;
- kgts = gru_base[bid]->bs_kgts;
- if (!kgts || !kgts->ts_gru)
- continue;
- off = cb - kgts->ts_gru->gs_gru_base_vaddr;
- if (off < GRU_SIZE)
- break;
- kgts = NULL;
- }
- BUG_ON(!kgts);
- cbrnum = thread_cbr_number(kgts, get_cb_number(cb));
- cbe = get_cbe(GRUBASE(cb), cbrnum);
- gru_flush_cache(cbe); /* CBE not coherent */
- sync_core();
- excdet->opc = cbe->opccpy;
- excdet->exopc = cbe->exopccpy;
- excdet->ecause = cbe->ecause;
- excdet->exceptdet0 = cbe->idef1upd;
- excdet->exceptdet1 = cbe->idef3upd;
- gru_flush_cache(cbe);
- return 0;
-}
-
-static char *gru_get_cb_exception_detail_str(int ret, void *cb,
- char *buf, int size)
-{
- struct gru_control_block_status *gen = cb;
- struct control_block_extended_exc_detail excdet;
-
- if (ret > 0 && gen->istatus == CBS_EXCEPTION) {
- gru_get_cb_exception_detail(cb, &excdet);
- snprintf(buf, size,
- "GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x,"
- "excdet0 0x%lx, excdet1 0x%x", smp_processor_id(),
- gen, excdet.opc, excdet.exopc, excdet.ecause,
- excdet.exceptdet0, excdet.exceptdet1);
- } else {
- snprintf(buf, size, "No exception");
- }
- return buf;
-}
-
-static int gru_wait_idle_or_exception(struct gru_control_block_status *gen)
-{
- while (gen->istatus >= CBS_ACTIVE) {
- cpu_relax();
- barrier();
- }
- return gen->istatus;
-}
-
-static int gru_retry_exception(void *cb)
-{
- struct gru_control_block_status *gen = cb;
- struct control_block_extended_exc_detail excdet;
- int retry = EXCEPTION_RETRY_LIMIT;
-
- while (1) {
- if (gru_wait_idle_or_exception(gen) == CBS_IDLE)
- return CBS_IDLE;
- if (gru_get_cb_message_queue_substatus(cb))
- return CBS_EXCEPTION;
- gru_get_cb_exception_detail(cb, &excdet);
- if ((excdet.ecause & ~EXCEPTION_RETRY_BITS) ||
- (excdet.cbrexecstatus & CBR_EXS_ABORT_OCC))
- break;
- if (retry-- == 0)
- break;
- gen->icmd = 1;
- gru_flush_cache(gen);
- }
- return CBS_EXCEPTION;
-}
-
-int gru_check_status_proc(void *cb)
-{
- struct gru_control_block_status *gen = cb;
- int ret;
-
- ret = gen->istatus;
- if (ret == CBS_EXCEPTION)
- ret = gru_retry_exception(cb);
- rmb();
- return ret;
-
-}
-
-int gru_wait_proc(void *cb)
-{
- struct gru_control_block_status *gen = cb;
- int ret;
-
- ret = gru_wait_idle_or_exception(gen);
- if (ret == CBS_EXCEPTION)
- ret = gru_retry_exception(cb);
- rmb();
- return ret;
-}
-
-static void gru_abort(int ret, void *cb, char *str)
-{
- char buf[GRU_EXC_STR_SIZE];
-
- panic("GRU FATAL ERROR: %s - %s\n", str,
- gru_get_cb_exception_detail_str(ret, cb, buf, sizeof(buf)));
-}
-
-void gru_wait_abort_proc(void *cb)
-{
- int ret;
-
- ret = gru_wait_proc(cb);
- if (ret)
- gru_abort(ret, cb, "gru_wait_abort");
-}
-
-
-/*------------------------------ MESSAGE QUEUES -----------------------------*/
-
-/* Internal status . These are NOT returned to the user. */
-#define MQIE_AGAIN -1 /* try again */
-
-
-/*
- * Save/restore the "present" flag that is in the second line of 2-line
- * messages
- */
-static inline int get_present2(void *p)
-{
- struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES;
- return mhdr->present;
-}
-
-static inline void restore_present2(void *p, int val)
-{
- struct message_header *mhdr = p + GRU_CACHE_LINE_BYTES;
- mhdr->present = val;
-}
-
-/*
- * Create a message queue.
- * qlines - message queue size in cache lines. Includes 2-line header.
- */
-int gru_create_message_queue(struct gru_message_queue_desc *mqd,
- void *p, unsigned int bytes, int nasid, int vector, int apicid)
-{
- struct message_queue *mq = p;
- unsigned int qlines;
-
- qlines = bytes / GRU_CACHE_LINE_BYTES - 2;
- memset(mq, 0, bytes);
- mq->start = &mq->data;
- mq->start2 = &mq->data + (qlines / 2 - 1) * GRU_CACHE_LINE_BYTES;
- mq->next = &mq->data;
- mq->limit = &mq->data + (qlines - 2) * GRU_CACHE_LINE_BYTES;
- mq->qlines = qlines;
- mq->hstatus[0] = 0;
- mq->hstatus[1] = 1;
- mq->head = gru_mesq_head(2, qlines / 2 + 1);
- mqd->mq = mq;
- mqd->mq_gpa = uv_gpa(mq);
- mqd->qlines = qlines;
- mqd->interrupt_pnode = nasid >> 1;
- mqd->interrupt_vector = vector;
- mqd->interrupt_apicid = apicid;
- return 0;
-}
-EXPORT_SYMBOL_GPL(gru_create_message_queue);
-
-/*
- * Send a NOOP message to a message queue
- * Returns:
- * 0 - if queue is full after the send. This is the normal case
- * but various races can change this.
- * -1 - if mesq sent successfully but queue not full
- * >0 - unexpected error. MQE_xxx returned
- */
-static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg)
-{
- const struct message_header noop_header = {
- .present = MQS_NOOP, .lines = 1};
- unsigned long m;
- int substatus, ret;
- struct message_header save_mhdr, *mhdr = mesg;
-
- STAT(mesq_noop);
- save_mhdr = *mhdr;
- *mhdr = noop_header;
- gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), 1, IMA);
- ret = gru_wait(cb);
-
- if (ret) {
- substatus = gru_get_cb_message_queue_substatus(cb);
- switch (substatus) {
- case CBSS_NO_ERROR:
- STAT(mesq_noop_unexpected_error);
- ret = MQE_UNEXPECTED_CB_ERR;
- break;
- case CBSS_LB_OVERFLOWED:
- STAT(mesq_noop_lb_overflow);
- ret = MQE_CONGESTION;
- break;
- case CBSS_QLIMIT_REACHED:
- STAT(mesq_noop_qlimit_reached);
- ret = 0;
- break;
- case CBSS_AMO_NACKED:
- STAT(mesq_noop_amo_nacked);
- ret = MQE_CONGESTION;
- break;
- case CBSS_PUT_NACKED:
- STAT(mesq_noop_put_nacked);
- m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
- gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, 1, 1,
- IMA);
- if (gru_wait(cb) == CBS_IDLE)
- ret = MQIE_AGAIN;
- else
- ret = MQE_UNEXPECTED_CB_ERR;
- break;
- case CBSS_PAGE_OVERFLOW:
- STAT(mesq_noop_page_overflow);
- fallthrough;
- default:
- BUG();
- }
- }
- *mhdr = save_mhdr;
- return ret;
-}
-
-/*
- * Handle a gru_mesq full.
- */
-static int send_message_queue_full(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg, int lines)
-{
- union gru_mesqhead mqh;
- unsigned int limit, head;
- unsigned long avalue;
- int half, qlines;
-
- /* Determine if switching to first/second half of q */
- avalue = gru_get_amo_value(cb);
- head = gru_get_amo_value_head(cb);
- limit = gru_get_amo_value_limit(cb);
-
- qlines = mqd->qlines;
- half = (limit != qlines);
-
- if (half)
- mqh = gru_mesq_head(qlines / 2 + 1, qlines);
- else
- mqh = gru_mesq_head(2, qlines / 2 + 1);
-
- /* Try to get lock for switching head pointer */
- gru_gamir(cb, EOP_IR_CLR, HSTATUS(mqd->mq_gpa, half), XTYPE_DW, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
- if (!gru_get_amo_value(cb)) {
- STAT(mesq_qf_locked);
- return MQE_QUEUE_FULL;
- }
-
- /* Got the lock. Send optional NOP if queue not full, */
- if (head != limit) {
- if (send_noop_message(cb, mqd, mesg)) {
- gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half),
- XTYPE_DW, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
- STAT(mesq_qf_noop_not_full);
- return MQIE_AGAIN;
- }
- avalue++;
- }
-
- /* Then flip queuehead to other half of queue. */
- gru_gamer(cb, EOP_ERR_CSWAP, mqd->mq_gpa, XTYPE_DW, mqh.val, avalue,
- IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
-
- /* If not successfully in swapping queue head, clear the hstatus lock */
- if (gru_get_amo_value(cb) != avalue) {
- STAT(mesq_qf_switch_head_failed);
- gru_gamir(cb, EOP_IR_INC, HSTATUS(mqd->mq_gpa, half), XTYPE_DW,
- IMA);
- if (gru_wait(cb) != CBS_IDLE)
- goto cberr;
- }
- return MQIE_AGAIN;
-cberr:
- STAT(mesq_qf_unexpected_error);
- return MQE_UNEXPECTED_CB_ERR;
-}
-
-/*
- * Handle a PUT failure. Note: if message was a 2-line message, one of the
- * lines might have successfully have been written. Before sending the
- * message, "present" must be cleared in BOTH lines to prevent the receiver
- * from prematurely seeing the full message.
- */
-static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg, int lines)
-{
- unsigned long m;
- int ret, loops = 200; /* experimentally determined */
-
- m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
- if (lines == 2) {
- gru_vset(cb, m, 0, XTYPE_CL, lines, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- return MQE_UNEXPECTED_CB_ERR;
- }
- gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE)
- return MQE_UNEXPECTED_CB_ERR;
-
- if (!mqd->interrupt_vector)
- return MQE_OK;
-
- /*
- * Send a noop message in order to deliver a cross-partition interrupt
- * to the SSI that contains the target message queue. Normally, the
- * interrupt is automatically delivered by hardware following mesq
- * operations, but some error conditions require explicit delivery.
- * The noop message will trigger delivery. Otherwise partition failures
- * could cause unrecovered errors.
- */
- do {
- ret = send_noop_message(cb, mqd, mesg);
- } while ((ret == MQIE_AGAIN || ret == MQE_CONGESTION) && (loops-- > 0));
-
- if (ret == MQIE_AGAIN || ret == MQE_CONGESTION) {
- /*
- * Don't indicate to the app to resend the message, as it's
- * already been successfully sent. We simply send an OK
- * (rather than fail the send with MQE_UNEXPECTED_CB_ERR),
- * assuming that the other side is receiving enough
- * interrupts to get this message processed anyway.
- */
- ret = MQE_OK;
- }
- return ret;
-}
-
-/*
- * Handle a gru_mesq failure. Some of these failures are software recoverable
- * or retryable.
- */
-static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd,
- void *mesg, int lines)
-{
- int substatus, ret = 0;
-
- substatus = gru_get_cb_message_queue_substatus(cb);
- switch (substatus) {
- case CBSS_NO_ERROR:
- STAT(mesq_send_unexpected_error);
- ret = MQE_UNEXPECTED_CB_ERR;
- break;
- case CBSS_LB_OVERFLOWED:
- STAT(mesq_send_lb_overflow);
- ret = MQE_CONGESTION;
- break;
- case CBSS_QLIMIT_REACHED:
- STAT(mesq_send_qlimit_reached);
- ret = send_message_queue_full(cb, mqd, mesg, lines);
- break;
- case CBSS_AMO_NACKED:
- STAT(mesq_send_amo_nacked);
- ret = MQE_CONGESTION;
- break;
- case CBSS_PUT_NACKED:
- STAT(mesq_send_put_nacked);
- ret = send_message_put_nacked(cb, mqd, mesg, lines);
- break;
- case CBSS_PAGE_OVERFLOW:
- STAT(mesq_page_overflow);
- fallthrough;
- default:
- BUG();
- }
- return ret;
-}
-
-/*
- * Send a message to a message queue
- * mqd message queue descriptor
- * mesg message. ust be vaddr within a GSEG
- * bytes message size (<= 2 CL)
- */
-int gru_send_message_gpa(struct gru_message_queue_desc *mqd, void *mesg,
- unsigned int bytes)
-{
- struct message_header *mhdr;
- void *cb;
- void *dsr;
- int istatus, clines, ret;
-
- STAT(mesq_send);
- BUG_ON(bytes < sizeof(int) || bytes > 2 * GRU_CACHE_LINE_BYTES);
-
- clines = DIV_ROUND_UP(bytes, GRU_CACHE_LINE_BYTES);
- if (gru_get_cpu_resources(bytes, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- memcpy(dsr, mesg, bytes);
- mhdr = dsr;
- mhdr->present = MQS_FULL;
- mhdr->lines = clines;
- if (clines == 2) {
- mhdr->present2 = get_present2(mhdr);
- restore_present2(mhdr, MQS_FULL);
- }
-
- do {
- ret = MQE_OK;
- gru_mesq(cb, mqd->mq_gpa, gru_get_tri(mhdr), clines, IMA);
- istatus = gru_wait(cb);
- if (istatus != CBS_IDLE)
- ret = send_message_failure(cb, mqd, dsr, clines);
- } while (ret == MQIE_AGAIN);
- gru_free_cpu_resources(cb, dsr);
-
- if (ret)
- STAT(mesq_send_failed);
- return ret;
-}
-EXPORT_SYMBOL_GPL(gru_send_message_gpa);
-
-/*
- * Advance the receive pointer for the queue to the next message.
- */
-void gru_free_message(struct gru_message_queue_desc *mqd, void *mesg)
-{
- struct message_queue *mq = mqd->mq;
- struct message_header *mhdr = mq->next;
- void *next, *pnext;
- int half = -1;
- int lines = mhdr->lines;
-
- if (lines == 2)
- restore_present2(mhdr, MQS_EMPTY);
- mhdr->present = MQS_EMPTY;
-
- pnext = mq->next;
- next = pnext + GRU_CACHE_LINE_BYTES * lines;
- if (next == mq->limit) {
- next = mq->start;
- half = 1;
- } else if (pnext < mq->start2 && next >= mq->start2) {
- half = 0;
- }
-
- if (half >= 0)
- mq->hstatus[half] = 1;
- mq->next = next;
-}
-EXPORT_SYMBOL_GPL(gru_free_message);
-
-/*
- * Get next message from message queue. Return NULL if no message
- * present. User must call next_message() to move to next message.
- * rmq message queue
- */
-void *gru_get_next_message(struct gru_message_queue_desc *mqd)
-{
- struct message_queue *mq = mqd->mq;
- struct message_header *mhdr = mq->next;
- int present = mhdr->present;
-
- /* skip NOOP messages */
- while (present == MQS_NOOP) {
- gru_free_message(mqd, mhdr);
- mhdr = mq->next;
- present = mhdr->present;
- }
-
- /* Wait for both halves of 2 line messages */
- if (present == MQS_FULL && mhdr->lines == 2 &&
- get_present2(mhdr) == MQS_EMPTY)
- present = MQS_EMPTY;
-
- if (!present) {
- STAT(mesq_receive_none);
- return NULL;
- }
-
- if (mhdr->lines == 2)
- restore_present2(mhdr, mhdr->present2);
-
- STAT(mesq_receive);
- return mhdr;
-}
-EXPORT_SYMBOL_GPL(gru_get_next_message);
-
-/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/
-
-/*
- * Load a DW from a global GPA. The GPA can be a memory or MMR address.
- */
-int gru_read_gpa(unsigned long *value, unsigned long gpa)
-{
- void *cb;
- void *dsr;
- int ret, iaa;
-
- STAT(read_gpa);
- if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- iaa = gpa >> 62;
- gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA);
- ret = gru_wait(cb);
- if (ret == CBS_IDLE)
- *value = *(unsigned long *)dsr;
- gru_free_cpu_resources(cb, dsr);
- return ret;
-}
-EXPORT_SYMBOL_GPL(gru_read_gpa);
-
-
-/*
- * Copy a block of data using the GRU resources
- */
-int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa,
- unsigned int bytes)
-{
- void *cb;
- void *dsr;
- int ret;
-
- STAT(copy_gpa);
- if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- gru_bcopy(cb, src_gpa, dest_gpa, gru_get_tri(dsr),
- XTYPE_B, bytes, GRU_NUM_KERNEL_DSR_CL, IMA);
- ret = gru_wait(cb);
- gru_free_cpu_resources(cb, dsr);
- return ret;
-}
-EXPORT_SYMBOL_GPL(gru_copy_gpa);
-
-/* ------------------- KERNEL QUICKTESTS RUN AT STARTUP ----------------*/
-/* Temp - will delete after we gain confidence in the GRU */
-
-static int quicktest0(unsigned long arg)
-{
- unsigned long word0;
- unsigned long word1;
- void *cb;
- void *dsr;
- unsigned long *p;
- int ret = -EIO;
-
- if (gru_get_cpu_resources(GRU_CACHE_LINE_BYTES, &cb, &dsr))
- return MQE_BUG_NO_RESOURCES;
- p = dsr;
- word0 = MAGIC;
- word1 = 0;
-
- gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE) {
- printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id());
- goto done;
- }
-
- if (*p != MAGIC) {
- printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p);
- goto done;
- }
- gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
- if (gru_wait(cb) != CBS_IDLE) {
- printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id());
- goto done;
- }
-
- if (word0 != word1 || word1 != MAGIC) {
- printk(KERN_DEBUG
- "GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n",
- smp_processor_id(), word1, MAGIC);
- goto done;
- }
- ret = 0;
-
-done:
- gru_free_cpu_resources(cb, dsr);
- return ret;
-}
-
-#define ALIGNUP(p, q) ((void *)(((unsigned long)(p) + (q) - 1) & ~(q - 1)))
-
-static int quicktest1(unsigned long arg)
-{
- struct gru_message_queue_desc mqd;
- void *p, *mq;
- int i, ret = -EIO;
- char mes[GRU_CACHE_LINE_BYTES], *m;
-
- /* Need 1K cacheline aligned that does not cross page boundary */
- p = kmalloc(4096, 0);
- if (p == NULL)
- return -ENOMEM;
- mq = ALIGNUP(p, 1024);
- memset(mes, 0xee, sizeof(mes));
-
- gru_create_message_queue(&mqd, mq, 8 * GRU_CACHE_LINE_BYTES, 0, 0, 0);
- for (i = 0; i < 6; i++) {
- mes[8] = i;
- do {
- ret = gru_send_message_gpa(&mqd, mes, sizeof(mes));
- } while (ret == MQE_CONGESTION);
- if (ret)
- break;
- }
- if (ret != MQE_QUEUE_FULL || i != 4) {
- printk(KERN_DEBUG "GRU:%d quicktest1: unexpected status %d, i %d\n",
- smp_processor_id(), ret, i);
- goto done;
- }
-
- for (i = 0; i < 6; i++) {
- m = gru_get_next_message(&mqd);
- if (!m || m[8] != i)
- break;
- gru_free_message(&mqd, m);
- }
- if (i != 4) {
- printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n",
- smp_processor_id(), i, m, m ? m[8] : -1);
- goto done;
- }
- ret = 0;
-
-done:
- kfree(p);
- return ret;
-}
-
-static int quicktest2(unsigned long arg)
-{
- static DECLARE_COMPLETION(cmp);
- unsigned long han;
- int blade_id = 0;
- int numcb = 4;
- int ret = 0;
- unsigned long *buf;
- void *cb0, *cb;
- struct gru_control_block_status *gen;
- int i, k, istatus, bytes;
-
- bytes = numcb * 4 * 8;
- buf = kmalloc(bytes, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- ret = -EBUSY;
- han = gru_reserve_async_resources(blade_id, numcb, 0, &cmp);
- if (!han)
- goto done;
-
- gru_lock_async_resource(han, &cb0, NULL);
- memset(buf, 0xee, bytes);
- for (i = 0; i < numcb; i++)
- gru_vset(cb0 + i * GRU_HANDLE_STRIDE, uv_gpa(&buf[i * 4]), 0,
- XTYPE_DW, 4, 1, IMA_INTERRUPT);
-
- ret = 0;
- k = numcb;
- do {
- gru_wait_async_cbr(han);
- for (i = 0; i < numcb; i++) {
- cb = cb0 + i * GRU_HANDLE_STRIDE;
- istatus = gru_check_status(cb);
- if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS)
- break;
- }
- if (i == numcb)
- continue;
- if (istatus != CBS_IDLE) {
- printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i);
- ret = -EFAULT;
- } else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] ||
- buf[4 * i + 3]) {
- printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
- smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]);
- ret = -EIO;
- }
- k--;
- gen = cb;
- gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */
- } while (k);
- BUG_ON(cmp.done);
-
- gru_unlock_async_resource(han);
- gru_release_async_resources(han);
-done:
- kfree(buf);
- return ret;
-}
-
-#define BUFSIZE 200
-static int quicktest3(unsigned long arg)
-{
- char buf1[BUFSIZE], buf2[BUFSIZE];
- int ret = 0;
-
- memset(buf2, 0, sizeof(buf2));
- memset(buf1, get_cycles() & 255, sizeof(buf1));
- gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE);
- if (memcmp(buf1, buf2, BUFSIZE)) {
- printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id());
- ret = -EIO;
- }
- return ret;
-}
-
-/*
- * Debugging only. User hook for various kernel tests
- * of driver & gru.
- */
-int gru_ktest(unsigned long arg)
-{
- int ret = -EINVAL;
-
- switch (arg & 0xff) {
- case 0:
- ret = quicktest0(arg);
- break;
- case 1:
- ret = quicktest1(arg);
- break;
- case 2:
- ret = quicktest2(arg);
- break;
- case 3:
- ret = quicktest3(arg);
- break;
- case 99:
- ret = gru_free_kernel_contexts();
- break;
- }
- return ret;
-
-}
-
-int gru_kservices_init(void)
-{
- return 0;
-}
-
-void gru_kservices_exit(void)
-{
- if (gru_free_kernel_contexts())
- BUG();
-}
-
diff --git a/drivers/misc/sgi-gru/grukservices.h b/drivers/misc/sgi-gru/grukservices.h
deleted file mode 100644
index 510e45e9737e..000000000000
--- a/drivers/misc/sgi-gru/grukservices.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-#ifndef __GRU_KSERVICES_H_
-#define __GRU_KSERVICES_H_
-
-
-/*
- * Message queues using the GRU to send/receive messages.
- *
- * These function allow the user to create a message queue for
- * sending/receiving 1 or 2 cacheline messages using the GRU.
- *
- * Processes SENDING messages will use a kernel CBR/DSR to send
- * the message. This is transparent to the caller.
- *
- * The receiver does not use any GRU resources.
- *
- * The functions support:
- * - single receiver
- * - multiple senders
- * - cross partition message
- *
- * Missing features ZZZ:
- * - user options for dealing with timeouts, queue full, etc.
- * - gru_create_message_queue() needs interrupt vector info
- */
-
-struct gru_message_queue_desc {
- void *mq; /* message queue vaddress */
- unsigned long mq_gpa; /* global address of mq */
- int qlines; /* queue size in CL */
- int interrupt_vector; /* interrupt vector */
- int interrupt_pnode; /* pnode for interrupt */
- int interrupt_apicid; /* lapicid for interrupt */
-};
-
-/*
- * Initialize a user allocated chunk of memory to be used as
- * a message queue. The caller must ensure that the queue is
- * in contiguous physical memory and is cacheline aligned.
- *
- * Message queue size is the total number of bytes allocated
- * to the queue including a 2 cacheline header that is used
- * to manage the queue.
- *
- * Input:
- * mqd pointer to message queue descriptor
- * p pointer to user allocated mesq memory.
- * bytes size of message queue in bytes
- * vector interrupt vector (zero if no interrupts)
- * nasid nasid of blade where interrupt is delivered
- * apicid apicid of cpu for interrupt
- *
- * Errors:
- * 0 OK
- * >0 error
- */
-extern int gru_create_message_queue(struct gru_message_queue_desc *mqd,
- void *p, unsigned int bytes, int nasid, int vector, int apicid);
-
-/*
- * Send a message to a message queue.
- *
- * Note: The message queue transport mechanism uses the first 32
- * bits of the message. Users should avoid using these bits.
- *
- *
- * Input:
- * mqd pointer to message queue descriptor
- * mesg pointer to message. Must be 64-bit aligned
- * bytes size of message in bytes
- *
- * Output:
- * 0 message sent
- * >0 Send failure - see error codes below
- *
- */
-extern int gru_send_message_gpa(struct gru_message_queue_desc *mqd,
- void *mesg, unsigned int bytes);
-
-/* Status values for gru_send_message() */
-#define MQE_OK 0 /* message sent successfully */
-#define MQE_CONGESTION 1 /* temporary congestion, try again */
-#define MQE_QUEUE_FULL 2 /* queue is full */
-#define MQE_UNEXPECTED_CB_ERR 3 /* unexpected CB error */
-#define MQE_PAGE_OVERFLOW 10 /* BUG - queue overflowed a page */
-#define MQE_BUG_NO_RESOURCES 11 /* BUG - could not alloc GRU cb/dsr */
-
-/*
- * Advance the receive pointer for the message queue to the next message.
- * Note: current API requires messages to be gotten & freed in order. Future
- * API extensions may allow for out-of-order freeing.
- *
- * Input
- * mqd pointer to message queue descriptor
- * mesq message being freed
- */
-extern void gru_free_message(struct gru_message_queue_desc *mqd,
- void *mesq);
-
-/*
- * Get next message from message queue. Returns pointer to
- * message OR NULL if no message present.
- * User must call gru_free_message() after message is processed
- * in order to move the queue pointers to next message.
- *
- * Input
- * mqd pointer to message queue descriptor
- *
- * Output:
- * p pointer to message
- * NULL no message available
- */
-extern void *gru_get_next_message(struct gru_message_queue_desc *mqd);
-
-
-/*
- * Read a GRU global GPA. Source can be located in a remote partition.
- *
- * Input:
- * value memory address where MMR value is returned
- * gpa source numalink physical address of GPA
- *
- * Output:
- * 0 OK
- * >0 error
- */
-int gru_read_gpa(unsigned long *value, unsigned long gpa);
-
-
-/*
- * Copy data using the GRU. Source or destination can be located in a remote
- * partition.
- *
- * Input:
- * dest_gpa destination global physical address
- * src_gpa source global physical address
- * bytes number of bytes to copy
- *
- * Output:
- * 0 OK
- * >0 error
- */
-extern int gru_copy_gpa(unsigned long dest_gpa, unsigned long src_gpa,
- unsigned int bytes);
-
-/*
- * Reserve GRU resources to be used asynchronously.
- *
- * input:
- * blade_id - blade on which resources should be reserved
- * cbrs - number of CBRs
- * dsr_bytes - number of DSR bytes needed
- * cmp - completion structure for waiting for
- * async completions
- * output:
- * handle to identify resource
- * (0 = no resources)
- */
-extern unsigned long gru_reserve_async_resources(int blade_id, int cbrs, int dsr_bytes,
- struct completion *cmp);
-
-/*
- * Release async resources previously reserved.
- *
- * input:
- * han - handle to identify resources
- */
-extern void gru_release_async_resources(unsigned long han);
-
-/*
- * Wait for async GRU instructions to complete.
- *
- * input:
- * han - handle to identify resources
- */
-extern void gru_wait_async_cbr(unsigned long han);
-
-/*
- * Lock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- * output:
- * cb - pointer to first CBR
- * dsr - pointer to first DSR
- */
-extern void gru_lock_async_resource(unsigned long han, void **cb, void **dsr);
-
-/*
- * Unlock previous reserved async GRU resources
- *
- * input:
- * han - handle to identify resources
- */
-extern void gru_unlock_async_resource(unsigned long han);
-
-#endif /* __GRU_KSERVICES_H_ */
diff --git a/drivers/misc/sgi-gru/grulib.h b/drivers/misc/sgi-gru/grulib.h
deleted file mode 100644
index 85c103923632..000000000000
--- a/drivers/misc/sgi-gru/grulib.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __GRULIB_H__
-#define __GRULIB_H__
-
-#define GRU_BASENAME "gru"
-#define GRU_FULLNAME "/dev/gru"
-#define GRU_IOCTL_NUM 'G'
-
-/*
- * Maximum number of GRU segments that a user can have open
- * ZZZ temp - set high for testing. Revisit.
- */
-#define GRU_MAX_OPEN_CONTEXTS 32
-
-/* Set Number of Request Blocks */
-#define GRU_CREATE_CONTEXT _IOWR(GRU_IOCTL_NUM, 1, void *)
-
-/* Set Context Options */
-#define GRU_SET_CONTEXT_OPTION _IOWR(GRU_IOCTL_NUM, 4, void *)
-
-/* Fetch exception detail */
-#define GRU_USER_GET_EXCEPTION_DETAIL _IOWR(GRU_IOCTL_NUM, 6, void *)
-
-/* For user call_os handling - normally a TLB fault */
-#define GRU_USER_CALL_OS _IOWR(GRU_IOCTL_NUM, 8, void *)
-
-/* For user unload context */
-#define GRU_USER_UNLOAD_CONTEXT _IOWR(GRU_IOCTL_NUM, 9, void *)
-
-/* For dumpping GRU chiplet state */
-#define GRU_DUMP_CHIPLET_STATE _IOWR(GRU_IOCTL_NUM, 11, void *)
-
-/* For getting gseg statistics */
-#define GRU_GET_GSEG_STATISTICS _IOWR(GRU_IOCTL_NUM, 12, void *)
-
-/* For user TLB flushing (primarily for tests) */
-#define GRU_USER_FLUSH_TLB _IOWR(GRU_IOCTL_NUM, 50, void *)
-
-/* Get some config options (primarily for tests & emulator) */
-#define GRU_GET_CONFIG_INFO _IOWR(GRU_IOCTL_NUM, 51, void *)
-
-/* Various kernel self-tests */
-#define GRU_KTEST _IOWR(GRU_IOCTL_NUM, 52, void *)
-
-#define CONTEXT_WINDOW_BYTES(th) (GRU_GSEG_PAGESIZE * (th))
-#define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th))
-#define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1)))
-
-struct gru_get_gseg_statistics_req {
- unsigned long gseg;
- struct gru_gseg_statistics stats;
-};
-
-/*
- * Structure used to pass TLB flush parameters to the driver
- */
-struct gru_create_context_req {
- unsigned long gseg;
- unsigned int data_segment_bytes;
- unsigned int control_blocks;
- unsigned int maximum_thread_count;
- unsigned int options;
- unsigned char tlb_preload_count;
-};
-
-/*
- * Structure used to pass unload context parameters to the driver
- */
-struct gru_unload_context_req {
- unsigned long gseg;
-};
-
-/*
- * Structure used to set context options
- */
-enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet};
-struct gru_set_context_option_req {
- unsigned long gseg;
- int op;
- int val0;
- long val1;
-};
-
-/*
- * Structure used to pass TLB flush parameters to the driver
- */
-struct gru_flush_tlb_req {
- unsigned long gseg;
- unsigned long vaddr;
- size_t len;
-};
-
-/*
- * Structure used to pass TLB flush parameters to the driver
- */
-enum {dcs_pid, dcs_gid};
-struct gru_dump_chiplet_state_req {
- unsigned int op;
- unsigned int gid;
- int ctxnum;
- char data_opt;
- char lock_cch;
- char flush_cbrs;
- char fill[10];
- pid_t pid;
- void *buf;
- size_t buflen;
- /* ---- output --- */
- unsigned int num_contexts;
-};
-
-#define GRU_DUMP_MAGIC 0x3474ab6c
-struct gru_dump_context_header {
- unsigned int magic;
- unsigned int gid;
- unsigned char ctxnum;
- unsigned char cbrcnt;
- unsigned char dsrcnt;
- pid_t pid;
- unsigned long vaddr;
- int cch_locked;
- unsigned long data[];
-};
-
-/*
- * GRU configuration info (temp - for testing)
- */
-struct gru_config_info {
- int cpus;
- int blades;
- int nodes;
- int chiplets;
- int fill[16];
-};
-
-#endif /* __GRULIB_H__ */
diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c
deleted file mode 100644
index 278b76cbd281..000000000000
--- a/drivers/misc/sgi-gru/grumain.c
+++ /dev/null
@@ -1,969 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * DRIVER TABLE MANAGER + GRU CONTEXT LOAD/UNLOAD
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/sched.h>
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/err.h>
-#include <linux/prefetch.h>
-#include <asm/uv/uv_hub.h>
-#include "gru.h"
-#include "grutables.h"
-#include "gruhandles.h"
-
-unsigned long gru_options __read_mostly;
-
-static struct device_driver gru_driver = {
- .name = "gru"
-};
-
-static struct device gru_device = {
- .init_name = "",
- .driver = &gru_driver,
-};
-
-struct device *grudev = &gru_device;
-
-/*
- * Select a gru fault map to be used by the current cpu. Note that
- * multiple cpus may be using the same map.
- * ZZZ should be inline but did not work on emulator
- */
-int gru_cpu_fault_map_id(void)
-{
- int cpu = smp_processor_id();
- int id, core;
-
- core = uv_cpu_core_number(cpu);
- id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
- return id;
-}
-
-/*--------- ASID Management -------------------------------------------
- *
- * Initially, assign asids sequentially from MIN_ASID .. MAX_ASID.
- * Once MAX is reached, flush the TLB & start over. However,
- * some asids may still be in use. There won't be many (percentage wise) still
- * in use. Search active contexts & determine the value of the first
- * asid in use ("x"s below). Set "limit" to this value.
- * This defines a block of assignable asids.
- *
- * When "limit" is reached, search forward from limit+1 and determine the
- * next block of assignable asids.
- *
- * Repeat until MAX_ASID is reached, then start over again.
- *
- * Each time MAX_ASID is reached, increment the asid generation. Since
- * the search for in-use asids only checks contexts with GRUs currently
- * assigned, asids in some contexts will be missed. Prior to loading
- * a context, the asid generation of the GTS asid is rechecked. If it
- * doesn't match the current generation, a new asid will be assigned.
- *
- * 0---------------x------------x---------------------x----|
- * ^-next ^-limit ^-MAX_ASID
- *
- * All asid manipulation & context loading/unloading is protected by the
- * gs_lock.
- */
-
-/* Hit the asid limit. Start over */
-static int gru_wrap_asid(struct gru_state *gru)
-{
- gru_dbg(grudev, "gid %d\n", gru->gs_gid);
- STAT(asid_wrap);
- gru->gs_asid_gen++;
- return MIN_ASID;
-}
-
-/* Find the next chunk of unused asids */
-static int gru_reset_asid_limit(struct gru_state *gru, int asid)
-{
- int i, gid, inuse_asid, limit;
-
- gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid);
- STAT(asid_next);
- limit = MAX_ASID;
- if (asid >= limit)
- asid = gru_wrap_asid(gru);
- gru_flush_all_tlb(gru);
- gid = gru->gs_gid;
-again:
- for (i = 0; i < GRU_NUM_CCH; i++) {
- if (!gru->gs_gts[i] || is_kernel_context(gru->gs_gts[i]))
- continue;
- inuse_asid = gru->gs_gts[i]->ts_gms->ms_asids[gid].mt_asid;
- gru_dbg(grudev, "gid %d, gts %p, gms %p, inuse 0x%x, cxt %d\n",
- gru->gs_gid, gru->gs_gts[i], gru->gs_gts[i]->ts_gms,
- inuse_asid, i);
- if (inuse_asid == asid) {
- asid += ASID_INC;
- if (asid >= limit) {
- /*
- * empty range: reset the range limit and
- * start over
- */
- limit = MAX_ASID;
- if (asid >= MAX_ASID)
- asid = gru_wrap_asid(gru);
- goto again;
- }
- }
-
- if ((inuse_asid > asid) && (inuse_asid < limit))
- limit = inuse_asid;
- }
- gru->gs_asid_limit = limit;
- gru->gs_asid = asid;
- gru_dbg(grudev, "gid %d, new asid 0x%x, new_limit 0x%x\n", gru->gs_gid,
- asid, limit);
- return asid;
-}
-
-/* Assign a new ASID to a thread context. */
-static int gru_assign_asid(struct gru_state *gru)
-{
- int asid;
-
- gru->gs_asid += ASID_INC;
- asid = gru->gs_asid;
- if (asid >= gru->gs_asid_limit)
- asid = gru_reset_asid_limit(gru, asid);
-
- gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid);
- return asid;
-}
-
-/*
- * Clear n bits in a word. Return a word indicating the bits that were cleared.
- * Optionally, build an array of chars that contain the bit numbers allocated.
- */
-static unsigned long reserve_resources(unsigned long *p, int n, int mmax,
- signed char *idx)
-{
- unsigned long bits = 0;
- int i;
-
- while (n--) {
- i = find_first_bit(p, mmax);
- if (i == mmax)
- BUG();
- __clear_bit(i, p);
- __set_bit(i, &bits);
- if (idx)
- *idx++ = i;
- }
- return bits;
-}
-
-unsigned long gru_reserve_cb_resources(struct gru_state *gru, int cbr_au_count,
- signed char *cbmap)
-{
- return reserve_resources(&gru->gs_cbr_map, cbr_au_count, GRU_CBR_AU,
- cbmap);
-}
-
-unsigned long gru_reserve_ds_resources(struct gru_state *gru, int dsr_au_count,
- signed char *dsmap)
-{
- return reserve_resources(&gru->gs_dsr_map, dsr_au_count, GRU_DSR_AU,
- dsmap);
-}
-
-static void reserve_gru_resources(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- gru->gs_active_contexts++;
- gts->ts_cbr_map =
- gru_reserve_cb_resources(gru, gts->ts_cbr_au_count,
- gts->ts_cbr_idx);
- gts->ts_dsr_map =
- gru_reserve_ds_resources(gru, gts->ts_dsr_au_count, NULL);
-}
-
-static void free_gru_resources(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- gru->gs_active_contexts--;
- gru->gs_cbr_map |= gts->ts_cbr_map;
- gru->gs_dsr_map |= gts->ts_dsr_map;
-}
-
-/*
- * Check if a GRU has sufficient free resources to satisfy an allocation
- * request. Note: GRU locks may or may not be held when this is called. If
- * not held, recheck after acquiring the appropriate locks.
- *
- * Returns 1 if sufficient resources, 0 if not
- */
-static int check_gru_resources(struct gru_state *gru, int cbr_au_count,
- int dsr_au_count, int max_active_contexts)
-{
- return hweight64(gru->gs_cbr_map) >= cbr_au_count
- && hweight64(gru->gs_dsr_map) >= dsr_au_count
- && gru->gs_active_contexts < max_active_contexts;
-}
-
-/*
- * TLB manangment requires tracking all GRU chiplets that have loaded a GSEG
- * context.
- */
-static int gru_load_mm_tracker(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- struct gru_mm_struct *gms = gts->ts_gms;
- struct gru_mm_tracker *asids = &gms->ms_asids[gru->gs_gid];
- unsigned short ctxbitmap = (1 << gts->ts_ctxnum);
- int asid;
-
- spin_lock(&gms->ms_asid_lock);
- asid = asids->mt_asid;
-
- spin_lock(&gru->gs_asid_lock);
- if (asid == 0 || (asids->mt_ctxbitmap == 0 && asids->mt_asid_gen !=
- gru->gs_asid_gen)) {
- asid = gru_assign_asid(gru);
- asids->mt_asid = asid;
- asids->mt_asid_gen = gru->gs_asid_gen;
- STAT(asid_new);
- } else {
- STAT(asid_reuse);
- }
- spin_unlock(&gru->gs_asid_lock);
-
- BUG_ON(asids->mt_ctxbitmap & ctxbitmap);
- asids->mt_ctxbitmap |= ctxbitmap;
- if (!test_bit(gru->gs_gid, gms->ms_asidmap))
- __set_bit(gru->gs_gid, gms->ms_asidmap);
- spin_unlock(&gms->ms_asid_lock);
-
- gru_dbg(grudev,
- "gid %d, gts %p, gms %p, ctxnum %d, asid 0x%x, asidmap 0x%lx\n",
- gru->gs_gid, gts, gms, gts->ts_ctxnum, asid,
- gms->ms_asidmap[0]);
- return asid;
-}
-
-static void gru_unload_mm_tracker(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- struct gru_mm_struct *gms = gts->ts_gms;
- struct gru_mm_tracker *asids;
- unsigned short ctxbitmap;
-
- asids = &gms->ms_asids[gru->gs_gid];
- ctxbitmap = (1 << gts->ts_ctxnum);
- spin_lock(&gms->ms_asid_lock);
- spin_lock(&gru->gs_asid_lock);
- BUG_ON((asids->mt_ctxbitmap & ctxbitmap) != ctxbitmap);
- asids->mt_ctxbitmap ^= ctxbitmap;
- gru_dbg(grudev, "gid %d, gts %p, gms %p, ctxnum %d, asidmap 0x%lx\n",
- gru->gs_gid, gts, gms, gts->ts_ctxnum, gms->ms_asidmap[0]);
- spin_unlock(&gru->gs_asid_lock);
- spin_unlock(&gms->ms_asid_lock);
-}
-
-/*
- * Decrement the reference count on a GTS structure. Free the structure
- * if the reference count goes to zero.
- */
-void gts_drop(struct gru_thread_state *gts)
-{
- if (gts && refcount_dec_and_test(&gts->ts_refcnt)) {
- if (gts->ts_gms)
- gru_drop_mmu_notifier(gts->ts_gms);
- kfree(gts);
- STAT(gts_free);
- }
-}
-
-/*
- * Locate the GTS structure for the current thread.
- */
-static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data
- *vdata, int tsid)
-{
- struct gru_thread_state *gts;
-
- list_for_each_entry(gts, &vdata->vd_head, ts_next)
- if (gts->ts_tsid == tsid)
- return gts;
- return NULL;
-}
-
-/*
- * Allocate a thread state structure.
- */
-struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
- int cbr_au_count, int dsr_au_count,
- unsigned char tlb_preload_count, int options, int tsid)
-{
- struct gru_thread_state *gts;
- struct gru_mm_struct *gms;
- int bytes;
-
- bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count);
- bytes += sizeof(struct gru_thread_state);
- gts = kmalloc(bytes, GFP_KERNEL);
- if (!gts)
- return ERR_PTR(-ENOMEM);
-
- STAT(gts_alloc);
- memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */
- refcount_set(&gts->ts_refcnt, 1);
- mutex_init(&gts->ts_ctxlock);
- gts->ts_cbr_au_count = cbr_au_count;
- gts->ts_dsr_au_count = dsr_au_count;
- gts->ts_tlb_preload_count = tlb_preload_count;
- gts->ts_user_options = options;
- gts->ts_user_blade_id = -1;
- gts->ts_user_chiplet_id = -1;
- gts->ts_tsid = tsid;
- gts->ts_ctxnum = NULLCTX;
- gts->ts_tlb_int_select = -1;
- gts->ts_cch_req_slice = -1;
- gts->ts_sizeavail = GRU_SIZEAVAIL(PAGE_SHIFT);
- if (vma) {
- gts->ts_mm = current->mm;
- gts->ts_vma = vma;
- gms = gru_register_mmu_notifier();
- if (IS_ERR(gms))
- goto err;
- gts->ts_gms = gms;
- }
-
- gru_dbg(grudev, "alloc gts %p\n", gts);
- return gts;
-
-err:
- gts_drop(gts);
- return ERR_CAST(gms);
-}
-
-/*
- * Allocate a vma private data structure.
- */
-struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid)
-{
- struct gru_vma_data *vdata = NULL;
-
- vdata = kmalloc_obj(*vdata);
- if (!vdata)
- return NULL;
-
- STAT(vdata_alloc);
- INIT_LIST_HEAD(&vdata->vd_head);
- spin_lock_init(&vdata->vd_lock);
- gru_dbg(grudev, "alloc vdata %p\n", vdata);
- return vdata;
-}
-
-/*
- * Find the thread state structure for the current thread.
- */
-struct gru_thread_state *gru_find_thread_state(struct vm_area_struct *vma,
- int tsid)
-{
- struct gru_vma_data *vdata = vma->vm_private_data;
- struct gru_thread_state *gts;
-
- spin_lock(&vdata->vd_lock);
- gts = gru_find_current_gts_nolock(vdata, tsid);
- spin_unlock(&vdata->vd_lock);
- gru_dbg(grudev, "vma %p, gts %p\n", vma, gts);
- return gts;
-}
-
-/*
- * Allocate a new thread state for a GSEG. Note that races may allow
- * another thread to race to create a gts.
- */
-struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma,
- int tsid)
-{
- struct gru_vma_data *vdata = vma->vm_private_data;
- struct gru_thread_state *gts, *ngts;
-
- gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count,
- vdata->vd_dsr_au_count,
- vdata->vd_tlb_preload_count,
- vdata->vd_user_options, tsid);
- if (IS_ERR(gts))
- return gts;
-
- spin_lock(&vdata->vd_lock);
- ngts = gru_find_current_gts_nolock(vdata, tsid);
- if (ngts) {
- gts_drop(gts);
- gts = ngts;
- STAT(gts_double_allocate);
- } else {
- list_add(&gts->ts_next, &vdata->vd_head);
- }
- spin_unlock(&vdata->vd_lock);
- gru_dbg(grudev, "vma %p, gts %p\n", vma, gts);
- return gts;
-}
-
-/*
- * Free the GRU context assigned to the thread state.
- */
-static void gru_free_gru_context(struct gru_thread_state *gts)
-{
- struct gru_state *gru;
-
- gru = gts->ts_gru;
- gru_dbg(grudev, "gts %p, gid %d\n", gts, gru->gs_gid);
-
- spin_lock(&gru->gs_lock);
- gru->gs_gts[gts->ts_ctxnum] = NULL;
- free_gru_resources(gru, gts);
- BUG_ON(test_bit(gts->ts_ctxnum, &gru->gs_context_map) == 0);
- __clear_bit(gts->ts_ctxnum, &gru->gs_context_map);
- gts->ts_ctxnum = NULLCTX;
- gts->ts_gru = NULL;
- gts->ts_blade = -1;
- spin_unlock(&gru->gs_lock);
-
- gts_drop(gts);
- STAT(free_context);
-}
-
-/*
- * Prefetching cachelines help hardware performance.
- * (Strictly a performance enhancement. Not functionally required).
- */
-static void prefetch_data(void *p, int num, int stride)
-{
- while (num-- > 0) {
- prefetchw(p);
- p += stride;
- }
-}
-
-static inline long gru_copy_handle(void *d, void *s)
-{
- memcpy(d, s, GRU_HANDLE_BYTES);
- return GRU_HANDLE_BYTES;
-}
-
-static void gru_prefetch_context(void *gseg, void *cb, void *cbe,
- unsigned long cbrmap, unsigned long length)
-{
- int i, scr;
-
- prefetch_data(gseg + GRU_DS_BASE, length / GRU_CACHE_LINE_BYTES,
- GRU_CACHE_LINE_BYTES);
-
- for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
- prefetch_data(cb, 1, GRU_CACHE_LINE_BYTES);
- prefetch_data(cbe + i * GRU_HANDLE_STRIDE, 1,
- GRU_CACHE_LINE_BYTES);
- cb += GRU_HANDLE_STRIDE;
- }
-}
-
-static void gru_load_context_data(void *save, void *grubase, int ctxnum,
- unsigned long cbrmap, unsigned long dsrmap,
- int data_valid)
-{
- void *gseg, *cb, *cbe;
- unsigned long length;
- int i, scr;
-
- gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
- cb = gseg + GRU_CB_BASE;
- cbe = grubase + GRU_CBE_BASE;
- length = hweight64(dsrmap) * GRU_DSR_AU_BYTES;
- gru_prefetch_context(gseg, cb, cbe, cbrmap, length);
-
- for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
- if (data_valid) {
- save += gru_copy_handle(cb, save);
- save += gru_copy_handle(cbe + i * GRU_HANDLE_STRIDE,
- save);
- } else {
- memset(cb, 0, GRU_CACHE_LINE_BYTES);
- memset(cbe + i * GRU_HANDLE_STRIDE, 0,
- GRU_CACHE_LINE_BYTES);
- }
- /* Flush CBE to hide race in context restart */
- mb();
- gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
- cb += GRU_HANDLE_STRIDE;
- }
-
- if (data_valid)
- memcpy(gseg + GRU_DS_BASE, save, length);
- else
- memset(gseg + GRU_DS_BASE, 0, length);
-}
-
-static void gru_unload_context_data(void *save, void *grubase, int ctxnum,
- unsigned long cbrmap, unsigned long dsrmap)
-{
- void *gseg, *cb, *cbe;
- unsigned long length;
- int i, scr;
-
- gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
- cb = gseg + GRU_CB_BASE;
- cbe = grubase + GRU_CBE_BASE;
- length = hweight64(dsrmap) * GRU_DSR_AU_BYTES;
-
- /* CBEs may not be coherent. Flush them from cache */
- for_each_cbr_in_allocation_map(i, &cbrmap, scr)
- gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
- mb(); /* Let the CL flush complete */
-
- gru_prefetch_context(gseg, cb, cbe, cbrmap, length);
-
- for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
- save += gru_copy_handle(save, cb);
- save += gru_copy_handle(save, cbe + i * GRU_HANDLE_STRIDE);
- cb += GRU_HANDLE_STRIDE;
- }
- memcpy(save, gseg + GRU_DS_BASE, length);
-}
-
-void gru_unload_context(struct gru_thread_state *gts, int savestate)
-{
- struct gru_state *gru = gts->ts_gru;
- struct gru_context_configuration_handle *cch;
- int ctxnum = gts->ts_ctxnum;
-
- if (!is_kernel_context(gts))
- zap_special_vma_range(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE);
- cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
-
- gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n",
- gts, gts->ts_cbr_map, gts->ts_dsr_map);
- lock_cch_handle(cch);
- if (cch_interrupt_sync(cch))
- BUG();
-
- if (!is_kernel_context(gts))
- gru_unload_mm_tracker(gru, gts);
- if (savestate) {
- gru_unload_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr,
- ctxnum, gts->ts_cbr_map,
- gts->ts_dsr_map);
- gts->ts_data_valid = 1;
- }
-
- if (cch_deallocate(cch))
- BUG();
- unlock_cch_handle(cch);
-
- gru_free_gru_context(gts);
-}
-
-/*
- * Load a GRU context by copying it from the thread data structure in memory
- * to the GRU.
- */
-void gru_load_context(struct gru_thread_state *gts)
-{
- struct gru_state *gru = gts->ts_gru;
- struct gru_context_configuration_handle *cch;
- int i, err, asid, ctxnum = gts->ts_ctxnum;
-
- cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
- lock_cch_handle(cch);
- cch->tfm_fault_bit_enable =
- (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
- || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
- cch->tlb_int_enable = (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
- if (cch->tlb_int_enable) {
- gts->ts_tlb_int_select = gru_cpu_fault_map_id();
- cch->tlb_int_select = gts->ts_tlb_int_select;
- }
- if (gts->ts_cch_req_slice >= 0) {
- cch->req_slice_set_enable = 1;
- cch->req_slice = gts->ts_cch_req_slice;
- } else {
- cch->req_slice_set_enable =0;
- }
- cch->tfm_done_bit_enable = 0;
- cch->dsr_allocation_map = gts->ts_dsr_map;
- cch->cbr_allocation_map = gts->ts_cbr_map;
-
- if (is_kernel_context(gts)) {
- cch->unmap_enable = 1;
- cch->tfm_done_bit_enable = 1;
- cch->cb_int_enable = 1;
- cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */
- } else {
- cch->unmap_enable = 0;
- cch->tfm_done_bit_enable = 0;
- cch->cb_int_enable = 0;
- asid = gru_load_mm_tracker(gru, gts);
- for (i = 0; i < 8; i++) {
- cch->asid[i] = asid + i;
- cch->sizeavail[i] = gts->ts_sizeavail;
- }
- }
-
- err = cch_allocate(cch);
- if (err) {
- gru_dbg(grudev,
- "err %d: cch %p, gts %p, cbr 0x%lx, dsr 0x%lx\n",
- err, cch, gts, gts->ts_cbr_map, gts->ts_dsr_map);
- BUG();
- }
-
- gru_load_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr, ctxnum,
- gts->ts_cbr_map, gts->ts_dsr_map, gts->ts_data_valid);
-
- if (cch_start(cch))
- BUG();
- unlock_cch_handle(cch);
-
- gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n",
- gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map,
- (gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select);
-}
-
-/*
- * Update fields in an active CCH:
- * - retarget interrupts on local blade
- * - update sizeavail mask
- */
-int gru_update_cch(struct gru_thread_state *gts)
-{
- struct gru_context_configuration_handle *cch;
- struct gru_state *gru = gts->ts_gru;
- int i, ctxnum = gts->ts_ctxnum, ret = 0;
-
- cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
-
- lock_cch_handle(cch);
- if (cch->state == CCHSTATE_ACTIVE) {
- if (gru->gs_gts[gts->ts_ctxnum] != gts)
- goto exit;
- if (cch_interrupt(cch))
- BUG();
- for (i = 0; i < 8; i++)
- cch->sizeavail[i] = gts->ts_sizeavail;
- gts->ts_tlb_int_select = gru_cpu_fault_map_id();
- cch->tlb_int_select = gru_cpu_fault_map_id();
- cch->tfm_fault_bit_enable =
- (gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
- || gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
- if (cch_start(cch))
- BUG();
- ret = 1;
- }
-exit:
- unlock_cch_handle(cch);
- return ret;
-}
-
-/*
- * Update CCH tlb interrupt select. Required when all the following is true:
- * - task's GRU context is loaded into a GRU
- * - task is using interrupt notification for TLB faults
- * - task has migrated to a different cpu on the same blade where
- * it was previously running.
- */
-static int gru_retarget_intr(struct gru_thread_state *gts)
-{
- if (gts->ts_tlb_int_select < 0
- || gts->ts_tlb_int_select == gru_cpu_fault_map_id())
- return 0;
-
- gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select,
- gru_cpu_fault_map_id());
- return gru_update_cch(gts);
-}
-
-/*
- * Check if a GRU context is allowed to use a specific chiplet. By default
- * a context is assigned to any blade-local chiplet. However, users can
- * override this.
- * Returns 1 if assignment allowed, 0 otherwise
- */
-static int gru_check_chiplet_assignment(struct gru_state *gru,
- struct gru_thread_state *gts)
-{
- int blade_id;
- int chiplet_id;
-
- blade_id = gts->ts_user_blade_id;
- if (blade_id < 0)
- blade_id = uv_numa_blade_id();
-
- chiplet_id = gts->ts_user_chiplet_id;
- return gru->gs_blade_id == blade_id &&
- (chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id);
-}
-
-/*
- * Unload the gru context if it is not assigned to the correct blade or
- * chiplet. Misassignment can occur if the process migrates to a different
- * blade or if the user changes the selected blade/chiplet.
- */
-int gru_check_context_placement(struct gru_thread_state *gts)
-{
- struct gru_state *gru;
- int ret = 0;
-
- /*
- * If the current task is the context owner, verify that the
- * context is correctly placed. This test is skipped for non-owner
- * references. Pthread apps use non-owner references to the CBRs.
- */
- gru = gts->ts_gru;
- /*
- * If gru or gts->ts_tgid_owner isn't initialized properly, return
- * success to indicate that the caller does not need to unload the
- * gru context.The caller is responsible for their inspection and
- * reinitialization if needed.
- */
- if (!gru || gts->ts_tgid_owner != current->tgid)
- return ret;
-
- if (!gru_check_chiplet_assignment(gru, gts)) {
- STAT(check_context_unload);
- ret = -EINVAL;
- } else if (gru_retarget_intr(gts)) {
- STAT(check_context_retarget_intr);
- }
-
- return ret;
-}
-
-
-/*
- * Insufficient GRU resources available on the local blade. Steal a context from
- * a process. This is a hack until a _real_ resource scheduler is written....
- */
-#define next_ctxnum(n) ((n) < GRU_NUM_CCH - 2 ? (n) + 1 : 0)
-#define next_gru(b, g) (((g) < &(b)->bs_grus[GRU_CHIPLETS_PER_BLADE - 1]) ? \
- ((g)+1) : &(b)->bs_grus[0])
-
-static int is_gts_stealable(struct gru_thread_state *gts,
- struct gru_blade_state *bs)
-{
- if (is_kernel_context(gts))
- return down_write_trylock(&bs->bs_kgts_sema);
- else
- return mutex_trylock(&gts->ts_ctxlock);
-}
-
-static void gts_stolen(struct gru_thread_state *gts,
- struct gru_blade_state *bs)
-{
- if (is_kernel_context(gts)) {
- up_write(&bs->bs_kgts_sema);
- STAT(steal_kernel_context);
- } else {
- mutex_unlock(&gts->ts_ctxlock);
- STAT(steal_user_context);
- }
-}
-
-void gru_steal_context(struct gru_thread_state *gts)
-{
- struct gru_blade_state *blade;
- struct gru_state *gru, *gru0;
- struct gru_thread_state *ngts = NULL;
- int ctxnum, ctxnum0, flag = 0, cbr, dsr;
- int blade_id;
-
- blade_id = gts->ts_user_blade_id;
- if (blade_id < 0)
- blade_id = uv_numa_blade_id();
- cbr = gts->ts_cbr_au_count;
- dsr = gts->ts_dsr_au_count;
-
- blade = gru_base[blade_id];
- spin_lock(&blade->bs_lock);
-
- ctxnum = next_ctxnum(blade->bs_lru_ctxnum);
- gru = blade->bs_lru_gru;
- if (ctxnum == 0)
- gru = next_gru(blade, gru);
- blade->bs_lru_gru = gru;
- blade->bs_lru_ctxnum = ctxnum;
- ctxnum0 = ctxnum;
- gru0 = gru;
- while (1) {
- if (gru_check_chiplet_assignment(gru, gts)) {
- if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH))
- break;
- spin_lock(&gru->gs_lock);
- for (; ctxnum < GRU_NUM_CCH; ctxnum++) {
- if (flag && gru == gru0 && ctxnum == ctxnum0)
- break;
- ngts = gru->gs_gts[ctxnum];
- /*
- * We are grabbing locks out of order, so trylock is
- * needed. GTSs are usually not locked, so the odds of
- * success are high. If trylock fails, try to steal a
- * different GSEG.
- */
- if (ngts && is_gts_stealable(ngts, blade))
- break;
- ngts = NULL;
- }
- spin_unlock(&gru->gs_lock);
- if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0))
- break;
- }
- if (flag && gru == gru0)
- break;
- flag = 1;
- ctxnum = 0;
- gru = next_gru(blade, gru);
- }
- spin_unlock(&blade->bs_lock);
-
- if (ngts) {
- gts->ustats.context_stolen++;
- ngts->ts_steal_jiffies = jiffies;
- gru_unload_context(ngts, is_kernel_context(ngts) ? 0 : 1);
- gts_stolen(ngts, blade);
- } else {
- STAT(steal_context_failed);
- }
- gru_dbg(grudev,
- "stole gid %d, ctxnum %d from gts %p. Need cb %d, ds %d;"
- " avail cb %ld, ds %ld\n",
- gru->gs_gid, ctxnum, ngts, cbr, dsr, hweight64(gru->gs_cbr_map),
- hweight64(gru->gs_dsr_map));
-}
-
-/*
- * Assign a gru context.
- */
-static int gru_assign_context_number(struct gru_state *gru)
-{
- int ctxnum;
-
- ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH);
- __set_bit(ctxnum, &gru->gs_context_map);
- return ctxnum;
-}
-
-/*
- * Scan the GRUs on the local blade & assign a GRU context.
- */
-struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts)
-{
- struct gru_state *gru, *grux;
- int i, max_active_contexts;
- int blade_id = gts->ts_user_blade_id;
-
- if (blade_id < 0)
- blade_id = uv_numa_blade_id();
-again:
- gru = NULL;
- max_active_contexts = GRU_NUM_CCH;
- for_each_gru_on_blade(grux, blade_id, i) {
- if (!gru_check_chiplet_assignment(grux, gts))
- continue;
- if (check_gru_resources(grux, gts->ts_cbr_au_count,
- gts->ts_dsr_au_count,
- max_active_contexts)) {
- gru = grux;
- max_active_contexts = grux->gs_active_contexts;
- if (max_active_contexts == 0)
- break;
- }
- }
-
- if (gru) {
- spin_lock(&gru->gs_lock);
- if (!check_gru_resources(gru, gts->ts_cbr_au_count,
- gts->ts_dsr_au_count, GRU_NUM_CCH)) {
- spin_unlock(&gru->gs_lock);
- goto again;
- }
- reserve_gru_resources(gru, gts);
- gts->ts_gru = gru;
- gts->ts_blade = gru->gs_blade_id;
- gts->ts_ctxnum = gru_assign_context_number(gru);
- refcount_inc(&gts->ts_refcnt);
- gru->gs_gts[gts->ts_ctxnum] = gts;
- spin_unlock(&gru->gs_lock);
-
- STAT(assign_context);
- gru_dbg(grudev,
- "gseg %p, gts %p, gid %d, ctx %d, cbr %d, dsr %d\n",
- gseg_virtual_address(gts->ts_gru, gts->ts_ctxnum), gts,
- gts->ts_gru->gs_gid, gts->ts_ctxnum,
- gts->ts_cbr_au_count, gts->ts_dsr_au_count);
- } else {
- gru_dbg(grudev, "failed to allocate a GTS %s\n", "");
- STAT(assign_context_failed);
- }
-
- return gru;
-}
-
-/*
- * gru_nopage
- *
- * Map the user's GRU segment
- *
- * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries.
- */
-vm_fault_t gru_fault(struct vm_fault *vmf)
-{
- struct vm_area_struct *vma = vmf->vma;
- struct gru_thread_state *gts;
- unsigned long paddr, vaddr;
- unsigned long expires;
-
- vaddr = vmf->address;
- gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n",
- vma, vaddr, GSEG_BASE(vaddr));
- STAT(nopfn);
-
- /* The following check ensures vaddr is a valid address in the VMA */
- gts = gru_find_thread_state(vma, TSID(vaddr, vma));
- if (!gts)
- return VM_FAULT_SIGBUS;
-
-again:
- mutex_lock(&gts->ts_ctxlock);
-
- if (gru_check_context_placement(gts)) {
- mutex_unlock(&gts->ts_ctxlock);
- gru_unload_context(gts, 1);
- return VM_FAULT_NOPAGE;
- }
-
- if (!gts->ts_gru) {
- STAT(load_user_context);
- if (!gru_assign_gru_context(gts)) {
- mutex_unlock(&gts->ts_ctxlock);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */
- expires = gts->ts_steal_jiffies + GRU_STEAL_DELAY;
- if (time_before(expires, jiffies))
- gru_steal_context(gts);
- goto again;
- }
- gru_load_context(gts);
- paddr = gseg_physical_address(gts->ts_gru, gts->ts_ctxnum);
- remap_pfn_range(vma, vaddr & ~(GRU_GSEG_PAGESIZE - 1),
- paddr >> PAGE_SHIFT, GRU_GSEG_PAGESIZE,
- vma->vm_page_prot);
- }
-
- mutex_unlock(&gts->ts_ctxlock);
-
- return VM_FAULT_NOPAGE;
-}
-
diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c
deleted file mode 100644
index 97b8b38ab47d..000000000000
--- a/drivers/misc/sgi-gru/gruprocfs.c
+++ /dev/null
@@ -1,308 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * PROC INTERFACES
- *
- * This file supports the /proc interfaces for the GRU driver
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/proc_fs.h>
-#include <linux/device.h>
-#include <linux/seq_file.h>
-#include <linux/uaccess.h>
-#include "gru.h"
-#include "grulib.h"
-#include "grutables.h"
-
-#define printstat(s, f) printstat_val(s, &gru_stats.f, #f)
-
-static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id)
-{
- unsigned long val = atomic_long_read(v);
-
- seq_printf(s, "%16lu %s\n", val, id);
-}
-
-static int statistics_show(struct seq_file *s, void *p)
-{
- printstat(s, vdata_alloc);
- printstat(s, vdata_free);
- printstat(s, gts_alloc);
- printstat(s, gts_free);
- printstat(s, gms_alloc);
- printstat(s, gms_free);
- printstat(s, gts_double_allocate);
- printstat(s, assign_context);
- printstat(s, assign_context_failed);
- printstat(s, free_context);
- printstat(s, load_user_context);
- printstat(s, load_kernel_context);
- printstat(s, lock_kernel_context);
- printstat(s, unlock_kernel_context);
- printstat(s, steal_user_context);
- printstat(s, steal_kernel_context);
- printstat(s, steal_context_failed);
- printstat(s, nopfn);
- printstat(s, asid_new);
- printstat(s, asid_next);
- printstat(s, asid_wrap);
- printstat(s, asid_reuse);
- printstat(s, intr);
- printstat(s, intr_cbr);
- printstat(s, intr_tfh);
- printstat(s, intr_spurious);
- printstat(s, intr_mm_lock_failed);
- printstat(s, call_os);
- printstat(s, call_os_wait_queue);
- printstat(s, user_flush_tlb);
- printstat(s, user_unload_context);
- printstat(s, user_exception);
- printstat(s, set_context_option);
- printstat(s, check_context_retarget_intr);
- printstat(s, check_context_unload);
- printstat(s, tlb_dropin);
- printstat(s, tlb_preload_page);
- printstat(s, tlb_dropin_fail_no_asid);
- printstat(s, tlb_dropin_fail_upm);
- printstat(s, tlb_dropin_fail_invalid);
- printstat(s, tlb_dropin_fail_range_active);
- printstat(s, tlb_dropin_fail_idle);
- printstat(s, tlb_dropin_fail_fmm);
- printstat(s, tlb_dropin_fail_no_exception);
- printstat(s, tfh_stale_on_fault);
- printstat(s, mmu_invalidate_range);
- printstat(s, mmu_invalidate_page);
- printstat(s, flush_tlb);
- printstat(s, flush_tlb_gru);
- printstat(s, flush_tlb_gru_tgh);
- printstat(s, flush_tlb_gru_zero_asid);
- printstat(s, copy_gpa);
- printstat(s, read_gpa);
- printstat(s, mesq_receive);
- printstat(s, mesq_receive_none);
- printstat(s, mesq_send);
- printstat(s, mesq_send_failed);
- printstat(s, mesq_noop);
- printstat(s, mesq_send_unexpected_error);
- printstat(s, mesq_send_lb_overflow);
- printstat(s, mesq_send_qlimit_reached);
- printstat(s, mesq_send_amo_nacked);
- printstat(s, mesq_send_put_nacked);
- printstat(s, mesq_qf_locked);
- printstat(s, mesq_qf_noop_not_full);
- printstat(s, mesq_qf_switch_head_failed);
- printstat(s, mesq_qf_unexpected_error);
- printstat(s, mesq_noop_unexpected_error);
- printstat(s, mesq_noop_lb_overflow);
- printstat(s, mesq_noop_qlimit_reached);
- printstat(s, mesq_noop_amo_nacked);
- printstat(s, mesq_noop_put_nacked);
- printstat(s, mesq_noop_page_overflow);
- return 0;
-}
-
-static ssize_t statistics_write(struct file *file, const char __user *userbuf,
- size_t count, loff_t *data)
-{
- memset(&gru_stats, 0, sizeof(gru_stats));
- return count;
-}
-
-static int mcs_statistics_show(struct seq_file *s, void *p)
-{
- int op;
- unsigned long total, count, max;
- static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt",
- "cch_interrupt_sync", "cch_deallocate", "tfh_write_only",
- "tfh_write_restart", "tgh_invalidate"};
-
- seq_puts(s, "#id count aver-clks max-clks\n");
- for (op = 0; op < mcsop_last; op++) {
- count = atomic_long_read(&mcs_op_statistics[op].count);
- total = atomic_long_read(&mcs_op_statistics[op].total);
- max = mcs_op_statistics[op].max;
- seq_printf(s, "%-20s%12ld%12ld%12ld\n", id[op], count,
- count ? total / count : 0, max);
- }
- return 0;
-}
-
-static ssize_t mcs_statistics_write(struct file *file,
- const char __user *userbuf, size_t count, loff_t *data)
-{
- memset(mcs_op_statistics, 0, sizeof(mcs_op_statistics));
- return count;
-}
-
-static int options_show(struct seq_file *s, void *p)
-{
- seq_printf(s, "#bitmask: 1=trace, 2=statistics\n");
- seq_printf(s, "0x%lx\n", gru_options);
- return 0;
-}
-
-static ssize_t options_write(struct file *file, const char __user *userbuf,
- size_t count, loff_t *data)
-{
- int ret;
-
- ret = kstrtoul_from_user(userbuf, count, 0, &gru_options);
- if (ret)
- return ret;
-
- return count;
-}
-
-static int cch_seq_show(struct seq_file *file, void *data)
-{
- long gid = *(long *)data;
- int i;
- struct gru_state *gru = GID_TO_GRU(gid);
- struct gru_thread_state *ts;
- const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" };
-
- if (gid == 0)
- seq_puts(file, "# gid bid ctx# asid pid cbrs dsbytes mode\n");
- if (gru)
- for (i = 0; i < GRU_NUM_CCH; i++) {
- ts = gru->gs_gts[i];
- if (!ts)
- continue;
- seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n",
- gru->gs_gid, gru->gs_blade_id, i,
- is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid,
- is_kernel_context(ts) ? 0 : ts->ts_tgid_owner,
- ts->ts_cbr_au_count * GRU_CBR_AU_SIZE,
- ts->ts_cbr_au_count * GRU_DSR_AU_BYTES,
- mode[ts->ts_user_options &
- GRU_OPT_MISS_MASK]);
- }
-
- return 0;
-}
-
-static int gru_seq_show(struct seq_file *file, void *data)
-{
- long gid = *(long *)data, ctxfree, cbrfree, dsrfree;
- struct gru_state *gru = GID_TO_GRU(gid);
-
- if (gid == 0) {
- seq_puts(file, "# gid nid ctx cbr dsr ctx cbr dsr\n");
- seq_puts(file, "# busy busy busy free free free\n");
- }
- if (gru) {
- ctxfree = GRU_NUM_CCH - gru->gs_active_contexts;
- cbrfree = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE;
- dsrfree = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES;
- seq_printf(file, " %5d%5d%7ld%6ld%6ld%8ld%6ld%6ld\n",
- gru->gs_gid, gru->gs_blade_id, GRU_NUM_CCH - ctxfree,
- GRU_NUM_CBE - cbrfree, GRU_NUM_DSR_BYTES - dsrfree,
- ctxfree, cbrfree, dsrfree);
- }
-
- return 0;
-}
-
-static void seq_stop(struct seq_file *file, void *data)
-{
-}
-
-static void *seq_start(struct seq_file *file, loff_t *gid)
-{
- if (*gid < gru_max_gids)
- return gid;
- return NULL;
-}
-
-static void *seq_next(struct seq_file *file, void *data, loff_t *gid)
-{
- (*gid)++;
- if (*gid < gru_max_gids)
- return gid;
- return NULL;
-}
-
-static const struct seq_operations cch_seq_ops = {
- .start = seq_start,
- .next = seq_next,
- .stop = seq_stop,
- .show = cch_seq_show
-};
-
-static const struct seq_operations gru_seq_ops = {
- .start = seq_start,
- .next = seq_next,
- .stop = seq_stop,
- .show = gru_seq_show
-};
-
-static int statistics_open(struct inode *inode, struct file *file)
-{
- return single_open(file, statistics_show, NULL);
-}
-
-static int mcs_statistics_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mcs_statistics_show, NULL);
-}
-
-static int options_open(struct inode *inode, struct file *file)
-{
- return single_open(file, options_show, NULL);
-}
-
-/* *INDENT-OFF* */
-static const struct proc_ops statistics_proc_ops = {
- .proc_open = statistics_open,
- .proc_read = seq_read,
- .proc_write = statistics_write,
- .proc_lseek = seq_lseek,
- .proc_release = single_release,
-};
-
-static const struct proc_ops mcs_statistics_proc_ops = {
- .proc_open = mcs_statistics_open,
- .proc_read = seq_read,
- .proc_write = mcs_statistics_write,
- .proc_lseek = seq_lseek,
- .proc_release = single_release,
-};
-
-static const struct proc_ops options_proc_ops = {
- .proc_open = options_open,
- .proc_read = seq_read,
- .proc_write = options_write,
- .proc_lseek = seq_lseek,
- .proc_release = single_release,
-};
-
-static struct proc_dir_entry *proc_gru __read_mostly;
-
-int gru_proc_init(void)
-{
- proc_gru = proc_mkdir("sgi_uv/gru", NULL);
- if (!proc_gru)
- return -1;
- if (!proc_create("statistics", 0644, proc_gru, &statistics_proc_ops))
- goto err;
- if (!proc_create("mcs_statistics", 0644, proc_gru, &mcs_statistics_proc_ops))
- goto err;
- if (!proc_create("debug_options", 0644, proc_gru, &options_proc_ops))
- goto err;
- if (!proc_create_seq("cch_status", 0444, proc_gru, &cch_seq_ops))
- goto err;
- if (!proc_create_seq("gru_status", 0444, proc_gru, &gru_seq_ops))
- goto err;
- return 0;
-err:
- remove_proc_subtree("sgi_uv/gru", NULL);
- return -1;
-}
-
-void gru_proc_exit(void)
-{
- remove_proc_subtree("sgi_uv/gru", NULL);
-}
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h
deleted file mode 100644
index 640daf1994df..000000000000
--- a/drivers/misc/sgi-gru/grutables.h
+++ /dev/null
@@ -1,659 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * SN Platform GRU Driver
- *
- * GRU DRIVER TABLES, MACROS, externs, etc
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#ifndef __GRUTABLES_H__
-#define __GRUTABLES_H__
-
-/*
- * GRU Chiplet:
- * The GRU is a user addressible memory accelerator. It provides
- * several forms of load, store, memset, bcopy instructions. In addition, it
- * contains special instructions for AMOs, sending messages to message
- * queues, etc.
- *
- * The GRU is an integral part of the node controller. It connects
- * directly to the cpu socket. In its current implementation, there are 2
- * GRU chiplets in the node controller on each blade (~node).
- *
- * The entire GRU memory space is fully coherent and cacheable by the cpus.
- *
- * Each GRU chiplet has a physical memory map that looks like the following:
- *
- * +-----------------+
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * |/////////////////|
- * +-----------------+
- * | system control |
- * +-----------------+ _______ +-------------+
- * |/////////////////| / | |
- * |/////////////////| / | |
- * |/////////////////| / | instructions|
- * |/////////////////| / | |
- * |/////////////////| / | |
- * |/////////////////| / |-------------|
- * |/////////////////| / | |
- * +-----------------+ | |
- * | context 15 | | data |
- * +-----------------+ | |
- * | ...... | \ | |
- * +-----------------+ \____________ +-------------+
- * | context 1 |
- * +-----------------+
- * | context 0 |
- * +-----------------+
- *
- * Each of the "contexts" is a chunk of memory that can be mmaped into user
- * space. The context consists of 2 parts:
- *
- * - an instruction space that can be directly accessed by the user
- * to issue GRU instructions and to check instruction status.
- *
- * - a data area that acts as normal RAM.
- *
- * User instructions contain virtual addresses of data to be accessed by the
- * GRU. The GRU contains a TLB that is used to convert these user virtual
- * addresses to physical addresses.
- *
- * The "system control" area of the GRU chiplet is used by the kernel driver
- * to manage user contexts and to perform functions such as TLB dropin and
- * purging.
- *
- * One context may be reserved for the kernel and used for cross-partition
- * communication. The GRU will also be used to asynchronously zero out
- * large blocks of memory (not currently implemented).
- *
- *
- * Tables:
- *
- * VDATA-VMA Data - Holds a few parameters. Head of linked list of
- * GTS tables for threads using the GSEG
- * GTS - Gru Thread State - contains info for managing a GSEG context. A
- * GTS is allocated for each thread accessing a
- * GSEG.
- * GTD - GRU Thread Data - contains shadow copy of GRU data when GSEG is
- * not loaded into a GRU
- * GMS - GRU Memory Struct - Used to manage TLB shootdowns. Tracks GRUs
- * where a GSEG has been loaded. Similar to
- * an mm_struct but for GRU.
- *
- * GS - GRU State - Used to manage the state of a GRU chiplet
- * BS - Blade State - Used to manage state of all GRU chiplets
- * on a blade
- *
- *
- * Normal task tables for task using GRU.
- * - 2 threads in process
- * - 2 GSEGs open in process
- * - GSEG1 is being used by both threads
- * - GSEG2 is used only by thread 2
- *
- * task -->|
- * task ---+---> mm ->------ (notifier) -------+-> gms
- * | |
- * |--> vma -> vdata ---> gts--->| GSEG1 (thread1)
- * | | |
- * | +-> gts--->| GSEG1 (thread2)
- * | |
- * |--> vma -> vdata ---> gts--->| GSEG2 (thread2)
- * .
- * .
- *
- * GSEGs are marked DONTCOPY on fork
- *
- * At open
- * file.private_data -> NULL
- *
- * At mmap,
- * vma -> vdata
- *
- * After gseg reference
- * vma -> vdata ->gts
- *
- * After fork
- * parent
- * vma -> vdata -> gts
- * child
- * (vma is not copied)
- *
- */
-
-#include <linux/refcount.h>
-#include <linux/rmap.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/mmu_notifier.h>
-#include <linux/mm_types.h>
-#include "gru.h"
-#include "grulib.h"
-#include "gruhandles.h"
-
-extern struct gru_stats_s gru_stats;
-extern struct gru_blade_state *gru_base[];
-extern unsigned long gru_start_paddr, gru_end_paddr;
-extern void *gru_start_vaddr;
-extern unsigned int gru_max_gids;
-
-#define GRU_MAX_BLADES MAX_NUMNODES
-#define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE)
-
-#define GRU_DRIVER_ID_STR "SGI GRU Device Driver"
-#define GRU_DRIVER_VERSION_STR "0.85"
-
-/*
- * GRU statistics.
- */
-struct gru_stats_s {
- atomic_long_t vdata_alloc;
- atomic_long_t vdata_free;
- atomic_long_t gts_alloc;
- atomic_long_t gts_free;
- atomic_long_t gms_alloc;
- atomic_long_t gms_free;
- atomic_long_t gts_double_allocate;
- atomic_long_t assign_context;
- atomic_long_t assign_context_failed;
- atomic_long_t free_context;
- atomic_long_t load_user_context;
- atomic_long_t load_kernel_context;
- atomic_long_t lock_kernel_context;
- atomic_long_t unlock_kernel_context;
- atomic_long_t steal_user_context;
- atomic_long_t steal_kernel_context;
- atomic_long_t steal_context_failed;
- atomic_long_t nopfn;
- atomic_long_t asid_new;
- atomic_long_t asid_next;
- atomic_long_t asid_wrap;
- atomic_long_t asid_reuse;
- atomic_long_t intr;
- atomic_long_t intr_cbr;
- atomic_long_t intr_tfh;
- atomic_long_t intr_spurious;
- atomic_long_t intr_mm_lock_failed;
- atomic_long_t call_os;
- atomic_long_t call_os_wait_queue;
- atomic_long_t user_flush_tlb;
- atomic_long_t user_unload_context;
- atomic_long_t user_exception;
- atomic_long_t set_context_option;
- atomic_long_t check_context_retarget_intr;
- atomic_long_t check_context_unload;
- atomic_long_t tlb_dropin;
- atomic_long_t tlb_preload_page;
- atomic_long_t tlb_dropin_fail_no_asid;
- atomic_long_t tlb_dropin_fail_upm;
- atomic_long_t tlb_dropin_fail_invalid;
- atomic_long_t tlb_dropin_fail_range_active;
- atomic_long_t tlb_dropin_fail_idle;
- atomic_long_t tlb_dropin_fail_fmm;
- atomic_long_t tlb_dropin_fail_no_exception;
- atomic_long_t tfh_stale_on_fault;
- atomic_long_t mmu_invalidate_range;
- atomic_long_t mmu_invalidate_page;
- atomic_long_t flush_tlb;
- atomic_long_t flush_tlb_gru;
- atomic_long_t flush_tlb_gru_tgh;
- atomic_long_t flush_tlb_gru_zero_asid;
-
- atomic_long_t copy_gpa;
- atomic_long_t read_gpa;
-
- atomic_long_t mesq_receive;
- atomic_long_t mesq_receive_none;
- atomic_long_t mesq_send;
- atomic_long_t mesq_send_failed;
- atomic_long_t mesq_noop;
- atomic_long_t mesq_send_unexpected_error;
- atomic_long_t mesq_send_lb_overflow;
- atomic_long_t mesq_send_qlimit_reached;
- atomic_long_t mesq_send_amo_nacked;
- atomic_long_t mesq_send_put_nacked;
- atomic_long_t mesq_page_overflow;
- atomic_long_t mesq_qf_locked;
- atomic_long_t mesq_qf_noop_not_full;
- atomic_long_t mesq_qf_switch_head_failed;
- atomic_long_t mesq_qf_unexpected_error;
- atomic_long_t mesq_noop_unexpected_error;
- atomic_long_t mesq_noop_lb_overflow;
- atomic_long_t mesq_noop_qlimit_reached;
- atomic_long_t mesq_noop_amo_nacked;
- atomic_long_t mesq_noop_put_nacked;
- atomic_long_t mesq_noop_page_overflow;
-
-};
-
-enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync,
- cchop_deallocate, tfhop_write_only, tfhop_write_restart,
- tghop_invalidate, mcsop_last};
-
-struct mcs_op_statistic {
- atomic_long_t count;
- atomic_long_t total;
- unsigned long max;
-};
-
-extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
-
-#define OPT_DPRINT 1
-#define OPT_STATS 2
-
-
-#define IRQ_GRU 110 /* Starting IRQ number for interrupts */
-
-/* Delay in jiffies between attempts to assign a GRU context */
-#define GRU_ASSIGN_DELAY ((HZ * 20) / 1000)
-
-/*
- * If a process has it's context stolen, min delay in jiffies before trying to
- * steal a context from another process.
- */
-#define GRU_STEAL_DELAY ((HZ * 200) / 1000)
-
-#define STAT(id) do { \
- if (gru_options & OPT_STATS) \
- atomic_long_inc(&gru_stats.id); \
- } while (0)
-
-#ifdef CONFIG_SGI_GRU_DEBUG
-#define gru_dbg(dev, fmt, x...) \
- do { \
- if (gru_options & OPT_DPRINT) \
- printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\
- } while (0)
-#else
-#define gru_dbg(x...)
-#endif
-
-/*-----------------------------------------------------------------------------
- * ASID management
- */
-#define MAX_ASID 0xfffff0
-#define MIN_ASID 8
-#define ASID_INC 8 /* number of regions */
-
-/* Generate a GRU asid value from a GRU base asid & a virtual address. */
-#define VADDR_HI_BIT 64
-#define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3)
-#define GRUASID(asid, addr) ((asid) + GRUREGION(addr))
-
-/*------------------------------------------------------------------------------
- * File & VMS Tables
- */
-
-struct gru_state;
-
-/*
- * This structure is pointed to from the mmstruct via the notifier pointer.
- * There is one of these per address space.
- */
-struct gru_mm_tracker { /* pack to reduce size */
- unsigned int mt_asid_gen:24; /* ASID wrap count */
- unsigned int mt_asid:24; /* current base ASID for gru */
- unsigned short mt_ctxbitmap:16;/* bitmap of contexts using
- asid */
-} __attribute__ ((packed));
-
-struct gru_mm_struct {
- struct mmu_notifier ms_notifier;
- spinlock_t ms_asid_lock; /* protects ASID assignment */
- atomic_t ms_range_active;/* num range_invals active */
- wait_queue_head_t ms_wait_queue;
- DECLARE_BITMAP(ms_asidmap, GRU_MAX_GRUS);
- struct gru_mm_tracker ms_asids[GRU_MAX_GRUS];
-};
-
-/*
- * One of these structures is allocated when a GSEG is mmaped. The
- * structure is pointed to by the vma->vm_private_data field in the vma struct.
- */
-struct gru_vma_data {
- spinlock_t vd_lock; /* Serialize access to vma */
- struct list_head vd_head; /* head of linked list of gts */
- long vd_user_options;/* misc user option flags */
- int vd_cbr_au_count;
- int vd_dsr_au_count;
- unsigned char vd_tlb_preload_count;
-};
-
-/*
- * One of these is allocated for each thread accessing a mmaped GRU. A linked
- * list of these structure is hung off the struct gru_vma_data in the mm_struct.
- */
-struct gru_thread_state {
- struct list_head ts_next; /* list - head at vma-private */
- struct mutex ts_ctxlock; /* load/unload CTX lock */
- struct mm_struct *ts_mm; /* mm currently mapped to
- context */
- struct vm_area_struct *ts_vma; /* vma of GRU context */
- struct gru_state *ts_gru; /* GRU where the context is
- loaded */
- struct gru_mm_struct *ts_gms; /* asid & ioproc struct */
- unsigned char ts_tlb_preload_count; /* TLB preload pages */
- unsigned long ts_cbr_map; /* map of allocated CBRs */
- unsigned long ts_dsr_map; /* map of allocated DATA
- resources */
- unsigned long ts_steal_jiffies;/* jiffies when context last
- stolen */
- long ts_user_options;/* misc user option flags */
- pid_t ts_tgid_owner; /* task that is using the
- context - for migration */
- short ts_user_blade_id;/* user selected blade */
- signed char ts_user_chiplet_id;/* user selected chiplet */
- unsigned short ts_sizeavail; /* Pagesizes in use */
- int ts_tsid; /* thread that owns the
- structure */
- int ts_tlb_int_select;/* target cpu if interrupts
- enabled */
- int ts_ctxnum; /* context number where the
- context is loaded */
- refcount_t ts_refcnt; /* reference count GTS */
- unsigned char ts_dsr_au_count;/* Number of DSR resources
- required for contest */
- unsigned char ts_cbr_au_count;/* Number of CBR resources
- required for contest */
- signed char ts_cch_req_slice;/* CCH packet slice */
- signed char ts_blade; /* If >= 0, migrate context if
- ref from different blade */
- signed char ts_force_cch_reload;
- signed char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each
- allocated CB */
- int ts_data_valid; /* Indicates if ts_gdata has
- valid data */
- struct gru_gseg_statistics ustats; /* User statistics */
- unsigned long ts_gdata[]; /* save area for GRU data (CB,
- DS, CBE) */
-};
-
-/*
- * Threaded programs actually allocate an array of GSEGs when a context is
- * created. Each thread uses a separate GSEG. TSID is the index into the GSEG
- * array.
- */
-#define TSID(a, v) (((a) - (v)->vm_start) / GRU_GSEG_PAGESIZE)
-#define UGRUADDR(gts) ((gts)->ts_vma->vm_start + \
- (gts)->ts_tsid * GRU_GSEG_PAGESIZE)
-
-#define NULLCTX (-1) /* if context not loaded into GRU */
-
-/*-----------------------------------------------------------------------------
- * GRU State Tables
- */
-
-/*
- * One of these exists for each GRU chiplet.
- */
-struct gru_state {
- struct gru_blade_state *gs_blade; /* GRU state for entire
- blade */
- unsigned long gs_gru_base_paddr; /* Physical address of
- gru segments (64) */
- void *gs_gru_base_vaddr; /* Virtual address of
- gru segments (64) */
- unsigned short gs_gid; /* unique GRU number */
- unsigned short gs_blade_id; /* blade of GRU */
- unsigned char gs_chiplet_id; /* blade chiplet of GRU */
- unsigned char gs_tgh_local_shift; /* used to pick TGH for
- local flush */
- unsigned char gs_tgh_first_remote; /* starting TGH# for
- remote flush */
- spinlock_t gs_asid_lock; /* lock used for
- assigning asids */
- spinlock_t gs_lock; /* lock used for
- assigning contexts */
-
- /* -- the following are protected by the gs_asid_lock spinlock ---- */
- unsigned int gs_asid; /* Next availe ASID */
- unsigned int gs_asid_limit; /* Limit of available
- ASIDs */
- unsigned int gs_asid_gen; /* asid generation.
- Inc on wrap */
-
- /* --- the following fields are protected by the gs_lock spinlock --- */
- unsigned long gs_context_map; /* bitmap to manage
- contexts in use */
- unsigned long gs_cbr_map; /* bitmap to manage CB
- resources */
- unsigned long gs_dsr_map; /* bitmap used to manage
- DATA resources */
- unsigned int gs_reserved_cbrs; /* Number of kernel-
- reserved cbrs */
- unsigned int gs_reserved_dsr_bytes; /* Bytes of kernel-
- reserved dsrs */
- unsigned short gs_active_contexts; /* number of contexts
- in use */
- struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using
- the context */
- int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */
-};
-
-/*
- * This structure contains the GRU state for all the GRUs on a blade.
- */
-struct gru_blade_state {
- void *kernel_cb; /* First kernel
- reserved cb */
- void *kernel_dsr; /* First kernel
- reserved DSR */
- struct rw_semaphore bs_kgts_sema; /* lock for kgts */
- struct gru_thread_state *bs_kgts; /* GTS for kernel use */
-
- /* ---- the following are used for managing kernel async GRU CBRs --- */
- int bs_async_dsr_bytes; /* DSRs for async */
- int bs_async_cbrs; /* CBRs AU for async */
- struct completion *bs_async_wq;
-
- /* ---- the following are protected by the bs_lock spinlock ---- */
- spinlock_t bs_lock; /* lock used for
- stealing contexts */
- int bs_lru_ctxnum; /* STEAL - last context
- stolen */
- struct gru_state *bs_lru_gru; /* STEAL - last gru
- stolen */
-
- struct gru_state bs_grus[GRU_CHIPLETS_PER_BLADE];
-};
-
-/*-----------------------------------------------------------------------------
- * Address Primitives
- */
-#define get_tfm_for_cpu(g, c) \
- ((struct gru_tlb_fault_map *)get_tfm((g)->gs_gru_base_vaddr, (c)))
-#define get_tfh_by_index(g, i) \
- ((struct gru_tlb_fault_handle *)get_tfh((g)->gs_gru_base_vaddr, (i)))
-#define get_tgh_by_index(g, i) \
- ((struct gru_tlb_global_handle *)get_tgh((g)->gs_gru_base_vaddr, (i)))
-#define get_cbe_by_index(g, i) \
- ((struct gru_control_block_extended *)get_cbe((g)->gs_gru_base_vaddr,\
- (i)))
-
-/*-----------------------------------------------------------------------------
- * Useful Macros
- */
-
-/* Given a blade# & chiplet#, get a pointer to the GRU */
-#define get_gru(b, c) (&gru_base[b]->bs_grus[c])
-
-/* Number of bytes to save/restore when unloading/loading GRU contexts */
-#define DSR_BYTES(dsr) ((dsr) * GRU_DSR_AU_BYTES)
-#define CBR_BYTES(cbr) ((cbr) * GRU_HANDLE_BYTES * GRU_CBR_AU_SIZE * 2)
-
-/* Convert a user CB number to the actual CBRNUM */
-#define thread_cbr_number(gts, n) ((gts)->ts_cbr_idx[(n) / GRU_CBR_AU_SIZE] \
- * GRU_CBR_AU_SIZE + (n) % GRU_CBR_AU_SIZE)
-
-/* Convert a gid to a pointer to the GRU */
-#define GID_TO_GRU(gid) \
- (gru_base[(gid) / GRU_CHIPLETS_PER_BLADE] ? \
- (&gru_base[(gid) / GRU_CHIPLETS_PER_BLADE]-> \
- bs_grus[(gid) % GRU_CHIPLETS_PER_BLADE]) : \
- NULL)
-
-/* Scan all active GRUs in a GRU bitmap */
-#define for_each_gru_in_bitmap(gid, map) \
- for_each_set_bit((gid), (map), GRU_MAX_GRUS)
-
-/* Scan all active GRUs on a specific blade */
-#define for_each_gru_on_blade(gru, nid, i) \
- for ((gru) = gru_base[nid]->bs_grus, (i) = 0; \
- (i) < GRU_CHIPLETS_PER_BLADE; \
- (i)++, (gru)++)
-
-/* Scan all GRUs */
-#define foreach_gid(gid) \
- for ((gid) = 0; (gid) < gru_max_gids; (gid)++)
-
-/* Scan all active GTSs on a gru. Note: must hold ss_lock to use this macro. */
-#define for_each_gts_on_gru(gts, gru, ctxnum) \
- for ((ctxnum) = 0; (ctxnum) < GRU_NUM_CCH; (ctxnum)++) \
- if (((gts) = (gru)->gs_gts[ctxnum]))
-
-/* Scan each CBR whose bit is set in a TFM (or copy of) */
-#define for_each_cbr_in_tfm(i, map) \
- for_each_set_bit((i), (map), GRU_NUM_CBE)
-
-/* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */
-#define for_each_cbr_in_allocation_map(i, map, k) \
- for_each_set_bit((k), (map), GRU_CBR_AU) \
- for ((i) = (k)*GRU_CBR_AU_SIZE; \
- (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++)
-
-#define gseg_physical_address(gru, ctxnum) \
- ((gru)->gs_gru_base_paddr + ctxnum * GRU_GSEG_STRIDE)
-#define gseg_virtual_address(gru, ctxnum) \
- ((gru)->gs_gru_base_vaddr + ctxnum * GRU_GSEG_STRIDE)
-
-/*-----------------------------------------------------------------------------
- * Lock / Unlock GRU handles
- * Use the "delresp" bit in the handle as a "lock" bit.
- */
-
-/* Lock hierarchy checking enabled only in emulator */
-
-/* 0 = lock failed, 1 = locked */
-static inline int __trylock_handle(void *h)
-{
- return !test_and_set_bit(1, h);
-}
-
-static inline void __lock_handle(void *h)
-{
- while (test_and_set_bit(1, h))
- cpu_relax();
-}
-
-static inline void __unlock_handle(void *h)
-{
- clear_bit(1, h);
-}
-
-static inline int trylock_cch_handle(struct gru_context_configuration_handle *cch)
-{
- return __trylock_handle(cch);
-}
-
-static inline void lock_cch_handle(struct gru_context_configuration_handle *cch)
-{
- __lock_handle(cch);
-}
-
-static inline void unlock_cch_handle(struct gru_context_configuration_handle
- *cch)
-{
- __unlock_handle(cch);
-}
-
-static inline void lock_tgh_handle(struct gru_tlb_global_handle *tgh)
-{
- __lock_handle(tgh);
-}
-
-static inline void unlock_tgh_handle(struct gru_tlb_global_handle *tgh)
-{
- __unlock_handle(tgh);
-}
-
-static inline int is_kernel_context(struct gru_thread_state *gts)
-{
- return !gts->ts_mm;
-}
-
-/*
- * The following are for Nehelem-EX. A more general scheme is needed for
- * future processors.
- */
-#define UV_MAX_INT_CORES 8
-#define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1)
-#define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1)
-#define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \
- ((cpu_physical_id(p) >> 1) & 3))
-/*-----------------------------------------------------------------------------
- * Function prototypes & externs
- */
-struct gru_unload_context_req;
-
-extern const struct vm_operations_struct gru_vm_ops;
-extern struct device *grudev;
-
-extern struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma,
- int tsid);
-extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct
- *vma, int tsid);
-extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct
- *vma, int tsid);
-extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts);
-extern void gru_load_context(struct gru_thread_state *gts);
-extern void gru_steal_context(struct gru_thread_state *gts);
-extern void gru_unload_context(struct gru_thread_state *gts, int savestate);
-extern int gru_update_cch(struct gru_thread_state *gts);
-extern void gts_drop(struct gru_thread_state *gts);
-extern void gru_tgh_flush_init(struct gru_state *gru);
-extern int gru_kservices_init(void);
-extern void gru_kservices_exit(void);
-extern irqreturn_t gru0_intr(int irq, void *dev_id);
-extern irqreturn_t gru1_intr(int irq, void *dev_id);
-extern irqreturn_t gru_intr_mblade(int irq, void *dev_id);
-extern int gru_dump_chiplet_request(unsigned long arg);
-extern long gru_get_gseg_statistics(unsigned long arg);
-extern int gru_handle_user_call_os(unsigned long address);
-extern int gru_user_flush_tlb(unsigned long arg);
-extern int gru_user_unload_context(unsigned long arg);
-extern int gru_get_exception_detail(unsigned long arg);
-extern int gru_set_context_option(unsigned long address);
-extern int gru_check_context_placement(struct gru_thread_state *gts);
-extern int gru_cpu_fault_map_id(void);
-extern struct vm_area_struct *gru_find_vma(unsigned long vaddr);
-extern void gru_flush_all_tlb(struct gru_state *gru);
-extern int gru_proc_init(void);
-extern void gru_proc_exit(void);
-
-extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
- int cbr_au_count, int dsr_au_count,
- unsigned char tlb_preload_count, int options, int tsid);
-extern unsigned long gru_reserve_cb_resources(struct gru_state *gru,
- int cbr_au_count, signed char *cbmap);
-extern unsigned long gru_reserve_ds_resources(struct gru_state *gru,
- int dsr_au_count, signed char *dsmap);
-extern vm_fault_t gru_fault(struct vm_fault *vmf);
-extern struct gru_mm_struct *gru_register_mmu_notifier(void);
-extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms);
-
-extern int gru_ktest(unsigned long arg);
-extern void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start,
- unsigned long len);
-
-extern unsigned long gru_options;
-
-#endif /* __GRUTABLES_H__ */
diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c
deleted file mode 100644
index a8121d40be68..000000000000
--- a/drivers/misc/sgi-gru/grutlbpurge.c
+++ /dev/null
@@ -1,316 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SN Platform GRU Driver
- *
- * MMUOPS callbacks + TLB flushing
- *
- * This file handles emu notifier callbacks from the core kernel. The callbacks
- * are used to update the TLB in the GRU as a result of changes in the
- * state of a process address space. This file also handles TLB invalidates
- * from the GRU driver.
- *
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/hugetlb.h>
-#include <linux/delay.h>
-#include <linux/timex.h>
-#include <linux/srcu.h>
-#include <asm/processor.h>
-#include "gru.h"
-#include "grutables.h"
-#include <asm/uv/uv_hub.h>
-
-#define gru_random() get_cycles()
-
-/* ---------------------------------- TLB Invalidation functions --------
- * get_tgh_handle
- *
- * Find a TGH to use for issuing a TLB invalidate. For GRUs that are on the
- * local blade, use a fixed TGH that is a function of the blade-local cpu
- * number. Normally, this TGH is private to the cpu & no contention occurs for
- * the TGH. For offblade GRUs, select a random TGH in the range above the
- * private TGHs. A spinlock is required to access this TGH & the lock must be
- * released when the invalidate is completes. This sucks, but it is the best we
- * can do.
- *
- * Note that the spinlock is IN the TGH handle so locking does not involve
- * additional cache lines.
- *
- */
-static inline int get_off_blade_tgh(struct gru_state *gru)
-{
- int n;
-
- n = GRU_NUM_TGH - gru->gs_tgh_first_remote;
- n = gru_random() % n;
- n += gru->gs_tgh_first_remote;
- return n;
-}
-
-static inline int get_on_blade_tgh(struct gru_state *gru)
-{
- return uv_blade_processor_id() >> gru->gs_tgh_local_shift;
-}
-
-static struct gru_tlb_global_handle *get_lock_tgh_handle(struct gru_state
- *gru)
-{
- struct gru_tlb_global_handle *tgh;
- int n;
-
- if (uv_numa_blade_id() == gru->gs_blade_id)
- n = get_on_blade_tgh(gru);
- else
- n = get_off_blade_tgh(gru);
- tgh = get_tgh_by_index(gru, n);
- lock_tgh_handle(tgh);
-
- return tgh;
-}
-
-static void get_unlock_tgh_handle(struct gru_tlb_global_handle *tgh)
-{
- unlock_tgh_handle(tgh);
-}
-
-/*
- * gru_flush_tlb_range
- *
- * General purpose TLB invalidation function. This function scans every GRU in
- * the ENTIRE system (partition) looking for GRUs where the specified MM has
- * been accessed by the GRU. For each GRU found, the TLB must be invalidated OR
- * the ASID invalidated. Invalidating an ASID causes a new ASID to be assigned
- * on the next fault. This effectively flushes the ENTIRE TLB for the MM at the
- * cost of (possibly) a large number of future TLBmisses.
- *
- * The current algorithm is optimized based on the following (somewhat true)
- * assumptions:
- * - GRU contexts are not loaded into a GRU unless a reference is made to
- * the data segment or control block (this is true, not an assumption).
- * If a DS/CB is referenced, the user will also issue instructions that
- * cause TLBmisses. It is not necessary to optimize for the case where
- * contexts are loaded but no instructions cause TLB misses. (I know
- * this will happen but I'm not optimizing for it).
- * - GRU instructions to invalidate TLB entries are SLOOOOWWW - normally
- * a few usec but in unusual cases, it could be longer. Avoid if
- * possible.
- * - intrablade process migration between cpus is not frequent but is
- * common.
- * - a GRU context is not typically migrated to a different GRU on the
- * blade because of intrablade migration
- * - interblade migration is rare. Processes migrate their GRU context to
- * the new blade.
- * - if interblade migration occurs, migration back to the original blade
- * is very very rare (ie., no optimization for this case)
- * - most GRU instruction operate on a subset of the user REGIONS. Code
- * & shared library regions are not likely targets of GRU instructions.
- *
- * To help improve the efficiency of TLB invalidation, the GMS data
- * structure is maintained for EACH address space (MM struct). The GMS is
- * also the structure that contains the pointer to the mmu callout
- * functions. This structure is linked to the mm_struct for the address space
- * using the mmu "register" function. The mmu interfaces are used to
- * provide the callbacks for TLB invalidation. The GMS contains:
- *
- * - asid[maxgrus] array. ASIDs are assigned to a GRU when a context is
- * loaded into the GRU.
- * - asidmap[maxgrus]. bitmap to make it easier to find non-zero asids in
- * the above array
- * - ctxbitmap[maxgrus]. Indicates the contexts that are currently active
- * in the GRU for the address space. This bitmap must be passed to the
- * GRU to do an invalidate.
- *
- * The current algorithm for invalidating TLBs is:
- * - scan the asidmap for GRUs where the context has been loaded, ie,
- * asid is non-zero.
- * - for each gru found:
- * - if the ctxtmap is non-zero, there are active contexts in the
- * GRU. TLB invalidate instructions must be issued to the GRU.
- * - if the ctxtmap is zero, no context is active. Set the ASID to
- * zero to force a full TLB invalidation. This is fast but will
- * cause a lot of TLB misses if the context is reloaded onto the
- * GRU
- *
- */
-
-void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start,
- unsigned long len)
-{
- struct gru_state *gru;
- struct gru_mm_tracker *asids;
- struct gru_tlb_global_handle *tgh;
- unsigned long num;
- int grupagesize, pagesize, pageshift, gid, asid;
-
- /* ZZZ TODO - handle huge pages */
- pageshift = PAGE_SHIFT;
- pagesize = (1UL << pageshift);
- grupagesize = GRU_PAGESIZE(pageshift);
- num = min(((len + pagesize - 1) >> pageshift), GRUMAXINVAL);
-
- STAT(flush_tlb);
- gru_dbg(grudev, "gms %p, start 0x%lx, len 0x%lx, asidmap 0x%lx\n", gms,
- start, len, gms->ms_asidmap[0]);
-
- spin_lock(&gms->ms_asid_lock);
- for_each_gru_in_bitmap(gid, gms->ms_asidmap) {
- STAT(flush_tlb_gru);
- gru = GID_TO_GRU(gid);
- asids = gms->ms_asids + gid;
- asid = asids->mt_asid;
- if (asids->mt_ctxbitmap && asid) {
- STAT(flush_tlb_gru_tgh);
- asid = GRUASID(asid, start);
- gru_dbg(grudev,
- " FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n",
- gid, asid, start, grupagesize, num, asids->mt_ctxbitmap);
- tgh = get_lock_tgh_handle(gru);
- tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0,
- num - 1, asids->mt_ctxbitmap);
- get_unlock_tgh_handle(tgh);
- } else {
- STAT(flush_tlb_gru_zero_asid);
- asids->mt_asid = 0;
- __clear_bit(gru->gs_gid, gms->ms_asidmap);
- gru_dbg(grudev,
- " CLEARASID gruid %d, asid 0x%x, cbtmap 0x%x, asidmap 0x%lx\n",
- gid, asid, asids->mt_ctxbitmap,
- gms->ms_asidmap[0]);
- }
- }
- spin_unlock(&gms->ms_asid_lock);
-}
-
-/*
- * Flush the entire TLB on a chiplet.
- */
-void gru_flush_all_tlb(struct gru_state *gru)
-{
- struct gru_tlb_global_handle *tgh;
-
- gru_dbg(grudev, "gid %d\n", gru->gs_gid);
- tgh = get_lock_tgh_handle(gru);
- tgh_invalidate(tgh, 0, ~0, 0, 1, 1, GRUMAXINVAL - 1, 0xffff);
- get_unlock_tgh_handle(tgh);
-}
-
-/*
- * MMUOPS notifier callout functions
- */
-static int gru_invalidate_range_start(struct mmu_notifier *mn,
- const struct mmu_notifier_range *range)
-{
- struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct,
- ms_notifier);
-
- STAT(mmu_invalidate_range);
- atomic_inc(&gms->ms_range_active);
- gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx, act %d\n", gms,
- range->start, range->end, atomic_read(&gms->ms_range_active));
- gru_flush_tlb_range(gms, range->start, range->end - range->start);
-
- return 0;
-}
-
-static void gru_invalidate_range_end(struct mmu_notifier *mn,
- const struct mmu_notifier_range *range)
-{
- struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct,
- ms_notifier);
-
- /* ..._and_test() provides needed barrier */
- (void)atomic_dec_and_test(&gms->ms_range_active);
-
- wake_up_all(&gms->ms_wait_queue);
- gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx\n",
- gms, range->start, range->end);
-}
-
-static struct mmu_notifier *gru_alloc_notifier(struct mm_struct *mm)
-{
- struct gru_mm_struct *gms;
-
- gms = kzalloc_obj(*gms);
- if (!gms)
- return ERR_PTR(-ENOMEM);
- STAT(gms_alloc);
- spin_lock_init(&gms->ms_asid_lock);
- init_waitqueue_head(&gms->ms_wait_queue);
-
- return &gms->ms_notifier;
-}
-
-static void gru_free_notifier(struct mmu_notifier *mn)
-{
- kfree(container_of(mn, struct gru_mm_struct, ms_notifier));
- STAT(gms_free);
-}
-
-static const struct mmu_notifier_ops gru_mmuops = {
- .invalidate_range_start = gru_invalidate_range_start,
- .invalidate_range_end = gru_invalidate_range_end,
- .alloc_notifier = gru_alloc_notifier,
- .free_notifier = gru_free_notifier,
-};
-
-struct gru_mm_struct *gru_register_mmu_notifier(void)
-{
- struct mmu_notifier *mn;
-
- mn = mmu_notifier_get_locked(&gru_mmuops, current->mm);
- if (IS_ERR(mn))
- return ERR_CAST(mn);
-
- return container_of(mn, struct gru_mm_struct, ms_notifier);
-}
-
-void gru_drop_mmu_notifier(struct gru_mm_struct *gms)
-{
- mmu_notifier_put(&gms->ms_notifier);
-}
-
-/*
- * Setup TGH parameters. There are:
- * - 24 TGH handles per GRU chiplet
- * - a portion (MAX_LOCAL_TGH) of the handles are reserved for
- * use by blade-local cpus
- * - the rest are used by off-blade cpus. This usage is
- * less frequent than blade-local usage.
- *
- * For now, use 16 handles for local flushes, 8 for remote flushes. If the blade
- * has less tan or equal to 16 cpus, each cpu has a unique handle that it can
- * use.
- */
-#define MAX_LOCAL_TGH 16
-
-void gru_tgh_flush_init(struct gru_state *gru)
-{
- int cpus, shift = 0, n;
-
- cpus = uv_blade_nr_possible_cpus(gru->gs_blade_id);
-
- /* n = cpus rounded up to next power of 2 */
- if (cpus) {
- n = 1 << fls(cpus - 1);
-
- /*
- * shift count for converting local cpu# to TGH index
- * 0 if cpus <= MAX_LOCAL_TGH,
- * 1 if cpus <= 2*MAX_LOCAL_TGH,
- * etc
- */
- shift = max(0, fls(n - 1) - fls(MAX_LOCAL_TGH - 1));
- }
- gru->gs_tgh_local_shift = shift;
-
- /* first starting TGH index to use for remote purges */
- gru->gs_tgh_first_remote = (cpus + (1 << shift) - 1) >> shift;
-
-}
diff --git a/drivers/misc/sgi-xp/Makefile b/drivers/misc/sgi-xp/Makefile
deleted file mode 100644
index 34c55a4045af..000000000000
--- a/drivers/misc/sgi-xp/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for SGI's XP devices.
-#
-
-obj-$(CONFIG_SGI_XP) += xp.o
-xp-y := xp_main.o xp_uv.o
-
-obj-$(CONFIG_SGI_XP) += xpc.o
-xpc-y := xpc_main.o xpc_channel.o xpc_partition.o \
- xpc_uv.o
-
-obj-$(CONFIG_SGI_XP) += xpnet.o
diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h
deleted file mode 100644
index 3185711beb07..000000000000
--- a/drivers/misc/sgi-xp/xp.h
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (C) 2004-2008 Silicon Graphics, Inc. All rights reserved.
- */
-
-/*
- * External Cross Partition (XP) structures and defines.
- */
-
-#ifndef _DRIVERS_MISC_SGIXP_XP_H
-#define _DRIVERS_MISC_SGIXP_XP_H
-
-#include <linux/mutex.h>
-
-#if defined CONFIG_X86_UV
-#include <asm/uv/uv.h>
-#endif
-
-#ifdef USE_DBUG_ON
-#define DBUG_ON(condition) BUG_ON(condition)
-#else
-#define DBUG_ON(condition)
-#endif
-
-/*
- * Define the maximum number of partitions the system can possibly support.
- * It is based on the maximum number of hardware partitionable regions. The
- * term 'region' in this context refers to the minimum number of nodes that
- * can comprise an access protection grouping. The access protection is in
- * regards to memory, IPI and IOI.
- *
- * The maximum number of hardware partitionable regions is equal to the
- * maximum number of nodes in the entire system divided by the minimum number
- * of nodes that comprise an access protection grouping.
- */
-#define XP_MAX_NPARTITIONS_SN2 64
-#define XP_MAX_NPARTITIONS_UV 256
-
-/*
- * XPC establishes channel connections between the local partition and any
- * other partition that is currently up. Over these channels, kernel-level
- * `users' can communicate with their counterparts on the other partitions.
- *
- * If the need for additional channels arises, one can simply increase
- * XPC_MAX_NCHANNELS accordingly. If the day should come where that number
- * exceeds the absolute MAXIMUM number of channels possible (eight), then one
- * will need to make changes to the XPC code to accommodate for this.
- *
- * The absolute maximum number of channels possible is limited to eight for
- * performance reasons on sn2 hardware. The internal cross partition structures
- * require sixteen bytes per channel, and eight allows all of this
- * interface-shared info to fit in one 128-byte cacheline.
- */
-#define XPC_MEM_CHANNEL 0 /* memory channel number */
-#define XPC_NET_CHANNEL 1 /* network channel number */
-
-#define XPC_MAX_NCHANNELS 2 /* max #of channels allowed */
-
-#if XPC_MAX_NCHANNELS > 8
-#error XPC_MAX_NCHANNELS exceeds absolute MAXIMUM possible.
-#endif
-
-/*
- * Define macro, XPC_MSG_SIZE(), is provided for the user
- * that wants to fit as many msg entries as possible in a given memory size
- * (e.g. a memory page).
- */
-#define XPC_MSG_MAX_SIZE 128
-#define XPC_MSG_HDR_MAX_SIZE 16
-#define XPC_MSG_PAYLOAD_MAX_SIZE (XPC_MSG_MAX_SIZE - XPC_MSG_HDR_MAX_SIZE)
-
-#define XPC_MSG_SIZE(_payload_size) \
- ALIGN(XPC_MSG_HDR_MAX_SIZE + (_payload_size), \
- is_uv_system() ? 64 : 128)
-
-
-/*
- * Define the return values and values passed to user's callout functions.
- * (It is important to add new value codes at the end just preceding
- * xpUnknownReason, which must have the highest numerical value.)
- */
-enum xp_retval {
- xpSuccess = 0,
-
- xpNotConnected, /* 1: channel is not connected */
- xpConnected, /* 2: channel connected (opened) */
- xpRETIRED1, /* 3: (formerly xpDisconnected) */
-
- xpMsgReceived, /* 4: message received */
- xpMsgDelivered, /* 5: message delivered and acknowledged */
-
- xpRETIRED2, /* 6: (formerly xpTransferFailed) */
-
- xpNoWait, /* 7: operation would require wait */
- xpRetry, /* 8: retry operation */
- xpTimeout, /* 9: timeout in xpc_allocate_msg_wait() */
- xpInterrupted, /* 10: interrupted wait */
-
- xpUnequalMsgSizes, /* 11: message size disparity between sides */
- xpInvalidAddress, /* 12: invalid address */
-
- xpNoMemory, /* 13: no memory available for XPC structures */
- xpLackOfResources, /* 14: insufficient resources for operation */
- xpUnregistered, /* 15: channel is not registered */
- xpAlreadyRegistered, /* 16: channel is already registered */
-
- xpPartitionDown, /* 17: remote partition is down */
- xpNotLoaded, /* 18: XPC module is not loaded */
- xpUnloading, /* 19: this side is unloading XPC module */
-
- xpBadMagic, /* 20: XPC MAGIC string not found */
-
- xpReactivating, /* 21: remote partition was reactivated */
-
- xpUnregistering, /* 22: this side is unregistering channel */
- xpOtherUnregistering, /* 23: other side is unregistering channel */
-
- xpCloneKThread, /* 24: cloning kernel thread */
- xpCloneKThreadFailed, /* 25: cloning kernel thread failed */
-
- xpNoHeartbeat, /* 26: remote partition has no heartbeat */
-
- xpPioReadError, /* 27: PIO read error */
- xpPhysAddrRegFailed, /* 28: registration of phys addr range failed */
-
- xpRETIRED3, /* 29: (formerly xpBteDirectoryError) */
- xpRETIRED4, /* 30: (formerly xpBtePoisonError) */
- xpRETIRED5, /* 31: (formerly xpBteWriteError) */
- xpRETIRED6, /* 32: (formerly xpBteAccessError) */
- xpRETIRED7, /* 33: (formerly xpBtePWriteError) */
- xpRETIRED8, /* 34: (formerly xpBtePReadError) */
- xpRETIRED9, /* 35: (formerly xpBteTimeOutError) */
- xpRETIRED10, /* 36: (formerly xpBteXtalkError) */
- xpRETIRED11, /* 37: (formerly xpBteNotAvailable) */
- xpRETIRED12, /* 38: (formerly xpBteUnmappedError) */
-
- xpBadVersion, /* 39: bad version number */
- xpVarsNotSet, /* 40: the XPC variables are not set up */
- xpNoRsvdPageAddr, /* 41: unable to get rsvd page's phys addr */
- xpInvalidPartid, /* 42: invalid partition ID */
- xpLocalPartid, /* 43: local partition ID */
-
- xpOtherGoingDown, /* 44: other side going down, reason unknown */
- xpSystemGoingDown, /* 45: system is going down, reason unknown */
- xpSystemHalt, /* 46: system is being halted */
- xpSystemReboot, /* 47: system is being rebooted */
- xpSystemPoweroff, /* 48: system is being powered off */
-
- xpDisconnecting, /* 49: channel disconnecting (closing) */
-
- xpOpenCloseError, /* 50: channel open/close protocol error */
-
- xpDisconnected, /* 51: channel disconnected (closed) */
-
- xpBteCopyError, /* 52: bte_copy() returned error */
- xpSalError, /* 53: sn SAL error */
- xpRsvdPageNotSet, /* 54: the reserved page is not set up */
- xpPayloadTooBig, /* 55: payload too large for message slot */
-
- xpUnsupported, /* 56: unsupported functionality or resource */
- xpNeedMoreInfo, /* 57: more info is needed by SAL */
-
- xpGruCopyError, /* 58: gru_copy_gru() returned error */
- xpGruSendMqError, /* 59: gru send message queue related error */
-
- xpBadChannelNumber, /* 60: invalid channel number */
- xpBadMsgType, /* 61: invalid message type */
- xpBiosError, /* 62: BIOS error */
-
- xpUnknownReason /* 63: unknown reason - must be last in enum */
-};
-
-/*
- * Define the callout function type used by XPC to update the user on
- * connection activity and state changes via the user function registered
- * by xpc_connect().
- *
- * Arguments:
- *
- * reason - reason code.
- * partid - partition ID associated with condition.
- * ch_number - channel # associated with condition.
- * data - pointer to optional data.
- * key - pointer to optional user-defined value provided as the "key"
- * argument to xpc_connect().
- *
- * A reason code of xpConnected indicates that a connection has been
- * established to the specified partition on the specified channel. The data
- * argument indicates the max number of entries allowed in the message queue.
- *
- * A reason code of xpMsgReceived indicates that a XPC message arrived from
- * the specified partition on the specified channel. The data argument
- * specifies the address of the message's payload. The user must call
- * xpc_received() when finished with the payload.
- *
- * All other reason codes indicate failure. The data argmument is NULL.
- * When a failure reason code is received, one can assume that the channel
- * is not connected.
- */
-typedef void (*xpc_channel_func) (enum xp_retval reason, short partid,
- int ch_number, void *data, void *key);
-
-/*
- * Define the callout function type used by XPC to notify the user of
- * messages received and delivered via the user function registered by
- * xpc_send_notify().
- *
- * Arguments:
- *
- * reason - reason code.
- * partid - partition ID associated with condition.
- * ch_number - channel # associated with condition.
- * key - pointer to optional user-defined value provided as the "key"
- * argument to xpc_send_notify().
- *
- * A reason code of xpMsgDelivered indicates that the message was delivered
- * to the intended recipient and that they have acknowledged its receipt by
- * calling xpc_received().
- *
- * All other reason codes indicate failure.
- *
- * NOTE: The user defined function must be callable by an interrupt handler
- * and thus cannot block.
- */
-typedef void (*xpc_notify_func) (enum xp_retval reason, short partid,
- int ch_number, void *key);
-
-/*
- * The following is a registration entry. There is a global array of these,
- * one per channel. It is used to record the connection registration made
- * by the users of XPC. As long as a registration entry exists, for any
- * partition that comes up, XPC will attempt to establish a connection on
- * that channel. Notification that a connection has been made will occur via
- * the xpc_channel_func function.
- *
- * The 'func' field points to the function to call when aynchronous
- * notification is required for such events as: a connection established/lost,
- * or an incoming message received, or an error condition encountered. A
- * non-NULL 'func' field indicates that there is an active registration for
- * the channel.
- */
-struct xpc_registration {
- struct mutex mutex;
- xpc_channel_func func; /* function to call */
- void *key; /* pointer to user's key */
- u16 nentries; /* #of msg entries in local msg queue */
- u16 entry_size; /* message queue's message entry size */
- u32 assigned_limit; /* limit on #of assigned kthreads */
- u32 idle_limit; /* limit on #of idle kthreads */
-} ____cacheline_aligned;
-
-#define XPC_CHANNEL_REGISTERED(_c) (xpc_registrations[_c].func != NULL)
-
-/* the following are valid xpc_send() or xpc_send_notify() flags */
-#define XPC_WAIT 0 /* wait flag */
-#define XPC_NOWAIT 1 /* no wait flag */
-
-struct xpc_interface {
- void (*connect) (int);
- void (*disconnect) (int);
- enum xp_retval (*send) (short, int, u32, void *, u16);
- enum xp_retval (*send_notify) (short, int, u32, void *, u16,
- xpc_notify_func, void *);
- void (*received) (short, int, void *);
- enum xp_retval (*partid_to_nasids) (short, void *);
-};
-
-extern struct xpc_interface xpc_interface;
-
-extern void xpc_set_interface(void (*)(int),
- void (*)(int),
- enum xp_retval (*)(short, int, u32, void *, u16),
- enum xp_retval (*)(short, int, u32, void *, u16,
- xpc_notify_func, void *),
- void (*)(short, int, void *),
- enum xp_retval (*)(short, void *));
-extern void xpc_clear_interface(void);
-
-extern enum xp_retval xpc_connect(int, xpc_channel_func, void *, u16,
- u16, u32, u32);
-extern void xpc_disconnect(int);
-
-static inline enum xp_retval
-xpc_send(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size)
-{
- if (!xpc_interface.send)
- return xpNotLoaded;
-
- return xpc_interface.send(partid, ch_number, flags, payload,
- payload_size);
-}
-
-static inline enum xp_retval
-xpc_send_notify(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size, xpc_notify_func func, void *key)
-{
- if (!xpc_interface.send_notify)
- return xpNotLoaded;
-
- return xpc_interface.send_notify(partid, ch_number, flags, payload,
- payload_size, func, key);
-}
-
-static inline void
-xpc_received(short partid, int ch_number, void *payload)
-{
- if (xpc_interface.received)
- xpc_interface.received(partid, ch_number, payload);
-}
-
-static inline enum xp_retval
-xpc_partid_to_nasids(short partid, void *nasids)
-{
- if (!xpc_interface.partid_to_nasids)
- return xpNotLoaded;
-
- return xpc_interface.partid_to_nasids(partid, nasids);
-}
-
-extern short xp_max_npartitions;
-extern short xp_partition_id;
-extern u8 xp_region_size;
-
-extern unsigned long (*xp_pa) (void *);
-extern unsigned long (*xp_socket_pa) (unsigned long);
-extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long,
- size_t);
-extern int (*xp_cpu_to_nasid) (int);
-extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long);
-extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long);
-
-extern struct device *xp;
-extern enum xp_retval xp_init_uv(void);
-extern void xp_exit_uv(void);
-
-#endif /* _DRIVERS_MISC_SGIXP_XP_H */
diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c
deleted file mode 100644
index 87d156c15f35..000000000000
--- a/drivers/misc/sgi-xp/xp_main.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition (XP) base.
- *
- * XP provides a base from which its users can interact
- * with XPC, yet not be dependent on XPC.
- *
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include "xp.h"
-
-/* define the XP debug device structures to be used with dev_dbg() et al */
-
-static struct device_driver xp_dbg_name = {
- .name = "xp"
-};
-
-static struct device xp_dbg_subname = {
- .init_name = "", /* set to "" */
- .driver = &xp_dbg_name
-};
-
-struct device *xp = &xp_dbg_subname;
-
-/* max #of partitions possible */
-short xp_max_npartitions;
-EXPORT_SYMBOL_GPL(xp_max_npartitions);
-
-short xp_partition_id;
-EXPORT_SYMBOL_GPL(xp_partition_id);
-
-u8 xp_region_size;
-EXPORT_SYMBOL_GPL(xp_region_size);
-
-unsigned long (*xp_pa) (void *addr);
-EXPORT_SYMBOL_GPL(xp_pa);
-
-unsigned long (*xp_socket_pa) (unsigned long gpa);
-EXPORT_SYMBOL_GPL(xp_socket_pa);
-
-enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa,
- const unsigned long src_gpa, size_t len);
-EXPORT_SYMBOL_GPL(xp_remote_memcpy);
-
-int (*xp_cpu_to_nasid) (int cpuid);
-EXPORT_SYMBOL_GPL(xp_cpu_to_nasid);
-
-enum xp_retval (*xp_expand_memprotect) (unsigned long phys_addr,
- unsigned long size);
-EXPORT_SYMBOL_GPL(xp_expand_memprotect);
-enum xp_retval (*xp_restrict_memprotect) (unsigned long phys_addr,
- unsigned long size);
-EXPORT_SYMBOL_GPL(xp_restrict_memprotect);
-
-/*
- * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level
- * users of XPC.
- */
-struct xpc_registration xpc_registrations[XPC_MAX_NCHANNELS];
-EXPORT_SYMBOL_GPL(xpc_registrations);
-
-/*
- * Initialize the XPC interface to NULL to indicate that XPC isn't loaded.
- */
-struct xpc_interface xpc_interface = { };
-EXPORT_SYMBOL_GPL(xpc_interface);
-
-/*
- * XPC calls this when it (the XPC module) has been loaded.
- */
-void
-xpc_set_interface(void (*connect) (int),
- void (*disconnect) (int),
- enum xp_retval (*send) (short, int, u32, void *, u16),
- enum xp_retval (*send_notify) (short, int, u32, void *, u16,
- xpc_notify_func, void *),
- void (*received) (short, int, void *),
- enum xp_retval (*partid_to_nasids) (short, void *))
-{
- xpc_interface.connect = connect;
- xpc_interface.disconnect = disconnect;
- xpc_interface.send = send;
- xpc_interface.send_notify = send_notify;
- xpc_interface.received = received;
- xpc_interface.partid_to_nasids = partid_to_nasids;
-}
-EXPORT_SYMBOL_GPL(xpc_set_interface);
-
-/*
- * XPC calls this when it (the XPC module) is being unloaded.
- */
-void
-xpc_clear_interface(void)
-{
- memset(&xpc_interface, 0, sizeof(xpc_interface));
-}
-EXPORT_SYMBOL_GPL(xpc_clear_interface);
-
-/*
- * Register for automatic establishment of a channel connection whenever
- * a partition comes up.
- *
- * Arguments:
- *
- * ch_number - channel # to register for connection.
- * func - function to call for asynchronous notification of channel
- * state changes (i.e., connection, disconnection, error) and
- * the arrival of incoming messages.
- * key - pointer to optional user-defined value that gets passed back
- * to the user on any callouts made to func.
- * payload_size - size in bytes of the XPC message's payload area which
- * contains a user-defined message. The user should make
- * this large enough to hold their largest message.
- * nentries - max #of XPC message entries a message queue can contain.
- * The actual number, which is determined when a connection
- * is established and may be less then requested, will be
- * passed to the user via the xpConnected callout.
- * assigned_limit - max number of kthreads allowed to be processing
- * messages (per connection) at any given instant.
- * idle_limit - max number of kthreads allowed to be idle at any given
- * instant.
- */
-enum xp_retval
-xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size,
- u16 nentries, u32 assigned_limit, u32 idle_limit)
-{
- struct xpc_registration *registration;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
- DBUG_ON(payload_size == 0 || nentries == 0);
- DBUG_ON(func == NULL);
- DBUG_ON(assigned_limit == 0 || idle_limit > assigned_limit);
-
- if (XPC_MSG_SIZE(payload_size) > XPC_MSG_MAX_SIZE)
- return xpPayloadTooBig;
-
- registration = &xpc_registrations[ch_number];
-
- if (mutex_lock_interruptible(&registration->mutex) != 0)
- return xpInterrupted;
-
- /* if XPC_CHANNEL_REGISTERED(ch_number) */
- if (registration->func != NULL) {
- mutex_unlock(&registration->mutex);
- return xpAlreadyRegistered;
- }
-
- /* register the channel for connection */
- registration->entry_size = XPC_MSG_SIZE(payload_size);
- registration->nentries = nentries;
- registration->assigned_limit = assigned_limit;
- registration->idle_limit = idle_limit;
- registration->key = key;
- registration->func = func;
-
- mutex_unlock(&registration->mutex);
-
- if (xpc_interface.connect)
- xpc_interface.connect(ch_number);
-
- return xpSuccess;
-}
-EXPORT_SYMBOL_GPL(xpc_connect);
-
-/*
- * Remove the registration for automatic connection of the specified channel
- * when a partition comes up.
- *
- * Before returning this xpc_disconnect() will wait for all connections on the
- * specified channel have been closed/torndown. So the caller can be assured
- * that they will not be receiving any more callouts from XPC to their
- * function registered via xpc_connect().
- *
- * Arguments:
- *
- * ch_number - channel # to unregister.
- */
-void
-xpc_disconnect(int ch_number)
-{
- struct xpc_registration *registration;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
-
- registration = &xpc_registrations[ch_number];
-
- /*
- * We've decided not to make this a down_interruptible(), since we
- * figured XPC's users will just turn around and call xpc_disconnect()
- * again anyways, so we might as well wait, if need be.
- */
- mutex_lock(&registration->mutex);
-
- /* if !XPC_CHANNEL_REGISTERED(ch_number) */
- if (registration->func == NULL) {
- mutex_unlock(&registration->mutex);
- return;
- }
-
- /* remove the connection registration for the specified channel */
- registration->func = NULL;
- registration->key = NULL;
- registration->nentries = 0;
- registration->entry_size = 0;
- registration->assigned_limit = 0;
- registration->idle_limit = 0;
-
- if (xpc_interface.disconnect)
- xpc_interface.disconnect(ch_number);
-
- mutex_unlock(&registration->mutex);
-
- return;
-}
-EXPORT_SYMBOL_GPL(xpc_disconnect);
-
-static int __init
-xp_init(void)
-{
- enum xp_retval ret;
- int ch_number;
-
- /* initialize the connection registration mutex */
- for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++)
- mutex_init(&xpc_registrations[ch_number].mutex);
-
- if (is_uv_system())
- ret = xp_init_uv();
- else
- ret = 0;
-
- if (ret != xpSuccess)
- return ret;
-
- return 0;
-}
-
-module_init(xp_init);
-
-static void __exit
-xp_exit(void)
-{
- if (is_uv_system())
- xp_exit_uv();
-}
-
-module_exit(xp_exit);
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_DESCRIPTION("Cross Partition (XP) base");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c
deleted file mode 100644
index 3faa7eadf679..000000000000
--- a/drivers/misc/sgi-xp/xp_uv.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition (XP) uv-based functions.
- *
- * Architecture specific implementation of common functions.
- *
- */
-
-#include <linux/device.h>
-#include <asm/uv/uv_hub.h>
-#if defined CONFIG_X86_64
-#include <asm/uv/bios.h>
-#endif
-#include "../sgi-gru/grukservices.h"
-#include "xp.h"
-
-/*
- * Convert a virtual memory address to a physical memory address.
- */
-static unsigned long
-xp_pa_uv(void *addr)
-{
- return uv_gpa(addr);
-}
-
-/*
- * Convert a global physical to socket physical address.
- */
-static unsigned long
-xp_socket_pa_uv(unsigned long gpa)
-{
- return uv_gpa_to_soc_phys_ram(gpa);
-}
-
-static enum xp_retval
-xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa,
- size_t len)
-{
- int ret;
- unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa));
-
- BUG_ON(!uv_gpa_in_mmr_space(src_gpa));
- BUG_ON(len != 8);
-
- ret = gru_read_gpa(dst_va, src_gpa);
- if (ret == 0)
- return xpSuccess;
-
- dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx "
- "len=%ld\n", dst_gpa, src_gpa, len);
- return xpGruCopyError;
-}
-
-
-static enum xp_retval
-xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa,
- size_t len)
-{
- int ret;
-
- if (uv_gpa_in_mmr_space(src_gpa))
- return xp_remote_mmr_read(dst_gpa, src_gpa, len);
-
- ret = gru_copy_gpa(dst_gpa, src_gpa, len);
- if (ret == 0)
- return xpSuccess;
-
- dev_err(xp, "gru_copy_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx "
- "len=%ld\n", dst_gpa, src_gpa, len);
- return xpGruCopyError;
-}
-
-static int
-xp_cpu_to_nasid_uv(int cpuid)
-{
- /* ??? Is this same as sn2 nasid in mach/part bitmaps set up by SAL? */
- return UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpuid));
-}
-
-static enum xp_retval
-xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size)
-{
- int ret;
-
-#if defined CONFIG_X86_64
- ret = uv_bios_change_memprotect(phys_addr, size, UV_MEMPROT_ALLOW_RW);
- if (ret != BIOS_STATUS_SUCCESS) {
- dev_err(xp, "uv_bios_change_memprotect(,, "
- "UV_MEMPROT_ALLOW_RW) failed, ret=%d\n", ret);
- return xpBiosError;
- }
-#else
- #error not a supported configuration
-#endif
- return xpSuccess;
-}
-
-static enum xp_retval
-xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size)
-{
- int ret;
-
-#if defined CONFIG_X86_64
- ret = uv_bios_change_memprotect(phys_addr, size,
- UV_MEMPROT_RESTRICT_ACCESS);
- if (ret != BIOS_STATUS_SUCCESS) {
- dev_err(xp, "uv_bios_change_memprotect(,, "
- "UV_MEMPROT_RESTRICT_ACCESS) failed, ret=%d\n", ret);
- return xpBiosError;
- }
-#else
- #error not a supported configuration
-#endif
- return xpSuccess;
-}
-
-enum xp_retval
-xp_init_uv(void)
-{
- WARN_ON(!is_uv_system());
- if (!is_uv_system())
- return xpUnsupported;
-
- xp_max_npartitions = XP_MAX_NPARTITIONS_UV;
-#ifdef CONFIG_X86
- xp_partition_id = sn_partition_id;
- xp_region_size = sn_region_size;
-#endif
- xp_pa = xp_pa_uv;
- xp_socket_pa = xp_socket_pa_uv;
- xp_remote_memcpy = xp_remote_memcpy_uv;
- xp_cpu_to_nasid = xp_cpu_to_nasid_uv;
- xp_expand_memprotect = xp_expand_memprotect_uv;
- xp_restrict_memprotect = xp_restrict_memprotect_uv;
-
- return xpSuccess;
-}
-
-void
-xp_exit_uv(void)
-{
- WARN_ON(!is_uv_system());
-}
diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h
deleted file mode 100644
index 225f2bb84e39..000000000000
--- a/drivers/misc/sgi-xp/xpc.h
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) structures and macros.
- */
-
-#ifndef _DRIVERS_MISC_SGIXP_XPC_H
-#define _DRIVERS_MISC_SGIXP_XPC_H
-
-#include <linux/wait.h>
-#include <linux/completion.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include "xp.h"
-
-/*
- * XPC Version numbers consist of a major and minor number. XPC can always
- * talk to versions with same major #, and never talk to versions with a
- * different major #.
- */
-#define _XPC_VERSION(_maj, _min) (((_maj) << 4) | ((_min) & 0xf))
-#define XPC_VERSION_MAJOR(_v) ((_v) >> 4)
-#define XPC_VERSION_MINOR(_v) ((_v) & 0xf)
-
-/* define frequency of the heartbeat and frequency how often it's checked */
-#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */
-#define XPC_HB_CHECK_DEFAULT_INTERVAL 20 /* check HB every x secs */
-
-/* define the process name of HB checker and the CPU it is pinned to */
-#define XPC_HB_CHECK_THREAD_NAME "xpc_hb"
-#define XPC_HB_CHECK_CPU 0
-
-/* define the process name of the discovery thread */
-#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery"
-
-/*
- * the reserved page
- *
- * SAL reserves one page of memory per partition for XPC. Though a full page
- * in length (16384 bytes), its starting address is not page aligned, but it
- * is cacheline aligned. The reserved page consists of the following:
- *
- * reserved page header
- *
- * The first two 64-byte cachelines of the reserved page contain the
- * header (struct xpc_rsvd_page). Before SAL initialization has completed,
- * SAL has set up the following fields of the reserved page header:
- * SAL_signature, SAL_version, SAL_partid, and SAL_nasids_size. The
- * other fields are set up by XPC. (xpc_rsvd_page points to the local
- * partition's reserved page.)
- *
- * part_nasids mask
- * mach_nasids mask
- *
- * SAL also sets up two bitmaps (or masks), one that reflects the actual
- * nasids in this partition (part_nasids), and the other that reflects
- * the actual nasids in the entire machine (mach_nasids). We're only
- * interested in the even numbered nasids (which contain the processors
- * and/or memory), so we only need half as many bits to represent the
- * nasids. When mapping nasid to bit in a mask (or bit to nasid) be sure
- * to either divide or multiply by 2. The part_nasids mask is located
- * starting at the first cacheline following the reserved page header. The
- * mach_nasids mask follows right after the part_nasids mask. The size in
- * bytes of each mask is reflected by the reserved page header field
- * 'SAL_nasids_size'. (Local partition's mask pointers are xpc_part_nasids
- * and xpc_mach_nasids.)
- *
- * Immediately following the mach_nasids mask are the XPC variables
- * required by other partitions. First are those that are generic to all
- * partitions (vars), followed on the next available cacheline by those
- * which are partition specific (vars part). These are setup by XPC.
- *
- * Note: Until 'ts_jiffies' is set non-zero, the partition XPC code has not been
- * initialized.
- */
-struct xpc_rsvd_page {
- u64 SAL_signature; /* SAL: unique signature */
- u64 SAL_version; /* SAL: version */
- short SAL_partid; /* SAL: partition ID */
- short max_npartitions; /* value of XPC_MAX_PARTITIONS */
- u8 version;
- u8 pad1[3]; /* align to next u64 in 1st 64-byte cacheline */
- unsigned long ts_jiffies; /* timestamp when rsvd pg was setup by XPC */
- union {
- struct {
- unsigned long heartbeat_gpa; /* phys addr */
- unsigned long activate_gru_mq_desc_gpa; /* phys addr */
- } uv;
- } sn;
- u64 pad2[9]; /* align to last u64 in 2nd 64-byte cacheline */
- u64 SAL_nasids_size; /* SAL: size of each nasid mask in bytes */
-};
-
-#define XPC_RP_VERSION _XPC_VERSION(3, 0) /* version 3.0 of the reserved page */
-
-/* the reserved page sizes and offsets */
-
-#define XPC_RP_HEADER_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page))
-
-#define XPC_RP_PART_NASIDS(_rp) ((unsigned long *)((u8 *)(_rp) + \
- XPC_RP_HEADER_SIZE))
-#define XPC_RP_MACH_NASIDS(_rp) (XPC_RP_PART_NASIDS(_rp) + \
- xpc_nasid_mask_nlongs)
-
-
-/*
- * The following structure describes the partition's heartbeat info which
- * will be periodically read by other partitions to determine whether this
- * XPC is still 'alive'.
- */
-struct xpc_heartbeat_uv {
- unsigned long value;
- unsigned long offline; /* if 0, heartbeat should be changing */
-};
-
-/*
- * Info pertinent to a GRU message queue using a watch list for irq generation.
- */
-struct xpc_gru_mq_uv {
- void *address; /* address of GRU message queue */
- unsigned int order; /* size of GRU message queue as a power of 2 */
- int irq; /* irq raised when message is received in mq */
- int mmr_blade; /* blade where watchlist was allocated from */
- unsigned long mmr_offset; /* offset of irq mmr located on mmr_blade */
- unsigned long mmr_value; /* value of irq mmr located on mmr_blade */
- int watchlist_num; /* number of watchlist allocatd by BIOS */
- void *gru_mq_desc; /* opaque structure used by the GRU driver */
-};
-
-/*
- * The activate_mq is used to send/receive GRU messages that affect XPC's
- * partition active state and channel state. This is uv only.
- */
-struct xpc_activate_mq_msghdr_uv {
- unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */
- short partid; /* sender's partid */
- u8 act_state; /* sender's act_state at time msg sent */
- u8 type; /* message's type */
- unsigned long rp_ts_jiffies; /* timestamp of sender's rp setup by XPC */
-};
-
-/* activate_mq defined message types */
-#define XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV 0
-
-#define XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV 1
-#define XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV 2
-
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV 3
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV 4
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV 5
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV 6
-#define XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV 7
-
-#define XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV 8
-#define XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV 9
-
-struct xpc_activate_mq_msg_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
-};
-
-struct xpc_activate_mq_msg_activate_req_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- unsigned long rp_gpa;
- unsigned long heartbeat_gpa;
- unsigned long activate_gru_mq_desc_gpa;
-};
-
-struct xpc_activate_mq_msg_deactivate_req_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- enum xp_retval reason;
-};
-
-struct xpc_activate_mq_msg_chctl_closerequest_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
- enum xp_retval reason;
-};
-
-struct xpc_activate_mq_msg_chctl_closereply_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
-};
-
-struct xpc_activate_mq_msg_chctl_openrequest_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
- short entry_size; /* size of notify_mq's GRU messages */
- short local_nentries; /* ??? Is this needed? What is? */
-};
-
-struct xpc_activate_mq_msg_chctl_openreply_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
- short remote_nentries; /* ??? Is this needed? What is? */
- short local_nentries; /* ??? Is this needed? What is? */
- unsigned long notify_gru_mq_desc_gpa;
-};
-
-struct xpc_activate_mq_msg_chctl_opencomplete_uv {
- struct xpc_activate_mq_msghdr_uv hdr;
- short ch_number;
-};
-
-/*
- * Functions registered by add_timer() or called by kernel_thread() only
- * allow for a single 64-bit argument. The following macros can be used to
- * pack and unpack two (32-bit, 16-bit or 8-bit) arguments into or out from
- * the passed argument.
- */
-#define XPC_PACK_ARGS(_arg1, _arg2) \
- ((((u64)_arg1) & 0xffffffff) | \
- ((((u64)_arg2) & 0xffffffff) << 32))
-
-#define XPC_UNPACK_ARG1(_args) (((u64)_args) & 0xffffffff)
-#define XPC_UNPACK_ARG2(_args) ((((u64)_args) >> 32) & 0xffffffff)
-
-/*
- * Define a structure that contains arguments associated with opening and
- * closing a channel.
- */
-struct xpc_openclose_args {
- u16 reason; /* reason why channel is closing */
- u16 entry_size; /* sizeof each message entry */
- u16 remote_nentries; /* #of message entries in remote msg queue */
- u16 local_nentries; /* #of message entries in local msg queue */
- unsigned long local_msgqueue_pa; /* phys addr of local message queue */
-};
-
-#define XPC_OPENCLOSE_ARGS_SIZE \
- L1_CACHE_ALIGN(sizeof(struct xpc_openclose_args) * \
- XPC_MAX_NCHANNELS)
-
-
-/*
- * Structures to define a fifo singly-linked list.
- */
-
-struct xpc_fifo_entry_uv {
- struct xpc_fifo_entry_uv *next;
-};
-
-struct xpc_fifo_head_uv {
- struct xpc_fifo_entry_uv *first;
- struct xpc_fifo_entry_uv *last;
- spinlock_t lock;
- int n_entries;
-};
-
-/*
- * The format of a uv XPC notify_mq GRU message is as follows:
- *
- * A user-defined message resides in the payload area. The max size of the
- * payload is defined by the user via xpc_connect().
- *
- * The size of a message (payload and header) sent via the GRU must be either 1
- * or 2 GRU_CACHE_LINE_BYTES in length.
- */
-
-struct xpc_notify_mq_msghdr_uv {
- union {
- unsigned int gru_msg_hdr; /* FOR GRU INTERNAL USE ONLY */
- struct xpc_fifo_entry_uv next; /* FOR XPC INTERNAL USE ONLY */
- } u;
- short partid; /* FOR XPC INTERNAL USE ONLY */
- u8 ch_number; /* FOR XPC INTERNAL USE ONLY */
- u8 size; /* FOR XPC INTERNAL USE ONLY */
- unsigned int msg_slot_number; /* FOR XPC INTERNAL USE ONLY */
-};
-
-struct xpc_notify_mq_msg_uv {
- struct xpc_notify_mq_msghdr_uv hdr;
- unsigned long payload;
-};
-
-/* struct xpc_notify_sn2 type of notification */
-
-#define XPC_N_CALL 0x01 /* notify function provided by user */
-
-/*
- * Define uv's version of the notify entry. It additionally is used to allocate
- * a msg slot on the remote partition into which is copied a sent message.
- */
-struct xpc_send_msg_slot_uv {
- struct xpc_fifo_entry_uv next;
- unsigned int msg_slot_number;
- xpc_notify_func func; /* user's notify function */
- void *key; /* pointer to user's key */
-};
-
-/*
- * Define the structure that manages all the stuff required by a channel. In
- * particular, they are used to manage the messages sent across the channel.
- *
- * This structure is private to a partition, and is NOT shared across the
- * partition boundary.
- *
- * There is an array of these structures for each remote partition. It is
- * allocated at the time a partition becomes active. The array contains one
- * of these structures for each potential channel connection to that partition.
- */
-
-struct xpc_channel_uv {
- void *cached_notify_gru_mq_desc; /* remote partition's notify mq's */
- /* gru mq descriptor */
-
- struct xpc_send_msg_slot_uv *send_msg_slots;
- void *recv_msg_slots; /* each slot will hold a xpc_notify_mq_msg_uv */
- /* structure plus the user's payload */
-
- struct xpc_fifo_head_uv msg_slot_free_list;
- struct xpc_fifo_head_uv recv_msg_list; /* deliverable payloads */
-};
-
-struct xpc_channel {
- short partid; /* ID of remote partition connected */
- spinlock_t lock; /* lock for updating this structure */
- unsigned int flags; /* general flags */
-
- enum xp_retval reason; /* reason why channel is disconnect'g */
- int reason_line; /* line# disconnect initiated from */
-
- u16 number; /* channel # */
-
- u16 entry_size; /* sizeof each msg entry */
- u16 local_nentries; /* #of msg entries in local msg queue */
- u16 remote_nentries; /* #of msg entries in remote msg queue */
-
- atomic_t references; /* #of external references to queues */
-
- atomic_t n_on_msg_allocate_wq; /* #on msg allocation wait queue */
- wait_queue_head_t msg_allocate_wq; /* msg allocation wait queue */
-
- u8 delayed_chctl_flags; /* chctl flags received, but delayed */
- /* action until channel disconnected */
-
- atomic_t n_to_notify; /* #of msg senders to notify */
-
- xpc_channel_func func; /* user's channel function */
- void *key; /* pointer to user's key */
-
- struct completion wdisconnect_wait; /* wait for channel disconnect */
-
- /* kthread management related fields */
-
- atomic_t kthreads_assigned; /* #of kthreads assigned to channel */
- u32 kthreads_assigned_limit; /* limit on #of kthreads assigned */
- atomic_t kthreads_idle; /* #of kthreads idle waiting for work */
- u32 kthreads_idle_limit; /* limit on #of kthreads idle */
- atomic_t kthreads_active; /* #of kthreads actively working */
-
- wait_queue_head_t idle_wq; /* idle kthread wait queue */
-
- union {
- struct xpc_channel_uv uv;
- } sn;
-
-} ____cacheline_aligned;
-
-/* struct xpc_channel flags */
-
-#define XPC_C_WASCONNECTED 0x00000001 /* channel was connected */
-
-#define XPC_C_ROPENCOMPLETE 0x00000002 /* remote open channel complete */
-#define XPC_C_OPENCOMPLETE 0x00000004 /* local open channel complete */
-#define XPC_C_ROPENREPLY 0x00000008 /* remote open channel reply */
-#define XPC_C_OPENREPLY 0x00000010 /* local open channel reply */
-#define XPC_C_ROPENREQUEST 0x00000020 /* remote open channel request */
-#define XPC_C_OPENREQUEST 0x00000040 /* local open channel request */
-
-#define XPC_C_SETUP 0x00000080 /* channel's msgqueues are alloc'd */
-#define XPC_C_CONNECTEDCALLOUT 0x00000100 /* connected callout initiated */
-#define XPC_C_CONNECTEDCALLOUT_MADE \
- 0x00000200 /* connected callout completed */
-#define XPC_C_CONNECTED 0x00000400 /* local channel is connected */
-#define XPC_C_CONNECTING 0x00000800 /* channel is being connected */
-
-#define XPC_C_RCLOSEREPLY 0x00001000 /* remote close channel reply */
-#define XPC_C_CLOSEREPLY 0x00002000 /* local close channel reply */
-#define XPC_C_RCLOSEREQUEST 0x00004000 /* remote close channel request */
-#define XPC_C_CLOSEREQUEST 0x00008000 /* local close channel request */
-
-#define XPC_C_DISCONNECTED 0x00010000 /* channel is disconnected */
-#define XPC_C_DISCONNECTING 0x00020000 /* channel is being disconnected */
-#define XPC_C_DISCONNECTINGCALLOUT \
- 0x00040000 /* disconnecting callout initiated */
-#define XPC_C_DISCONNECTINGCALLOUT_MADE \
- 0x00080000 /* disconnecting callout completed */
-#define XPC_C_WDISCONNECT 0x00100000 /* waiting for channel disconnect */
-
-/*
- * The channel control flags (chctl) union consists of a 64-bit variable which
- * is divided up into eight bytes, ordered from right to left. Byte zero
- * pertains to channel 0, byte one to channel 1, and so on. Each channel's byte
- * can have one or more of the chctl flags set in it.
- */
-
-union xpc_channel_ctl_flags {
- u64 all_flags;
- u8 flags[XPC_MAX_NCHANNELS];
-};
-
-/* chctl flags */
-#define XPC_CHCTL_CLOSEREQUEST 0x01
-#define XPC_CHCTL_CLOSEREPLY 0x02
-#define XPC_CHCTL_OPENREQUEST 0x04
-#define XPC_CHCTL_OPENREPLY 0x08
-#define XPC_CHCTL_OPENCOMPLETE 0x10
-#define XPC_CHCTL_MSGREQUEST 0x20
-
-#define XPC_OPENCLOSE_CHCTL_FLAGS \
- (XPC_CHCTL_CLOSEREQUEST | XPC_CHCTL_CLOSEREPLY | \
- XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | \
- XPC_CHCTL_OPENCOMPLETE)
-#define XPC_MSG_CHCTL_FLAGS XPC_CHCTL_MSGREQUEST
-
-static inline int
-xpc_any_openclose_chctl_flags_set(union xpc_channel_ctl_flags *chctl)
-{
- int ch_number;
-
- for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) {
- if (chctl->flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS)
- return 1;
- }
- return 0;
-}
-
-static inline int
-xpc_any_msg_chctl_flags_set(union xpc_channel_ctl_flags *chctl)
-{
- int ch_number;
-
- for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) {
- if (chctl->flags[ch_number] & XPC_MSG_CHCTL_FLAGS)
- return 1;
- }
- return 0;
-}
-
-struct xpc_partition_uv {
- unsigned long heartbeat_gpa; /* phys addr of partition's heartbeat */
- struct xpc_heartbeat_uv cached_heartbeat; /* cached copy of */
- /* partition's heartbeat */
- unsigned long activate_gru_mq_desc_gpa; /* phys addr of parititon's */
- /* activate mq's gru mq */
- /* descriptor */
- void *cached_activate_gru_mq_desc; /* cached copy of partition's */
- /* activate mq's gru mq descriptor */
- struct mutex cached_activate_gru_mq_desc_mutex;
- spinlock_t flags_lock; /* protect updating of flags */
- unsigned int flags; /* general flags */
- u8 remote_act_state; /* remote partition's act_state */
- u8 act_state_req; /* act_state request from remote partition */
- enum xp_retval reason; /* reason for deactivate act_state request */
-};
-
-/* struct xpc_partition_uv flags */
-
-#define XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV 0x00000001
-#define XPC_P_ENGAGED_UV 0x00000002
-
-/* struct xpc_partition_uv act_state change requests */
-
-#define XPC_P_ASR_ACTIVATE_UV 0x01
-#define XPC_P_ASR_REACTIVATE_UV 0x02
-#define XPC_P_ASR_DEACTIVATE_UV 0x03
-
-struct xpc_partition {
-
- /* XPC HB infrastructure */
-
- u8 remote_rp_version; /* version# of partition's rsvd pg */
- unsigned long remote_rp_ts_jiffies; /* timestamp when rsvd pg setup */
- unsigned long remote_rp_pa; /* phys addr of partition's rsvd pg */
- u64 last_heartbeat; /* HB at last read */
- u32 activate_IRQ_rcvd; /* IRQs since activation */
- spinlock_t act_lock; /* protect updating of act_state */
- u8 act_state; /* from XPC HB viewpoint */
- enum xp_retval reason; /* reason partition is deactivating */
- int reason_line; /* line# deactivation initiated from */
-
- unsigned long disengage_timeout; /* timeout in jiffies */
- struct timer_list disengage_timer;
-
- /* XPC infrastructure referencing and teardown control */
-
- u8 setup_state; /* infrastructure setup state */
- wait_queue_head_t teardown_wq; /* kthread waiting to teardown infra */
- atomic_t references; /* #of references to infrastructure */
-
- u8 nchannels; /* #of defined channels supported */
- atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */
- atomic_t nchannels_engaged; /* #of channels engaged with remote part */
- struct xpc_channel *channels; /* array of channel structures */
-
- /* fields used for managing channel avialability and activity */
-
- union xpc_channel_ctl_flags chctl; /* chctl flags yet to be processed */
- spinlock_t chctl_lock; /* chctl flags lock */
-
- void *remote_openclose_args_base; /* base address of kmalloc'd space */
- struct xpc_openclose_args *remote_openclose_args; /* copy of remote's */
- /* args */
-
- /* channel manager related fields */
-
- atomic_t channel_mgr_requests; /* #of requests to activate chan mgr */
- wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */
-
- union {
- struct xpc_partition_uv uv;
- } sn;
-
-} ____cacheline_aligned;
-
-struct xpc_arch_operations {
- int (*setup_partitions) (void);
- void (*teardown_partitions) (void);
- void (*process_activate_IRQ_rcvd) (void);
- enum xp_retval (*get_partition_rsvd_page_pa)
- (void *, u64 *, unsigned long *, size_t *);
- int (*setup_rsvd_page) (struct xpc_rsvd_page *);
-
- void (*allow_hb) (short);
- void (*disallow_hb) (short);
- void (*disallow_all_hbs) (void);
- void (*increment_heartbeat) (void);
- void (*offline_heartbeat) (void);
- void (*online_heartbeat) (void);
- void (*heartbeat_init) (void);
- void (*heartbeat_exit) (void);
- enum xp_retval (*get_remote_heartbeat) (struct xpc_partition *);
-
- void (*request_partition_activation) (struct xpc_rsvd_page *,
- unsigned long, int);
- void (*request_partition_reactivation) (struct xpc_partition *);
- void (*request_partition_deactivation) (struct xpc_partition *);
- void (*cancel_partition_deactivation_request) (struct xpc_partition *);
- enum xp_retval (*setup_ch_structures) (struct xpc_partition *);
- void (*teardown_ch_structures) (struct xpc_partition *);
-
- enum xp_retval (*make_first_contact) (struct xpc_partition *);
-
- u64 (*get_chctl_all_flags) (struct xpc_partition *);
- void (*send_chctl_closerequest) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_closereply) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_openrequest) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_openreply) (struct xpc_channel *, unsigned long *);
- void (*send_chctl_opencomplete) (struct xpc_channel *, unsigned long *);
- void (*process_msg_chctl_flags) (struct xpc_partition *, int);
-
- enum xp_retval (*save_remote_msgqueue_pa) (struct xpc_channel *,
- unsigned long);
-
- enum xp_retval (*setup_msg_structures) (struct xpc_channel *);
- void (*teardown_msg_structures) (struct xpc_channel *);
-
- void (*indicate_partition_engaged) (struct xpc_partition *);
- void (*indicate_partition_disengaged) (struct xpc_partition *);
- void (*assume_partition_disengaged) (short);
- int (*partition_engaged) (short);
- int (*any_partition_engaged) (void);
-
- int (*n_of_deliverable_payloads) (struct xpc_channel *);
- enum xp_retval (*send_payload) (struct xpc_channel *, u32, void *,
- u16, u8, xpc_notify_func, void *);
- void *(*get_deliverable_payload) (struct xpc_channel *);
- void (*received_payload) (struct xpc_channel *, void *);
- void (*notify_senders_of_disconnect) (struct xpc_channel *);
-};
-
-/* struct xpc_partition act_state values (for XPC HB) */
-
-#define XPC_P_AS_INACTIVE 0x00 /* partition is not active */
-#define XPC_P_AS_ACTIVATION_REQ 0x01 /* created thread to activate */
-#define XPC_P_AS_ACTIVATING 0x02 /* activation thread started */
-#define XPC_P_AS_ACTIVE 0x03 /* xpc_partition_up() was called */
-#define XPC_P_AS_DEACTIVATING 0x04 /* partition deactivation initiated */
-
-#define XPC_DEACTIVATE_PARTITION(_p, _reason) \
- xpc_deactivate_partition(__LINE__, (_p), (_reason))
-
-/* struct xpc_partition setup_state values */
-
-#define XPC_P_SS_UNSET 0x00 /* infrastructure was never setup */
-#define XPC_P_SS_SETUP 0x01 /* infrastructure is setup */
-#define XPC_P_SS_WTEARDOWN 0x02 /* waiting to teardown infrastructure */
-#define XPC_P_SS_TORNDOWN 0x03 /* infrastructure is torndown */
-
-/* number of seconds to wait for other partitions to disengage */
-#define XPC_DISENGAGE_DEFAULT_TIMELIMIT 90
-
-/* interval in seconds to print 'waiting deactivation' messages */
-#define XPC_DEACTIVATE_PRINTMSG_INTERVAL 10
-
-#define XPC_PARTID(_p) ((short)((_p) - &xpc_partitions[0]))
-
-/* found in xp_main.c */
-extern struct xpc_registration xpc_registrations[];
-
-/* found in xpc_main.c */
-extern struct device *xpc_part;
-extern struct device *xpc_chan;
-extern struct xpc_arch_operations xpc_arch_ops;
-extern int xpc_disengage_timelimit;
-extern int xpc_disengage_timedout;
-extern int xpc_activate_IRQ_rcvd;
-extern spinlock_t xpc_activate_IRQ_rcvd_lock;
-extern wait_queue_head_t xpc_activate_IRQ_wq;
-extern void *xpc_kzalloc_cacheline_aligned(size_t, gfp_t, void **);
-extern void xpc_activate_partition(struct xpc_partition *);
-extern void xpc_activate_kthreads(struct xpc_channel *, int);
-extern void xpc_create_kthreads(struct xpc_channel *, int, int);
-extern void xpc_disconnect_wait(int);
-
-/* found in xpc_uv.c */
-extern int xpc_init_uv(void);
-extern void xpc_exit_uv(void);
-
-/* found in xpc_partition.c */
-extern int xpc_exiting;
-extern int xpc_nasid_mask_nlongs;
-extern struct xpc_rsvd_page *xpc_rsvd_page;
-extern unsigned long *xpc_mach_nasids;
-extern struct xpc_partition *xpc_partitions;
-extern void *xpc_kmalloc_cacheline_aligned(size_t, gfp_t, void **);
-extern int xpc_setup_rsvd_page(void);
-extern void xpc_teardown_rsvd_page(void);
-extern int xpc_identify_activate_IRQ_sender(void);
-extern int xpc_partition_disengaged(struct xpc_partition *);
-extern int xpc_partition_disengaged_from_timer(struct xpc_partition *part);
-extern enum xp_retval xpc_mark_partition_active(struct xpc_partition *);
-extern void xpc_mark_partition_inactive(struct xpc_partition *);
-extern void xpc_discovery(void);
-extern enum xp_retval xpc_get_remote_rp(int, unsigned long *,
- struct xpc_rsvd_page *,
- unsigned long *);
-extern void xpc_deactivate_partition(const int, struct xpc_partition *,
- enum xp_retval);
-extern enum xp_retval xpc_initiate_partid_to_nasids(short, void *);
-
-/* found in xpc_channel.c */
-extern void xpc_initiate_connect(int);
-extern void xpc_initiate_disconnect(int);
-extern enum xp_retval xpc_allocate_msg_wait(struct xpc_channel *);
-extern enum xp_retval xpc_initiate_send(short, int, u32, void *, u16);
-extern enum xp_retval xpc_initiate_send_notify(short, int, u32, void *, u16,
- xpc_notify_func, void *);
-extern void xpc_initiate_received(short, int, void *);
-extern void xpc_process_sent_chctl_flags(struct xpc_partition *);
-extern void xpc_connected_callout(struct xpc_channel *);
-extern void xpc_deliver_payload(struct xpc_channel *);
-extern void xpc_disconnect_channel(const int, struct xpc_channel *,
- enum xp_retval, unsigned long *);
-extern void xpc_disconnect_callout(struct xpc_channel *, enum xp_retval);
-extern void xpc_partition_going_down(struct xpc_partition *, enum xp_retval);
-
-static inline void
-xpc_wakeup_channel_mgr(struct xpc_partition *part)
-{
- if (atomic_inc_return(&part->channel_mgr_requests) == 1)
- wake_up(&part->channel_mgr_wq);
-}
-
-/*
- * These next two inlines are used to keep us from tearing down a channel's
- * msg queues while a thread may be referencing them.
- */
-static inline void
-xpc_msgqueue_ref(struct xpc_channel *ch)
-{
- atomic_inc(&ch->references);
-}
-
-static inline void
-xpc_msgqueue_deref(struct xpc_channel *ch)
-{
- s32 refs = atomic_dec_return(&ch->references);
-
- DBUG_ON(refs < 0);
- if (refs == 0)
- xpc_wakeup_channel_mgr(&xpc_partitions[ch->partid]);
-}
-
-#define XPC_DISCONNECT_CHANNEL(_ch, _reason, _irqflgs) \
- xpc_disconnect_channel(__LINE__, _ch, _reason, _irqflgs)
-
-/*
- * These two inlines are used to keep us from tearing down a partition's
- * setup infrastructure while a thread may be referencing it.
- */
-static inline void
-xpc_part_deref(struct xpc_partition *part)
-{
- s32 refs = atomic_dec_return(&part->references);
-
- DBUG_ON(refs < 0);
- if (refs == 0 && part->setup_state == XPC_P_SS_WTEARDOWN)
- wake_up(&part->teardown_wq);
-}
-
-static inline int
-xpc_part_ref(struct xpc_partition *part)
-{
- int setup;
-
- atomic_inc(&part->references);
- setup = (part->setup_state == XPC_P_SS_SETUP);
- if (!setup)
- xpc_part_deref(part);
-
- return setup;
-}
-
-/*
- * The following macro is to be used for the setting of the reason and
- * reason_line fields in both the struct xpc_channel and struct xpc_partition
- * structures.
- */
-#define XPC_SET_REASON(_p, _reason, _line) \
- { \
- (_p)->reason = _reason; \
- (_p)->reason_line = _line; \
- }
-
-#endif /* _DRIVERS_MISC_SGIXP_XPC_H */
diff --git a/drivers/misc/sgi-xp/xpc_channel.c b/drivers/misc/sgi-xp/xpc_channel.c
deleted file mode 100644
index 8e6607fc8a67..000000000000
--- a/drivers/misc/sgi-xp/xpc_channel.c
+++ /dev/null
@@ -1,1011 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) channel support.
- *
- * This is the part of XPC that manages the channels and
- * sends/receives messages across them to/from other partitions.
- *
- */
-
-#include <linux/device.h>
-#include "xpc.h"
-
-/*
- * Process a connect message from a remote partition.
- *
- * Note: xpc_process_connect() is expecting to be called with the
- * spin_lock_irqsave held and will leave it locked upon return.
- */
-static void
-xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- enum xp_retval ret;
-
- lockdep_assert_held(&ch->lock);
-
- if (!(ch->flags & XPC_C_OPENREQUEST) ||
- !(ch->flags & XPC_C_ROPENREQUEST)) {
- /* nothing more to do for now */
- return;
- }
- DBUG_ON(!(ch->flags & XPC_C_CONNECTING));
-
- if (!(ch->flags & XPC_C_SETUP)) {
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- ret = xpc_arch_ops.setup_msg_structures(ch);
- spin_lock_irqsave(&ch->lock, *irq_flags);
-
- if (ret != xpSuccess)
- XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags);
- else
- ch->flags |= XPC_C_SETUP;
-
- if (ch->flags & XPC_C_DISCONNECTING)
- return;
- }
-
- if (!(ch->flags & XPC_C_OPENREPLY)) {
- ch->flags |= XPC_C_OPENREPLY;
- xpc_arch_ops.send_chctl_openreply(ch, irq_flags);
- }
-
- if (!(ch->flags & XPC_C_ROPENREPLY))
- return;
-
- if (!(ch->flags & XPC_C_OPENCOMPLETE)) {
- ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED);
- xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags);
- }
-
- if (!(ch->flags & XPC_C_ROPENCOMPLETE))
- return;
-
- dev_info(xpc_chan, "channel %d to partition %d connected\n",
- ch->number, ch->partid);
-
- ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */
-}
-
-/*
- * spin_lock_irqsave() is expected to be held on entry.
- */
-static void
-xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_partition *part = &xpc_partitions[ch->partid];
- u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED);
-
- lockdep_assert_held(&ch->lock);
-
- if (!(ch->flags & XPC_C_DISCONNECTING))
- return;
-
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
-
- /* make sure all activity has settled down first */
-
- if (atomic_read(&ch->kthreads_assigned) > 0 ||
- atomic_read(&ch->references) > 0) {
- return;
- }
- DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
- !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE));
-
- if (part->act_state == XPC_P_AS_DEACTIVATING) {
- /* can't proceed until the other side disengages from us */
- if (xpc_arch_ops.partition_engaged(ch->partid))
- return;
-
- } else {
-
- /* as long as the other side is up do the full protocol */
-
- if (!(ch->flags & XPC_C_RCLOSEREQUEST))
- return;
-
- if (!(ch->flags & XPC_C_CLOSEREPLY)) {
- ch->flags |= XPC_C_CLOSEREPLY;
- xpc_arch_ops.send_chctl_closereply(ch, irq_flags);
- }
-
- if (!(ch->flags & XPC_C_RCLOSEREPLY))
- return;
- }
-
- /* wake those waiting for notify completion */
- if (atomic_read(&ch->n_to_notify) > 0) {
- /* we do callout while holding ch->lock, callout can't block */
- xpc_arch_ops.notify_senders_of_disconnect(ch);
- }
-
- /* both sides are disconnected now */
-
- if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) {
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
- xpc_disconnect_callout(ch, xpDisconnected);
- spin_lock_irqsave(&ch->lock, *irq_flags);
- }
-
- DBUG_ON(atomic_read(&ch->n_to_notify) != 0);
-
- /* it's now safe to free the channel's message queues */
- xpc_arch_ops.teardown_msg_structures(ch);
-
- ch->func = NULL;
- ch->key = NULL;
- ch->entry_size = 0;
- ch->local_nentries = 0;
- ch->remote_nentries = 0;
- ch->kthreads_assigned_limit = 0;
- ch->kthreads_idle_limit = 0;
-
- /*
- * Mark the channel disconnected and clear all other flags, including
- * XPC_C_SETUP (because of call to
- * xpc_arch_ops.teardown_msg_structures()) but not including
- * XPC_C_WDISCONNECT (if it was set).
- */
- ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT));
-
- atomic_dec(&part->nchannels_active);
-
- if (channel_was_connected) {
- dev_info(xpc_chan, "channel %d to partition %d disconnected, "
- "reason=%d\n", ch->number, ch->partid, ch->reason);
- }
-
- if (ch->flags & XPC_C_WDISCONNECT) {
- /* we won't lose the CPU since we're holding ch->lock */
- complete(&ch->wdisconnect_wait);
- } else if (ch->delayed_chctl_flags) {
- if (part->act_state != XPC_P_AS_DEACTIVATING) {
- /* time to take action on any delayed chctl flags */
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch->number] |=
- ch->delayed_chctl_flags;
- spin_unlock(&part->chctl_lock);
- }
- ch->delayed_chctl_flags = 0;
- }
-}
-
-/*
- * Process a change in the channel's remote connection state.
- */
-static void
-xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number,
- u8 chctl_flags)
-{
- unsigned long irq_flags;
- struct xpc_openclose_args *args =
- &part->remote_openclose_args[ch_number];
- struct xpc_channel *ch = &part->channels[ch_number];
- enum xp_retval reason;
- enum xp_retval ret;
- int create_kthread = 0;
-
- spin_lock_irqsave(&ch->lock, irq_flags);
-
-again:
-
- if ((ch->flags & XPC_C_DISCONNECTED) &&
- (ch->flags & XPC_C_WDISCONNECT)) {
- /*
- * Delay processing chctl flags until thread waiting disconnect
- * has had a chance to see that the channel is disconnected.
- */
- ch->delayed_chctl_flags |= chctl_flags;
- goto out;
- }
-
- if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received "
- "from partid=%d, channel=%d\n", args->reason,
- ch->partid, ch->number);
-
- /*
- * If RCLOSEREQUEST is set, we're probably waiting for
- * RCLOSEREPLY. We should find it and a ROPENREQUEST packed
- * with this RCLOSEREQUEST in the chctl_flags.
- */
-
- if (ch->flags & XPC_C_RCLOSEREQUEST) {
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY));
- DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY);
-
- DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY));
- chctl_flags &= ~XPC_CHCTL_CLOSEREPLY;
- ch->flags |= XPC_C_RCLOSEREPLY;
-
- /* both sides have finished disconnecting */
- xpc_process_disconnect(ch, &irq_flags);
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));
- goto again;
- }
-
- if (ch->flags & XPC_C_DISCONNECTED) {
- if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) {
- if (part->chctl.flags[ch_number] &
- XPC_CHCTL_OPENREQUEST) {
-
- DBUG_ON(ch->delayed_chctl_flags != 0);
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch_number] |=
- XPC_CHCTL_CLOSEREQUEST;
- spin_unlock(&part->chctl_lock);
- }
- goto out;
- }
-
- XPC_SET_REASON(ch, 0, 0);
- ch->flags &= ~XPC_C_DISCONNECTED;
-
- atomic_inc(&part->nchannels_active);
- ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST);
- }
-
- chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY |
- XPC_CHCTL_OPENCOMPLETE);
-
- /*
- * The meaningful CLOSEREQUEST connection state fields are:
- * reason = reason connection is to be closed
- */
-
- ch->flags |= XPC_C_RCLOSEREQUEST;
-
- if (!(ch->flags & XPC_C_DISCONNECTING)) {
- reason = args->reason;
- if (reason <= xpSuccess || reason > xpUnknownReason)
- reason = xpUnknownReason;
- else if (reason == xpUnregistering)
- reason = xpOtherUnregistering;
-
- XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
-
- DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY);
- goto out;
- }
-
- xpc_process_disconnect(ch, &irq_flags);
- }
-
- if (chctl_flags & XPC_CHCTL_CLOSEREPLY) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid="
- "%d, channel=%d\n", ch->partid, ch->number);
-
- if (ch->flags & XPC_C_DISCONNECTED) {
- DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING);
- goto out;
- }
-
- DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
-
- if (!(ch->flags & XPC_C_RCLOSEREQUEST)) {
- if (part->chctl.flags[ch_number] &
- XPC_CHCTL_CLOSEREQUEST) {
-
- DBUG_ON(ch->delayed_chctl_flags != 0);
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch_number] |=
- XPC_CHCTL_CLOSEREPLY;
- spin_unlock(&part->chctl_lock);
- }
- goto out;
- }
-
- ch->flags |= XPC_C_RCLOSEREPLY;
-
- if (ch->flags & XPC_C_CLOSEREPLY) {
- /* both sides have finished disconnecting */
- xpc_process_disconnect(ch, &irq_flags);
- }
- }
-
- if (chctl_flags & XPC_CHCTL_OPENREQUEST) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, "
- "local_nentries=%d) received from partid=%d, "
- "channel=%d\n", args->entry_size, args->local_nentries,
- ch->partid, ch->number);
-
- if (part->act_state == XPC_P_AS_DEACTIVATING ||
- (ch->flags & XPC_C_ROPENREQUEST)) {
- goto out;
- }
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) {
- ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST;
- goto out;
- }
- DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED |
- XPC_C_OPENREQUEST)));
- DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
- XPC_C_OPENREPLY | XPC_C_CONNECTED));
-
- /*
- * The meaningful OPENREQUEST connection state fields are:
- * entry_size = size of channel's messages in bytes
- * local_nentries = remote partition's local_nentries
- */
- if (args->entry_size == 0 || args->local_nentries == 0) {
- /* assume OPENREQUEST was delayed by mistake */
- goto out;
- }
-
- ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING);
- ch->remote_nentries = args->local_nentries;
-
- if (ch->flags & XPC_C_OPENREQUEST) {
- if (args->entry_size != ch->entry_size) {
- XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
- &irq_flags);
- goto out;
- }
- } else {
- ch->entry_size = args->entry_size;
-
- XPC_SET_REASON(ch, 0, 0);
- ch->flags &= ~XPC_C_DISCONNECTED;
-
- atomic_inc(&part->nchannels_active);
- }
-
- xpc_process_connect(ch, &irq_flags);
- }
-
- if (chctl_flags & XPC_CHCTL_OPENREPLY) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa="
- "0x%lx, local_nentries=%d, remote_nentries=%d) "
- "received from partid=%d, channel=%d\n",
- args->local_msgqueue_pa, args->local_nentries,
- args->remote_nentries, ch->partid, ch->number);
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
- goto out;
-
- if (!(ch->flags & XPC_C_OPENREQUEST)) {
- XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
- &irq_flags);
- goto out;
- }
-
- DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
- DBUG_ON(ch->flags & XPC_C_CONNECTED);
-
- /*
- * The meaningful OPENREPLY connection state fields are:
- * local_msgqueue_pa = physical address of remote
- * partition's local_msgqueue
- * local_nentries = remote partition's local_nentries
- * remote_nentries = remote partition's remote_nentries
- */
- DBUG_ON(args->local_msgqueue_pa == 0);
- DBUG_ON(args->local_nentries == 0);
- DBUG_ON(args->remote_nentries == 0);
-
- ret = xpc_arch_ops.save_remote_msgqueue_pa(ch,
- args->local_msgqueue_pa);
- if (ret != xpSuccess) {
- XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags);
- goto out;
- }
- ch->flags |= XPC_C_ROPENREPLY;
-
- if (args->local_nentries < ch->remote_nentries) {
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
- "remote_nentries=%d, old remote_nentries=%d, "
- "partid=%d, channel=%d\n",
- args->local_nentries, ch->remote_nentries,
- ch->partid, ch->number);
-
- ch->remote_nentries = args->local_nentries;
- }
- if (args->remote_nentries < ch->local_nentries) {
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
- "local_nentries=%d, old local_nentries=%d, "
- "partid=%d, channel=%d\n",
- args->remote_nentries, ch->local_nentries,
- ch->partid, ch->number);
-
- ch->local_nentries = args->remote_nentries;
- }
-
- xpc_process_connect(ch, &irq_flags);
- }
-
- if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) {
-
- dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from "
- "partid=%d, channel=%d\n", ch->partid, ch->number);
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
- goto out;
-
- if (!(ch->flags & XPC_C_OPENREQUEST) ||
- !(ch->flags & XPC_C_OPENREPLY)) {
- XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
- &irq_flags);
- goto out;
- }
-
- DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
- DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY));
- DBUG_ON(!(ch->flags & XPC_C_CONNECTED));
-
- ch->flags |= XPC_C_ROPENCOMPLETE;
-
- xpc_process_connect(ch, &irq_flags);
- create_kthread = 1;
- }
-
-out:
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- if (create_kthread)
- xpc_create_kthreads(ch, 1, 0);
-}
-
-/*
- * Attempt to establish a channel connection to a remote partition.
- */
-static enum xp_retval
-xpc_connect_channel(struct xpc_channel *ch)
-{
- unsigned long irq_flags;
- struct xpc_registration *registration = &xpc_registrations[ch->number];
-
- if (mutex_trylock(&registration->mutex) == 0)
- return xpRetry;
-
- if (!XPC_CHANNEL_REGISTERED(ch->number)) {
- mutex_unlock(&registration->mutex);
- return xpUnregistered;
- }
-
- spin_lock_irqsave(&ch->lock, irq_flags);
-
- DBUG_ON(ch->flags & XPC_C_CONNECTED);
- DBUG_ON(ch->flags & XPC_C_OPENREQUEST);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- mutex_unlock(&registration->mutex);
- return ch->reason;
- }
-
- /* add info from the channel connect registration to the channel */
-
- ch->kthreads_assigned_limit = registration->assigned_limit;
- ch->kthreads_idle_limit = registration->idle_limit;
- DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
- DBUG_ON(atomic_read(&ch->kthreads_idle) != 0);
- DBUG_ON(atomic_read(&ch->kthreads_active) != 0);
-
- ch->func = registration->func;
- DBUG_ON(registration->func == NULL);
- ch->key = registration->key;
-
- ch->local_nentries = registration->nentries;
-
- if (ch->flags & XPC_C_ROPENREQUEST) {
- if (registration->entry_size != ch->entry_size) {
- /* the local and remote sides aren't the same */
-
- /*
- * Because XPC_DISCONNECT_CHANNEL() can block we're
- * forced to up the registration sema before we unlock
- * the channel lock. But that's okay here because we're
- * done with the part that required the registration
- * sema. XPC_DISCONNECT_CHANNEL() requires that the
- * channel lock be locked and will unlock and relock
- * the channel lock as needed.
- */
- mutex_unlock(&registration->mutex);
- XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
- &irq_flags);
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return xpUnequalMsgSizes;
- }
- } else {
- ch->entry_size = registration->entry_size;
-
- XPC_SET_REASON(ch, 0, 0);
- ch->flags &= ~XPC_C_DISCONNECTED;
-
- atomic_inc(&xpc_partitions[ch->partid].nchannels_active);
- }
-
- mutex_unlock(&registration->mutex);
-
- /* initiate the connection */
-
- ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING);
- xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags);
-
- xpc_process_connect(ch, &irq_flags);
-
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- return xpSuccess;
-}
-
-void
-xpc_process_sent_chctl_flags(struct xpc_partition *part)
-{
- unsigned long irq_flags;
- union xpc_channel_ctl_flags chctl;
- struct xpc_channel *ch;
- int ch_number;
- u32 ch_flags;
-
- chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part);
-
- /*
- * Initiate channel connections for registered channels.
- *
- * For each connected channel that has pending messages activate idle
- * kthreads and/or create new kthreads as needed.
- */
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch = &part->channels[ch_number];
-
- /*
- * Process any open or close related chctl flags, and then deal
- * with connecting or disconnecting the channel as required.
- */
-
- if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) {
- xpc_process_openclose_chctl_flags(part, ch_number,
- chctl.flags[ch_number]);
- }
-
- ch_flags = ch->flags; /* need an atomic snapshot of flags */
-
- if (ch_flags & XPC_C_DISCONNECTING) {
- spin_lock_irqsave(&ch->lock, irq_flags);
- xpc_process_disconnect(ch, &irq_flags);
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- continue;
- }
-
- if (part->act_state == XPC_P_AS_DEACTIVATING)
- continue;
-
- if (!(ch_flags & XPC_C_CONNECTED)) {
- if (!(ch_flags & XPC_C_OPENREQUEST)) {
- DBUG_ON(ch_flags & XPC_C_SETUP);
- (void)xpc_connect_channel(ch);
- }
- continue;
- }
-
- /*
- * Process any message related chctl flags, this may involve
- * the activation of kthreads to deliver any pending messages
- * sent from the other partition.
- */
-
- if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS)
- xpc_arch_ops.process_msg_chctl_flags(part, ch_number);
- }
-}
-
-/*
- * XPC's heartbeat code calls this function to inform XPC that a partition is
- * going down. XPC responds by tearing down the XPartition Communication
- * infrastructure used for the just downed partition.
- *
- * XPC's heartbeat code will never call this function and xpc_partition_up()
- * at the same time. Nor will it ever make multiple calls to either function
- * at the same time.
- */
-void
-xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason)
-{
- unsigned long irq_flags;
- int ch_number;
- struct xpc_channel *ch;
-
- dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n",
- XPC_PARTID(part), reason);
-
- if (!xpc_part_ref(part)) {
- /* infrastructure for this partition isn't currently set up */
- return;
- }
-
- /* disconnect channels associated with the partition going down */
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch = &part->channels[ch_number];
-
- xpc_msgqueue_ref(ch);
- spin_lock_irqsave(&ch->lock, irq_flags);
-
- XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
-
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- xpc_msgqueue_deref(ch);
- }
-
- xpc_wakeup_channel_mgr(part);
-
- xpc_part_deref(part);
-}
-
-/*
- * Called by XP at the time of channel connection registration to cause
- * XPC to establish connections to all currently active partitions.
- */
-void
-xpc_initiate_connect(int ch_number)
-{
- short partid;
- struct xpc_partition *part;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_part_ref(part)) {
- /*
- * Initiate the establishment of a connection on the
- * newly registered channel to the remote partition.
- */
- xpc_wakeup_channel_mgr(part);
- xpc_part_deref(part);
- }
- }
-}
-
-void
-xpc_connected_callout(struct xpc_channel *ch)
-{
- /* let the registerer know that a connection has been established */
-
- if (ch->func != NULL) {
- dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, "
- "partid=%d, channel=%d\n", ch->partid, ch->number);
-
- ch->func(xpConnected, ch->partid, ch->number,
- (void *)(u64)ch->local_nentries, ch->key);
-
- dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, "
- "partid=%d, channel=%d\n", ch->partid, ch->number);
- }
-}
-
-/*
- * Called by XP at the time of channel connection unregistration to cause
- * XPC to teardown all current connections for the specified channel.
- *
- * Before returning xpc_initiate_disconnect() will wait until all connections
- * on the specified channel have been closed/torndown. So the caller can be
- * assured that they will not be receiving any more callouts from XPC to the
- * function they registered via xpc_connect().
- *
- * Arguments:
- *
- * ch_number - channel # to unregister.
- */
-void
-xpc_initiate_disconnect(int ch_number)
-{
- unsigned long irq_flags;
- short partid;
- struct xpc_partition *part;
- struct xpc_channel *ch;
-
- DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
-
- /* initiate the channel disconnect for every active partition */
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_part_ref(part)) {
- ch = &part->channels[ch_number];
- xpc_msgqueue_ref(ch);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
-
- if (!(ch->flags & XPC_C_DISCONNECTED)) {
- ch->flags |= XPC_C_WDISCONNECT;
-
- XPC_DISCONNECT_CHANNEL(ch, xpUnregistering,
- &irq_flags);
- }
-
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- xpc_msgqueue_deref(ch);
- xpc_part_deref(part);
- }
- }
-
- xpc_disconnect_wait(ch_number);
-}
-
-/*
- * To disconnect a channel, and reflect it back to all who may be waiting.
- *
- * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
- * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by
- * xpc_disconnect_wait().
- *
- * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
- */
-void
-xpc_disconnect_channel(const int line, struct xpc_channel *ch,
- enum xp_retval reason, unsigned long *irq_flags)
-{
- u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED);
-
- lockdep_assert_held(&ch->lock);
-
- if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
- return;
-
- DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)));
-
- dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n",
- reason, line, ch->partid, ch->number);
-
- XPC_SET_REASON(ch, reason, line);
-
- ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
- /* some of these may not have been set */
- ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |
- XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
- XPC_C_CONNECTING | XPC_C_CONNECTED);
-
- xpc_arch_ops.send_chctl_closerequest(ch, irq_flags);
-
- if (channel_was_connected)
- ch->flags |= XPC_C_WASCONNECTED;
-
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
-
- /* wake all idle kthreads so they can exit */
- if (atomic_read(&ch->kthreads_idle) > 0) {
- wake_up_all(&ch->idle_wq);
-
- } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
- !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
- /* start a kthread that will do the xpDisconnecting callout */
- xpc_create_kthreads(ch, 1, 1);
- }
-
- /* wake those waiting to allocate an entry from the local msg queue */
- if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)
- wake_up(&ch->msg_allocate_wq);
-
- spin_lock_irqsave(&ch->lock, *irq_flags);
-}
-
-void
-xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason)
-{
- /*
- * Let the channel's registerer know that the channel is being
- * disconnected. We don't want to do this if the registerer was never
- * informed of a connection being made.
- */
-
- if (ch->func != NULL) {
- dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, "
- "channel=%d\n", reason, ch->partid, ch->number);
-
- ch->func(reason, ch->partid, ch->number, NULL, ch->key);
-
- dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, "
- "channel=%d\n", reason, ch->partid, ch->number);
- }
-}
-
-/*
- * Wait for a message entry to become available for the specified channel,
- * but don't wait any longer than 1 jiffy.
- */
-enum xp_retval
-xpc_allocate_msg_wait(struct xpc_channel *ch)
-{
- enum xp_retval ret;
- DEFINE_WAIT(wait);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- DBUG_ON(ch->reason == xpInterrupted);
- return ch->reason;
- }
-
- atomic_inc(&ch->n_on_msg_allocate_wq);
- prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE);
- ret = schedule_timeout(1);
- finish_wait(&ch->msg_allocate_wq, &wait);
- atomic_dec(&ch->n_on_msg_allocate_wq);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- ret = ch->reason;
- DBUG_ON(ch->reason == xpInterrupted);
- } else if (ret == 0) {
- ret = xpTimeout;
- } else {
- ret = xpInterrupted;
- }
-
- return ret;
-}
-
-/*
- * Send a message that contains the user's payload on the specified channel
- * connected to the specified partition.
- *
- * NOTE that this routine can sleep waiting for a message entry to become
- * available. To not sleep, pass in the XPC_NOWAIT flag.
- *
- * Once sent, this routine will not wait for the message to be received, nor
- * will notification be given when it does happen.
- *
- * Arguments:
- *
- * partid - ID of partition to which the channel is connected.
- * ch_number - channel # to send message on.
- * flags - see xp.h for valid flags.
- * payload - pointer to the payload which is to be sent.
- * payload_size - size of the payload in bytes.
- */
-enum xp_retval
-xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size)
-{
- struct xpc_partition *part = &xpc_partitions[partid];
- enum xp_retval ret = xpUnknownReason;
-
- dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
- partid, ch_number);
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
- DBUG_ON(payload == NULL);
-
- if (xpc_part_ref(part)) {
- ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
- flags, payload, payload_size, 0, NULL, NULL);
- xpc_part_deref(part);
- }
-
- return ret;
-}
-
-/*
- * Send a message that contains the user's payload on the specified channel
- * connected to the specified partition.
- *
- * NOTE that this routine can sleep waiting for a message entry to become
- * available. To not sleep, pass in the XPC_NOWAIT flag.
- *
- * This routine will not wait for the message to be sent or received.
- *
- * Once the remote end of the channel has received the message, the function
- * passed as an argument to xpc_initiate_send_notify() will be called. This
- * allows the sender to free up or re-use any buffers referenced by the
- * message, but does NOT mean the message has been processed at the remote
- * end by a receiver.
- *
- * If this routine returns an error, the caller's function will NOT be called.
- *
- * Arguments:
- *
- * partid - ID of partition to which the channel is connected.
- * ch_number - channel # to send message on.
- * flags - see xp.h for valid flags.
- * payload - pointer to the payload which is to be sent.
- * payload_size - size of the payload in bytes.
- * func - function to call with asynchronous notification of message
- * receipt. THIS FUNCTION MUST BE NON-BLOCKING.
- * key - user-defined key to be passed to the function when it's called.
- */
-enum xp_retval
-xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload,
- u16 payload_size, xpc_notify_func func, void *key)
-{
- struct xpc_partition *part = &xpc_partitions[partid];
- enum xp_retval ret = xpUnknownReason;
-
- dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
- partid, ch_number);
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
- DBUG_ON(payload == NULL);
- DBUG_ON(func == NULL);
-
- if (xpc_part_ref(part)) {
- ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
- flags, payload, payload_size, XPC_N_CALL, func, key);
- xpc_part_deref(part);
- }
- return ret;
-}
-
-/*
- * Deliver a message's payload to its intended recipient.
- */
-void
-xpc_deliver_payload(struct xpc_channel *ch)
-{
- void *payload;
-
- payload = xpc_arch_ops.get_deliverable_payload(ch);
- if (payload != NULL) {
-
- /*
- * This ref is taken to protect the payload itself from being
- * freed before the user is finished with it, which the user
- * indicates by calling xpc_initiate_received().
- */
- xpc_msgqueue_ref(ch);
-
- atomic_inc(&ch->kthreads_active);
-
- if (ch->func != NULL) {
- dev_dbg(xpc_chan, "ch->func() called, payload=0x%p "
- "partid=%d channel=%d\n", payload, ch->partid,
- ch->number);
-
- /* deliver the message to its intended recipient */
- ch->func(xpMsgReceived, ch->partid, ch->number, payload,
- ch->key);
-
- dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p "
- "partid=%d channel=%d\n", payload, ch->partid,
- ch->number);
- }
-
- atomic_dec(&ch->kthreads_active);
- }
-}
-
-/*
- * Acknowledge receipt of a delivered message's payload.
- *
- * This function, although called by users, does not call xpc_part_ref() to
- * ensure that the partition infrastructure is in place. It relies on the
- * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload().
- *
- * Arguments:
- *
- * partid - ID of partition to which the channel is connected.
- * ch_number - channel # message received on.
- * payload - pointer to the payload area allocated via
- * xpc_initiate_send() or xpc_initiate_send_notify().
- */
-void
-xpc_initiate_received(short partid, int ch_number, void *payload)
-{
- struct xpc_partition *part = &xpc_partitions[partid];
- struct xpc_channel *ch;
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
-
- ch = &part->channels[ch_number];
- xpc_arch_ops.received_payload(ch, payload);
-
- /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */
- xpc_msgqueue_deref(ch);
-}
diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c
deleted file mode 100644
index 15b6a8e35681..000000000000
--- a/drivers/misc/sgi-xp/xpc_main.c
+++ /dev/null
@@ -1,1309 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) support - standard version.
- *
- * XPC provides a message passing capability that crosses partition
- * boundaries. This module is made up of two parts:
- *
- * partition This part detects the presence/absence of other
- * partitions. It provides a heartbeat and monitors
- * the heartbeats of other partitions.
- *
- * channel This part manages the channels and sends/receives
- * messages across them to/from other partitions.
- *
- * There are a couple of additional functions residing in XP, which
- * provide an interface to XPC for its users.
- *
- *
- * Caveats:
- *
- * . Currently on sn2, we have no way to determine which nasid an IRQ
- * came from. Thus, xpc_send_IRQ_sn2() does a remote amo write
- * followed by an IPI. The amo indicates where data is to be pulled
- * from, so after the IPI arrives, the remote partition checks the amo
- * word. The IPI can actually arrive before the amo however, so other
- * code must periodically check for this case. Also, remote amo
- * operations do not reliably time out. Thus we do a remote PIO read
- * solely to know whether the remote partition is down and whether we
- * should stop sending IPIs to it. This remote PIO read operation is
- * set up in a special nofault region so SAL knows to ignore (and
- * cleanup) any errors due to the remote amo write, PIO read, and/or
- * PIO write operations.
- *
- * If/when new hardware solves this IPI problem, we should abandon
- * the current approach.
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/sysctl.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/reboot.h>
-#include <linux/kdebug.h>
-#include <linux/kthread.h>
-#include "xpc.h"
-
-#ifdef CONFIG_X86_64
-#include <asm/traps.h>
-#endif
-
-/* define two XPC debug device structures to be used with dev_dbg() et al */
-
-static struct device_driver xpc_dbg_name = {
- .name = "xpc"
-};
-
-static struct device xpc_part_dbg_subname = {
- .init_name = "", /* set to "part" at xpc_init() time */
- .driver = &xpc_dbg_name
-};
-
-static struct device xpc_chan_dbg_subname = {
- .init_name = "", /* set to "chan" at xpc_init() time */
- .driver = &xpc_dbg_name
-};
-
-struct device *xpc_part = &xpc_part_dbg_subname;
-struct device *xpc_chan = &xpc_chan_dbg_subname;
-
-static int xpc_kdebug_ignore;
-
-/* systune related variables for /proc/sys directories */
-
-static int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL;
-static int xpc_hb_min_interval = 1;
-static int xpc_hb_max_interval = 10;
-
-static int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL;
-static int xpc_hb_check_min_interval = 10;
-static int xpc_hb_check_max_interval = 120;
-
-int xpc_disengage_timelimit = XPC_DISENGAGE_DEFAULT_TIMELIMIT;
-static int xpc_disengage_min_timelimit; /* = 0 */
-static int xpc_disengage_max_timelimit = 120;
-
-static const struct ctl_table xpc_sys_xpc_hb[] = {
- {
- .procname = "hb_interval",
- .data = &xpc_hb_interval,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &xpc_hb_min_interval,
- .extra2 = &xpc_hb_max_interval},
- {
- .procname = "hb_check_interval",
- .data = &xpc_hb_check_interval,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &xpc_hb_check_min_interval,
- .extra2 = &xpc_hb_check_max_interval},
-};
-static const struct ctl_table xpc_sys_xpc[] = {
- {
- .procname = "disengage_timelimit",
- .data = &xpc_disengage_timelimit,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &xpc_disengage_min_timelimit,
- .extra2 = &xpc_disengage_max_timelimit},
-};
-
-static struct ctl_table_header *xpc_sysctl;
-static struct ctl_table_header *xpc_sysctl_hb;
-
-/* non-zero if any remote partition disengage was timed out */
-int xpc_disengage_timedout;
-
-/* #of activate IRQs received and not yet processed */
-int xpc_activate_IRQ_rcvd;
-DEFINE_SPINLOCK(xpc_activate_IRQ_rcvd_lock);
-
-/* IRQ handler notifies this wait queue on receipt of an IRQ */
-DECLARE_WAIT_QUEUE_HEAD(xpc_activate_IRQ_wq);
-
-static unsigned long xpc_hb_check_timeout;
-static struct timer_list xpc_hb_timer;
-
-/* notification that the xpc_hb_checker thread has exited */
-static DECLARE_COMPLETION(xpc_hb_checker_exited);
-
-/* notification that the xpc_discovery thread has exited */
-static DECLARE_COMPLETION(xpc_discovery_exited);
-
-static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *);
-
-static int xpc_system_reboot(struct notifier_block *, unsigned long, void *);
-static struct notifier_block xpc_reboot_notifier = {
- .notifier_call = xpc_system_reboot,
-};
-
-static int xpc_system_die(struct notifier_block *, unsigned long, void *);
-static struct notifier_block xpc_die_notifier = {
- .notifier_call = xpc_system_die,
-};
-
-struct xpc_arch_operations xpc_arch_ops;
-
-/*
- * Timer function to enforce the timelimit on the partition disengage.
- */
-static void
-xpc_timeout_partition_disengage(struct timer_list *t)
-{
- struct xpc_partition *part = timer_container_of(part, t,
- disengage_timer);
-
- DBUG_ON(time_is_after_jiffies(part->disengage_timeout));
-
- xpc_partition_disengaged_from_timer(part);
-
- DBUG_ON(part->disengage_timeout != 0);
- DBUG_ON(xpc_arch_ops.partition_engaged(XPC_PARTID(part)));
-}
-
-/*
- * Timer to produce the heartbeat. The timer structures function is
- * already set when this is initially called. A tunable is used to
- * specify when the next timeout should occur.
- */
-static void
-xpc_hb_beater(struct timer_list *unused)
-{
- xpc_arch_ops.increment_heartbeat();
-
- if (time_is_before_eq_jiffies(xpc_hb_check_timeout))
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-
- xpc_hb_timer.expires = jiffies + (xpc_hb_interval * HZ);
- add_timer(&xpc_hb_timer);
-}
-
-static void
-xpc_start_hb_beater(void)
-{
- xpc_arch_ops.heartbeat_init();
- timer_setup(&xpc_hb_timer, xpc_hb_beater, 0);
- xpc_hb_beater(NULL);
-}
-
-static void
-xpc_stop_hb_beater(void)
-{
- timer_delete_sync(&xpc_hb_timer);
- xpc_arch_ops.heartbeat_exit();
-}
-
-/*
- * At periodic intervals, scan through all active partitions and ensure
- * their heartbeat is still active. If not, the partition is deactivated.
- */
-static void
-xpc_check_remote_hb(void)
-{
- struct xpc_partition *part;
- short partid;
- enum xp_retval ret;
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
-
- if (xpc_exiting)
- break;
-
- if (partid == xp_partition_id)
- continue;
-
- part = &xpc_partitions[partid];
-
- if (part->act_state == XPC_P_AS_INACTIVE ||
- part->act_state == XPC_P_AS_DEACTIVATING) {
- continue;
- }
-
- ret = xpc_arch_ops.get_remote_heartbeat(part);
- if (ret != xpSuccess)
- XPC_DEACTIVATE_PARTITION(part, ret);
- }
-}
-
-/*
- * This thread is responsible for nearly all of the partition
- * activation/deactivation.
- */
-static int
-xpc_hb_checker(void *ignore)
-{
- int force_IRQ = 0;
-
- /* this thread was marked active by xpc_hb_init() */
-
- set_cpus_allowed_ptr(current, cpumask_of(XPC_HB_CHECK_CPU));
-
- /* set our heartbeating to other partitions into motion */
- xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ);
- xpc_start_hb_beater();
-
- while (!xpc_exiting) {
-
- dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have "
- "been received\n",
- (int)(xpc_hb_check_timeout - jiffies),
- xpc_activate_IRQ_rcvd);
-
- /* checking of remote heartbeats is skewed by IRQ handling */
- if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) {
- xpc_hb_check_timeout = jiffies +
- (xpc_hb_check_interval * HZ);
-
- dev_dbg(xpc_part, "checking remote heartbeats\n");
- xpc_check_remote_hb();
- }
-
- /* check for outstanding IRQs */
- if (xpc_activate_IRQ_rcvd > 0 || force_IRQ != 0) {
- force_IRQ = 0;
- dev_dbg(xpc_part, "processing activate IRQs "
- "received\n");
- xpc_arch_ops.process_activate_IRQ_rcvd();
- }
-
- /* wait for IRQ or timeout */
- (void)wait_event_interruptible(xpc_activate_IRQ_wq,
- (time_is_before_eq_jiffies(
- xpc_hb_check_timeout) ||
- xpc_activate_IRQ_rcvd > 0 ||
- xpc_exiting));
- }
-
- xpc_stop_hb_beater();
-
- dev_dbg(xpc_part, "heartbeat checker is exiting\n");
-
- /* mark this thread as having exited */
- complete(&xpc_hb_checker_exited);
- return 0;
-}
-
-/*
- * This thread will attempt to discover other partitions to activate
- * based on info provided by SAL. This new thread is short lived and
- * will exit once discovery is complete.
- */
-static int
-xpc_initiate_discovery(void *ignore)
-{
- xpc_discovery();
-
- dev_dbg(xpc_part, "discovery thread is exiting\n");
-
- /* mark this thread as having exited */
- complete(&xpc_discovery_exited);
- return 0;
-}
-
-/*
- * The first kthread assigned to a newly activated partition is the one
- * created by XPC HB with which it calls xpc_activating(). XPC hangs on to
- * that kthread until the partition is brought down, at which time that kthread
- * returns back to XPC HB. (The return of that kthread will signify to XPC HB
- * that XPC has dismantled all communication infrastructure for the associated
- * partition.) This kthread becomes the channel manager for that partition.
- *
- * Each active partition has a channel manager, who, besides connecting and
- * disconnecting channels, will ensure that each of the partition's connected
- * channels has the required number of assigned kthreads to get the work done.
- */
-static void
-xpc_channel_mgr(struct xpc_partition *part)
-{
- while (part->act_state != XPC_P_AS_DEACTIVATING ||
- atomic_read(&part->nchannels_active) > 0 ||
- !xpc_partition_disengaged(part)) {
-
- xpc_process_sent_chctl_flags(part);
-
- /*
- * Wait until we've been requested to activate kthreads or
- * all of the channel's message queues have been torn down or
- * a signal is pending.
- *
- * The channel_mgr_requests is set to 1 after being awakened,
- * This is done to prevent the channel mgr from making one pass
- * through the loop for each request, since he will
- * be servicing all the requests in one pass. The reason it's
- * set to 1 instead of 0 is so that other kthreads will know
- * that the channel mgr is running and won't bother trying to
- * wake him up.
- */
- atomic_dec(&part->channel_mgr_requests);
- (void)wait_event_interruptible(part->channel_mgr_wq,
- (atomic_read(&part->channel_mgr_requests) > 0 ||
- part->chctl.all_flags != 0 ||
- (part->act_state == XPC_P_AS_DEACTIVATING &&
- atomic_read(&part->nchannels_active) == 0 &&
- xpc_partition_disengaged(part))));
- atomic_set(&part->channel_mgr_requests, 1);
- }
-}
-
-/*
- * Guarantee that the kzalloc'd memory is cacheline aligned.
- */
-void *
-xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
-{
- /* see if kzalloc will give us cachline aligned memory by default */
- *base = kzalloc(size, flags);
- if (*base == NULL)
- return NULL;
-
- if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
- return *base;
-
- kfree(*base);
-
- /* nope, we'll have to do it ourselves */
- *base = kzalloc(size + L1_CACHE_BYTES, flags);
- if (*base == NULL)
- return NULL;
-
- return (void *)L1_CACHE_ALIGN((u64)*base);
-}
-
-/*
- * Setup the channel structures necessary to support XPartition Communication
- * between the specified remote partition and the local one.
- */
-static enum xp_retval
-xpc_setup_ch_structures(struct xpc_partition *part)
-{
- enum xp_retval ret;
- int ch_number;
- struct xpc_channel *ch;
- short partid = XPC_PARTID(part);
-
- /*
- * Allocate all of the channel structures as a contiguous chunk of
- * memory.
- */
- DBUG_ON(part->channels != NULL);
- part->channels = kzalloc_objs(struct xpc_channel, XPC_MAX_NCHANNELS);
- if (part->channels == NULL) {
- dev_err(xpc_chan, "can't get memory for channels\n");
- return xpNoMemory;
- }
-
- /* allocate the remote open and close args */
-
- part->remote_openclose_args =
- xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE,
- GFP_KERNEL, &part->
- remote_openclose_args_base);
- if (part->remote_openclose_args == NULL) {
- dev_err(xpc_chan, "can't get memory for remote connect args\n");
- ret = xpNoMemory;
- goto out_1;
- }
-
- part->chctl.all_flags = 0;
- spin_lock_init(&part->chctl_lock);
-
- atomic_set(&part->channel_mgr_requests, 1);
- init_waitqueue_head(&part->channel_mgr_wq);
-
- part->nchannels = XPC_MAX_NCHANNELS;
-
- atomic_set(&part->nchannels_active, 0);
- atomic_set(&part->nchannels_engaged, 0);
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch = &part->channels[ch_number];
-
- ch->partid = partid;
- ch->number = ch_number;
- ch->flags = XPC_C_DISCONNECTED;
-
- atomic_set(&ch->kthreads_assigned, 0);
- atomic_set(&ch->kthreads_idle, 0);
- atomic_set(&ch->kthreads_active, 0);
-
- atomic_set(&ch->references, 0);
- atomic_set(&ch->n_to_notify, 0);
-
- spin_lock_init(&ch->lock);
- init_completion(&ch->wdisconnect_wait);
-
- atomic_set(&ch->n_on_msg_allocate_wq, 0);
- init_waitqueue_head(&ch->msg_allocate_wq);
- init_waitqueue_head(&ch->idle_wq);
- }
-
- ret = xpc_arch_ops.setup_ch_structures(part);
- if (ret != xpSuccess)
- goto out_2;
-
- /*
- * With the setting of the partition setup_state to XPC_P_SS_SETUP,
- * we're declaring that this partition is ready to go.
- */
- part->setup_state = XPC_P_SS_SETUP;
-
- return xpSuccess;
-
- /* setup of ch structures failed */
-out_2:
- kfree(part->remote_openclose_args_base);
- part->remote_openclose_args = NULL;
-out_1:
- kfree(part->channels);
- part->channels = NULL;
- return ret;
-}
-
-/*
- * Teardown the channel structures necessary to support XPartition Communication
- * between the specified remote partition and the local one.
- */
-static void
-xpc_teardown_ch_structures(struct xpc_partition *part)
-{
- DBUG_ON(atomic_read(&part->nchannels_engaged) != 0);
- DBUG_ON(atomic_read(&part->nchannels_active) != 0);
-
- /*
- * Make this partition inaccessible to local processes by marking it
- * as no longer setup. Then wait before proceeding with the teardown
- * until all existing references cease.
- */
- DBUG_ON(part->setup_state != XPC_P_SS_SETUP);
- part->setup_state = XPC_P_SS_WTEARDOWN;
-
- wait_event(part->teardown_wq, (atomic_read(&part->references) == 0));
-
- /* now we can begin tearing down the infrastructure */
-
- xpc_arch_ops.teardown_ch_structures(part);
-
- kfree(part->remote_openclose_args_base);
- part->remote_openclose_args = NULL;
- kfree(part->channels);
- part->channels = NULL;
-
- part->setup_state = XPC_P_SS_TORNDOWN;
-}
-
-/*
- * When XPC HB determines that a partition has come up, it will create a new
- * kthread and that kthread will call this function to attempt to set up the
- * basic infrastructure used for Cross Partition Communication with the newly
- * upped partition.
- *
- * The kthread that was created by XPC HB and which setup the XPC
- * infrastructure will remain assigned to the partition becoming the channel
- * manager for that partition until the partition is deactivating, at which
- * time the kthread will teardown the XPC infrastructure and then exit.
- */
-static int
-xpc_activating(void *__partid)
-{
- short partid = (u64)__partid;
- struct xpc_partition *part = &xpc_partitions[partid];
- unsigned long irq_flags;
-
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
-
- if (part->act_state == XPC_P_AS_DEACTIVATING) {
- part->act_state = XPC_P_AS_INACTIVE;
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- part->remote_rp_pa = 0;
- return 0;
- }
-
- /* indicate the thread is activating */
- DBUG_ON(part->act_state != XPC_P_AS_ACTIVATION_REQ);
- part->act_state = XPC_P_AS_ACTIVATING;
-
- XPC_SET_REASON(part, 0, 0);
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- dev_dbg(xpc_part, "activating partition %d\n", partid);
-
- xpc_arch_ops.allow_hb(partid);
-
- if (xpc_setup_ch_structures(part) == xpSuccess) {
- (void)xpc_part_ref(part); /* this will always succeed */
-
- if (xpc_arch_ops.make_first_contact(part) == xpSuccess) {
- xpc_mark_partition_active(part);
- xpc_channel_mgr(part);
- /* won't return until partition is deactivating */
- }
-
- xpc_part_deref(part);
- xpc_teardown_ch_structures(part);
- }
-
- xpc_arch_ops.disallow_hb(partid);
- xpc_mark_partition_inactive(part);
-
- if (part->reason == xpReactivating) {
- /* interrupting ourselves results in activating partition */
- xpc_arch_ops.request_partition_reactivation(part);
- }
-
- return 0;
-}
-
-void
-xpc_activate_partition(struct xpc_partition *part)
-{
- short partid = XPC_PARTID(part);
- unsigned long irq_flags;
- struct task_struct *kthread;
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
-
- DBUG_ON(part->act_state != XPC_P_AS_INACTIVE);
-
- part->act_state = XPC_P_AS_ACTIVATION_REQ;
- XPC_SET_REASON(part, xpCloneKThread, __LINE__);
-
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- kthread = kthread_run(xpc_activating, (void *)((u64)partid), "xpc%02d",
- partid);
- if (IS_ERR(kthread)) {
- spin_lock_irqsave(&part->act_lock, irq_flags);
- part->act_state = XPC_P_AS_INACTIVE;
- XPC_SET_REASON(part, xpCloneKThreadFailed, __LINE__);
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- }
-}
-
-void
-xpc_activate_kthreads(struct xpc_channel *ch, int needed)
-{
- int idle = atomic_read(&ch->kthreads_idle);
- int assigned = atomic_read(&ch->kthreads_assigned);
- int wakeup;
-
- DBUG_ON(needed <= 0);
-
- if (idle > 0) {
- wakeup = (needed > idle) ? idle : needed;
- needed -= wakeup;
-
- dev_dbg(xpc_chan, "wakeup %d idle kthreads, partid=%d, "
- "channel=%d\n", wakeup, ch->partid, ch->number);
-
- /* only wakeup the requested number of kthreads */
- wake_up_nr(&ch->idle_wq, wakeup);
- }
-
- if (needed <= 0)
- return;
-
- if (needed + assigned > ch->kthreads_assigned_limit) {
- needed = ch->kthreads_assigned_limit - assigned;
- if (needed <= 0)
- return;
- }
-
- dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n",
- needed, ch->partid, ch->number);
-
- xpc_create_kthreads(ch, needed, 0);
-}
-
-/*
- * This function is where XPC's kthreads wait for messages to deliver.
- */
-static void
-xpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch)
-{
- int (*n_of_deliverable_payloads) (struct xpc_channel *) =
- xpc_arch_ops.n_of_deliverable_payloads;
-
- do {
- /* deliver messages to their intended recipients */
-
- while (n_of_deliverable_payloads(ch) > 0 &&
- !(ch->flags & XPC_C_DISCONNECTING)) {
- xpc_deliver_payload(ch);
- }
-
- if (atomic_inc_return(&ch->kthreads_idle) >
- ch->kthreads_idle_limit) {
- /* too many idle kthreads on this channel */
- atomic_dec(&ch->kthreads_idle);
- break;
- }
-
- dev_dbg(xpc_chan, "idle kthread calling "
- "wait_event_interruptible_exclusive()\n");
-
- (void)wait_event_interruptible_exclusive(ch->idle_wq,
- (n_of_deliverable_payloads(ch) > 0 ||
- (ch->flags & XPC_C_DISCONNECTING)));
-
- atomic_dec(&ch->kthreads_idle);
-
- } while (!(ch->flags & XPC_C_DISCONNECTING));
-}
-
-static int
-xpc_kthread_start(void *args)
-{
- short partid = XPC_UNPACK_ARG1(args);
- u16 ch_number = XPC_UNPACK_ARG2(args);
- struct xpc_partition *part = &xpc_partitions[partid];
- struct xpc_channel *ch;
- int n_needed;
- unsigned long irq_flags;
- int (*n_of_deliverable_payloads) (struct xpc_channel *) =
- xpc_arch_ops.n_of_deliverable_payloads;
-
- dev_dbg(xpc_chan, "kthread starting, partid=%d, channel=%d\n",
- partid, ch_number);
-
- ch = &part->channels[ch_number];
-
- if (!(ch->flags & XPC_C_DISCONNECTING)) {
-
- /* let registerer know that connection has been established */
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if (!(ch->flags & XPC_C_CONNECTEDCALLOUT)) {
- ch->flags |= XPC_C_CONNECTEDCALLOUT;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- xpc_connected_callout(ch);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- ch->flags |= XPC_C_CONNECTEDCALLOUT_MADE;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- /*
- * It is possible that while the callout was being
- * made that the remote partition sent some messages.
- * If that is the case, we may need to activate
- * additional kthreads to help deliver them. We only
- * need one less than total #of messages to deliver.
- */
- n_needed = n_of_deliverable_payloads(ch) - 1;
- if (n_needed > 0 && !(ch->flags & XPC_C_DISCONNECTING))
- xpc_activate_kthreads(ch, n_needed);
-
- } else {
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- }
-
- xpc_kthread_waitmsgs(part, ch);
- }
-
- /* let registerer know that connection is disconnecting */
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
- !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
- ch->flags |= XPC_C_DISCONNECTINGCALLOUT;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- xpc_disconnect_callout(ch, xpDisconnecting);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE;
- }
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- if (atomic_dec_return(&ch->kthreads_assigned) == 0 &&
- atomic_dec_return(&part->nchannels_engaged) == 0) {
- xpc_arch_ops.indicate_partition_disengaged(part);
- }
-
- xpc_msgqueue_deref(ch);
-
- dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n",
- partid, ch_number);
-
- xpc_part_deref(part);
- return 0;
-}
-
-/*
- * For each partition that XPC has established communications with, there is
- * a minimum of one kernel thread assigned to perform any operation that
- * may potentially sleep or block (basically the callouts to the asynchronous
- * functions registered via xpc_connect()).
- *
- * Additional kthreads are created and destroyed by XPC as the workload
- * demands.
- *
- * A kthread is assigned to one of the active channels that exists for a given
- * partition.
- */
-void
-xpc_create_kthreads(struct xpc_channel *ch, int needed,
- int ignore_disconnecting)
-{
- unsigned long irq_flags;
- u64 args = XPC_PACK_ARGS(ch->partid, ch->number);
- struct xpc_partition *part = &xpc_partitions[ch->partid];
- struct task_struct *kthread;
- void (*indicate_partition_disengaged) (struct xpc_partition *) =
- xpc_arch_ops.indicate_partition_disengaged;
-
- while (needed-- > 0) {
-
- /*
- * The following is done on behalf of the newly created
- * kthread. That kthread is responsible for doing the
- * counterpart to the following before it exits.
- */
- if (ignore_disconnecting) {
- if (!atomic_inc_not_zero(&ch->kthreads_assigned)) {
- /* kthreads assigned had gone to zero */
- BUG_ON(!(ch->flags &
- XPC_C_DISCONNECTINGCALLOUT_MADE));
- break;
- }
-
- } else if (ch->flags & XPC_C_DISCONNECTING) {
- break;
-
- } else if (atomic_inc_return(&ch->kthreads_assigned) == 1 &&
- atomic_inc_return(&part->nchannels_engaged) == 1) {
- xpc_arch_ops.indicate_partition_engaged(part);
- }
- (void)xpc_part_ref(part);
- xpc_msgqueue_ref(ch);
-
- kthread = kthread_run(xpc_kthread_start, (void *)args,
- "xpc%02dc%d", ch->partid, ch->number);
- if (IS_ERR(kthread)) {
- /* the fork failed */
-
- /*
- * NOTE: if (ignore_disconnecting &&
- * !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) is true,
- * then we'll deadlock if all other kthreads assigned
- * to this channel are blocked in the channel's
- * registerer, because the only thing that will unblock
- * them is the xpDisconnecting callout that this
- * failed kthread_run() would have made.
- */
-
- if (atomic_dec_return(&ch->kthreads_assigned) == 0 &&
- atomic_dec_return(&part->nchannels_engaged) == 0) {
- indicate_partition_disengaged(part);
- }
- xpc_msgqueue_deref(ch);
- xpc_part_deref(part);
-
- if (atomic_read(&ch->kthreads_assigned) <
- ch->kthreads_idle_limit) {
- /*
- * Flag this as an error only if we have an
- * insufficient #of kthreads for the channel
- * to function.
- */
- spin_lock_irqsave(&ch->lock, irq_flags);
- XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources,
- &irq_flags);
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- }
- break;
- }
- }
-}
-
-void
-xpc_disconnect_wait(int ch_number)
-{
- unsigned long irq_flags;
- short partid;
- struct xpc_partition *part;
- struct xpc_channel *ch;
- int wakeup_channel_mgr;
-
- /* now wait for all callouts to the caller's function to cease */
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (!xpc_part_ref(part))
- continue;
-
- ch = &part->channels[ch_number];
-
- if (!(ch->flags & XPC_C_WDISCONNECT)) {
- xpc_part_deref(part);
- continue;
- }
-
- wait_for_completion(&ch->wdisconnect_wait);
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));
- wakeup_channel_mgr = 0;
-
- if (ch->delayed_chctl_flags) {
- if (part->act_state != XPC_P_AS_DEACTIVATING) {
- spin_lock(&part->chctl_lock);
- part->chctl.flags[ch->number] |=
- ch->delayed_chctl_flags;
- spin_unlock(&part->chctl_lock);
- wakeup_channel_mgr = 1;
- }
- ch->delayed_chctl_flags = 0;
- }
-
- ch->flags &= ~XPC_C_WDISCONNECT;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
-
- if (wakeup_channel_mgr)
- xpc_wakeup_channel_mgr(part);
-
- xpc_part_deref(part);
- }
-}
-
-static int
-xpc_setup_partitions(void)
-{
- short partid;
- struct xpc_partition *part;
-
- xpc_partitions = kzalloc_objs(struct xpc_partition, xp_max_npartitions);
- if (xpc_partitions == NULL) {
- dev_err(xpc_part, "can't get memory for partition structure\n");
- return -ENOMEM;
- }
-
- /*
- * The first few fields of each entry of xpc_partitions[] need to
- * be initialized now so that calls to xpc_connect() and
- * xpc_disconnect() can be made prior to the activation of any remote
- * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE
- * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING
- * PARTITION HAS BEEN ACTIVATED.
- */
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part));
-
- part->activate_IRQ_rcvd = 0;
- spin_lock_init(&part->act_lock);
- part->act_state = XPC_P_AS_INACTIVE;
- XPC_SET_REASON(part, 0, 0);
-
- timer_setup(&part->disengage_timer,
- xpc_timeout_partition_disengage, 0);
-
- part->setup_state = XPC_P_SS_UNSET;
- init_waitqueue_head(&part->teardown_wq);
- atomic_set(&part->references, 0);
- }
-
- return xpc_arch_ops.setup_partitions();
-}
-
-static void
-xpc_teardown_partitions(void)
-{
- xpc_arch_ops.teardown_partitions();
- kfree(xpc_partitions);
-}
-
-static void
-xpc_do_exit(enum xp_retval reason)
-{
- short partid;
- int active_part_count, printed_waiting_msg = 0;
- struct xpc_partition *part;
- unsigned long printmsg_time, disengage_timeout = 0;
-
- /* a 'rmmod XPC' and a 'reboot' cannot both end up here together */
- DBUG_ON(xpc_exiting == 1);
-
- /*
- * Let the heartbeat checker thread and the discovery thread
- * (if one is running) know that they should exit. Also wake up
- * the heartbeat checker thread in case it's sleeping.
- */
- xpc_exiting = 1;
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-
- /* wait for the discovery thread to exit */
- wait_for_completion(&xpc_discovery_exited);
-
- /* wait for the heartbeat checker thread to exit */
- wait_for_completion(&xpc_hb_checker_exited);
-
- /* sleep for a 1/3 of a second or so */
- (void)msleep_interruptible(300);
-
- /* wait for all partitions to become inactive */
-
- printmsg_time = jiffies + (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ);
- xpc_disengage_timedout = 0;
-
- do {
- active_part_count = 0;
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_partition_disengaged(part) &&
- part->act_state == XPC_P_AS_INACTIVE) {
- continue;
- }
-
- active_part_count++;
-
- XPC_DEACTIVATE_PARTITION(part, reason);
-
- if (part->disengage_timeout > disengage_timeout)
- disengage_timeout = part->disengage_timeout;
- }
-
- if (xpc_arch_ops.any_partition_engaged()) {
- if (time_is_before_jiffies(printmsg_time)) {
- dev_info(xpc_part, "waiting for remote "
- "partitions to deactivate, timeout in "
- "%ld seconds\n", (disengage_timeout -
- jiffies) / HZ);
- printmsg_time = jiffies +
- (XPC_DEACTIVATE_PRINTMSG_INTERVAL * HZ);
- printed_waiting_msg = 1;
- }
-
- } else if (active_part_count > 0) {
- if (printed_waiting_msg) {
- dev_info(xpc_part, "waiting for local partition"
- " to deactivate\n");
- printed_waiting_msg = 0;
- }
-
- } else {
- if (!xpc_disengage_timedout) {
- dev_info(xpc_part, "all partitions have "
- "deactivated\n");
- }
- break;
- }
-
- /* sleep for a 1/3 of a second or so */
- (void)msleep_interruptible(300);
-
- } while (1);
-
- DBUG_ON(xpc_arch_ops.any_partition_engaged());
-
- xpc_teardown_rsvd_page();
-
- if (reason == xpUnloading) {
- (void)unregister_die_notifier(&xpc_die_notifier);
- (void)unregister_reboot_notifier(&xpc_reboot_notifier);
- }
-
- /* clear the interface to XPC's functions */
- xpc_clear_interface();
-
- if (xpc_sysctl)
- unregister_sysctl_table(xpc_sysctl);
- if (xpc_sysctl_hb)
- unregister_sysctl_table(xpc_sysctl_hb);
-
- xpc_teardown_partitions();
-
- if (is_uv_system())
- xpc_exit_uv();
-}
-
-/*
- * This function is called when the system is being rebooted.
- */
-static int
-xpc_system_reboot(struct notifier_block *nb, unsigned long event, void *unused)
-{
- enum xp_retval reason;
-
- switch (event) {
- case SYS_RESTART:
- reason = xpSystemReboot;
- break;
- case SYS_HALT:
- reason = xpSystemHalt;
- break;
- case SYS_POWER_OFF:
- reason = xpSystemPoweroff;
- break;
- default:
- reason = xpSystemGoingDown;
- }
-
- xpc_do_exit(reason);
- return NOTIFY_DONE;
-}
-
-/* Used to only allow one cpu to complete disconnect */
-static unsigned int xpc_die_disconnecting;
-
-/*
- * Notify other partitions to deactivate from us by first disengaging from all
- * references to our memory.
- */
-static void
-xpc_die_deactivate(void)
-{
- struct xpc_partition *part;
- short partid;
- int any_engaged;
- long keep_waiting;
- long wait_to_print;
-
- if (cmpxchg(&xpc_die_disconnecting, 0, 1))
- return;
-
- /* keep xpc_hb_checker thread from doing anything (just in case) */
- xpc_exiting = 1;
-
- xpc_arch_ops.disallow_all_hbs(); /*indicate we're deactivated */
-
- for (partid = 0; partid < xp_max_npartitions; partid++) {
- part = &xpc_partitions[partid];
-
- if (xpc_arch_ops.partition_engaged(partid) ||
- part->act_state != XPC_P_AS_INACTIVE) {
- xpc_arch_ops.request_partition_deactivation(part);
- xpc_arch_ops.indicate_partition_disengaged(part);
- }
- }
-
- /*
- * Though we requested that all other partitions deactivate from us,
- * we only wait until they've all disengaged or we've reached the
- * defined timelimit.
- *
- * Given that one iteration through the following while-loop takes
- * approximately 200 microseconds, calculate the #of loops to take
- * before bailing and the #of loops before printing a waiting message.
- */
- keep_waiting = xpc_disengage_timelimit * 1000 * 5;
- wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL * 1000 * 5;
-
- while (1) {
- any_engaged = xpc_arch_ops.any_partition_engaged();
- if (!any_engaged) {
- dev_info(xpc_part, "all partitions have deactivated\n");
- break;
- }
-
- if (!keep_waiting--) {
- for (partid = 0; partid < xp_max_npartitions;
- partid++) {
- if (xpc_arch_ops.partition_engaged(partid)) {
- dev_info(xpc_part, "deactivate from "
- "remote partition %d timed "
- "out\n", partid);
- }
- }
- break;
- }
-
- if (!wait_to_print--) {
- dev_info(xpc_part, "waiting for remote partitions to "
- "deactivate, timeout in %ld seconds\n",
- keep_waiting / (1000 * 5));
- wait_to_print = XPC_DEACTIVATE_PRINTMSG_INTERVAL *
- 1000 * 5;
- }
-
- udelay(200);
- }
-}
-
-/*
- * This function is called when the system is being restarted or halted due
- * to some sort of system failure. If this is the case we need to notify the
- * other partitions to disengage from all references to our memory.
- * This function can also be called when our heartbeater could be offlined
- * for a time. In this case we need to notify other partitions to not worry
- * about the lack of a heartbeat.
- */
-static int
-xpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args)
-{
- struct die_args *die_args = _die_args;
-
- switch (event) {
- case DIE_TRAP:
- if (die_args->trapnr == X86_TRAP_DF)
- xpc_die_deactivate();
-
- if (((die_args->trapnr == X86_TRAP_MF) ||
- (die_args->trapnr == X86_TRAP_XF)) &&
- !user_mode(die_args->regs))
- xpc_die_deactivate();
-
- break;
- case DIE_INT3:
- case DIE_DEBUG:
- break;
- case DIE_OOPS:
- case DIE_GPF:
- default:
- xpc_die_deactivate();
- }
-
- return NOTIFY_DONE;
-}
-
-static int __init
-xpc_init(void)
-{
- int ret;
- struct task_struct *kthread;
-
- dev_set_name(xpc_part, "part");
- dev_set_name(xpc_chan, "chan");
-
- if (is_uv_system()) {
- ret = xpc_init_uv();
-
- } else {
- ret = -ENODEV;
- }
-
- if (ret != 0)
- return ret;
-
- ret = xpc_setup_partitions();
- if (ret != 0) {
- dev_err(xpc_part, "can't get memory for partition structure\n");
- goto out_1;
- }
-
- xpc_sysctl = register_sysctl("xpc", xpc_sys_xpc);
- xpc_sysctl_hb = register_sysctl("xpc/hb", xpc_sys_xpc_hb);
-
- /*
- * Fill the partition reserved page with the information needed by
- * other partitions to discover we are alive and establish initial
- * communications.
- */
- ret = xpc_setup_rsvd_page();
- if (ret != 0) {
- dev_err(xpc_part, "can't setup our reserved page\n");
- goto out_2;
- }
-
- /* add ourselves to the reboot_notifier_list */
- ret = register_reboot_notifier(&xpc_reboot_notifier);
- if (ret != 0)
- dev_warn(xpc_part, "can't register reboot notifier\n");
-
- /* add ourselves to the die_notifier list */
- ret = register_die_notifier(&xpc_die_notifier);
- if (ret != 0)
- dev_warn(xpc_part, "can't register die notifier\n");
-
- /*
- * The real work-horse behind xpc. This processes incoming
- * interrupts and monitors remote heartbeats.
- */
- kthread = kthread_run(xpc_hb_checker, NULL, XPC_HB_CHECK_THREAD_NAME);
- if (IS_ERR(kthread)) {
- dev_err(xpc_part, "failed while forking hb check thread\n");
- ret = -EBUSY;
- goto out_3;
- }
-
- /*
- * Startup a thread that will attempt to discover other partitions to
- * activate based on info provided by SAL. This new thread is short
- * lived and will exit once discovery is complete.
- */
- kthread = kthread_run(xpc_initiate_discovery, NULL,
- XPC_DISCOVERY_THREAD_NAME);
- if (IS_ERR(kthread)) {
- dev_err(xpc_part, "failed while forking discovery thread\n");
-
- /* mark this new thread as a non-starter */
- complete(&xpc_discovery_exited);
-
- xpc_do_exit(xpUnloading);
- return -EBUSY;
- }
-
- /* set the interface to point at XPC's functions */
- xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect,
- xpc_initiate_send, xpc_initiate_send_notify,
- xpc_initiate_received, xpc_initiate_partid_to_nasids);
-
- return 0;
-
- /* initialization was not successful */
-out_3:
- xpc_teardown_rsvd_page();
-
- (void)unregister_die_notifier(&xpc_die_notifier);
- (void)unregister_reboot_notifier(&xpc_reboot_notifier);
-out_2:
- if (xpc_sysctl_hb)
- unregister_sysctl_table(xpc_sysctl_hb);
- if (xpc_sysctl)
- unregister_sysctl_table(xpc_sysctl);
-
- xpc_teardown_partitions();
-out_1:
- if (is_uv_system())
- xpc_exit_uv();
- return ret;
-}
-
-module_init(xpc_init);
-
-static void __exit
-xpc_exit(void)
-{
- xpc_do_exit(xpUnloading);
-}
-
-module_exit(xpc_exit);
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_DESCRIPTION("Cross Partition Communication (XPC) support");
-MODULE_LICENSE("GPL");
-
-module_param(xpc_hb_interval, int, 0);
-MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between "
- "heartbeat increments.");
-
-module_param(xpc_hb_check_interval, int, 0);
-MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between "
- "heartbeat checks.");
-
-module_param(xpc_disengage_timelimit, int, 0);
-MODULE_PARM_DESC(xpc_disengage_timelimit, "Number of seconds to wait "
- "for disengage to complete.");
-
-module_param(xpc_kdebug_ignore, int, 0);
-MODULE_PARM_DESC(xpc_kdebug_ignore, "Should lack of heartbeat be ignored by "
- "other partitions when dropping into kdebug.");
diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c
deleted file mode 100644
index d0467010558c..000000000000
--- a/drivers/misc/sgi-xp/xpc_partition.c
+++ /dev/null
@@ -1,545 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) partition support.
- *
- * This is the part of XPC that detects the presence/absence of
- * other partitions. It provides a heartbeat and monitors the
- * heartbeats of other partitions.
- *
- */
-
-#include <linux/device.h>
-#include <linux/hardirq.h>
-#include <linux/slab.h>
-#include "xpc.h"
-#include <asm/uv/uv_hub.h>
-
-/* XPC is exiting flag */
-int xpc_exiting;
-
-/* this partition's reserved page pointers */
-struct xpc_rsvd_page *xpc_rsvd_page;
-static unsigned long *xpc_part_nasids;
-unsigned long *xpc_mach_nasids;
-
-static int xpc_nasid_mask_nbytes; /* #of bytes in nasid mask */
-int xpc_nasid_mask_nlongs; /* #of longs in nasid mask */
-
-struct xpc_partition *xpc_partitions;
-
-/*
- * Guarantee that the kmalloc'd memory is cacheline aligned.
- */
-void *
-xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
-{
- /* see if kmalloc will give us cachline aligned memory by default */
- *base = kmalloc(size, flags);
- if (*base == NULL)
- return NULL;
-
- if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
- return *base;
-
- kfree(*base);
-
- /* nope, we'll have to do it ourselves */
- *base = kmalloc(size + L1_CACHE_BYTES, flags);
- if (*base == NULL)
- return NULL;
-
- return (void *)L1_CACHE_ALIGN((u64)*base);
-}
-
-/*
- * Given a nasid, get the physical address of the partition's reserved page
- * for that nasid. This function returns 0 on any error.
- */
-static unsigned long
-xpc_get_rsvd_page_pa(int nasid)
-{
- enum xp_retval ret;
- u64 cookie = 0;
- unsigned long rp_pa = nasid; /* seed with nasid */
- size_t len = 0;
- size_t buf_len = 0;
- void *buf = NULL;
- void *buf_base = NULL;
- enum xp_retval (*get_partition_rsvd_page_pa)
- (void *, u64 *, unsigned long *, size_t *) =
- xpc_arch_ops.get_partition_rsvd_page_pa;
-
- while (1) {
-
- /* !!! rp_pa will need to be _gpa on UV.
- * ??? So do we save it into the architecture specific parts
- * ??? of the xpc_partition structure? Do we rename this
- * ??? function or have two versions? Rename rp_pa for UV to
- * ??? rp_gpa?
- */
- ret = get_partition_rsvd_page_pa(buf, &cookie, &rp_pa, &len);
-
- dev_dbg(xpc_part, "SAL returned with ret=%d, cookie=0x%016lx, "
- "address=0x%016lx, len=0x%016lx\n", ret,
- (unsigned long)cookie, rp_pa, len);
-
- if (ret != xpNeedMoreInfo)
- break;
-
- if (len > buf_len) {
- kfree(buf_base);
- buf_len = L1_CACHE_ALIGN(len);
- buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
- &buf_base);
- if (buf_base == NULL) {
- dev_err(xpc_part, "unable to kmalloc "
- "len=0x%016lx\n", buf_len);
- ret = xpNoMemory;
- break;
- }
- }
-
- ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
- if (ret != xpSuccess) {
- dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
- break;
- }
- }
-
- kfree(buf_base);
-
- if (ret != xpSuccess)
- rp_pa = 0;
-
- dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
- return rp_pa;
-}
-
-/*
- * Fill the partition reserved page with the information needed by
- * other partitions to discover we are alive and establish initial
- * communications.
- */
-int
-xpc_setup_rsvd_page(void)
-{
- int ret;
- struct xpc_rsvd_page *rp;
- unsigned long rp_pa;
- unsigned long new_ts_jiffies;
-
- /* get the local reserved page's address */
-
- preempt_disable();
- rp_pa = xpc_get_rsvd_page_pa(xp_cpu_to_nasid(smp_processor_id()));
- preempt_enable();
- if (rp_pa == 0) {
- dev_err(xpc_part, "SAL failed to locate the reserved page\n");
- return -ESRCH;
- }
- rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
-
- if (rp->SAL_version < 3) {
- /* SAL_versions < 3 had a SAL_partid defined as a u8 */
- rp->SAL_partid &= 0xff;
- }
- BUG_ON(rp->SAL_partid != xp_partition_id);
-
- if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
- dev_err(xpc_part, "the reserved page's partid of %d is outside "
- "supported range (< 0 || >= %d)\n", rp->SAL_partid,
- xp_max_npartitions);
- return -EINVAL;
- }
-
- rp->version = XPC_RP_VERSION;
- rp->max_npartitions = xp_max_npartitions;
-
- /* establish the actual sizes of the nasid masks */
- if (rp->SAL_version == 1) {
- /* SAL_version 1 didn't set the nasids_size field */
- rp->SAL_nasids_size = 128;
- }
- xpc_nasid_mask_nbytes = rp->SAL_nasids_size;
- xpc_nasid_mask_nlongs = BITS_TO_LONGS(rp->SAL_nasids_size *
- BITS_PER_BYTE);
-
- /* setup the pointers to the various items in the reserved page */
- xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
- xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
-
- ret = xpc_arch_ops.setup_rsvd_page(rp);
- if (ret != 0)
- return ret;
-
- /*
- * Set timestamp of when reserved page was setup by XPC.
- * This signifies to the remote partition that our reserved
- * page is initialized.
- */
- new_ts_jiffies = jiffies;
- if (new_ts_jiffies == 0 || new_ts_jiffies == rp->ts_jiffies)
- new_ts_jiffies++;
- rp->ts_jiffies = new_ts_jiffies;
-
- xpc_rsvd_page = rp;
- return 0;
-}
-
-void
-xpc_teardown_rsvd_page(void)
-{
- /* a zero timestamp indicates our rsvd page is not initialized */
- xpc_rsvd_page->ts_jiffies = 0;
-}
-
-/*
- * Get a copy of a portion of the remote partition's rsvd page.
- *
- * remote_rp points to a buffer that is cacheline aligned for BTE copies and
- * is large enough to contain a copy of their reserved page header and
- * part_nasids mask.
- */
-enum xp_retval
-xpc_get_remote_rp(int nasid, unsigned long *discovered_nasids,
- struct xpc_rsvd_page *remote_rp, unsigned long *remote_rp_pa)
-{
- int l;
- enum xp_retval ret;
-
- /* get the reserved page's physical address */
-
- *remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
- if (*remote_rp_pa == 0)
- return xpNoRsvdPageAddr;
-
- /* pull over the reserved page header and part_nasids mask */
- ret = xp_remote_memcpy(xp_pa(remote_rp), *remote_rp_pa,
- XPC_RP_HEADER_SIZE + xpc_nasid_mask_nbytes);
- if (ret != xpSuccess)
- return ret;
-
- if (discovered_nasids != NULL) {
- unsigned long *remote_part_nasids =
- XPC_RP_PART_NASIDS(remote_rp);
-
- for (l = 0; l < xpc_nasid_mask_nlongs; l++)
- discovered_nasids[l] |= remote_part_nasids[l];
- }
-
- /* zero timestamp indicates the reserved page has not been setup */
- if (remote_rp->ts_jiffies == 0)
- return xpRsvdPageNotSet;
-
- if (XPC_VERSION_MAJOR(remote_rp->version) !=
- XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
- return xpBadVersion;
- }
-
- /* check that both remote and local partids are valid for each side */
- if (remote_rp->SAL_partid < 0 ||
- remote_rp->SAL_partid >= xp_max_npartitions ||
- remote_rp->max_npartitions <= xp_partition_id) {
- return xpInvalidPartid;
- }
-
- if (remote_rp->SAL_partid == xp_partition_id)
- return xpLocalPartid;
-
- return xpSuccess;
-}
-
-/*
- * See if the other side has responded to a partition deactivate request
- * from us. Though we requested the remote partition to deactivate with regard
- * to us, we really only need to wait for the other side to disengage from us.
- */
-static int __xpc_partition_disengaged(struct xpc_partition *part,
- bool from_timer)
-{
- short partid = XPC_PARTID(part);
- int disengaged;
-
- disengaged = !xpc_arch_ops.partition_engaged(partid);
- if (part->disengage_timeout) {
- if (!disengaged) {
- if (time_is_after_jiffies(part->disengage_timeout)) {
- /* timelimit hasn't been reached yet */
- return 0;
- }
-
- /*
- * Other side hasn't responded to our deactivate
- * request in a timely fashion, so assume it's dead.
- */
-
- dev_info(xpc_part, "deactivate request to remote "
- "partition %d timed out\n", partid);
- xpc_disengage_timedout = 1;
- xpc_arch_ops.assume_partition_disengaged(partid);
- disengaged = 1;
- }
- part->disengage_timeout = 0;
-
- /* Cancel the timer function if not called from it */
- if (!from_timer)
- timer_delete_sync(&part->disengage_timer);
-
- DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING &&
- part->act_state != XPC_P_AS_INACTIVE);
- if (part->act_state != XPC_P_AS_INACTIVE)
- xpc_wakeup_channel_mgr(part);
-
- xpc_arch_ops.cancel_partition_deactivation_request(part);
- }
- return disengaged;
-}
-
-int xpc_partition_disengaged(struct xpc_partition *part)
-{
- return __xpc_partition_disengaged(part, false);
-}
-
-int xpc_partition_disengaged_from_timer(struct xpc_partition *part)
-{
- return __xpc_partition_disengaged(part, true);
-}
-
-/*
- * Mark specified partition as active.
- */
-enum xp_retval
-xpc_mark_partition_active(struct xpc_partition *part)
-{
- unsigned long irq_flags;
- enum xp_retval ret;
-
- dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
- if (part->act_state == XPC_P_AS_ACTIVATING) {
- part->act_state = XPC_P_AS_ACTIVE;
- ret = xpSuccess;
- } else {
- DBUG_ON(part->reason == xpSuccess);
- ret = part->reason;
- }
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- return ret;
-}
-
-/*
- * Start the process of deactivating the specified partition.
- */
-void
-xpc_deactivate_partition(const int line, struct xpc_partition *part,
- enum xp_retval reason)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
-
- if (part->act_state == XPC_P_AS_INACTIVE) {
- XPC_SET_REASON(part, reason, line);
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- if (reason == xpReactivating) {
- /* we interrupt ourselves to reactivate partition */
- xpc_arch_ops.request_partition_reactivation(part);
- }
- return;
- }
- if (part->act_state == XPC_P_AS_DEACTIVATING) {
- if ((part->reason == xpUnloading && reason != xpUnloading) ||
- reason == xpReactivating) {
- XPC_SET_REASON(part, reason, line);
- }
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- return;
- }
-
- part->act_state = XPC_P_AS_DEACTIVATING;
- XPC_SET_REASON(part, reason, line);
-
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
-
- /* ask remote partition to deactivate with regard to us */
- xpc_arch_ops.request_partition_deactivation(part);
-
- /* set a timelimit on the disengage phase of the deactivation request */
- part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ);
- part->disengage_timer.expires = part->disengage_timeout;
- add_timer(&part->disengage_timer);
-
- dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
- XPC_PARTID(part), reason);
-
- xpc_partition_going_down(part, reason);
-}
-
-/*
- * Mark specified partition as inactive.
- */
-void
-xpc_mark_partition_inactive(struct xpc_partition *part)
-{
- unsigned long irq_flags;
-
- dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
- XPC_PARTID(part));
-
- spin_lock_irqsave(&part->act_lock, irq_flags);
- part->act_state = XPC_P_AS_INACTIVE;
- spin_unlock_irqrestore(&part->act_lock, irq_flags);
- part->remote_rp_pa = 0;
-}
-
-/*
- * SAL has provided a partition and machine mask. The partition mask
- * contains a bit for each even nasid in our partition. The machine
- * mask contains a bit for each even nasid in the entire machine.
- *
- * Using those two bit arrays, we can determine which nasids are
- * known in the machine. Each should also have a reserved page
- * initialized if they are available for partitioning.
- */
-void
-xpc_discovery(void)
-{
- void *remote_rp_base;
- struct xpc_rsvd_page *remote_rp;
- unsigned long remote_rp_pa;
- int region;
- int region_size;
- int max_regions;
- int nasid;
- unsigned long *discovered_nasids;
- enum xp_retval ret;
-
- remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
- xpc_nasid_mask_nbytes,
- GFP_KERNEL, &remote_rp_base);
- if (remote_rp == NULL)
- return;
-
- discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long),
- GFP_KERNEL);
- if (discovered_nasids == NULL) {
- kfree(remote_rp_base);
- return;
- }
-
- /*
- * The term 'region' in this context refers to the minimum number of
- * nodes that can comprise an access protection grouping. The access
- * protection is in regards to memory, IOI and IPI.
- */
- region_size = xp_region_size;
-
- if (is_uv_system())
- max_regions = 256;
- else {
- max_regions = 64;
-
- switch (region_size) {
- case 128:
- max_regions *= 2;
- fallthrough;
- case 64:
- max_regions *= 2;
- fallthrough;
- case 32:
- max_regions *= 2;
- region_size = 16;
- }
- }
-
- for (region = 0; region < max_regions; region++) {
-
- if (xpc_exiting)
- break;
-
- dev_dbg(xpc_part, "searching region %d\n", region);
-
- for (nasid = (region * region_size * 2);
- nasid < ((region + 1) * region_size * 2); nasid += 2) {
-
- if (xpc_exiting)
- break;
-
- dev_dbg(xpc_part, "checking nasid %d\n", nasid);
-
- if (test_bit(nasid / 2, xpc_part_nasids)) {
- dev_dbg(xpc_part, "PROM indicates Nasid %d is "
- "part of the local partition; skipping "
- "region\n", nasid);
- break;
- }
-
- if (!(test_bit(nasid / 2, xpc_mach_nasids))) {
- dev_dbg(xpc_part, "PROM indicates Nasid %d was "
- "not on Numa-Link network at reset\n",
- nasid);
- continue;
- }
-
- if (test_bit(nasid / 2, discovered_nasids)) {
- dev_dbg(xpc_part, "Nasid %d is part of a "
- "partition which was previously "
- "discovered\n", nasid);
- continue;
- }
-
- /* pull over the rsvd page header & part_nasids mask */
-
- ret = xpc_get_remote_rp(nasid, discovered_nasids,
- remote_rp, &remote_rp_pa);
- if (ret != xpSuccess) {
- dev_dbg(xpc_part, "unable to get reserved page "
- "from nasid %d, reason=%d\n", nasid,
- ret);
-
- if (ret == xpLocalPartid)
- break;
-
- continue;
- }
-
- xpc_arch_ops.request_partition_activation(remote_rp,
- remote_rp_pa, nasid);
- }
- }
-
- kfree(discovered_nasids);
- kfree(remote_rp_base);
-}
-
-/*
- * Given a partid, get the nasids owned by that partition from the
- * remote partition's reserved page.
- */
-enum xp_retval
-xpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
-{
- struct xpc_partition *part;
- unsigned long part_nasid_pa;
-
- part = &xpc_partitions[partid];
- if (part->remote_rp_pa == 0)
- return xpPartitionDown;
-
- memset(nasid_mask, 0, xpc_nasid_mask_nbytes);
-
- part_nasid_pa = (unsigned long)XPC_RP_PART_NASIDS(part->remote_rp_pa);
-
- return xp_remote_memcpy(xp_pa(nasid_mask), part_nasid_pa,
- xpc_nasid_mask_nbytes);
-}
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
deleted file mode 100644
index 79c2f00ed4d7..000000000000
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ /dev/null
@@ -1,1727 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * Cross Partition Communication (XPC) uv-based functions.
- *
- * Architecture specific implementation of common functions.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/cpu.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/numa.h>
-#include <asm/uv/uv_hub.h>
-#include <asm/uv/bios.h>
-#include <asm/uv/uv_irq.h>
-#include "../sgi-gru/gru.h"
-#include "../sgi-gru/grukservices.h"
-#include "xpc.h"
-
-static struct xpc_heartbeat_uv *xpc_heartbeat_uv;
-
-#define XPC_ACTIVATE_MSG_SIZE_UV (1 * GRU_CACHE_LINE_BYTES)
-#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
- XPC_ACTIVATE_MSG_SIZE_UV)
-#define XPC_ACTIVATE_IRQ_NAME "xpc_activate"
-
-#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES)
-#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
- XPC_NOTIFY_MSG_SIZE_UV)
-#define XPC_NOTIFY_IRQ_NAME "xpc_notify"
-
-static int xpc_mq_node = NUMA_NO_NODE;
-
-static struct xpc_gru_mq_uv *xpc_activate_mq_uv;
-static struct xpc_gru_mq_uv *xpc_notify_mq_uv;
-
-static int
-xpc_setup_partitions_uv(void)
-{
- short partid;
- struct xpc_partition_uv *part_uv;
-
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part_uv = &xpc_partitions[partid].sn.uv;
-
- mutex_init(&part_uv->cached_activate_gru_mq_desc_mutex);
- spin_lock_init(&part_uv->flags_lock);
- part_uv->remote_act_state = XPC_P_AS_INACTIVE;
- }
- return 0;
-}
-
-static void
-xpc_teardown_partitions_uv(void)
-{
- short partid;
- struct xpc_partition_uv *part_uv;
- unsigned long irq_flags;
-
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part_uv = &xpc_partitions[partid].sn.uv;
-
- if (part_uv->cached_activate_gru_mq_desc != NULL) {
- mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex);
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- kfree(part_uv->cached_activate_gru_mq_desc);
- part_uv->cached_activate_gru_mq_desc = NULL;
- mutex_unlock(&part_uv->
- cached_activate_gru_mq_desc_mutex);
- }
- }
-}
-
-static int
-xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name)
-{
- int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
-
- mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset,
- UV_AFFINITY_CPU);
- if (mq->irq < 0)
- return mq->irq;
-
- mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset);
-
- return 0;
-}
-
-static void
-xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq)
-{
- uv_teardown_irq(mq->irq);
-}
-
-static int
-xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq)
-{
- int ret;
-
- ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address),
- mq->order, &mq->mmr_offset);
- if (ret < 0) {
- dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
- "ret=%d\n", ret);
- return ret;
- }
-
- mq->watchlist_num = ret;
- return 0;
-}
-
-static void
-xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq)
-{
- int ret;
- int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
-
- ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num);
- BUG_ON(ret != BIOS_STATUS_SUCCESS);
-}
-
-static struct xpc_gru_mq_uv *
-xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
- irq_handler_t irq_handler)
-{
- enum xp_retval xp_ret;
- int ret;
- int nid;
- int nasid;
- int pg_order;
- struct page *page;
- struct xpc_gru_mq_uv *mq;
- struct uv_IO_APIC_route_entry *mmr_value;
-
- mq = kmalloc_obj(struct xpc_gru_mq_uv);
- if (mq == NULL) {
- dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() "
- "a xpc_gru_mq_uv structure\n");
- ret = -ENOMEM;
- goto out_0;
- }
-
- mq->gru_mq_desc = kzalloc_obj(struct gru_message_queue_desc);
- if (mq->gru_mq_desc == NULL) {
- dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() "
- "a gru_message_queue_desc structure\n");
- ret = -ENOMEM;
- goto out_1;
- }
-
- pg_order = get_order(mq_size);
- mq->order = pg_order + PAGE_SHIFT;
- mq_size = 1UL << mq->order;
-
- mq->mmr_blade = uv_cpu_to_blade_id(cpu);
-
- nid = cpu_to_node(cpu);
- page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE,
- pg_order);
- if (page == NULL) {
- dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
- "bytes of memory on nid=%d for GRU mq\n", mq_size, nid);
- ret = -ENOMEM;
- goto out_2;
- }
- mq->address = page_address(page);
-
- /* enable generation of irq when GRU mq operation occurs to this mq */
- ret = xpc_gru_mq_watchlist_alloc_uv(mq);
- if (ret != 0)
- goto out_3;
-
- ret = xpc_get_gru_mq_irq_uv(mq, cpu, irq_name);
- if (ret != 0)
- goto out_4;
-
- ret = request_irq(mq->irq, irq_handler, 0, irq_name, NULL);
- if (ret != 0) {
- dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n",
- mq->irq, -ret);
- goto out_5;
- }
-
- nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu));
-
- mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value;
- ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size,
- nasid, mmr_value->vector, mmr_value->dest);
- if (ret != 0) {
- dev_err(xpc_part, "gru_create_message_queue() returned "
- "error=%d\n", ret);
- ret = -EINVAL;
- goto out_6;
- }
-
- /* allow other partitions to access this GRU mq */
- xp_ret = xp_expand_memprotect(xp_pa(mq->address), mq_size);
- if (xp_ret != xpSuccess) {
- ret = -EACCES;
- goto out_6;
- }
-
- return mq;
-
- /* something went wrong */
-out_6:
- free_irq(mq->irq, NULL);
-out_5:
- xpc_release_gru_mq_irq_uv(mq);
-out_4:
- xpc_gru_mq_watchlist_free_uv(mq);
-out_3:
- free_pages((unsigned long)mq->address, pg_order);
-out_2:
- kfree(mq->gru_mq_desc);
-out_1:
- kfree(mq);
-out_0:
- return ERR_PTR(ret);
-}
-
-static void
-xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq)
-{
- unsigned int mq_size;
- int pg_order;
- int ret;
-
- /* disallow other partitions to access GRU mq */
- mq_size = 1UL << mq->order;
- ret = xp_restrict_memprotect(xp_pa(mq->address), mq_size);
- BUG_ON(ret != xpSuccess);
-
- /* unregister irq handler and release mq irq/vector mapping */
- free_irq(mq->irq, NULL);
- xpc_release_gru_mq_irq_uv(mq);
-
- /* disable generation of irq when GRU mq op occurs to this mq */
- xpc_gru_mq_watchlist_free_uv(mq);
-
- pg_order = mq->order - PAGE_SHIFT;
- free_pages((unsigned long)mq->address, pg_order);
-
- kfree(mq);
-}
-
-static enum xp_retval
-xpc_send_gru_msg(struct gru_message_queue_desc *gru_mq_desc, void *msg,
- size_t msg_size)
-{
- enum xp_retval xp_ret;
- int ret;
-
- while (1) {
- ret = gru_send_message_gpa(gru_mq_desc, msg, msg_size);
- if (ret == MQE_OK) {
- xp_ret = xpSuccess;
- break;
- }
-
- if (ret == MQE_QUEUE_FULL) {
- dev_dbg(xpc_chan, "gru_send_message_gpa() returned "
- "error=MQE_QUEUE_FULL\n");
- /* !!! handle QLimit reached; delay & try again */
- /* ??? Do we add a limit to the number of retries? */
- (void)msleep_interruptible(10);
- } else if (ret == MQE_CONGESTION) {
- dev_dbg(xpc_chan, "gru_send_message_gpa() returned "
- "error=MQE_CONGESTION\n");
- /* !!! handle LB Overflow; simply try again */
- /* ??? Do we add a limit to the number of retries? */
- } else {
- /* !!! Currently this is MQE_UNEXPECTED_CB_ERR */
- dev_err(xpc_chan, "gru_send_message_gpa() returned "
- "error=%d\n", ret);
- xp_ret = xpGruSendMqError;
- break;
- }
- }
- return xp_ret;
-}
-
-static void
-xpc_process_activate_IRQ_rcvd_uv(void)
-{
- unsigned long irq_flags;
- short partid;
- struct xpc_partition *part;
- u8 act_state_req;
-
- DBUG_ON(xpc_activate_IRQ_rcvd == 0);
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part = &xpc_partitions[partid];
-
- if (part->sn.uv.act_state_req == 0)
- continue;
-
- xpc_activate_IRQ_rcvd--;
- BUG_ON(xpc_activate_IRQ_rcvd < 0);
-
- act_state_req = part->sn.uv.act_state_req;
- part->sn.uv.act_state_req = 0;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- if (act_state_req == XPC_P_ASR_ACTIVATE_UV) {
- if (part->act_state == XPC_P_AS_INACTIVE)
- xpc_activate_partition(part);
- else if (part->act_state == XPC_P_AS_DEACTIVATING)
- XPC_DEACTIVATE_PARTITION(part, xpReactivating);
-
- } else if (act_state_req == XPC_P_ASR_REACTIVATE_UV) {
- if (part->act_state == XPC_P_AS_INACTIVE)
- xpc_activate_partition(part);
- else
- XPC_DEACTIVATE_PARTITION(part, xpReactivating);
-
- } else if (act_state_req == XPC_P_ASR_DEACTIVATE_UV) {
- XPC_DEACTIVATE_PARTITION(part, part->sn.uv.reason);
-
- } else {
- BUG();
- }
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (xpc_activate_IRQ_rcvd == 0)
- break;
- }
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
-}
-
-static void
-xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
- struct xpc_activate_mq_msghdr_uv *msg_hdr,
- int part_setup,
- int *wakeup_hb_checker)
-{
- unsigned long irq_flags;
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- struct xpc_openclose_args *args;
-
- part_uv->remote_act_state = msg_hdr->act_state;
-
- switch (msg_hdr->type) {
- case XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV:
- /* syncing of remote_act_state was just done above */
- break;
-
- case XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV: {
- struct xpc_activate_mq_msg_activate_req_uv *msg;
-
- /*
- * ??? Do we deal here with ts_jiffies being different
- * ??? if act_state != XPC_P_AS_INACTIVE instead of
- * ??? below?
- */
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_activate_req_uv, hdr);
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_ACTIVATE_UV;
- part->remote_rp_pa = msg->rp_gpa; /* !!! _pa is _gpa */
- part->remote_rp_ts_jiffies = msg_hdr->rp_ts_jiffies;
- part_uv->heartbeat_gpa = msg->heartbeat_gpa;
-
- if (msg->activate_gru_mq_desc_gpa !=
- part_uv->activate_gru_mq_desc_gpa) {
- spin_lock(&part_uv->flags_lock);
- part_uv->flags &= ~XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV;
- spin_unlock(&part_uv->flags_lock);
- part_uv->activate_gru_mq_desc_gpa =
- msg->activate_gru_mq_desc_gpa;
- }
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV: {
- struct xpc_activate_mq_msg_deactivate_req_uv *msg;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_deactivate_req_uv, hdr);
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV;
- part_uv->reason = msg->reason;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- return;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV: {
- struct xpc_activate_mq_msg_chctl_closerequest_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_closerequest_uv,
- hdr);
- args = &part->remote_openclose_args[msg->ch_number];
- args->reason = msg->reason;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREQUEST;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV: {
- struct xpc_activate_mq_msg_chctl_closereply_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_closereply_uv,
- hdr);
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_CLOSEREPLY;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV: {
- struct xpc_activate_mq_msg_chctl_openrequest_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_openrequest_uv,
- hdr);
- args = &part->remote_openclose_args[msg->ch_number];
- args->entry_size = msg->entry_size;
- args->local_nentries = msg->local_nentries;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREQUEST;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV: {
- struct xpc_activate_mq_msg_chctl_openreply_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_openreply_uv, hdr);
- args = &part->remote_openclose_args[msg->ch_number];
- args->remote_nentries = msg->remote_nentries;
- args->local_nentries = msg->local_nentries;
- args->local_msgqueue_pa = msg->notify_gru_mq_desc_gpa;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENREPLY;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- break;
- }
- case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV: {
- struct xpc_activate_mq_msg_chctl_opencomplete_uv *msg;
-
- if (!part_setup)
- break;
-
- msg = container_of(msg_hdr, struct
- xpc_activate_mq_msg_chctl_opencomplete_uv, hdr);
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[msg->ch_number] |= XPC_CHCTL_OPENCOMPLETE;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
- }
- fallthrough;
- case XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV:
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags |= XPC_P_ENGAGED_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- break;
-
- case XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV:
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags &= ~XPC_P_ENGAGED_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- break;
-
- default:
- dev_err(xpc_part, "received unknown activate_mq msg type=%d "
- "from partition=%d\n", msg_hdr->type, XPC_PARTID(part));
-
- /* get hb checker to deactivate from the remote partition */
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV;
- part_uv->reason = xpBadMsgType;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- return;
- }
-
- if (msg_hdr->rp_ts_jiffies != part->remote_rp_ts_jiffies &&
- part->remote_rp_ts_jiffies != 0) {
- /*
- * ??? Does what we do here need to be sensitive to
- * ??? act_state or remote_act_state?
- */
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_REACTIVATE_UV;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- (*wakeup_hb_checker)++;
- }
-}
-
-static irqreturn_t
-xpc_handle_activate_IRQ_uv(int irq, void *dev_id)
-{
- struct xpc_activate_mq_msghdr_uv *msg_hdr;
- short partid;
- struct xpc_partition *part;
- int wakeup_hb_checker = 0;
- int part_referenced;
-
- while (1) {
- msg_hdr = gru_get_next_message(xpc_activate_mq_uv->gru_mq_desc);
- if (msg_hdr == NULL)
- break;
-
- partid = msg_hdr->partid;
- if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) {
- dev_err(xpc_part, "xpc_handle_activate_IRQ_uv() "
- "received invalid partid=0x%x in message\n",
- partid);
- } else {
- part = &xpc_partitions[partid];
-
- part_referenced = xpc_part_ref(part);
- xpc_handle_activate_mq_msg_uv(part, msg_hdr,
- part_referenced,
- &wakeup_hb_checker);
- if (part_referenced)
- xpc_part_deref(part);
- }
-
- gru_free_message(xpc_activate_mq_uv->gru_mq_desc, msg_hdr);
- }
-
- if (wakeup_hb_checker)
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-
- return IRQ_HANDLED;
-}
-
-static enum xp_retval
-xpc_cache_remote_gru_mq_desc_uv(struct gru_message_queue_desc *gru_mq_desc,
- unsigned long gru_mq_desc_gpa)
-{
- enum xp_retval ret;
-
- ret = xp_remote_memcpy(uv_gpa(gru_mq_desc), gru_mq_desc_gpa,
- sizeof(struct gru_message_queue_desc));
- if (ret == xpSuccess)
- gru_mq_desc->mq = NULL;
-
- return ret;
-}
-
-static enum xp_retval
-xpc_send_activate_IRQ_uv(struct xpc_partition *part, void *msg, size_t msg_size,
- int msg_type)
-{
- struct xpc_activate_mq_msghdr_uv *msg_hdr = msg;
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- struct gru_message_queue_desc *gru_mq_desc;
- unsigned long irq_flags;
- enum xp_retval ret;
-
- DBUG_ON(msg_size > XPC_ACTIVATE_MSG_SIZE_UV);
-
- msg_hdr->type = msg_type;
- msg_hdr->partid = xp_partition_id;
- msg_hdr->act_state = part->act_state;
- msg_hdr->rp_ts_jiffies = xpc_rsvd_page->ts_jiffies;
-
- mutex_lock(&part_uv->cached_activate_gru_mq_desc_mutex);
-again:
- if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV)) {
- gru_mq_desc = part_uv->cached_activate_gru_mq_desc;
- if (gru_mq_desc == NULL) {
- gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc,
- GFP_ATOMIC);
- if (gru_mq_desc == NULL) {
- ret = xpNoMemory;
- goto done;
- }
- part_uv->cached_activate_gru_mq_desc = gru_mq_desc;
- }
-
- ret = xpc_cache_remote_gru_mq_desc_uv(gru_mq_desc,
- part_uv->
- activate_gru_mq_desc_gpa);
- if (ret != xpSuccess)
- goto done;
-
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags |= XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
- }
-
- /* ??? Is holding a spin_lock (ch->lock) during this call a bad idea? */
- ret = xpc_send_gru_msg(part_uv->cached_activate_gru_mq_desc, msg,
- msg_size);
- if (ret != xpSuccess) {
- smp_rmb(); /* ensure a fresh copy of part_uv->flags */
- if (!(part_uv->flags & XPC_P_CACHED_ACTIVATE_GRU_MQ_DESC_UV))
- goto again;
- }
-done:
- mutex_unlock(&part_uv->cached_activate_gru_mq_desc_mutex);
- return ret;
-}
-
-static void
-xpc_send_activate_IRQ_part_uv(struct xpc_partition *part, void *msg,
- size_t msg_size, int msg_type)
-{
- enum xp_retval ret;
-
- ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type);
- if (unlikely(ret != xpSuccess))
- XPC_DEACTIVATE_PARTITION(part, ret);
-}
-
-static void
-xpc_send_activate_IRQ_ch_uv(struct xpc_channel *ch, unsigned long *irq_flags,
- void *msg, size_t msg_size, int msg_type)
-{
- struct xpc_partition *part = &xpc_partitions[ch->partid];
- enum xp_retval ret;
-
- ret = xpc_send_activate_IRQ_uv(part, msg, msg_size, msg_type);
- if (unlikely(ret != xpSuccess)) {
- if (irq_flags != NULL)
- spin_unlock_irqrestore(&ch->lock, *irq_flags);
-
- XPC_DEACTIVATE_PARTITION(part, ret);
-
- if (irq_flags != NULL)
- spin_lock_irqsave(&ch->lock, *irq_flags);
- }
-}
-
-static void
-xpc_send_local_activate_IRQ_uv(struct xpc_partition *part, int act_state_req)
-{
- unsigned long irq_flags;
- struct xpc_partition_uv *part_uv = &part->sn.uv;
-
- /*
- * !!! Make our side think that the remote partition sent an activate
- * !!! mq message our way by doing what the activate IRQ handler would
- * !!! do had one really been sent.
- */
-
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = act_state_req;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- wake_up_interruptible(&xpc_activate_IRQ_wq);
-}
-
-static enum xp_retval
-xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa,
- size_t *len)
-{
- s64 status;
- enum xp_retval ret;
-
- status = uv_bios_reserved_page_pa((u64)buf, cookie, (u64 *)rp_pa,
- (u64 *)len);
- if (status == BIOS_STATUS_SUCCESS)
- ret = xpSuccess;
- else if (status == BIOS_STATUS_MORE_PASSES)
- ret = xpNeedMoreInfo;
- else
- ret = xpBiosError;
-
- return ret;
-}
-
-static int
-xpc_setup_rsvd_page_uv(struct xpc_rsvd_page *rp)
-{
- xpc_heartbeat_uv =
- &xpc_partitions[sn_partition_id].sn.uv.cached_heartbeat;
- rp->sn.uv.heartbeat_gpa = uv_gpa(xpc_heartbeat_uv);
- rp->sn.uv.activate_gru_mq_desc_gpa =
- uv_gpa(xpc_activate_mq_uv->gru_mq_desc);
- return 0;
-}
-
-static void
-xpc_allow_hb_uv(short partid)
-{
-}
-
-static void
-xpc_disallow_hb_uv(short partid)
-{
-}
-
-static void
-xpc_disallow_all_hbs_uv(void)
-{
-}
-
-static void
-xpc_increment_heartbeat_uv(void)
-{
- xpc_heartbeat_uv->value++;
-}
-
-static void
-xpc_offline_heartbeat_uv(void)
-{
- xpc_increment_heartbeat_uv();
- xpc_heartbeat_uv->offline = 1;
-}
-
-static void
-xpc_online_heartbeat_uv(void)
-{
- xpc_increment_heartbeat_uv();
- xpc_heartbeat_uv->offline = 0;
-}
-
-static void
-xpc_heartbeat_init_uv(void)
-{
- xpc_heartbeat_uv->value = 1;
- xpc_heartbeat_uv->offline = 0;
-}
-
-static void
-xpc_heartbeat_exit_uv(void)
-{
- xpc_offline_heartbeat_uv();
-}
-
-static enum xp_retval
-xpc_get_remote_heartbeat_uv(struct xpc_partition *part)
-{
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- enum xp_retval ret;
-
- ret = xp_remote_memcpy(uv_gpa(&part_uv->cached_heartbeat),
- part_uv->heartbeat_gpa,
- sizeof(struct xpc_heartbeat_uv));
- if (ret != xpSuccess)
- return ret;
-
- if (part_uv->cached_heartbeat.value == part->last_heartbeat &&
- !part_uv->cached_heartbeat.offline) {
-
- ret = xpNoHeartbeat;
- } else {
- part->last_heartbeat = part_uv->cached_heartbeat.value;
- }
- return ret;
-}
-
-static void
-xpc_request_partition_activation_uv(struct xpc_rsvd_page *remote_rp,
- unsigned long remote_rp_gpa, int nasid)
-{
- short partid = remote_rp->SAL_partid;
- struct xpc_partition *part = &xpc_partitions[partid];
- struct xpc_activate_mq_msg_activate_req_uv msg;
-
- part->remote_rp_pa = remote_rp_gpa; /* !!! _pa here is really _gpa */
- part->remote_rp_ts_jiffies = remote_rp->ts_jiffies;
- part->sn.uv.heartbeat_gpa = remote_rp->sn.uv.heartbeat_gpa;
- part->sn.uv.activate_gru_mq_desc_gpa =
- remote_rp->sn.uv.activate_gru_mq_desc_gpa;
-
- /*
- * ??? Is it a good idea to make this conditional on what is
- * ??? potentially stale state information?
- */
- if (part->sn.uv.remote_act_state == XPC_P_AS_INACTIVE) {
- msg.rp_gpa = uv_gpa(xpc_rsvd_page);
- msg.heartbeat_gpa = xpc_rsvd_page->sn.uv.heartbeat_gpa;
- msg.activate_gru_mq_desc_gpa =
- xpc_rsvd_page->sn.uv.activate_gru_mq_desc_gpa;
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_ACTIVATE_REQ_UV);
- }
-
- if (part->act_state == XPC_P_AS_INACTIVE)
- xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV);
-}
-
-static void
-xpc_request_partition_reactivation_uv(struct xpc_partition *part)
-{
- xpc_send_local_activate_IRQ_uv(part, XPC_P_ASR_ACTIVATE_UV);
-}
-
-static void
-xpc_request_partition_deactivation_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_deactivate_req_uv msg;
-
- /*
- * ??? Is it a good idea to make this conditional on what is
- * ??? potentially stale state information?
- */
- if (part->sn.uv.remote_act_state != XPC_P_AS_DEACTIVATING &&
- part->sn.uv.remote_act_state != XPC_P_AS_INACTIVE) {
-
- msg.reason = part->reason;
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_DEACTIVATE_REQ_UV);
- }
-}
-
-static void
-xpc_cancel_partition_deactivation_request_uv(struct xpc_partition *part)
-{
- /* nothing needs to be done */
- return;
-}
-
-static void
-xpc_init_fifo_uv(struct xpc_fifo_head_uv *head)
-{
- head->first = NULL;
- head->last = NULL;
- spin_lock_init(&head->lock);
- head->n_entries = 0;
-}
-
-static void *
-xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head)
-{
- unsigned long irq_flags;
- struct xpc_fifo_entry_uv *first;
-
- spin_lock_irqsave(&head->lock, irq_flags);
- first = head->first;
- if (head->first != NULL) {
- head->first = first->next;
- if (head->first == NULL)
- head->last = NULL;
-
- head->n_entries--;
- BUG_ON(head->n_entries < 0);
-
- first->next = NULL;
- }
- spin_unlock_irqrestore(&head->lock, irq_flags);
- return first;
-}
-
-static void
-xpc_put_fifo_entry_uv(struct xpc_fifo_head_uv *head,
- struct xpc_fifo_entry_uv *last)
-{
- unsigned long irq_flags;
-
- last->next = NULL;
- spin_lock_irqsave(&head->lock, irq_flags);
- if (head->last != NULL)
- head->last->next = last;
- else
- head->first = last;
- head->last = last;
- head->n_entries++;
- spin_unlock_irqrestore(&head->lock, irq_flags);
-}
-
-static int
-xpc_n_of_fifo_entries_uv(struct xpc_fifo_head_uv *head)
-{
- return head->n_entries;
-}
-
-/*
- * Setup the channel structures that are uv specific.
- */
-static enum xp_retval
-xpc_setup_ch_structures_uv(struct xpc_partition *part)
-{
- struct xpc_channel_uv *ch_uv;
- int ch_number;
-
- for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
- ch_uv = &part->channels[ch_number].sn.uv;
-
- xpc_init_fifo_uv(&ch_uv->msg_slot_free_list);
- xpc_init_fifo_uv(&ch_uv->recv_msg_list);
- }
-
- return xpSuccess;
-}
-
-/*
- * Teardown the channel structures that are uv specific.
- */
-static void
-xpc_teardown_ch_structures_uv(struct xpc_partition *part)
-{
- /* nothing needs to be done */
- return;
-}
-
-static enum xp_retval
-xpc_make_first_contact_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_uv msg;
-
- /*
- * We send a sync msg to get the remote partition's remote_act_state
- * updated to our current act_state which at this point should
- * be XPC_P_AS_ACTIVATING.
- */
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV);
-
- while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) ||
- (part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) {
-
- dev_dbg(xpc_part, "waiting to make first contact with "
- "partition %d\n", XPC_PARTID(part));
-
- /* wait a 1/4 of a second or so */
- (void)msleep_interruptible(250);
-
- if (part->act_state == XPC_P_AS_DEACTIVATING)
- return part->reason;
- }
-
- return xpSuccess;
-}
-
-static u64
-xpc_get_chctl_all_flags_uv(struct xpc_partition *part)
-{
- unsigned long irq_flags;
- union xpc_channel_ctl_flags chctl;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- chctl = part->chctl;
- if (chctl.all_flags != 0)
- part->chctl.all_flags = 0;
-
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
- return chctl.all_flags;
-}
-
-static enum xp_retval
-xpc_allocate_send_msg_slot_uv(struct xpc_channel *ch)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
- struct xpc_send_msg_slot_uv *msg_slot;
- unsigned long irq_flags;
- int nentries;
- int entry;
- size_t nbytes;
-
- for (nentries = ch->local_nentries; nentries > 0; nentries--) {
- nbytes = nentries * sizeof(struct xpc_send_msg_slot_uv);
- ch_uv->send_msg_slots = kzalloc(nbytes, GFP_KERNEL);
- if (ch_uv->send_msg_slots == NULL)
- continue;
-
- for (entry = 0; entry < nentries; entry++) {
- msg_slot = &ch_uv->send_msg_slots[entry];
-
- msg_slot->msg_slot_number = entry;
- xpc_put_fifo_entry_uv(&ch_uv->msg_slot_free_list,
- &msg_slot->next);
- }
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if (nentries < ch->local_nentries)
- ch->local_nentries = nentries;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return xpSuccess;
- }
-
- return xpNoMemory;
-}
-
-static enum xp_retval
-xpc_allocate_recv_msg_slot_uv(struct xpc_channel *ch)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
- struct xpc_notify_mq_msg_uv *msg_slot;
- unsigned long irq_flags;
- int nentries;
- int entry;
- size_t nbytes;
-
- for (nentries = ch->remote_nentries; nentries > 0; nentries--) {
- nbytes = nentries * ch->entry_size;
- ch_uv->recv_msg_slots = kzalloc(nbytes, GFP_KERNEL);
- if (ch_uv->recv_msg_slots == NULL)
- continue;
-
- for (entry = 0; entry < nentries; entry++) {
- msg_slot = ch_uv->recv_msg_slots +
- entry * ch->entry_size;
-
- msg_slot->hdr.msg_slot_number = entry;
- }
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- if (nentries < ch->remote_nentries)
- ch->remote_nentries = nentries;
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return xpSuccess;
- }
-
- return xpNoMemory;
-}
-
-/*
- * Allocate msg_slots associated with the channel.
- */
-static enum xp_retval
-xpc_setup_msg_structures_uv(struct xpc_channel *ch)
-{
- static enum xp_retval ret;
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
-
- DBUG_ON(ch->flags & XPC_C_SETUP);
-
- ch_uv->cached_notify_gru_mq_desc = kmalloc_obj(struct gru_message_queue_desc);
- if (ch_uv->cached_notify_gru_mq_desc == NULL)
- return xpNoMemory;
-
- ret = xpc_allocate_send_msg_slot_uv(ch);
- if (ret == xpSuccess) {
-
- ret = xpc_allocate_recv_msg_slot_uv(ch);
- if (ret != xpSuccess) {
- kfree(ch_uv->send_msg_slots);
- xpc_init_fifo_uv(&ch_uv->msg_slot_free_list);
- }
- }
- return ret;
-}
-
-/*
- * Free up msg_slots and clear other stuff that were setup for the specified
- * channel.
- */
-static void
-xpc_teardown_msg_structures_uv(struct xpc_channel *ch)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
-
- lockdep_assert_held(&ch->lock);
-
- kfree(ch_uv->cached_notify_gru_mq_desc);
- ch_uv->cached_notify_gru_mq_desc = NULL;
-
- if (ch->flags & XPC_C_SETUP) {
- xpc_init_fifo_uv(&ch_uv->msg_slot_free_list);
- kfree(ch_uv->send_msg_slots);
- xpc_init_fifo_uv(&ch_uv->recv_msg_list);
- kfree(ch_uv->recv_msg_slots);
- }
-}
-
-static void
-xpc_send_chctl_closerequest_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_closerequest_uv msg;
-
- msg.ch_number = ch->number;
- msg.reason = ch->reason;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV);
-}
-
-static void
-xpc_send_chctl_closereply_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_closereply_uv msg;
-
- msg.ch_number = ch->number;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV);
-}
-
-static void
-xpc_send_chctl_openrequest_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_openrequest_uv msg;
-
- msg.ch_number = ch->number;
- msg.entry_size = ch->entry_size;
- msg.local_nentries = ch->local_nentries;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV);
-}
-
-static void
-xpc_send_chctl_openreply_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_openreply_uv msg;
-
- msg.ch_number = ch->number;
- msg.local_nentries = ch->local_nentries;
- msg.remote_nentries = ch->remote_nentries;
- msg.notify_gru_mq_desc_gpa = uv_gpa(xpc_notify_mq_uv->gru_mq_desc);
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV);
-}
-
-static void
-xpc_send_chctl_opencomplete_uv(struct xpc_channel *ch, unsigned long *irq_flags)
-{
- struct xpc_activate_mq_msg_chctl_opencomplete_uv msg;
-
- msg.ch_number = ch->number;
- xpc_send_activate_IRQ_ch_uv(ch, irq_flags, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV);
-}
-
-static void
-xpc_send_chctl_local_msgrequest_uv(struct xpc_partition *part, int ch_number)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&part->chctl_lock, irq_flags);
- part->chctl.flags[ch_number] |= XPC_CHCTL_MSGREQUEST;
- spin_unlock_irqrestore(&part->chctl_lock, irq_flags);
-
- xpc_wakeup_channel_mgr(part);
-}
-
-static enum xp_retval
-xpc_save_remote_msgqueue_pa_uv(struct xpc_channel *ch,
- unsigned long gru_mq_desc_gpa)
-{
- struct xpc_channel_uv *ch_uv = &ch->sn.uv;
-
- DBUG_ON(ch_uv->cached_notify_gru_mq_desc == NULL);
- return xpc_cache_remote_gru_mq_desc_uv(ch_uv->cached_notify_gru_mq_desc,
- gru_mq_desc_gpa);
-}
-
-static void
-xpc_indicate_partition_engaged_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_uv msg;
-
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_MARK_ENGAGED_UV);
-}
-
-static void
-xpc_indicate_partition_disengaged_uv(struct xpc_partition *part)
-{
- struct xpc_activate_mq_msg_uv msg;
-
- xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
- XPC_ACTIVATE_MQ_MSG_MARK_DISENGAGED_UV);
-}
-
-static void
-xpc_assume_partition_disengaged_uv(short partid)
-{
- struct xpc_partition_uv *part_uv = &xpc_partitions[partid].sn.uv;
- unsigned long irq_flags;
-
- spin_lock_irqsave(&part_uv->flags_lock, irq_flags);
- part_uv->flags &= ~XPC_P_ENGAGED_UV;
- spin_unlock_irqrestore(&part_uv->flags_lock, irq_flags);
-}
-
-static int
-xpc_partition_engaged_uv(short partid)
-{
- return (xpc_partitions[partid].sn.uv.flags & XPC_P_ENGAGED_UV) != 0;
-}
-
-static int
-xpc_any_partition_engaged_uv(void)
-{
- struct xpc_partition_uv *part_uv;
- short partid;
-
- for (partid = 0; partid < XP_MAX_NPARTITIONS_UV; partid++) {
- part_uv = &xpc_partitions[partid].sn.uv;
- if ((part_uv->flags & XPC_P_ENGAGED_UV) != 0)
- return 1;
- }
- return 0;
-}
-
-static enum xp_retval
-xpc_allocate_msg_slot_uv(struct xpc_channel *ch, u32 flags,
- struct xpc_send_msg_slot_uv **address_of_msg_slot)
-{
- enum xp_retval ret;
- struct xpc_send_msg_slot_uv *msg_slot;
- struct xpc_fifo_entry_uv *entry;
-
- while (1) {
- entry = xpc_get_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list);
- if (entry != NULL)
- break;
-
- if (flags & XPC_NOWAIT)
- return xpNoWait;
-
- ret = xpc_allocate_msg_wait(ch);
- if (ret != xpInterrupted && ret != xpTimeout)
- return ret;
- }
-
- msg_slot = container_of(entry, struct xpc_send_msg_slot_uv, next);
- *address_of_msg_slot = msg_slot;
- return xpSuccess;
-}
-
-static void
-xpc_free_msg_slot_uv(struct xpc_channel *ch,
- struct xpc_send_msg_slot_uv *msg_slot)
-{
- xpc_put_fifo_entry_uv(&ch->sn.uv.msg_slot_free_list, &msg_slot->next);
-
- /* wakeup anyone waiting for a free msg slot */
- if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)
- wake_up(&ch->msg_allocate_wq);
-}
-
-static void
-xpc_notify_sender_uv(struct xpc_channel *ch,
- struct xpc_send_msg_slot_uv *msg_slot,
- enum xp_retval reason)
-{
- xpc_notify_func func = msg_slot->func;
-
- if (func != NULL && cmpxchg(&msg_slot->func, func, NULL) == func) {
-
- atomic_dec(&ch->n_to_notify);
-
- dev_dbg(xpc_chan, "msg_slot->func() called, msg_slot=0x%p "
- "msg_slot_number=%d partid=%d channel=%d\n", msg_slot,
- msg_slot->msg_slot_number, ch->partid, ch->number);
-
- func(reason, ch->partid, ch->number, msg_slot->key);
-
- dev_dbg(xpc_chan, "msg_slot->func() returned, msg_slot=0x%p "
- "msg_slot_number=%d partid=%d channel=%d\n", msg_slot,
- msg_slot->msg_slot_number, ch->partid, ch->number);
- }
-}
-
-static void
-xpc_handle_notify_mq_ack_uv(struct xpc_channel *ch,
- struct xpc_notify_mq_msg_uv *msg)
-{
- struct xpc_send_msg_slot_uv *msg_slot;
- int entry = msg->hdr.msg_slot_number % ch->local_nentries;
-
- msg_slot = &ch->sn.uv.send_msg_slots[entry];
-
- BUG_ON(msg_slot->msg_slot_number != msg->hdr.msg_slot_number);
- msg_slot->msg_slot_number += ch->local_nentries;
-
- if (msg_slot->func != NULL)
- xpc_notify_sender_uv(ch, msg_slot, xpMsgDelivered);
-
- xpc_free_msg_slot_uv(ch, msg_slot);
-}
-
-static void
-xpc_handle_notify_mq_msg_uv(struct xpc_partition *part,
- struct xpc_notify_mq_msg_uv *msg)
-{
- struct xpc_partition_uv *part_uv = &part->sn.uv;
- struct xpc_channel *ch;
- struct xpc_channel_uv *ch_uv;
- struct xpc_notify_mq_msg_uv *msg_slot;
- unsigned long irq_flags;
- int ch_number = msg->hdr.ch_number;
-
- if (unlikely(ch_number >= part->nchannels)) {
- dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received invalid "
- "channel number=0x%x in message from partid=%d\n",
- ch_number, XPC_PARTID(part));
-
- /* get hb checker to deactivate from the remote partition */
- spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags);
- if (part_uv->act_state_req == 0)
- xpc_activate_IRQ_rcvd++;
- part_uv->act_state_req = XPC_P_ASR_DEACTIVATE_UV;
- part_uv->reason = xpBadChannelNumber;
- spin_unlock_irqrestore(&xpc_activate_IRQ_rcvd_lock, irq_flags);
-
- wake_up_interruptible(&xpc_activate_IRQ_wq);
- return;
- }
-
- ch = &part->channels[ch_number];
- xpc_msgqueue_ref(ch);
-
- if (!(ch->flags & XPC_C_CONNECTED)) {
- xpc_msgqueue_deref(ch);
- return;
- }
-
- /* see if we're really dealing with an ACK for a previously sent msg */
- if (msg->hdr.size == 0) {
- xpc_handle_notify_mq_ack_uv(ch, msg);
- xpc_msgqueue_deref(ch);
- return;
- }
-
- /* we're dealing with a normal message sent via the notify_mq */
- ch_uv = &ch->sn.uv;
-
- msg_slot = ch_uv->recv_msg_slots +
- (msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size;
-
- BUG_ON(msg_slot->hdr.size != 0);
-
- memcpy(msg_slot, msg, msg->hdr.size);
-
- xpc_put_fifo_entry_uv(&ch_uv->recv_msg_list, &msg_slot->hdr.u.next);
-
- if (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) {
- /*
- * If there is an existing idle kthread get it to deliver
- * the payload, otherwise we'll have to get the channel mgr
- * for this partition to create a kthread to do the delivery.
- */
- if (atomic_read(&ch->kthreads_idle) > 0)
- wake_up_nr(&ch->idle_wq, 1);
- else
- xpc_send_chctl_local_msgrequest_uv(part, ch->number);
- }
- xpc_msgqueue_deref(ch);
-}
-
-static irqreturn_t
-xpc_handle_notify_IRQ_uv(int irq, void *dev_id)
-{
- struct xpc_notify_mq_msg_uv *msg;
- short partid;
- struct xpc_partition *part;
-
- while ((msg = gru_get_next_message(xpc_notify_mq_uv->gru_mq_desc)) !=
- NULL) {
-
- partid = msg->hdr.partid;
- if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) {
- dev_err(xpc_part, "xpc_handle_notify_IRQ_uv() received "
- "invalid partid=0x%x in message\n", partid);
- } else {
- part = &xpc_partitions[partid];
-
- if (xpc_part_ref(part)) {
- xpc_handle_notify_mq_msg_uv(part, msg);
- xpc_part_deref(part);
- }
- }
-
- gru_free_message(xpc_notify_mq_uv->gru_mq_desc, msg);
- }
-
- return IRQ_HANDLED;
-}
-
-static int
-xpc_n_of_deliverable_payloads_uv(struct xpc_channel *ch)
-{
- return xpc_n_of_fifo_entries_uv(&ch->sn.uv.recv_msg_list);
-}
-
-static void
-xpc_process_msg_chctl_flags_uv(struct xpc_partition *part, int ch_number)
-{
- struct xpc_channel *ch = &part->channels[ch_number];
- int ndeliverable_payloads;
-
- xpc_msgqueue_ref(ch);
-
- ndeliverable_payloads = xpc_n_of_deliverable_payloads_uv(ch);
-
- if (ndeliverable_payloads > 0 &&
- (ch->flags & XPC_C_CONNECTED) &&
- (ch->flags & XPC_C_CONNECTEDCALLOUT_MADE)) {
-
- xpc_activate_kthreads(ch, ndeliverable_payloads);
- }
-
- xpc_msgqueue_deref(ch);
-}
-
-static enum xp_retval
-xpc_send_payload_uv(struct xpc_channel *ch, u32 flags, void *payload,
- u16 payload_size, u8 notify_type, xpc_notify_func func,
- void *key)
-{
- enum xp_retval ret = xpSuccess;
- struct xpc_send_msg_slot_uv *msg_slot = NULL;
- struct xpc_notify_mq_msg_uv *msg;
- u8 msg_buffer[XPC_NOTIFY_MSG_SIZE_UV];
- size_t msg_size;
-
- DBUG_ON(notify_type != XPC_N_CALL);
-
- msg_size = sizeof(struct xpc_notify_mq_msghdr_uv) + payload_size;
- if (msg_size > ch->entry_size)
- return xpPayloadTooBig;
-
- xpc_msgqueue_ref(ch);
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- ret = ch->reason;
- goto out_1;
- }
- if (!(ch->flags & XPC_C_CONNECTED)) {
- ret = xpNotConnected;
- goto out_1;
- }
-
- ret = xpc_allocate_msg_slot_uv(ch, flags, &msg_slot);
- if (ret != xpSuccess)
- goto out_1;
-
- if (func != NULL) {
- atomic_inc(&ch->n_to_notify);
-
- msg_slot->key = key;
- smp_wmb(); /* a non-NULL func must hit memory after the key */
- msg_slot->func = func;
-
- if (ch->flags & XPC_C_DISCONNECTING) {
- ret = ch->reason;
- goto out_2;
- }
- }
-
- msg = (struct xpc_notify_mq_msg_uv *)&msg_buffer;
- msg->hdr.partid = xp_partition_id;
- msg->hdr.ch_number = ch->number;
- msg->hdr.size = msg_size;
- msg->hdr.msg_slot_number = msg_slot->msg_slot_number;
- memcpy(&msg->payload, payload, payload_size);
-
- ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg,
- msg_size);
- if (ret == xpSuccess)
- goto out_1;
-
- XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret);
-out_2:
- if (func != NULL) {
- /*
- * Try to NULL the msg_slot's func field. If we fail, then
- * xpc_notify_senders_of_disconnect_uv() beat us to it, in which
- * case we need to pretend we succeeded to send the message
- * since the user will get a callout for the disconnect error
- * by xpc_notify_senders_of_disconnect_uv(), and to also get an
- * error returned here will confuse them. Additionally, since
- * in this case the channel is being disconnected we don't need
- * to put the msg_slot back on the free list.
- */
- if (cmpxchg(&msg_slot->func, func, NULL) != func) {
- ret = xpSuccess;
- goto out_1;
- }
-
- msg_slot->key = NULL;
- atomic_dec(&ch->n_to_notify);
- }
- xpc_free_msg_slot_uv(ch, msg_slot);
-out_1:
- xpc_msgqueue_deref(ch);
- return ret;
-}
-
-/*
- * Tell the callers of xpc_send_notify() that the status of their payloads
- * is unknown because the channel is now disconnecting.
- *
- * We don't worry about putting these msg_slots on the free list since the
- * msg_slots themselves are about to be kfree'd.
- */
-static void
-xpc_notify_senders_of_disconnect_uv(struct xpc_channel *ch)
-{
- struct xpc_send_msg_slot_uv *msg_slot;
- int entry;
-
- DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
-
- for (entry = 0; entry < ch->local_nentries; entry++) {
-
- if (atomic_read(&ch->n_to_notify) == 0)
- break;
-
- msg_slot = &ch->sn.uv.send_msg_slots[entry];
- if (msg_slot->func != NULL)
- xpc_notify_sender_uv(ch, msg_slot, ch->reason);
- }
-}
-
-/*
- * Get the next deliverable message's payload.
- */
-static void *
-xpc_get_deliverable_payload_uv(struct xpc_channel *ch)
-{
- struct xpc_fifo_entry_uv *entry;
- struct xpc_notify_mq_msg_uv *msg;
- void *payload = NULL;
-
- if (!(ch->flags & XPC_C_DISCONNECTING)) {
- entry = xpc_get_fifo_entry_uv(&ch->sn.uv.recv_msg_list);
- if (entry != NULL) {
- msg = container_of(entry, struct xpc_notify_mq_msg_uv,
- hdr.u.next);
- payload = &msg->payload;
- }
- }
- return payload;
-}
-
-static void
-xpc_received_payload_uv(struct xpc_channel *ch, void *payload)
-{
- struct xpc_notify_mq_msg_uv *msg;
- enum xp_retval ret;
-
- msg = container_of(payload, struct xpc_notify_mq_msg_uv, payload);
-
- /* return an ACK to the sender of this message */
-
- msg->hdr.partid = xp_partition_id;
- msg->hdr.size = 0; /* size of zero indicates this is an ACK */
-
- ret = xpc_send_gru_msg(ch->sn.uv.cached_notify_gru_mq_desc, msg,
- sizeof(struct xpc_notify_mq_msghdr_uv));
- if (ret != xpSuccess)
- XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret);
-}
-
-static const struct xpc_arch_operations xpc_arch_ops_uv = {
- .setup_partitions = xpc_setup_partitions_uv,
- .teardown_partitions = xpc_teardown_partitions_uv,
- .process_activate_IRQ_rcvd = xpc_process_activate_IRQ_rcvd_uv,
- .get_partition_rsvd_page_pa = xpc_get_partition_rsvd_page_pa_uv,
- .setup_rsvd_page = xpc_setup_rsvd_page_uv,
-
- .allow_hb = xpc_allow_hb_uv,
- .disallow_hb = xpc_disallow_hb_uv,
- .disallow_all_hbs = xpc_disallow_all_hbs_uv,
- .increment_heartbeat = xpc_increment_heartbeat_uv,
- .offline_heartbeat = xpc_offline_heartbeat_uv,
- .online_heartbeat = xpc_online_heartbeat_uv,
- .heartbeat_init = xpc_heartbeat_init_uv,
- .heartbeat_exit = xpc_heartbeat_exit_uv,
- .get_remote_heartbeat = xpc_get_remote_heartbeat_uv,
-
- .request_partition_activation =
- xpc_request_partition_activation_uv,
- .request_partition_reactivation =
- xpc_request_partition_reactivation_uv,
- .request_partition_deactivation =
- xpc_request_partition_deactivation_uv,
- .cancel_partition_deactivation_request =
- xpc_cancel_partition_deactivation_request_uv,
-
- .setup_ch_structures = xpc_setup_ch_structures_uv,
- .teardown_ch_structures = xpc_teardown_ch_structures_uv,
-
- .make_first_contact = xpc_make_first_contact_uv,
-
- .get_chctl_all_flags = xpc_get_chctl_all_flags_uv,
- .send_chctl_closerequest = xpc_send_chctl_closerequest_uv,
- .send_chctl_closereply = xpc_send_chctl_closereply_uv,
- .send_chctl_openrequest = xpc_send_chctl_openrequest_uv,
- .send_chctl_openreply = xpc_send_chctl_openreply_uv,
- .send_chctl_opencomplete = xpc_send_chctl_opencomplete_uv,
- .process_msg_chctl_flags = xpc_process_msg_chctl_flags_uv,
-
- .save_remote_msgqueue_pa = xpc_save_remote_msgqueue_pa_uv,
-
- .setup_msg_structures = xpc_setup_msg_structures_uv,
- .teardown_msg_structures = xpc_teardown_msg_structures_uv,
-
- .indicate_partition_engaged = xpc_indicate_partition_engaged_uv,
- .indicate_partition_disengaged = xpc_indicate_partition_disengaged_uv,
- .assume_partition_disengaged = xpc_assume_partition_disengaged_uv,
- .partition_engaged = xpc_partition_engaged_uv,
- .any_partition_engaged = xpc_any_partition_engaged_uv,
-
- .n_of_deliverable_payloads = xpc_n_of_deliverable_payloads_uv,
- .send_payload = xpc_send_payload_uv,
- .get_deliverable_payload = xpc_get_deliverable_payload_uv,
- .received_payload = xpc_received_payload_uv,
- .notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_uv,
-};
-
-static int
-xpc_init_mq_node(int nid)
-{
- int cpu;
-
- cpus_read_lock();
-
- for_each_cpu(cpu, cpumask_of_node(nid)) {
- xpc_activate_mq_uv =
- xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, nid,
- XPC_ACTIVATE_IRQ_NAME,
- xpc_handle_activate_IRQ_uv);
- if (!IS_ERR(xpc_activate_mq_uv))
- break;
- }
- if (IS_ERR(xpc_activate_mq_uv)) {
- cpus_read_unlock();
- return PTR_ERR(xpc_activate_mq_uv);
- }
-
- for_each_cpu(cpu, cpumask_of_node(nid)) {
- xpc_notify_mq_uv =
- xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, nid,
- XPC_NOTIFY_IRQ_NAME,
- xpc_handle_notify_IRQ_uv);
- if (!IS_ERR(xpc_notify_mq_uv))
- break;
- }
- if (IS_ERR(xpc_notify_mq_uv)) {
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
- cpus_read_unlock();
- return PTR_ERR(xpc_notify_mq_uv);
- }
-
- cpus_read_unlock();
- return 0;
-}
-
-int
-xpc_init_uv(void)
-{
- int nid;
- int ret = 0;
-
- xpc_arch_ops = xpc_arch_ops_uv;
-
- if (sizeof(struct xpc_notify_mq_msghdr_uv) > XPC_MSG_HDR_MAX_SIZE) {
- dev_err(xpc_part, "xpc_notify_mq_msghdr_uv is larger than %d\n",
- XPC_MSG_HDR_MAX_SIZE);
- return -E2BIG;
- }
-
- if (xpc_mq_node < 0)
- for_each_online_node(nid) {
- ret = xpc_init_mq_node(nid);
-
- if (!ret)
- break;
- }
- else
- ret = xpc_init_mq_node(xpc_mq_node);
-
- if (ret < 0)
- dev_err(xpc_part, "xpc_init_mq_node() returned error=%d\n",
- -ret);
-
- return ret;
-}
-
-void
-xpc_exit_uv(void)
-{
- xpc_destroy_gru_mq_uv(xpc_notify_mq_uv);
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
-}
-
-module_param(xpc_mq_node, int, 0);
-MODULE_PARM_DESC(xpc_mq_node, "Node number on which to allocate message queues.");
diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c
deleted file mode 100644
index 1533b72d57b1..000000000000
--- a/drivers/misc/sgi-xp/xpnet.c
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
- * Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved.
- */
-
-/*
- * Cross Partition Network Interface (XPNET) support
- *
- * XPNET provides a virtual network layered on top of the Cross
- * Partition communication layer.
- *
- * XPNET provides direct point-to-point and broadcast-like support
- * for an ethernet-like device. The ethernet broadcast medium is
- * replaced with a point-to-point message structure which passes
- * pointers to a DMA-capable block that a remote partition should
- * retrieve and pass to the upper level networking layer.
- *
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include "xp.h"
-
-/*
- * The message payload transferred by XPC.
- *
- * buf_pa is the physical address where the DMA should pull from.
- *
- * NOTE: for performance reasons, buf_pa should _ALWAYS_ begin on a
- * cacheline boundary. To accomplish this, we record the number of
- * bytes from the beginning of the first cacheline to the first useful
- * byte of the skb (leadin_ignore) and the number of bytes from the
- * last useful byte of the skb to the end of the last cacheline
- * (tailout_ignore).
- *
- * size is the number of bytes to transfer which includes the skb->len
- * (useful bytes of the senders skb) plus the leadin and tailout
- */
-struct xpnet_message {
- u16 version; /* Version for this message */
- u16 embedded_bytes; /* #of bytes embedded in XPC message */
- u32 magic; /* Special number indicating this is xpnet */
- unsigned long buf_pa; /* phys address of buffer to retrieve */
- u32 size; /* #of bytes in buffer */
- u8 leadin_ignore; /* #of bytes to ignore at the beginning */
- u8 tailout_ignore; /* #of bytes to ignore at the end */
- unsigned char data; /* body of small packets */
-};
-
-/*
- * Determine the size of our message, the cacheline aligned size,
- * and then the number of message will request from XPC.
- *
- * XPC expects each message to exist in an individual cacheline.
- */
-#define XPNET_MSG_SIZE XPC_MSG_PAYLOAD_MAX_SIZE
-#define XPNET_MSG_DATA_MAX \
- (XPNET_MSG_SIZE - offsetof(struct xpnet_message, data))
-#define XPNET_MSG_NENTRIES (PAGE_SIZE / XPC_MSG_MAX_SIZE)
-
-#define XPNET_MAX_KTHREADS (XPNET_MSG_NENTRIES + 1)
-#define XPNET_MAX_IDLE_KTHREADS (XPNET_MSG_NENTRIES + 1)
-
-/*
- * Version number of XPNET implementation. XPNET can always talk to versions
- * with same major #, and never talk to versions with a different version.
- */
-#define _XPNET_VERSION(_major, _minor) (((_major) << 4) | (_minor))
-#define XPNET_VERSION_MAJOR(_v) ((_v) >> 4)
-#define XPNET_VERSION_MINOR(_v) ((_v) & 0xf)
-
-#define XPNET_VERSION _XPNET_VERSION(1, 0) /* version 1.0 */
-#define XPNET_VERSION_EMBED _XPNET_VERSION(1, 1) /* version 1.1 */
-#define XPNET_MAGIC 0x88786984 /* "XNET" */
-
-#define XPNET_VALID_MSG(_m) \
- ((XPNET_VERSION_MAJOR(_m->version) == XPNET_VERSION_MAJOR(XPNET_VERSION)) \
- && (msg->magic == XPNET_MAGIC))
-
-#define XPNET_DEVICE_NAME "xp0"
-
-/*
- * When messages are queued with xpc_send_notify, a kmalloc'd buffer
- * of the following type is passed as a notification cookie. When the
- * notification function is called, we use the cookie to decide
- * whether all outstanding message sends have completed. The skb can
- * then be released.
- */
-struct xpnet_pending_msg {
- struct sk_buff *skb;
- atomic_t use_count;
-};
-
-static struct net_device *xpnet_device;
-
-/*
- * When we are notified of other partitions activating, we add them to
- * our bitmask of partitions to which we broadcast.
- */
-static unsigned long *xpnet_broadcast_partitions;
-/* protect above */
-static DEFINE_SPINLOCK(xpnet_broadcast_lock);
-
-/*
- * Since the Block Transfer Engine (BTE) is being used for the transfer
- * and it relies upon cache-line size transfers, we need to reserve at
- * least one cache-line for head and tail alignment. The BTE is
- * limited to 8MB transfers.
- *
- * Testing has shown that changing MTU to greater than 64KB has no effect
- * on TCP as the two sides negotiate a Max Segment Size that is limited
- * to 64K. Other protocols May use packets greater than this, but for
- * now, the default is 64KB.
- */
-#define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES)
-/* 68 comes from min TCP+IP+MAC header */
-#define XPNET_MIN_MTU 68
-/* 32KB has been determined to be the ideal */
-#define XPNET_DEF_MTU (0x8000UL)
-
-/*
- * The partid is encapsulated in the MAC address beginning in the following
- * octet and it consists of two octets.
- */
-#define XPNET_PARTID_OCTET 2
-
-/* Define the XPNET debug device structures to be used with dev_dbg() et al */
-
-static struct device_driver xpnet_dbg_name = {
- .name = "xpnet"
-};
-
-static struct device xpnet_dbg_subname = {
- .init_name = "", /* set to "" */
- .driver = &xpnet_dbg_name
-};
-
-static struct device *xpnet = &xpnet_dbg_subname;
-
-/*
- * Packet was recevied by XPC and forwarded to us.
- */
-static void
-xpnet_receive(short partid, int channel, struct xpnet_message *msg)
-{
- struct sk_buff *skb;
- void *dst;
- enum xp_retval ret;
-
- if (!XPNET_VALID_MSG(msg)) {
- /*
- * Packet with a different XPC version. Ignore.
- */
- xpc_received(partid, channel, (void *)msg);
-
- xpnet_device->stats.rx_errors++;
-
- return;
- }
- dev_dbg(xpnet, "received 0x%lx, %d, %d, %d\n", msg->buf_pa, msg->size,
- msg->leadin_ignore, msg->tailout_ignore);
-
- /* reserve an extra cache line */
- skb = dev_alloc_skb(msg->size + L1_CACHE_BYTES);
- if (!skb) {
- dev_err(xpnet, "failed on dev_alloc_skb(%d)\n",
- msg->size + L1_CACHE_BYTES);
-
- xpc_received(partid, channel, (void *)msg);
-
- xpnet_device->stats.rx_errors++;
-
- return;
- }
-
- /*
- * The allocated skb has some reserved space.
- * In order to use xp_remote_memcpy(), we need to get the
- * skb->data pointer moved forward.
- */
- skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data &
- (L1_CACHE_BYTES - 1)) +
- msg->leadin_ignore));
-
- /*
- * Update the tail pointer to indicate data actually
- * transferred.
- */
- skb_put(skb, (msg->size - msg->leadin_ignore - msg->tailout_ignore));
-
- /*
- * Move the data over from the other side.
- */
- if ((XPNET_VERSION_MINOR(msg->version) == 1) &&
- (msg->embedded_bytes != 0)) {
- dev_dbg(xpnet, "copying embedded message. memcpy(0x%p, 0x%p, "
- "%lu)\n", skb->data, &msg->data,
- (size_t)msg->embedded_bytes);
-
- skb_copy_to_linear_data(skb, &msg->data,
- (size_t)msg->embedded_bytes);
- } else {
- dst = (void *)((u64)skb->data & ~(L1_CACHE_BYTES - 1));
- dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t"
- "xp_remote_memcpy(0x%p, 0x%p, %u)\n", dst,
- (void *)msg->buf_pa, msg->size);
-
- ret = xp_remote_memcpy(xp_pa(dst), msg->buf_pa, msg->size);
- if (ret != xpSuccess) {
- /*
- * !!! Need better way of cleaning skb. Currently skb
- * !!! appears in_use and we can't just call
- * !!! dev_kfree_skb.
- */
- dev_err(xpnet, "xp_remote_memcpy(0x%p, 0x%p, 0x%x) "
- "returned error=0x%x\n", dst,
- (void *)msg->buf_pa, msg->size, ret);
-
- xpc_received(partid, channel, (void *)msg);
-
- xpnet_device->stats.rx_errors++;
-
- return;
- }
- }
-
- dev_dbg(xpnet, "<skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
- "skb->end=0x%p skb->len=%d\n", (void *)skb->head,
- (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
- skb->len);
-
- skb->protocol = eth_type_trans(skb, xpnet_device);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-
- dev_dbg(xpnet, "passing skb to network layer\n"
- "\tskb->head=0x%p skb->data=0x%p skb->tail=0x%p "
- "skb->end=0x%p skb->len=%d\n",
- (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb),
- skb_end_pointer(skb), skb->len);
-
- xpnet_device->stats.rx_packets++;
- xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN;
-
- netif_rx(skb);
- xpc_received(partid, channel, (void *)msg);
-}
-
-/*
- * This is the handler which XPC calls during any sort of change in
- * state or message reception on a connection.
- */
-static void
-xpnet_connection_activity(enum xp_retval reason, short partid, int channel,
- void *data, void *key)
-{
- DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
- DBUG_ON(channel != XPC_NET_CHANNEL);
-
- switch (reason) {
- case xpMsgReceived: /* message received */
- DBUG_ON(data == NULL);
-
- xpnet_receive(partid, channel, (struct xpnet_message *)data);
- break;
-
- case xpConnected: /* connection completed to a partition */
- spin_lock_bh(&xpnet_broadcast_lock);
- __set_bit(partid, xpnet_broadcast_partitions);
- spin_unlock_bh(&xpnet_broadcast_lock);
-
- netif_carrier_on(xpnet_device);
-
- dev_dbg(xpnet, "%s connected to partition %d\n",
- xpnet_device->name, partid);
- break;
-
- default:
- spin_lock_bh(&xpnet_broadcast_lock);
- __clear_bit(partid, xpnet_broadcast_partitions);
- spin_unlock_bh(&xpnet_broadcast_lock);
-
- if (bitmap_empty(xpnet_broadcast_partitions,
- xp_max_npartitions)) {
- netif_carrier_off(xpnet_device);
- }
-
- dev_dbg(xpnet, "%s disconnected from partition %d\n",
- xpnet_device->name, partid);
- break;
- }
-}
-
-static int
-xpnet_dev_open(struct net_device *dev)
-{
- enum xp_retval ret;
-
- dev_dbg(xpnet, "calling xpc_connect(%d, 0x%p, NULL, %ld, %ld, %ld, "
- "%ld)\n", XPC_NET_CHANNEL, xpnet_connection_activity,
- (unsigned long)XPNET_MSG_SIZE,
- (unsigned long)XPNET_MSG_NENTRIES,
- (unsigned long)XPNET_MAX_KTHREADS,
- (unsigned long)XPNET_MAX_IDLE_KTHREADS);
-
- ret = xpc_connect(XPC_NET_CHANNEL, xpnet_connection_activity, NULL,
- XPNET_MSG_SIZE, XPNET_MSG_NENTRIES,
- XPNET_MAX_KTHREADS, XPNET_MAX_IDLE_KTHREADS);
- if (ret != xpSuccess) {
- dev_err(xpnet, "ifconfig up of %s failed on XPC connect, "
- "ret=%d\n", dev->name, ret);
-
- return -ENOMEM;
- }
-
- dev_dbg(xpnet, "ifconfig up of %s; XPC connected\n", dev->name);
-
- return 0;
-}
-
-static int
-xpnet_dev_stop(struct net_device *dev)
-{
- xpc_disconnect(XPC_NET_CHANNEL);
-
- dev_dbg(xpnet, "ifconfig down of %s; XPC disconnected\n", dev->name);
-
- return 0;
-}
-
-/*
- * Notification that the other end has received the message and
- * DMA'd the skb information. At this point, they are done with
- * our side. When all recipients are done processing, we
- * release the skb and then release our pending message structure.
- */
-static void
-xpnet_send_completed(enum xp_retval reason, short partid, int channel,
- void *__qm)
-{
- struct xpnet_pending_msg *queued_msg = (struct xpnet_pending_msg *)__qm;
-
- DBUG_ON(queued_msg == NULL);
-
- dev_dbg(xpnet, "message to %d notified with reason %d\n",
- partid, reason);
-
- if (atomic_dec_return(&queued_msg->use_count) == 0) {
- dev_dbg(xpnet, "all acks for skb->head=-x%p\n",
- (void *)queued_msg->skb->head);
-
- dev_kfree_skb_any(queued_msg->skb);
- kfree(queued_msg);
- }
-}
-
-static void
-xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg,
- u64 start_addr, u64 end_addr, u16 embedded_bytes, int dest_partid)
-{
- u8 msg_buffer[XPNET_MSG_SIZE];
- struct xpnet_message *msg = (struct xpnet_message *)&msg_buffer;
- u16 msg_size = sizeof(struct xpnet_message);
- enum xp_retval ret;
-
- msg->embedded_bytes = embedded_bytes;
- if (unlikely(embedded_bytes != 0)) {
- msg->version = XPNET_VERSION_EMBED;
- dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n",
- &msg->data, skb->data, (size_t)embedded_bytes);
- skb_copy_from_linear_data(skb, &msg->data,
- (size_t)embedded_bytes);
- msg_size += embedded_bytes - 1;
- } else {
- msg->version = XPNET_VERSION;
- }
- msg->magic = XPNET_MAGIC;
- msg->size = end_addr - start_addr;
- msg->leadin_ignore = (u64)skb->data - start_addr;
- msg->tailout_ignore = end_addr - (u64)skb_tail_pointer(skb);
- msg->buf_pa = xp_pa((void *)start_addr);
-
- dev_dbg(xpnet, "sending XPC message to %d:%d\n"
- "msg->buf_pa=0x%lx, msg->size=%u, "
- "msg->leadin_ignore=%u, msg->tailout_ignore=%u\n",
- dest_partid, XPC_NET_CHANNEL, msg->buf_pa, msg->size,
- msg->leadin_ignore, msg->tailout_ignore);
-
- atomic_inc(&queued_msg->use_count);
-
- ret = xpc_send_notify(dest_partid, XPC_NET_CHANNEL, XPC_NOWAIT, msg,
- msg_size, xpnet_send_completed, queued_msg);
- if (unlikely(ret != xpSuccess))
- atomic_dec(&queued_msg->use_count);
-}
-
-/*
- * Network layer has formatted a packet (skb) and is ready to place it
- * "on the wire". Prepare and send an xpnet_message to all partitions
- * which have connected with us and are targets of this packet.
- *
- * MAC-NOTE: For the XPNET driver, the MAC address contains the
- * destination partid. If the destination partid octets are 0xffff,
- * this packet is to be broadcast to all connected partitions.
- */
-static netdev_tx_t
-xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct xpnet_pending_msg *queued_msg;
- u64 start_addr, end_addr;
- short dest_partid;
- u16 embedded_bytes = 0;
-
- dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
- "skb->end=0x%p skb->len=%d\n", (void *)skb->head,
- (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
- skb->len);
-
- if (skb->data[0] == 0x33) {
- dev_kfree_skb(skb);
- return NETDEV_TX_OK; /* nothing needed to be done */
- }
-
- /*
- * The xpnet_pending_msg tracks how many outstanding
- * xpc_send_notifies are relying on this skb. When none
- * remain, release the skb.
- */
- queued_msg = kmalloc_obj(struct xpnet_pending_msg, GFP_ATOMIC);
- if (queued_msg == NULL) {
- dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping "
- "packet\n", sizeof(struct xpnet_pending_msg));
-
- dev->stats.tx_errors++;
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- /* get the beginning of the first cacheline and end of last */
- start_addr = ((u64)skb->data & ~(L1_CACHE_BYTES - 1));
- end_addr = L1_CACHE_ALIGN((u64)skb_tail_pointer(skb));
-
- /* calculate how many bytes to embed in the XPC message */
- if (unlikely(skb->len <= XPNET_MSG_DATA_MAX)) {
- /* skb->data does fit so embed */
- embedded_bytes = skb->len;
- }
-
- /*
- * Since the send occurs asynchronously, we set the count to one
- * and begin sending. Any sends that happen to complete before
- * we are done sending will not free the skb. We will be left
- * with that task during exit. This also handles the case of
- * a packet destined for a partition which is no longer up.
- */
- atomic_set(&queued_msg->use_count, 1);
- queued_msg->skb = skb;
-
- if (skb->data[0] == 0xff) {
- /* we are being asked to broadcast to all partitions */
- for_each_set_bit(dest_partid, xpnet_broadcast_partitions,
- xp_max_npartitions) {
-
- xpnet_send(skb, queued_msg, start_addr, end_addr,
- embedded_bytes, dest_partid);
- }
- } else {
- dest_partid = (short)skb->data[XPNET_PARTID_OCTET + 1];
- dest_partid |= (short)skb->data[XPNET_PARTID_OCTET + 0] << 8;
-
- if (dest_partid >= 0 &&
- dest_partid < xp_max_npartitions &&
- test_bit(dest_partid, xpnet_broadcast_partitions) != 0) {
-
- xpnet_send(skb, queued_msg, start_addr, end_addr,
- embedded_bytes, dest_partid);
- }
- }
-
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- if (atomic_dec_return(&queued_msg->use_count) == 0) {
- dev_kfree_skb(skb);
- kfree(queued_msg);
- }
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Deal with transmit timeouts coming from the network layer.
- */
-static void
-xpnet_dev_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- dev->stats.tx_errors++;
-}
-
-static const struct net_device_ops xpnet_netdev_ops = {
- .ndo_open = xpnet_dev_open,
- .ndo_stop = xpnet_dev_stop,
- .ndo_start_xmit = xpnet_dev_hard_start_xmit,
- .ndo_tx_timeout = xpnet_dev_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int __init
-xpnet_init(void)
-{
- u8 addr[ETH_ALEN];
- int result;
-
- if (!is_uv_system())
- return -ENODEV;
-
- dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME);
-
- xpnet_broadcast_partitions = bitmap_zalloc(xp_max_npartitions,
- GFP_KERNEL);
- if (xpnet_broadcast_partitions == NULL)
- return -ENOMEM;
-
- /*
- * use ether_setup() to init the majority of our device
- * structure and then override the necessary pieces.
- */
- xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, NET_NAME_UNKNOWN,
- ether_setup);
- if (xpnet_device == NULL) {
- bitmap_free(xpnet_broadcast_partitions);
- return -ENOMEM;
- }
-
- netif_carrier_off(xpnet_device);
-
- xpnet_device->netdev_ops = &xpnet_netdev_ops;
- xpnet_device->mtu = XPNET_DEF_MTU;
- xpnet_device->min_mtu = XPNET_MIN_MTU;
- xpnet_device->max_mtu = XPNET_MAX_MTU;
-
- memset(addr, 0, sizeof(addr));
- /*
- * Multicast assumes the LSB of the first octet is set for multicast
- * MAC addresses. We chose the first octet of the MAC to be unlikely
- * to collide with any vendor's officially issued MAC.
- */
- addr[0] = 0x02; /* locally administered, no OUI */
-
- addr[XPNET_PARTID_OCTET + 1] = xp_partition_id;
- addr[XPNET_PARTID_OCTET + 0] = (xp_partition_id >> 8);
- eth_hw_addr_set(xpnet_device, addr);
-
- /*
- * ether_setup() sets this to a multicast device. We are
- * really not supporting multicast at this time.
- */
- xpnet_device->flags &= ~IFF_MULTICAST;
-
- /*
- * No need to checksum as it is a DMA transfer. The BTE will
- * report an error if the data is not retrievable and the
- * packet will be dropped.
- */
- xpnet_device->features = NETIF_F_HW_CSUM;
-
- result = register_netdev(xpnet_device);
- if (result != 0) {
- free_netdev(xpnet_device);
- bitmap_free(xpnet_broadcast_partitions);
- }
-
- return result;
-}
-
-module_init(xpnet_init);
-
-static void __exit
-xpnet_exit(void)
-{
- dev_info(xpnet, "unregistering network device %s\n",
- xpnet_device[0].name);
-
- unregister_netdev(xpnet_device);
- free_netdev(xpnet_device);
- bitmap_free(xpnet_broadcast_partitions);
-}
-
-module_exit(xpnet_exit);
-
-MODULE_AUTHOR("Silicon Graphics, Inc.");
-MODULE_DESCRIPTION("Cross Partition Network adapter (XPNET)");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
index 1919d24c8236..c8990e83a65a 100644
--- a/drivers/misc/sram.c
+++ b/drivers/misc/sram.c
@@ -370,7 +370,7 @@ static const struct of_device_id sram_dt_ids[] = {
{ .compatible = "nvidia,tegra186-sysram", .data = &tegra_sysram_config },
{ .compatible = "nvidia,tegra194-sysram", .data = &tegra_sysram_config },
{ .compatible = "nvidia,tegra234-sysram", .data = &tegra_sysram_config },
- {}
+ { }
};
static int sram_probe(struct platform_device *pdev)
diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c
index 2aac83ec4a39..9e2d24f89457 100644
--- a/drivers/misc/ti_fpc202.c
+++ b/drivers/misc/ti_fpc202.c
@@ -718,7 +718,7 @@ static void fpc202_remove(struct i2c_client *client)
static const struct of_device_id fpc202_of_match[] = {
{ .compatible = "ti,fpc202" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, fpc202_of_match);
diff --git a/drivers/misc/tps6594-esm.c b/drivers/misc/tps6594-esm.c
index 2fbd3fbdf713..50e121669c6d 100644
--- a/drivers/misc/tps6594-esm.c
+++ b/drivers/misc/tps6594-esm.c
@@ -62,7 +62,7 @@ static int tps6594_esm_probe(struct platform_device *pdev)
tps6594_esm_isr, IRQF_ONESHOT,
pdev->resource[i].name, pdev);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request irq\n");
+ return ret;
}
ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG,
diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c
index 44fa81d6cec2..c3a0d6367f2c 100644
--- a/drivers/misc/tps6594-pfsm.c
+++ b/drivers/misc/tps6594-pfsm.c
@@ -308,7 +308,7 @@ static int tps6594_pfsm_probe(struct platform_device *pdev)
tps6594_pfsm_isr, IRQF_ONESHOT,
pdev->resource[i].name, pdev);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request irq\n");
+ return ret;
}
platform_set_drvdata(pdev, pfsm);
diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c
index 03f19eda641e..c871ea860bb5 100644
--- a/drivers/misc/tsl2550.c
+++ b/drivers/misc/tsl2550.c
@@ -420,7 +420,7 @@ static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume);
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id tsl2550_id[] = {
- { "tsl2550" },
+ { .name = "tsl2550" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tsl2550_id);
diff --git a/drivers/misc/vcpu_stall_detector.c b/drivers/misc/vcpu_stall_detector.c
index 26166357b255..1306b2c1ef99 100644
--- a/drivers/misc/vcpu_stall_detector.c
+++ b/drivers/misc/vcpu_stall_detector.c
@@ -224,8 +224,8 @@ static void vcpu_stall_detect_remove(struct platform_device *pdev)
}
static const struct of_device_id vcpu_stall_detect_of_match[] = {
- { .compatible = "qemu,vcpu-stall-detector", },
- {}
+ { .compatible = "qemu,vcpu-stall-detector" },
+ { }
};
MODULE_DEVICE_TABLE(of, vcpu_stall_detect_of_match);
diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c
index fffe068a26eb..c3ef3b98c432 100644
--- a/drivers/misc/vmw_vmci/vmci_event.c
+++ b/drivers/misc/vmw_vmci/vmci_event.c
@@ -179,16 +179,16 @@ int vmci_event_subscribe(u32 event,
}
}
+ *new_subscription_id = sub->id;
if (have_new_id) {
list_add_rcu(&sub->node, &subscriber_array[event]);
retval = VMCI_SUCCESS;
} else {
+ kfree(sub);
retval = VMCI_ERROR_NO_RESOURCES;
}
mutex_unlock(&subscriber_mutex);
-
- *new_subscription_id = sub->id;
return retval;
}
EXPORT_SYMBOL_GPL(vmci_event_subscribe);
diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c
index 476af89e751b..83355c0ec1d2 100644
--- a/drivers/misc/vmw_vmci/vmci_guest.c
+++ b/drivers/misc/vmw_vmci/vmci_guest.c
@@ -960,8 +960,8 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
}
static const struct pci_device_id vmci_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), },
- { 0 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI) },
+ { }
};
MODULE_DEVICE_TABLE(pci, vmci_ids);
diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c
index 3135ba3a58ee..a19eed39c516 100644
--- a/drivers/misc/xilinx_sdfec.c
+++ b/drivers/misc/xilinx_sdfec.c
@@ -456,10 +456,25 @@ static int xsdfec_get_turbo(struct xsdfec_dev *xsdfec, void __user *arg)
return err;
}
+static int xsdfec_ldpc_reg_addr(struct xsdfec_dev *xsdfec, u32 base, u32 high,
+ u32 offset, u32 *addr)
+{
+ if (high < base || offset > (high - base) / XSDFEC_LDPC_REG_JUMP) {
+ dev_dbg(xsdfec->dev,
+ "LDPC register offset %u outside space 0x%x-0x%x",
+ offset, base, high);
+ return -EINVAL;
+ }
+
+ *addr = base + offset * XSDFEC_LDPC_REG_JUMP;
+ return 0;
+}
+
static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize,
u32 offset)
{
u32 wdata;
+ u32 addr;
if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || psize == 0 ||
(n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) {
@@ -476,17 +491,11 @@ static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize,
k = k << XSDFEC_REG0_K_LSB;
wdata = k | n;
- if (XSDFEC_LDPC_CODE_REG0_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG0_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg0 space 0x%x",
- XSDFEC_LDPC_CODE_REG0_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG0_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG0_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG0_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -494,6 +503,7 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize,
u32 no_packing, u32 nm, u32 offset)
{
u32 wdata;
+ u32 addr;
if (psize < XSDFEC_REG1_PSIZE_MIN || psize > XSDFEC_REG1_PSIZE_MAX) {
dev_dbg(xsdfec->dev, "Psize is not in range");
@@ -510,17 +520,11 @@ static int xsdfec_reg1_write(struct xsdfec_dev *xsdfec, u32 psize,
nm = (nm << XSDFEC_REG1_NM_LSB) & XSDFEC_REG1_NM_MASK;
wdata = nm | no_packing | psize;
- if (XSDFEC_LDPC_CODE_REG1_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG1_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg1 space 0x%x",
- XSDFEC_LDPC_CODE_REG1_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG1_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG1_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG1_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -529,6 +533,7 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc,
u32 max_schedule, u32 offset)
{
u32 wdata;
+ u32 addr;
if (nlayers < XSDFEC_REG2_NLAYERS_MIN ||
nlayers > XSDFEC_REG2_NLAYERS_MAX) {
@@ -563,17 +568,11 @@ static int xsdfec_reg2_write(struct xsdfec_dev *xsdfec, u32 nlayers, u32 nmqc,
wdata = (max_schedule | no_final_parity | special_qc | norm_type |
nmqc | nlayers);
- if (XSDFEC_LDPC_CODE_REG2_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG2_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg2 space 0x%x",
- XSDFEC_LDPC_CODE_REG2_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG2_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG2_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG2_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -581,20 +580,15 @@ static int xsdfec_reg3_write(struct xsdfec_dev *xsdfec, u8 sc_off, u8 la_off,
u16 qc_off, u32 offset)
{
u32 wdata;
+ u32 addr;
wdata = ((qc_off << XSDFEC_REG3_QC_OFF_LSB) |
(la_off << XSDFEC_REG3_LA_OFF_LSB) | sc_off);
- if (XSDFEC_LDPC_CODE_REG3_ADDR_BASE + (offset * XSDFEC_LDPC_REG_JUMP) >
- XSDFEC_LDPC_CODE_REG3_ADDR_HIGH) {
- dev_dbg(xsdfec->dev, "Writing outside of LDPC reg3 space 0x%x",
- XSDFEC_LDPC_CODE_REG3_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP));
+ if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG3_ADDR_BASE,
+ XSDFEC_LDPC_CODE_REG3_ADDR_HIGH, offset,
+ &addr))
return -EINVAL;
- }
- xsdfec_regwrite(xsdfec,
- XSDFEC_LDPC_CODE_REG3_ADDR_BASE +
- (offset * XSDFEC_LDPC_REG_JUMP),
- wdata);
+ xsdfec_regwrite(xsdfec, addr, wdata);
return 0;
}
@@ -1390,10 +1384,8 @@ static int xsdfec_probe(struct platform_device *pdev)
err = devm_request_threaded_irq(dev, xsdfec->irq, NULL,
xsdfec_irq_thread, IRQF_ONESHOT,
"xilinx-sdfec16", xsdfec);
- if (err < 0) {
- dev_err(dev, "unable to request IRQ%d", xsdfec->irq);
+ if (err < 0)
goto err_xsdfec_dev;
- }
}
err = ida_alloc(&dev_nrs, GFP_KERNEL);