From a63ca4236e6799cf4343f9aec9d92afdfa582446 Mon Sep 17 00:00:00 2001 From: Ackerley Tng Date: Thu, 16 Oct 2025 10:28:44 -0700 Subject: KVM: guest_memfd: Use guest mem inodes instead of anonymous inodes guest_memfd's inode represents memory the guest_memfd is providing. guest_memfd's file represents a struct kvm's view of that memory. Using a custom inode allows customization of the inode teardown process via callbacks. For example, ->evict_inode() allows customization of the truncation process on file close, and ->destroy_inode() and ->free_inode() allow customization of the inode freeing process. Customizing the truncation process allows flexibility in management of guest_memfd memory and customization of the inode freeing process allows proper cleanup of memory metadata stored on the inode. Memory metadata is more appropriately stored on the inode (as opposed to the file), since the metadata is for the memory and is not unique to a specific binding and struct kvm. Acked-by: David Hildenbrand Co-developed-by: Fuad Tabba Signed-off-by: Fuad Tabba Signed-off-by: Ackerley Tng Signed-off-by: Shivank Garg Tested-by: Ashish Kalra [sean: drop helpers, open code logic in __kvm_gmem_create()] Link: https://lore.kernel.org/r/20251016172853.52451-4-seanjc@google.com Signed-off-by: Sean Christopherson --- include/uapi/linux/magic.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index bb575f3ab45e..638ca21b7a90 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -103,5 +103,6 @@ #define DEVMEM_MAGIC 0x454d444d /* "DMEM" */ #define SECRETMEM_MAGIC 0x5345434d /* "SECM" */ #define PID_FS_MAGIC 0x50494446 /* "PIDF" */ +#define GUEST_MEMFD_MAGIC 0x474d454d /* "GMEM" */ #endif /* __LINUX_MAGIC_H__ */ -- cgit v1.2.3 From f74ee32963f1b74865fe679e2475450434fea51c Mon Sep 17 00:00:00 2001 From: Qinxin Xia Date: Tue, 28 Oct 2025 20:09:00 +0800 Subject: tools/dma: move dma_map_benchmark from selftests to tools/dma dma_map_benchmark is a standalone developer tool rather than an automated selftest. It has no pass/fail criteria, expects manual invocation, and is built as a normal userspace binary. Move it to tools/dma/ and add a minimal Makefile. Suggested-by: Marek Szyprowski Suggested-by: Barry Song Signed-off-by: Qinxin Xia Acked-by: Barry Song Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20251028120900.2265511-3-xiaqinxin@huawei.com --- include/linux/map_benchmark.h | 32 ------ include/uapi/linux/map_benchmark.h | 35 +++++++ kernel/dma/map_benchmark.c | 2 +- tools/Makefile | 13 +-- tools/dma/.gitignore | 3 + tools/dma/Makefile | 55 ++++++++++ tools/dma/config | 1 + tools/dma/dma_map_benchmark.c | 127 +++++++++++++++++++++++ tools/testing/selftests/dma/Makefile | 7 -- tools/testing/selftests/dma/config | 1 - tools/testing/selftests/dma/dma_map_benchmark.c | 128 ------------------------ 11 files changed, 229 insertions(+), 175 deletions(-) delete mode 100644 include/linux/map_benchmark.h create mode 100644 include/uapi/linux/map_benchmark.h create mode 100644 tools/dma/.gitignore create mode 100644 tools/dma/Makefile create mode 100644 tools/dma/config create mode 100644 tools/dma/dma_map_benchmark.c delete mode 100644 tools/testing/selftests/dma/Makefile delete mode 100644 tools/testing/selftests/dma/config delete mode 100644 tools/testing/selftests/dma/dma_map_benchmark.c (limited to 'include/uapi/linux') diff --git a/include/linux/map_benchmark.h b/include/linux/map_benchmark.h deleted file mode 100644 index 48e2ff95332f..000000000000 --- a/include/linux/map_benchmark.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2022 HiSilicon Limited. - */ - -#ifndef _KERNEL_DMA_BENCHMARK_H -#define _KERNEL_DMA_BENCHMARK_H - -#define DMA_MAP_BENCHMARK _IOWR('d', 1, struct map_benchmark) -#define DMA_MAP_MAX_THREADS 1024 -#define DMA_MAP_MAX_SECONDS 300 -#define DMA_MAP_MAX_TRANS_DELAY (10 * NSEC_PER_MSEC) - -#define DMA_MAP_BIDIRECTIONAL 0 -#define DMA_MAP_TO_DEVICE 1 -#define DMA_MAP_FROM_DEVICE 2 - -struct map_benchmark { - __u64 avg_map_100ns; /* average map latency in 100ns */ - __u64 map_stddev; /* standard deviation of map latency */ - __u64 avg_unmap_100ns; /* as above */ - __u64 unmap_stddev; - __u32 threads; /* how many threads will do map/unmap in parallel */ - __u32 seconds; /* how long the test will last */ - __s32 node; /* which numa node this benchmark will run on */ - __u32 dma_bits; /* DMA addressing capability */ - __u32 dma_dir; /* DMA data direction */ - __u32 dma_trans_ns; /* time for DMA transmission in ns */ - __u32 granule; /* how many PAGE_SIZE will do map/unmap once a time */ - __u8 expansion[76]; /* For future use */ -}; -#endif /* _KERNEL_DMA_BENCHMARK_H */ diff --git a/include/uapi/linux/map_benchmark.h b/include/uapi/linux/map_benchmark.h new file mode 100644 index 000000000000..c2d91088a40d --- /dev/null +++ b/include/uapi/linux/map_benchmark.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Copyright (C) 2022-2025 HiSilicon Limited. + */ + +#ifndef _UAPI_DMA_BENCHMARK_H +#define _UAPI_DMA_BENCHMARK_H + +#include + +#define DMA_MAP_BENCHMARK _IOWR('d', 1, struct map_benchmark) +#define DMA_MAP_MAX_THREADS 1024 +#define DMA_MAP_MAX_SECONDS 300 +#define DMA_MAP_MAX_TRANS_DELAY (10 * NSEC_PER_MSEC) + +#define DMA_MAP_BIDIRECTIONAL 0 +#define DMA_MAP_TO_DEVICE 1 +#define DMA_MAP_FROM_DEVICE 2 + +struct map_benchmark { + __u64 avg_map_100ns; /* average map latency in 100ns */ + __u64 map_stddev; /* standard deviation of map latency */ + __u64 avg_unmap_100ns; /* as above */ + __u64 unmap_stddev; + __u32 threads; /* how many threads will do map/unmap in parallel */ + __u32 seconds; /* how long the test will last */ + __s32 node; /* which numa node this benchmark will run on */ + __u32 dma_bits; /* DMA addressing capability */ + __u32 dma_dir; /* DMA data direction */ + __u32 dma_trans_ns; /* time for DMA transmission in ns */ + __u32 granule; /* how many PAGE_SIZE will do map/unmap once a time */ + __u8 expansion[76]; /* For future use */ +}; + +#endif /* _UAPI_DMA_BENCHMARK_H */ diff --git a/kernel/dma/map_benchmark.c b/kernel/dma/map_benchmark.c index cc19a3efea89..794041a39e65 100644 --- a/kernel/dma/map_benchmark.c +++ b/kernel/dma/map_benchmark.c @@ -11,13 +11,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include struct map_benchmark_data { struct map_benchmark bparam; diff --git a/tools/Makefile b/tools/Makefile index c31cbbd12c45..cb40961a740f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -14,6 +14,7 @@ help: @echo ' counter - counter tools' @echo ' cpupower - a tool for all things x86 CPU power' @echo ' debugging - tools for debugging' + @echo ' dma - tools for DMA mapping' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @echo ' firmware - Firmware tools' @echo ' freefall - laptop accelerometer program for disk protection' @@ -69,7 +70,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -counter firewire hv guest bootconfig spi usb virtio mm bpf iio gpio objtool leds wmi firmware debugging tracing: FORCE +counter dma firewire hv guest bootconfig spi usb virtio mm bpf iio gpio objtool leds wmi firmware debugging tracing: FORCE $(call descend,$@) bpf/%: FORCE @@ -122,7 +123,7 @@ kvm_stat: FORCE ynl: FORCE $(call descend,net/ynl) -all: acpi counter cpupower gpio hv firewire \ +all: acpi counter cpupower dma gpio hv firewire \ perf selftests bootconfig spi turbostat usb \ virtio mm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi \ @@ -134,7 +135,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install mm_install bpf_install objtool_install wmi_install debugging_install tracing_install: +counter_install dma_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install mm_install bpf_install objtool_install wmi_install debugging_install tracing_install: $(call descend,$(@:_install=),install) selftests_install: @@ -164,7 +165,7 @@ kvm_stat_install: ynl_install: $(call descend,net/$(@:_install=),install) -install: acpi_install counter_install cpupower_install gpio_install \ +install: acpi_install counter_install cpupower_install dma_install gpio_install \ hv_install firewire_install iio_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install mm_install bpf_install x86_energy_perf_policy_install \ @@ -178,7 +179,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean mm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean firmware_clean debugging_clean tracing_clean: +counter_clean dma_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean mm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean firmware_clean debugging_clean tracing_clean: $(call descend,$(@:_clean=),clean) libapi_clean: @@ -224,7 +225,7 @@ build_clean: ynl_clean: $(call descend,net/$(@:_clean=),clean) -clean: acpi_clean counter_clean cpupower_clean hv_clean firewire_clean \ +clean: acpi_clean counter_clean cpupower_clean dma_clean hv_clean firewire_clean \ perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ mm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean \ diff --git a/tools/dma/.gitignore b/tools/dma/.gitignore new file mode 100644 index 000000000000..94b68cf4147b --- /dev/null +++ b/tools/dma/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +dma_map_benchmark +include/linux/map_benchmark.h diff --git a/tools/dma/Makefile b/tools/dma/Makefile new file mode 100644 index 000000000000..e4abf37bf020 --- /dev/null +++ b/tools/dma/Makefile @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../scripts/Makefile.include + +bindir ?= /usr/bin + +# This will work when dma is built in tools env. where srctree +# isn't set and when invoked from selftests build, where srctree +# is set to ".". building_out_of_srctree is undefined for in srctree +# builds +ifndef building_out_of_srctree +srctree := $(patsubst %/,%,$(dir $(CURDIR))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +endif + +# Do not use make's built-in rules +# (this improves performance and avoids hard-to-debug behaviour); +MAKEFLAGS += -r + +override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include + +ALL_TARGETS := dma_map_benchmark +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) + +all: $(ALL_PROGRAMS) + +export srctree OUTPUT CC LD CFLAGS +include $(srctree)/tools/build/Makefile.include + +# +# We need the following to be outside of kernel tree +# +$(OUTPUT)include/linux/map_benchmark.h: ../../include/uapi/linux/map_benchmark.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../include/uapi/linux/map_benchmark.h $@ + +prepare: $(OUTPUT)include/linux/map_benchmark.h + +FORCE: + +DMA_MAP_BENCHMARK = dma_map_benchmark +$(DMA_MAP_BENCHMARK): prepare FORCE + $(CC) $(CFLAGS) $(DMA_MAP_BENCHMARK).c -o $(DMA_MAP_BENCHMARK) + +clean: + rm -f $(ALL_PROGRAMS) + rm -rf $(OUTPUT)include + find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete -o -name '\.*.cmd' -delete + +install: $(ALL_PROGRAMS) + install -d -m 755 $(DESTDIR)$(bindir); \ + for program in $(ALL_PROGRAMS); do \ + install $$program $(DESTDIR)$(bindir); \ + done + +.PHONY: all install clean prepare FORCE diff --git a/tools/dma/config b/tools/dma/config new file mode 100644 index 000000000000..6102ee3c43cd --- /dev/null +++ b/tools/dma/config @@ -0,0 +1 @@ +CONFIG_DMA_MAP_BENCHMARK=y diff --git a/tools/dma/dma_map_benchmark.c b/tools/dma/dma_map_benchmark.c new file mode 100644 index 000000000000..5474a450863c --- /dev/null +++ b/tools/dma/dma_map_benchmark.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 HiSilicon Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NSEC_PER_MSEC 1000000L + +static char *directions[] = { + "BIDIRECTIONAL", + "TO_DEVICE", + "FROM_DEVICE", +}; + +int main(int argc, char **argv) +{ + struct map_benchmark map; + int fd, opt; + /* default single thread, run 20 seconds on NUMA_NO_NODE */ + int threads = 1, seconds = 20, node = -1; + /* default dma mask 32bit, bidirectional DMA */ + int bits = 32, xdelay = 0, dir = DMA_MAP_BIDIRECTIONAL; + /* default granule 1 PAGESIZE */ + int granule = 1; + + int cmd = DMA_MAP_BENCHMARK; + + while ((opt = getopt(argc, argv, "t:s:n:b:d:x:g:")) != -1) { + switch (opt) { + case 't': + threads = atoi(optarg); + break; + case 's': + seconds = atoi(optarg); + break; + case 'n': + node = atoi(optarg); + break; + case 'b': + bits = atoi(optarg); + break; + case 'd': + dir = atoi(optarg); + break; + case 'x': + xdelay = atoi(optarg); + break; + case 'g': + granule = atoi(optarg); + break; + default: + return -1; + } + } + + if (threads <= 0 || threads > DMA_MAP_MAX_THREADS) { + fprintf(stderr, "invalid number of threads, must be in 1-%d\n", + DMA_MAP_MAX_THREADS); + exit(1); + } + + if (seconds <= 0 || seconds > DMA_MAP_MAX_SECONDS) { + fprintf(stderr, "invalid number of seconds, must be in 1-%d\n", + DMA_MAP_MAX_SECONDS); + exit(1); + } + + if (xdelay < 0 || xdelay > DMA_MAP_MAX_TRANS_DELAY) { + fprintf(stderr, "invalid transmit delay, must be in 0-%ld\n", + DMA_MAP_MAX_TRANS_DELAY); + exit(1); + } + + /* suppose the mininum DMA zone is 1MB in the world */ + if (bits < 20 || bits > 64) { + fprintf(stderr, "invalid dma mask bit, must be in 20-64\n"); + exit(1); + } + + if (dir != DMA_MAP_BIDIRECTIONAL && dir != DMA_MAP_TO_DEVICE && + dir != DMA_MAP_FROM_DEVICE) { + fprintf(stderr, "invalid dma direction\n"); + exit(1); + } + + if (granule < 1 || granule > 1024) { + fprintf(stderr, "invalid granule size\n"); + exit(1); + } + + fd = open("/sys/kernel/debug/dma_map_benchmark", O_RDWR); + if (fd == -1) { + perror("open"); + exit(1); + } + + memset(&map, 0, sizeof(map)); + map.seconds = seconds; + map.threads = threads; + map.node = node; + map.dma_bits = bits; + map.dma_dir = dir; + map.dma_trans_ns = xdelay; + map.granule = granule; + + if (ioctl(fd, cmd, &map)) { + perror("ioctl"); + exit(1); + } + + printf("dma mapping benchmark: threads:%d seconds:%d node:%d dir:%s granule: %d\n", + threads, seconds, node, dir[directions], granule); + printf("average map latency(us):%.1f standard deviation:%.1f\n", + map.avg_map_100ns/10.0, map.map_stddev/10.0); + printf("average unmap latency(us):%.1f standard deviation:%.1f\n", + map.avg_unmap_100ns/10.0, map.unmap_stddev/10.0); + + return 0; +} diff --git a/tools/testing/selftests/dma/Makefile b/tools/testing/selftests/dma/Makefile deleted file mode 100644 index cd8c5ece1cba..000000000000 --- a/tools/testing/selftests/dma/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -CFLAGS += -I../../../../usr/include/ -CFLAGS += -I../../../../include/ - -TEST_GEN_PROGS := dma_map_benchmark - -include ../lib.mk diff --git a/tools/testing/selftests/dma/config b/tools/testing/selftests/dma/config deleted file mode 100644 index 6102ee3c43cd..000000000000 --- a/tools/testing/selftests/dma/config +++ /dev/null @@ -1 +0,0 @@ -CONFIG_DMA_MAP_BENCHMARK=y diff --git a/tools/testing/selftests/dma/dma_map_benchmark.c b/tools/testing/selftests/dma/dma_map_benchmark.c deleted file mode 100644 index b12f1f9babf8..000000000000 --- a/tools/testing/selftests/dma/dma_map_benchmark.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 HiSilicon Limited. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NSEC_PER_MSEC 1000000L - -static char *directions[] = { - "BIDIRECTIONAL", - "TO_DEVICE", - "FROM_DEVICE", -}; - -int main(int argc, char **argv) -{ - struct map_benchmark map; - int fd, opt; - /* default single thread, run 20 seconds on NUMA_NO_NODE */ - int threads = 1, seconds = 20, node = -1; - /* default dma mask 32bit, bidirectional DMA */ - int bits = 32, xdelay = 0, dir = DMA_MAP_BIDIRECTIONAL; - /* default granule 1 PAGESIZE */ - int granule = 1; - - int cmd = DMA_MAP_BENCHMARK; - - while ((opt = getopt(argc, argv, "t:s:n:b:d:x:g:")) != -1) { - switch (opt) { - case 't': - threads = atoi(optarg); - break; - case 's': - seconds = atoi(optarg); - break; - case 'n': - node = atoi(optarg); - break; - case 'b': - bits = atoi(optarg); - break; - case 'd': - dir = atoi(optarg); - break; - case 'x': - xdelay = atoi(optarg); - break; - case 'g': - granule = atoi(optarg); - break; - default: - return -1; - } - } - - if (threads <= 0 || threads > DMA_MAP_MAX_THREADS) { - fprintf(stderr, "invalid number of threads, must be in 1-%d\n", - DMA_MAP_MAX_THREADS); - exit(1); - } - - if (seconds <= 0 || seconds > DMA_MAP_MAX_SECONDS) { - fprintf(stderr, "invalid number of seconds, must be in 1-%d\n", - DMA_MAP_MAX_SECONDS); - exit(1); - } - - if (xdelay < 0 || xdelay > DMA_MAP_MAX_TRANS_DELAY) { - fprintf(stderr, "invalid transmit delay, must be in 0-%ld\n", - DMA_MAP_MAX_TRANS_DELAY); - exit(1); - } - - /* suppose the mininum DMA zone is 1MB in the world */ - if (bits < 20 || bits > 64) { - fprintf(stderr, "invalid dma mask bit, must be in 20-64\n"); - exit(1); - } - - if (dir != DMA_MAP_BIDIRECTIONAL && dir != DMA_MAP_TO_DEVICE && - dir != DMA_MAP_FROM_DEVICE) { - fprintf(stderr, "invalid dma direction\n"); - exit(1); - } - - if (granule < 1 || granule > 1024) { - fprintf(stderr, "invalid granule size\n"); - exit(1); - } - - fd = open("/sys/kernel/debug/dma_map_benchmark", O_RDWR); - if (fd == -1) { - perror("open"); - exit(1); - } - - memset(&map, 0, sizeof(map)); - map.seconds = seconds; - map.threads = threads; - map.node = node; - map.dma_bits = bits; - map.dma_dir = dir; - map.dma_trans_ns = xdelay; - map.granule = granule; - - if (ioctl(fd, cmd, &map)) { - perror("ioctl"); - exit(1); - } - - printf("dma mapping benchmark: threads:%d seconds:%d node:%d dir:%s granule: %d\n", - threads, seconds, node, dir[directions], granule); - printf("average map latency(us):%.1f standard deviation:%.1f\n", - map.avg_map_100ns/10.0, map.map_stddev/10.0); - printf("average unmap latency(us):%.1f standard deviation:%.1f\n", - map.avg_unmap_100ns/10.0, map.unmap_stddev/10.0); - - return 0; -} -- cgit v1.2.3 From f16469ee733ac52b2373216803699cbb05e82786 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 31 Oct 2025 14:28:54 -0700 Subject: PCI/IDE: Enumerate Selective Stream IDE capabilities Link encryption is a new PCIe feature enumerated by "PCIe r7.0 section 7.9.26 IDE Extended Capability". It is both a standalone port + endpoint capability, and a building block for the security protocol defined by "PCIe r7.0 section 11 TEE Device Interface Security Protocol (TDISP)". That protocol coordinates device security setup between a platform TSM (TEE Security Manager) and a device DSM (Device Security Manager). While the platform TSM can allocate resources like Stream ID and manage keys, it still requires system software to manage the IDE capability register block. Add register definitions and basic enumeration in preparation for Selective IDE Stream establishment. A follow on change selects the new CONFIG_PCI_IDE symbol. Note that while the IDE specification defines both a point-to-point "Link Stream" and a Root Port to endpoint "Selective Stream", only "Selective Stream" is considered for Linux as that is the predominant mode expected by Trusted Execution Environment Security Managers (TSMs), and it is the security model that limits the number of PCI components within the TCB in a PCIe topology with switches. Co-developed-by: Alexey Kardashevskiy Signed-off-by: Alexey Kardashevskiy Co-developed-by: Xu Yilun Signed-off-by: Xu Yilun Reviewed-by: Jonathan Cameron Reviewed-by: Alexey Kardashevskiy Reviewed-by: Aneesh Kumar K.V Link: https://patch.msgid.link/20251031212902.2256310-3-dan.j.williams@intel.com Signed-off-by: Dan Williams --- drivers/pci/Kconfig | 3 ++ drivers/pci/Makefile | 1 + drivers/pci/ide.c | 88 +++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 6 +++ drivers/pci/probe.c | 1 + include/linux/pci.h | 7 ++++ include/uapi/linux/pci_regs.h | 81 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 187 insertions(+) create mode 100644 drivers/pci/ide.c (limited to 'include/uapi/linux') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index f94f5d384362..b28423e2057f 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -122,6 +122,9 @@ config XEN_PCIDEV_FRONTEND config PCI_ATS bool +config PCI_IDE + bool + config PCI_DOE bool "Enable PCI Data Object Exchange (DOE) support" help diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 67647f1880fb..6612256fd37d 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_PCI_P2PDMA) += p2pdma.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o obj-$(CONFIG_VGA_ARB) += vgaarb.o obj-$(CONFIG_PCI_DOE) += doe.o +obj-$(CONFIG_PCI_IDE) += ide.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.o obj-$(CONFIG_PCIE_TPH) += tph.o diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c new file mode 100644 index 000000000000..26866edf91b4 --- /dev/null +++ b/drivers/pci/ide.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */ + +/* PCIe r7.0 section 6.33 Integrity & Data Encryption (IDE) */ + +#define dev_fmt(fmt) "PCI/IDE: " fmt +#include +#include +#include + +#include "pci.h" + +static int __sel_ide_offset(u16 ide_cap, u8 nr_link_ide, u8 stream_index, + u8 nr_ide_mem) +{ + u32 offset = ide_cap + PCI_IDE_LINK_STREAM_0 + + nr_link_ide * PCI_IDE_LINK_BLOCK_SIZE; + + /* + * Assume a constant number of address association resources per stream + * index + */ + return offset + stream_index * PCI_IDE_SEL_BLOCK_SIZE(nr_ide_mem); +} + +void pci_ide_init(struct pci_dev *pdev) +{ + u16 nr_link_ide, nr_ide_mem, nr_streams; + u16 ide_cap; + u32 val; + + if (!pci_is_pcie(pdev)) + return; + + ide_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_IDE); + if (!ide_cap) + return; + + pci_read_config_dword(pdev, ide_cap + PCI_IDE_CAP, &val); + if ((val & PCI_IDE_CAP_SELECTIVE) == 0) + return; + + /* + * Require endpoint IDE capability to be paired with IDE Root Port IDE + * capability. + */ + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ENDPOINT) { + struct pci_dev *rp = pcie_find_root_port(pdev); + + if (!rp->ide_cap) + return; + } + + pdev->ide_cfg = FIELD_GET(PCI_IDE_CAP_SEL_CFG, val); + pdev->ide_tee_limit = FIELD_GET(PCI_IDE_CAP_TEE_LIMITED, val); + + if (val & PCI_IDE_CAP_LINK) + nr_link_ide = 1 + FIELD_GET(PCI_IDE_CAP_LINK_TC_NUM, val); + else + nr_link_ide = 0; + + nr_ide_mem = 0; + nr_streams = 1 + FIELD_GET(PCI_IDE_CAP_SEL_NUM, val); + for (u16 i = 0; i < nr_streams; i++) { + int pos = __sel_ide_offset(ide_cap, nr_link_ide, i, nr_ide_mem); + int nr_assoc; + u32 val; + + pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CAP, &val); + + /* + * Let's not entertain streams that do not have a constant + * number of address association blocks + */ + nr_assoc = FIELD_GET(PCI_IDE_SEL_CAP_ASSOC_NUM, val); + if (i && (nr_assoc != nr_ide_mem)) { + pci_info(pdev, "Unsupported Selective Stream %d capability, SKIP the rest\n", i); + nr_streams = i; + break; + } + + nr_ide_mem = nr_assoc; + } + + pdev->ide_cap = ide_cap; + pdev->nr_link_ide = nr_link_ide; + pdev->nr_ide_mem = nr_ide_mem; +} diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4492b809094b..86ef13e7cece 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -613,6 +613,12 @@ static inline void pci_doe_sysfs_init(struct pci_dev *pdev) { } static inline void pci_doe_sysfs_teardown(struct pci_dev *pdev) { } #endif +#ifdef CONFIG_PCI_IDE +void pci_ide_init(struct pci_dev *dev); +#else +static inline void pci_ide_init(struct pci_dev *dev) { } +#endif + /** * pci_dev_set_io_state - Set the new error state if possible. * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0ce98e18b5a8..4c55020f3ddf 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2667,6 +2667,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_doe_init(dev); /* Data Object Exchange */ pci_tph_init(dev); /* TLP Processing Hints */ pci_rebar_init(dev); /* Resizable BAR */ + pci_ide_init(dev); /* Link Integrity and Data Encryption */ pcie_report_downtraining(dev); pci_init_reset_methods(dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index d1fdf81fbe1e..4402ca931124 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -539,6 +539,13 @@ struct pci_dev { #endif #ifdef CONFIG_PCI_NPEM struct npem *npem; /* Native PCIe Enclosure Management */ +#endif +#ifdef CONFIG_PCI_IDE + u16 ide_cap; /* Link Integrity & Data Encryption */ + u8 nr_ide_mem; /* Address association resources for streams */ + u8 nr_link_ide; /* Link Stream count (Selective Stream offset) */ + unsigned int ide_cfg:1; /* Config cycles over IDE */ + unsigned int ide_tee_limit:1; /* Disallow T=0 traffic over IDE */ #endif u16 acs_cap; /* ACS Capability offset */ u8 supported_speeds; /* Supported Link Speeds Vector */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 07e06aafec50..05bd22d9e352 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -754,6 +754,7 @@ #define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */ #define PCI_EXT_CAP_ID_PL_32GT 0x2A /* Physical Layer 32.0 GT/s */ #define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */ +#define PCI_EXT_CAP_ID_IDE 0x30 /* Integrity and Data Encryption */ #define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */ #define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT @@ -1249,4 +1250,84 @@ #define PCI_DVSEC_CXL_PORT_CTL 0x0c #define PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR 0x00000001 +/* Integrity and Data Encryption Extended Capability */ +#define PCI_IDE_CAP 0x04 +#define PCI_IDE_CAP_LINK 0x1 /* Link IDE Stream Supported */ +#define PCI_IDE_CAP_SELECTIVE 0x2 /* Selective IDE Streams Supported */ +#define PCI_IDE_CAP_FLOWTHROUGH 0x4 /* Flow-Through IDE Stream Supported */ +#define PCI_IDE_CAP_PARTIAL_HEADER_ENC 0x8 /* Partial Header Encryption Supported */ +#define PCI_IDE_CAP_AGGREGATION 0x10 /* Aggregation Supported */ +#define PCI_IDE_CAP_PCRC 0x20 /* PCRC Supported */ +#define PCI_IDE_CAP_IDE_KM 0x40 /* IDE_KM Protocol Supported */ +#define PCI_IDE_CAP_SEL_CFG 0x80 /* Selective IDE for Config Request Support */ +#define PCI_IDE_CAP_ALG __GENMASK(12, 8) /* Supported Algorithms */ +#define PCI_IDE_CAP_ALG_AES_GCM_256 0 /* AES-GCM 256 key size, 96b MAC */ +#define PCI_IDE_CAP_LINK_TC_NUM __GENMASK(15, 13) /* Link IDE TCs */ +#define PCI_IDE_CAP_SEL_NUM __GENMASK(23, 16) /* Supported Selective IDE Streams */ +#define PCI_IDE_CAP_TEE_LIMITED 0x1000000 /* TEE-Limited Stream Supported */ +#define PCI_IDE_CTL 0x08 +#define PCI_IDE_CTL_FLOWTHROUGH_IDE 0x4 /* Flow-Through IDE Stream Enabled */ + +#define PCI_IDE_LINK_STREAM_0 0xc /* First Link Stream Register Block */ +#define PCI_IDE_LINK_BLOCK_SIZE 8 +/* Link IDE Stream block, up to PCI_IDE_CAP_LINK_TC_NUM */ +#define PCI_IDE_LINK_CTL_0 0x00 /* First Link Control Register Offset in block */ +#define PCI_IDE_LINK_CTL_EN 0x1 /* Link IDE Stream Enable */ +#define PCI_IDE_LINK_CTL_TX_AGGR_NPR __GENMASK(3, 2) /* Tx Aggregation Mode NPR */ +#define PCI_IDE_LINK_CTL_TX_AGGR_PR __GENMASK(5, 4) /* Tx Aggregation Mode PR */ +#define PCI_IDE_LINK_CTL_TX_AGGR_CPL __GENMASK(7, 6) /* Tx Aggregation Mode CPL */ +#define PCI_IDE_LINK_CTL_PCRC_EN 0x100 /* PCRC Enable */ +#define PCI_IDE_LINK_CTL_PART_ENC __GENMASK(13, 10) /* Partial Header Encryption Mode */ +#define PCI_IDE_LINK_CTL_ALG __GENMASK(18, 14) /* Selection from PCI_IDE_CAP_ALG */ +#define PCI_IDE_LINK_CTL_TC __GENMASK(21, 19) /* Traffic Class */ +#define PCI_IDE_LINK_CTL_ID __GENMASK(31, 24) /* Stream ID */ +#define PCI_IDE_LINK_STS_0 0x4 /* First Link Status Register Offset in block */ +#define PCI_IDE_LINK_STS_STATE __GENMASK(3, 0) /* Link IDE Stream State */ +#define PCI_IDE_LINK_STS_IDE_FAIL 0x80000000 /* IDE fail message received */ + +/* Selective IDE Stream block, up to PCI_IDE_CAP_SELECTIVE_STREAMS_NUM */ +/* Selective IDE Stream Capability Register */ +#define PCI_IDE_SEL_CAP 0x00 +#define PCI_IDE_SEL_CAP_ASSOC_NUM __GENMASK(3, 0) +/* Selective IDE Stream Control Register */ +#define PCI_IDE_SEL_CTL 0x04 +#define PCI_IDE_SEL_CTL_EN 0x1 /* Selective IDE Stream Enable */ +#define PCI_IDE_SEL_CTL_TX_AGGR_NPR __GENMASK(3, 2) /* Tx Aggregation Mode NPR */ +#define PCI_IDE_SEL_CTL_TX_AGGR_PR __GENMASK(5, 4) /* Tx Aggregation Mode PR */ +#define PCI_IDE_SEL_CTL_TX_AGGR_CPL __GENMASK(7, 6) /* Tx Aggregation Mode CPL */ +#define PCI_IDE_SEL_CTL_PCRC_EN 0x100 /* PCRC Enable */ +#define PCI_IDE_SEL_CTL_CFG_EN 0x200 /* Selective IDE for Configuration Requests */ +#define PCI_IDE_SEL_CTL_PART_ENC __GENMASK(13, 10) /* Partial Header Encryption Mode */ +#define PCI_IDE_SEL_CTL_ALG __GENMASK(18, 14) /* Selection from PCI_IDE_CAP_ALG */ +#define PCI_IDE_SEL_CTL_TC __GENMASK(21, 19) /* Traffic Class */ +#define PCI_IDE_SEL_CTL_DEFAULT 0x400000 /* Default Stream */ +#define PCI_IDE_SEL_CTL_TEE_LIMITED 0x800000 /* TEE-Limited Stream */ +#define PCI_IDE_SEL_CTL_ID __GENMASK(31, 24) /* Stream ID */ +#define PCI_IDE_SEL_CTL_ID_MAX 255 +/* Selective IDE Stream Status Register */ +#define PCI_IDE_SEL_STS 0x08 +#define PCI_IDE_SEL_STS_STATE __GENMASK(3, 0) /* Selective IDE Stream State */ +#define PCI_IDE_SEL_STS_STATE_INSECURE 0 +#define PCI_IDE_SEL_STS_STATE_SECURE 2 +#define PCI_IDE_SEL_STS_IDE_FAIL 0x80000000 /* IDE fail message received */ +/* IDE RID Association Register 1 */ +#define PCI_IDE_SEL_RID_1 0x0c +#define PCI_IDE_SEL_RID_1_LIMIT __GENMASK(23, 8) +/* IDE RID Association Register 2 */ +#define PCI_IDE_SEL_RID_2 0x10 +#define PCI_IDE_SEL_RID_2_VALID 0x1 +#define PCI_IDE_SEL_RID_2_BASE __GENMASK(23, 8) +#define PCI_IDE_SEL_RID_2_SEG __GENMASK(31, 24) +/* Selective IDE Address Association Register Block, up to PCI_IDE_SEL_CAP_ASSOC_NUM */ +#define PCI_IDE_SEL_ADDR_BLOCK_SIZE 12 +#define PCI_IDE_SEL_ADDR_1(x) (20 + (x) * PCI_IDE_SEL_ADDR_BLOCK_SIZE) +#define PCI_IDE_SEL_ADDR_1_VALID 0x1 +#define PCI_IDE_SEL_ADDR_1_BASE_LOW __GENMASK(19, 8) +#define PCI_IDE_SEL_ADDR_1_LIMIT_LOW __GENMASK(31, 20) +/* IDE Address Association Register 2 is "Memory Limit Upper" */ +#define PCI_IDE_SEL_ADDR_2(x) (24 + (x) * PCI_IDE_SEL_ADDR_BLOCK_SIZE) +/* IDE Address Association Register 3 is "Memory Base Upper" */ +#define PCI_IDE_SEL_ADDR_3(x) (28 + (x) * PCI_IDE_SEL_ADDR_BLOCK_SIZE) +#define PCI_IDE_SEL_BLOCK_SIZE(nr_assoc) (20 + PCI_IDE_SEL_ADDR_BLOCK_SIZE * (nr_assoc)) + #endif /* LINUX_PCI_REGS_H */ -- cgit v1.2.3 From 3225f52cde56f46789a4972d3c54df8a4d75f022 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 31 Oct 2025 14:28:56 -0700 Subject: PCI/TSM: Establish Secure Sessions and Link Encryption The PCIe 7.0 specification, section 11, defines the Trusted Execution Environment (TEE) Device Interface Security Protocol (TDISP). This protocol definition builds upon Component Measurement and Authentication (CMA), and link Integrity and Data Encryption (IDE). It adds support for assigning devices (PCI physical or virtual function) to a confidential VM such that the assigned device is enabled to access guest private memory protected by technologies like Intel TDX, AMD SEV-SNP, RISCV COVE, or ARM CCA. The "TSM" (TEE Security Manager) is a concept in the TDISP specification of an agent that mediates between a "DSM" (Device Security Manager) and system software in both a VMM and a confidential VM. A VMM uses TSM ABIs to setup link security and assign devices. A confidential VM uses TSM ABIs to transition an assigned device into the TDISP "RUN" state and validate its configuration. From a Linux perspective the TSM abstracts many of the details of TDISP, IDE, and CMA. Some of those details leak through at times, but for the most part TDISP is an internal implementation detail of the TSM. CONFIG_PCI_TSM adds an "authenticated" attribute and "tsm/" subdirectory to pci-sysfs. Consider that the TSM driver may itself be a PCI driver. Userspace can watch for the arrival of a "TSM" device, /sys/class/tsm/tsm0/uevent KOBJ_CHANGE, to know when the PCI core has initialized TSM services. The operations that can be executed against a PCI device are split into two mutually exclusive operation sets, "Link" and "Security" (struct pci_tsm_{link,security}_ops). The "Link" operations manage physical link security properties and communication with the device's Device Security Manager firmware. These are the host side operations in TDISP. The "Security" operations coordinate the security state of the assigned virtual device (TDI). These are the guest side operations in TDISP. Only "link" (Secure Session and physical Link Encryption) operations are defined at this stage. There are placeholders for the device security (Trusted Computing Base entry / exit) operations. The locking allows for multiple devices to be executing commands simultaneously, one outstanding command per-device and an rwsem synchronizes the implementation relative to TSM registration/unregistration events. Thanks to Wu Hao for his work on an early draft of this support. Cc: Lukas Wunner Cc: Samuel Ortiz Acked-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron Reviewed-by: Alexey Kardashevskiy Co-developed-by: Xu Yilun Signed-off-by: Xu Yilun Link: https://patch.msgid.link/20251031212902.2256310-5-dan.j.williams@intel.com Signed-off-by: Dan Williams --- Documentation/ABI/testing/sysfs-bus-pci | 51 +++ Documentation/driver-api/pci/index.rst | 1 + Documentation/driver-api/pci/tsm.rst | 21 ++ MAINTAINERS | 4 +- drivers/pci/Kconfig | 15 + drivers/pci/Makefile | 1 + drivers/pci/doe.c | 2 - drivers/pci/pci-sysfs.c | 4 + drivers/pci/pci.h | 10 + drivers/pci/probe.c | 3 + drivers/pci/remove.c | 6 + drivers/pci/tsm.c | 643 ++++++++++++++++++++++++++++++++ drivers/virt/coco/tsm-core.c | 46 ++- include/linux/pci-doe.h | 4 + include/linux/pci-tsm.h | 157 ++++++++ include/linux/pci.h | 3 + include/linux/tsm.h | 5 +- include/uapi/linux/pci_regs.h | 1 + 18 files changed, 971 insertions(+), 6 deletions(-) create mode 100644 Documentation/driver-api/pci/tsm.rst create mode 100644 drivers/pci/tsm.c create mode 100644 include/linux/pci-tsm.h (limited to 'include/uapi/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 92debe879ffb..6ffe02f854d6 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -621,3 +621,54 @@ Description: number extended capability. The file is read only and due to the possible sensitivity of accessible serial numbers, admin only. + +What: /sys/bus/pci/devices/.../tsm/ +Contact: linux-coco@lists.linux.dev +Description: + This directory only appears if a physical device function + supports authentication (PCIe CMA-SPDM), interface security + (PCIe TDISP), and is accepted for secure operation by the + platform TSM driver. This attribute directory appears + dynamically after the platform TSM driver loads. So, only after + the /sys/class/tsm/tsm0 device arrives can tools assume that + devices without a tsm/ attribute directory will never have one; + before that, the security capabilities of the device relative to + the platform TSM are unknown. See + Documentation/ABI/testing/sysfs-class-tsm. + +What: /sys/bus/pci/devices/.../tsm/connect +Contact: linux-coco@lists.linux.dev +Description: + (RW) Write the name of a TSM (TEE Security Manager) device from + /sys/class/tsm to this file to establish a connection with the + device. This typically includes an SPDM (DMTF Security + Protocols and Data Models) session over PCIe DOE (Data Object + Exchange) and may also include PCIe IDE (Integrity and Data + Encryption) establishment. Reads from this attribute return the + name of the connected TSM or the empty string if not + connected. A TSM device signals its readiness to accept PCI + connection via a KOBJ_CHANGE event. + +What: /sys/bus/pci/devices/.../tsm/disconnect +Contact: linux-coco@lists.linux.dev +Description: + (WO) Write the name of the TSM device that was specified + to 'connect' to teardown the connection. + +What: /sys/bus/pci/devices/.../authenticated +Contact: linux-pci@vger.kernel.org +Description: + When the device's tsm/ directory is present device + authentication (PCIe CMA-SPDM) and link encryption (PCIe IDE) + are handled by the platform TSM (TEE Security Manager). When the + tsm/ directory is not present this attribute reflects only the + native CMA-SPDM authentication state with the kernel's + certificate store. + + If the attribute is not present, it indicates that + authentication is unsupported by the device, or the TSM has no + available authentication methods for the device. + + When present and the tsm/ attribute directory is present, the + authenticated attribute is an alias for the device 'connect' + state. See the 'tsm/connect' attribute for more details. diff --git a/Documentation/driver-api/pci/index.rst b/Documentation/driver-api/pci/index.rst index a38e475cdbe3..9e1b801d0f74 100644 --- a/Documentation/driver-api/pci/index.rst +++ b/Documentation/driver-api/pci/index.rst @@ -10,6 +10,7 @@ The Linux PCI driver implementer's API guide pci p2pdma + tsm .. only:: subproject and html diff --git a/Documentation/driver-api/pci/tsm.rst b/Documentation/driver-api/pci/tsm.rst new file mode 100644 index 000000000000..232b92bec93f --- /dev/null +++ b/Documentation/driver-api/pci/tsm.rst @@ -0,0 +1,21 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: + +======================================================== +PCI Trusted Execution Environment Security Manager (TSM) +======================================================== + +Subsystem Interfaces +==================== + +.. kernel-doc:: include/linux/pci-ide.h + :internal: + +.. kernel-doc:: drivers/pci/ide.c + :export: + +.. kernel-doc:: include/linux/pci-tsm.h + :internal: + +.. kernel-doc:: drivers/pci/tsm.c + :export: diff --git a/MAINTAINERS b/MAINTAINERS index b8c9929532ed..f1c8793bf03e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26118,8 +26118,10 @@ L: linux-coco@lists.linux.dev S: Maintained F: Documentation/ABI/testing/configfs-tsm-report F: Documentation/driver-api/coco/ +F: Documentation/driver-api/pci/tsm.rst +F: drivers/pci/tsm.c F: drivers/virt/coco/guest/ -F: include/linux/tsm*.h +F: include/linux/*tsm*.h F: samples/tsm-mr/ TRUSTED SERVICES TEE DRIVER diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index b28423e2057f..00b0210e1f1d 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -125,6 +125,21 @@ config PCI_ATS config PCI_IDE bool +config PCI_TSM + bool "PCI TSM: Device security protocol support" + select PCI_IDE + select PCI_DOE + select TSM + help + The TEE (Trusted Execution Environment) Device Interface + Security Protocol (TDISP) defines a "TSM" as a platform agent + that manages device authentication, link encryption, link + integrity protection, and assignment of PCI device functions + (virtual or physical) to confidential computing VMs that can + access (DMA) guest private memory. + + Enable a platform TSM driver to use this capability. + config PCI_DOE bool "Enable PCI Data Object Exchange (DOE) support" help diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6612256fd37d..2c545f877062 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o obj-$(CONFIG_VGA_ARB) += vgaarb.o obj-$(CONFIG_PCI_DOE) += doe.o obj-$(CONFIG_PCI_IDE) += ide.o +obj-$(CONFIG_PCI_TSM) += tsm.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.o obj-$(CONFIG_PCIE_TPH) += tph.o diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index aae9a8a00406..62be9c8dbc52 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -24,8 +24,6 @@ #include "pci.h" -#define PCI_DOE_FEATURE_DISCOVERY 0 - /* Timeout of 1 second from 6.30.2 Operation, PCI Spec r6.0 */ #define PCI_DOE_TIMEOUT HZ #define PCI_DOE_POLL_INTERVAL (PCI_DOE_TIMEOUT / 128) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 9d6f74bd95f8..7f9237a926c2 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1868,6 +1868,10 @@ const struct attribute_group *pci_dev_attr_groups[] = { #endif #ifdef CONFIG_PCI_DOE &pci_doe_sysfs_group, +#endif +#ifdef CONFIG_PCI_TSM + &pci_tsm_auth_attr_group, + &pci_tsm_attr_group, #endif NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 86ef13e7cece..6e4cc1c9aa58 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -619,6 +619,16 @@ void pci_ide_init(struct pci_dev *dev); static inline void pci_ide_init(struct pci_dev *dev) { } #endif +#ifdef CONFIG_PCI_TSM +void pci_tsm_init(struct pci_dev *pdev); +void pci_tsm_destroy(struct pci_dev *pdev); +extern const struct attribute_group pci_tsm_attr_group; +extern const struct attribute_group pci_tsm_auth_attr_group; +#else +static inline void pci_tsm_init(struct pci_dev *pdev) { } +static inline void pci_tsm_destroy(struct pci_dev *pdev) { } +#endif + /** * pci_dev_set_io_state - Set the new error state if possible. * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 4c55020f3ddf..d1467348c169 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2763,6 +2763,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) ret = device_add(&dev->dev); WARN_ON(ret < 0); + /* Establish pdev->tsm for newly added (e.g. new SR-IOV VFs) */ + pci_tsm_init(dev); + pci_npem_create(dev); pci_doe_sysfs_init(dev); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index ce5c25adef55..803391892c4a 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -57,6 +57,12 @@ static void pci_destroy_dev(struct pci_dev *dev) pci_doe_sysfs_teardown(dev); pci_npem_remove(dev); + /* + * While device is in D0 drop the device from TSM link operations + * including unbind and disconnect (IDE + SPDM teardown). + */ + pci_tsm_destroy(dev); + device_del(&dev->dev); down_write(&pci_bus_sem); diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c new file mode 100644 index 000000000000..6a2849f77adc --- /dev/null +++ b/drivers/pci/tsm.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interface with platform TEE Security Manager (TSM) objects as defined by + * PCIe r7.0 section 11 TEE Device Interface Security Protocol (TDISP) + * + * Copyright(c) 2024-2025 Intel Corporation. All rights reserved. + */ + +#define dev_fmt(fmt) "PCI/TSM: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include "pci.h" + +/* + * Provide a read/write lock against the init / exit of pdev tsm + * capabilities and arrival/departure of a TSM instance + */ +static DECLARE_RWSEM(pci_tsm_rwsem); + +/* + * Count of TSMs registered that support physical link operations vs device + * security state management. + */ +static int pci_tsm_link_count; +static int pci_tsm_devsec_count; + +static const struct pci_tsm_ops *to_pci_tsm_ops(struct pci_tsm *tsm) +{ + return tsm->tsm_dev->pci_ops; +} + +static inline bool is_dsm(struct pci_dev *pdev) +{ + return pdev->tsm && pdev->tsm->dsm_dev == pdev; +} + +static inline bool has_tee(struct pci_dev *pdev) +{ + return pdev->devcap & PCI_EXP_DEVCAP_TEE; +} + +/* 'struct pci_tsm_pf0' wraps 'struct pci_tsm' when ->dsm_dev == ->pdev (self) */ +static struct pci_tsm_pf0 *to_pci_tsm_pf0(struct pci_tsm *tsm) +{ + /* + * All "link" TSM contexts reference the device that hosts the DSM + * interface for a set of devices. Walk to the DSM device and cast its + * ->tsm context to a 'struct pci_tsm_pf0 *'. + */ + struct pci_dev *pf0 = tsm->dsm_dev; + + if (!is_pci_tsm_pf0(pf0) || !is_dsm(pf0)) { + pci_WARN_ONCE(tsm->pdev, 1, "invalid context object\n"); + return NULL; + } + + return container_of(pf0->tsm, struct pci_tsm_pf0, base_tsm); +} + +static void tsm_remove(struct pci_tsm *tsm) +{ + struct pci_dev *pdev; + + if (!tsm) + return; + + pdev = tsm->pdev; + to_pci_tsm_ops(tsm)->remove(tsm); + pdev->tsm = NULL; +} +DEFINE_FREE(tsm_remove, struct pci_tsm *, if (_T) tsm_remove(_T)) + +static void pci_tsm_walk_fns(struct pci_dev *pdev, + int (*cb)(struct pci_dev *pdev, void *data), + void *data) +{ + /* Walk subordinate physical functions */ + for (int i = 0; i < 8; i++) { + struct pci_dev *pf __free(pci_dev_put) = pci_get_slot( + pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), i)); + + if (!pf) + continue; + + /* on entry function 0 has already run @cb */ + if (i > 0) + cb(pf, data); + + /* walk virtual functions of each pf */ + for (int j = 0; j < pci_num_vf(pf); j++) { + struct pci_dev *vf __free(pci_dev_put) = + pci_get_domain_bus_and_slot( + pci_domain_nr(pf->bus), + pci_iov_virtfn_bus(pf, j), + pci_iov_virtfn_devfn(pf, j)); + + if (!vf) + continue; + + cb(vf, data); + } + } + + /* + * Walk downstream devices, assumes that an upstream DSM is + * limited to downstream physical functions + */ + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM && is_dsm(pdev)) + pci_walk_bus(pdev->subordinate, cb, data); +} + +static void pci_tsm_walk_fns_reverse(struct pci_dev *pdev, + int (*cb)(struct pci_dev *pdev, + void *data), + void *data) +{ + /* Reverse walk downstream devices */ + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM && is_dsm(pdev)) + pci_walk_bus_reverse(pdev->subordinate, cb, data); + + /* Reverse walk subordinate physical functions */ + for (int i = 7; i >= 0; i--) { + struct pci_dev *pf __free(pci_dev_put) = pci_get_slot( + pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), i)); + + if (!pf) + continue; + + /* reverse walk virtual functions */ + for (int j = pci_num_vf(pf) - 1; j >= 0; j--) { + struct pci_dev *vf __free(pci_dev_put) = + pci_get_domain_bus_and_slot( + pci_domain_nr(pf->bus), + pci_iov_virtfn_bus(pf, j), + pci_iov_virtfn_devfn(pf, j)); + + if (!vf) + continue; + cb(vf, data); + } + + /* on exit, caller will run @cb on function 0 */ + if (i > 0) + cb(pf, data); + } +} + +static int probe_fn(struct pci_dev *pdev, void *dsm) +{ + struct pci_dev *dsm_dev = dsm; + const struct pci_tsm_ops *ops = to_pci_tsm_ops(dsm_dev->tsm); + + pdev->tsm = ops->probe(dsm_dev->tsm->tsm_dev, pdev); + pci_dbg(pdev, "setup TSM context: DSM: %s status: %s\n", + pci_name(dsm_dev), pdev->tsm ? "success" : "failed"); + return 0; +} + +static int pci_tsm_connect(struct pci_dev *pdev, struct tsm_dev *tsm_dev) +{ + int rc; + struct pci_tsm_pf0 *tsm_pf0; + const struct pci_tsm_ops *ops = tsm_dev->pci_ops; + struct pci_tsm *pci_tsm __free(tsm_remove) = ops->probe(tsm_dev, pdev); + + /* connect() mutually exclusive with subfunction pci_tsm_init() */ + lockdep_assert_held_write(&pci_tsm_rwsem); + + if (!pci_tsm) + return -ENXIO; + + pdev->tsm = pci_tsm; + tsm_pf0 = to_pci_tsm_pf0(pdev->tsm); + + /* mutex_intr assumes connect() is always sysfs/user driven */ + ACQUIRE(mutex_intr, lock)(&tsm_pf0->lock); + if ((rc = ACQUIRE_ERR(mutex_intr, &lock))) + return rc; + + rc = ops->connect(pdev); + if (rc) + return rc; + + pdev->tsm = no_free_ptr(pci_tsm); + + /* + * Now that the DSM is established, probe() all the potential + * dependent functions. Failure to probe a function is not fatal + * to connect(), it just disables subsequent security operations + * for that function. + * + * Note this is done unconditionally, without regard to finding + * PCI_EXP_DEVCAP_TEE on the dependent function, for robustness. The DSM + * is the ultimate arbiter of security state relative to a given + * interface id, and if it says it can manage TDISP state of a function, + * let it. + */ + if (has_tee(pdev)) + pci_tsm_walk_fns(pdev, probe_fn, pdev); + return 0; +} + +static ssize_t connect_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct tsm_dev *tsm_dev; + int rc; + + ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock))) + return rc; + + if (!pdev->tsm) + return sysfs_emit(buf, "\n"); + + tsm_dev = pdev->tsm->tsm_dev; + return sysfs_emit(buf, "%s\n", dev_name(&tsm_dev->dev)); +} + +/* Is @tsm_dev managing physical link / session properties... */ +static bool is_link_tsm(struct tsm_dev *tsm_dev) +{ + return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->link_ops.probe; +} + +/* ...or is @tsm_dev managing device security state ? */ +static bool is_devsec_tsm(struct tsm_dev *tsm_dev) +{ + return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->devsec_ops.lock; +} + +static ssize_t connect_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int rc, id; + + rc = sscanf(buf, "tsm%d\n", &id); + if (rc != 1) + return -EINVAL; + + ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock))) + return rc; + + if (pdev->tsm) + return -EBUSY; + + struct tsm_dev *tsm_dev __free(put_tsm_dev) = find_tsm_dev(id); + if (!is_link_tsm(tsm_dev)) + return -ENXIO; + + rc = pci_tsm_connect(pdev, tsm_dev); + if (rc) + return rc; + return len; +} +static DEVICE_ATTR_RW(connect); + +static int remove_fn(struct pci_dev *pdev, void *data) +{ + tsm_remove(pdev->tsm); + return 0; +} + +static void __pci_tsm_disconnect(struct pci_dev *pdev) +{ + struct pci_tsm_pf0 *tsm_pf0 = to_pci_tsm_pf0(pdev->tsm); + const struct pci_tsm_ops *ops = to_pci_tsm_ops(pdev->tsm); + + /* disconnect() mutually exclusive with subfunction pci_tsm_init() */ + lockdep_assert_held_write(&pci_tsm_rwsem); + + /* + * disconnect() is uninterruptible as it may be called for device + * teardown + */ + guard(mutex)(&tsm_pf0->lock); + pci_tsm_walk_fns_reverse(pdev, remove_fn, NULL); + ops->disconnect(pdev); +} + +static void pci_tsm_disconnect(struct pci_dev *pdev) +{ + __pci_tsm_disconnect(pdev); + tsm_remove(pdev->tsm); +} + +static ssize_t disconnect_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct tsm_dev *tsm_dev; + int rc; + + ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock))) + return rc; + + if (!pdev->tsm) + return -ENXIO; + + tsm_dev = pdev->tsm->tsm_dev; + if (!sysfs_streq(buf, dev_name(&tsm_dev->dev))) + return -EINVAL; + + pci_tsm_disconnect(pdev); + return len; +} +static DEVICE_ATTR_WO(disconnect); + +/* The 'authenticated' attribute is exclusive to the presence of a 'link' TSM */ +static bool pci_tsm_link_group_visible(struct kobject *kobj) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + + return pci_tsm_link_count && is_pci_tsm_pf0(pdev); +} +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_tsm_link); + +/* + * 'link' and 'devsec' TSMs share the same 'tsm/' sysfs group, so the TSM type + * specific attributes need individual visibility checks. + */ +static umode_t pci_tsm_attr_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + if (pci_tsm_link_group_visible(kobj)) { + if (attr == &dev_attr_connect.attr || + attr == &dev_attr_disconnect.attr) + return attr->mode; + } + + return 0; +} + +static bool pci_tsm_group_visible(struct kobject *kobj) +{ + return pci_tsm_link_group_visible(kobj); +} +DEFINE_SYSFS_GROUP_VISIBLE(pci_tsm); + +static struct attribute *pci_tsm_attrs[] = { + &dev_attr_connect.attr, + &dev_attr_disconnect.attr, + NULL +}; + +const struct attribute_group pci_tsm_attr_group = { + .name = "tsm", + .attrs = pci_tsm_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm), +}; + +static ssize_t authenticated_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* + * When the SPDM session established via TSM the 'authenticated' state + * of the device is identical to the connect state. + */ + return connect_show(dev, attr, buf); +} +static DEVICE_ATTR_RO(authenticated); + +static struct attribute *pci_tsm_auth_attrs[] = { + &dev_attr_authenticated.attr, + NULL +}; + +const struct attribute_group pci_tsm_auth_attr_group = { + .attrs = pci_tsm_auth_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(pci_tsm_link), +}; + +/* + * Retrieve physical function0 device whether it has TEE capability or not + */ +static struct pci_dev *pf0_dev_get(struct pci_dev *pdev) +{ + struct pci_dev *pf_dev = pci_physfn(pdev); + + if (PCI_FUNC(pf_dev->devfn) == 0) + return pci_dev_get(pf_dev); + + return pci_get_slot(pf_dev->bus, + pf_dev->devfn - PCI_FUNC(pf_dev->devfn)); +} + +/* + * Find the PCI Device instance that serves as the Device Security Manager (DSM) + * for @pdev. Note that no additional reference is held for the resulting device + * because that resulting object always has a registered lifetime + * greater-than-or-equal to that of the @pdev argument. This is by virtue of + * @pdev being a descendant of, or identical to, the returned DSM device. + */ +static struct pci_dev *find_dsm_dev(struct pci_dev *pdev) +{ + struct device *grandparent; + struct pci_dev *uport; + + if (is_pci_tsm_pf0(pdev)) + return pdev; + + struct pci_dev *pf0 __free(pci_dev_put) = pf0_dev_get(pdev); + if (!pf0) + return NULL; + + if (is_dsm(pf0)) + return pf0; + + /* + * For cases where a switch may be hosting TDISP services on behalf of + * downstream devices, check the first upstream port relative to this + * endpoint. + */ + if (!pdev->dev.parent) + return NULL; + grandparent = pdev->dev.parent->parent; + if (!grandparent) + return NULL; + if (!dev_is_pci(grandparent)) + return NULL; + uport = to_pci_dev(grandparent); + if (!pci_is_pcie(uport) || + pci_pcie_type(uport) != PCI_EXP_TYPE_UPSTREAM) + return NULL; + + if (is_dsm(uport)) + return uport; + return NULL; +} + +/** + * pci_tsm_link_constructor() - base 'struct pci_tsm' initialization for link TSMs + * @pdev: The PCI device + * @tsm: context to initialize + * @tsm_dev: Platform TEE Security Manager, initiator of security operations + */ +int pci_tsm_link_constructor(struct pci_dev *pdev, struct pci_tsm *tsm, + struct tsm_dev *tsm_dev) +{ + if (!is_link_tsm(tsm_dev)) + return -EINVAL; + + tsm->dsm_dev = find_dsm_dev(pdev); + if (!tsm->dsm_dev) { + pci_warn(pdev, "failed to find Device Security Manager\n"); + return -ENXIO; + } + tsm->pdev = pdev; + tsm->tsm_dev = tsm_dev; + + return 0; +} +EXPORT_SYMBOL_GPL(pci_tsm_link_constructor); + +/** + * pci_tsm_pf0_constructor() - common 'struct pci_tsm_pf0' (DSM) initialization + * @pdev: Physical Function 0 PCI device (as indicated by is_pci_tsm_pf0()) + * @tsm: context to initialize + * @tsm_dev: Platform TEE Security Manager, initiator of security operations + */ +int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm, + struct tsm_dev *tsm_dev) +{ + mutex_init(&tsm->lock); + tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, + PCI_DOE_FEATURE_CMA); + if (!tsm->doe_mb) { + pci_warn(pdev, "TSM init failure, no CMA mailbox\n"); + return -ENODEV; + } + + return pci_tsm_link_constructor(pdev, &tsm->base_tsm, tsm_dev); +} +EXPORT_SYMBOL_GPL(pci_tsm_pf0_constructor); + +void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *pf0_tsm) +{ + mutex_destroy(&pf0_tsm->lock); +} +EXPORT_SYMBOL_GPL(pci_tsm_pf0_destructor); + +static void pf0_sysfs_enable(struct pci_dev *pdev) +{ + bool tee = has_tee(pdev); + + pci_dbg(pdev, "Device Security Manager detected (%s%s%s)\n", + pdev->ide_cap ? "IDE" : "", pdev->ide_cap && tee ? " " : "", + tee ? "TEE" : ""); + + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); +} + +int pci_tsm_register(struct tsm_dev *tsm_dev) +{ + struct pci_dev *pdev = NULL; + + if (!tsm_dev) + return -EINVAL; + + /* The TSM device must only implement one of link_ops or devsec_ops */ + if (!is_link_tsm(tsm_dev) && !is_devsec_tsm(tsm_dev)) + return -EINVAL; + + if (is_link_tsm(tsm_dev) && is_devsec_tsm(tsm_dev)) + return -EINVAL; + + guard(rwsem_write)(&pci_tsm_rwsem); + + /* On first enable, update sysfs groups */ + if (is_link_tsm(tsm_dev) && pci_tsm_link_count++ == 0) { + for_each_pci_dev(pdev) + if (is_pci_tsm_pf0(pdev)) + pf0_sysfs_enable(pdev); + } else if (is_devsec_tsm(tsm_dev)) { + pci_tsm_devsec_count++; + } + + return 0; +} + +static void pci_tsm_fn_exit(struct pci_dev *pdev) +{ + /* TODO: unbind the fn */ + tsm_remove(pdev->tsm); +} + +/** + * __pci_tsm_destroy() - destroy the TSM context for @pdev + * @pdev: device to cleanup + * @tsm_dev: the TSM device being removed, or NULL if @pdev is being removed. + * + * At device removal or TSM unregistration all established context + * with the TSM is torn down. Additionally, if there are no more TSMs + * registered, the PCI tsm/ sysfs attributes are hidden. + */ +static void __pci_tsm_destroy(struct pci_dev *pdev, struct tsm_dev *tsm_dev) +{ + struct pci_tsm *tsm = pdev->tsm; + + lockdep_assert_held_write(&pci_tsm_rwsem); + + /* + * First, handle the TSM removal case to shutdown @pdev sysfs, this is + * skipped if the device itself is being removed since sysfs goes away + * naturally at that point + */ + if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev) && !pci_tsm_link_count) { + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group); + sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group); + } + + /* Nothing else to do if this device never attached to the departing TSM */ + if (!tsm) + return; + + /* Now lookup the tsm_dev to destroy TSM context */ + if (!tsm_dev) + tsm_dev = tsm->tsm_dev; + else if (tsm_dev != tsm->tsm_dev) + return; + + if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev)) + pci_tsm_disconnect(pdev); + else + pci_tsm_fn_exit(pdev); +} + +void pci_tsm_destroy(struct pci_dev *pdev) +{ + guard(rwsem_write)(&pci_tsm_rwsem); + __pci_tsm_destroy(pdev, NULL); +} + +void pci_tsm_init(struct pci_dev *pdev) +{ + guard(rwsem_read)(&pci_tsm_rwsem); + + /* + * Subfunctions are either probed synchronous with connect() or later + * when either the SR-IOV configuration is changed, or, unlikely, + * connect() raced initial bus scanning. + */ + if (pdev->tsm) + return; + + if (pci_tsm_link_count) { + struct pci_dev *dsm = find_dsm_dev(pdev); + + if (!dsm) + return; + + /* + * The only path to init a Device Security Manager capable + * device is via connect(). + */ + if (!dsm->tsm) + return; + + probe_fn(pdev, dsm); + } +} + +void pci_tsm_unregister(struct tsm_dev *tsm_dev) +{ + struct pci_dev *pdev = NULL; + + guard(rwsem_write)(&pci_tsm_rwsem); + if (is_link_tsm(tsm_dev)) + pci_tsm_link_count--; + if (is_devsec_tsm(tsm_dev)) + pci_tsm_devsec_count--; + for_each_pci_dev_reverse(pdev) + __pci_tsm_destroy(pdev, tsm_dev); +} + +int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req, + size_t req_sz, void *resp, size_t resp_sz) +{ + struct pci_tsm_pf0 *tsm; + + if (!pdev->tsm || !is_pci_tsm_pf0(pdev)) + return -ENXIO; + + tsm = to_pci_tsm_pf0(pdev->tsm); + if (!tsm->doe_mb) + return -ENXIO; + + return pci_doe(tsm->doe_mb, PCI_VENDOR_ID_PCI_SIG, type, req, req_sz, + resp, resp_sz); +} +EXPORT_SYMBOL_GPL(pci_tsm_doe_transfer); diff --git a/drivers/virt/coco/tsm-core.c b/drivers/virt/coco/tsm-core.c index 347507cc5e3f..0e705f3067a1 100644 --- a/drivers/virt/coco/tsm-core.c +++ b/drivers/virt/coco/tsm-core.c @@ -8,11 +8,29 @@ #include #include #include +#include static struct class *tsm_class; static DECLARE_RWSEM(tsm_rwsem); static DEFINE_IDA(tsm_ida); +static int match_id(struct device *dev, const void *data) +{ + struct tsm_dev *tsm_dev = container_of(dev, struct tsm_dev, dev); + int id = *(const int *)data; + + return tsm_dev->id == id; +} + +struct tsm_dev *find_tsm_dev(int id) +{ + struct device *dev = class_find_device(tsm_class, NULL, &id, match_id); + + if (!dev) + return NULL; + return container_of(dev, struct tsm_dev, dev); +} + static struct tsm_dev *alloc_tsm_dev(struct device *parent) { struct device *dev; @@ -36,7 +54,29 @@ static struct tsm_dev *alloc_tsm_dev(struct device *parent) return no_free_ptr(tsm_dev); } -struct tsm_dev *tsm_register(struct device *parent) +static struct tsm_dev *tsm_register_pci_or_reset(struct tsm_dev *tsm_dev, + struct pci_tsm_ops *pci_ops) +{ + int rc; + + if (!pci_ops) + return tsm_dev; + + tsm_dev->pci_ops = pci_ops; + rc = pci_tsm_register(tsm_dev); + if (rc) { + dev_err(tsm_dev->dev.parent, + "PCI/TSM registration failure: %d\n", rc); + device_unregister(&tsm_dev->dev); + return ERR_PTR(rc); + } + + /* Notify TSM userspace that PCI/TSM operations are now possible */ + kobject_uevent(&tsm_dev->dev.kobj, KOBJ_CHANGE); + return tsm_dev; +} + +struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops) { struct tsm_dev *tsm_dev __free(put_tsm_dev) = alloc_tsm_dev(parent); struct device *dev; @@ -54,12 +94,14 @@ struct tsm_dev *tsm_register(struct device *parent) if (rc) return ERR_PTR(rc); - return no_free_ptr(tsm_dev); + return tsm_register_pci_or_reset(no_free_ptr(tsm_dev), pci_ops); } EXPORT_SYMBOL_GPL(tsm_register); void tsm_unregister(struct tsm_dev *tsm_dev) { + if (tsm_dev->pci_ops) + pci_tsm_unregister(tsm_dev); device_unregister(&tsm_dev->dev); } EXPORT_SYMBOL_GPL(tsm_unregister); diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h index 1f14aed4354b..bd4346a7c4e7 100644 --- a/include/linux/pci-doe.h +++ b/include/linux/pci-doe.h @@ -15,6 +15,10 @@ struct pci_doe_mb; +#define PCI_DOE_FEATURE_DISCOVERY 0 +#define PCI_DOE_FEATURE_CMA 1 +#define PCI_DOE_FEATURE_SSESSION 2 + struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor, u8 type); diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h new file mode 100644 index 000000000000..e921d30f9b6c --- /dev/null +++ b/include/linux/pci-tsm.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PCI_TSM_H +#define __PCI_TSM_H +#include +#include + +struct pci_tsm; +struct tsm_dev; + +/* + * struct pci_tsm_ops - manage confidential links and security state + * @link_ops: Coordinate PCIe SPDM and IDE establishment via a platform TSM. + * Provide a secure session transport for TDISP state management + * (typically bare metal physical function operations). + * @devsec_ops: Lock, unlock, and interrogate the security state of the + * function via the platform TSM (typically virtual function + * operations). + * + * This operations are mutually exclusive either a tsm_dev instance + * manages physical link properties or it manages function security + * states like TDISP lock/unlock. + */ +struct pci_tsm_ops { + /* + * struct pci_tsm_link_ops - Manage physical link and the TSM/DSM session + * @probe: establish context with the TSM (allocate / wrap 'struct + * pci_tsm') for follow-on link operations + * @remove: destroy link operations context + * @connect: establish / validate a secure connection (e.g. IDE) + * with the device + * @disconnect: teardown the secure link + * + * Context: @probe, @remove, @connect, and @disconnect run under + * pci_tsm_rwsem held for write to sync with TSM unregistration and + * mutual exclusion of @connect and @disconnect. @connect and + * @disconnect additionally run under the DSM lock (struct + * pci_tsm_pf0::lock) as well as @probe and @remove of the subfunctions. + */ + struct_group_tagged(pci_tsm_link_ops, link_ops, + struct pci_tsm *(*probe)(struct tsm_dev *tsm_dev, + struct pci_dev *pdev); + void (*remove)(struct pci_tsm *tsm); + int (*connect)(struct pci_dev *pdev); + void (*disconnect)(struct pci_dev *pdev); + ); + + /* + * struct pci_tsm_devsec_ops - Manage the security state of the function + * @lock: establish context with the TSM (allocate / wrap 'struct + * pci_tsm') for follow-on security state transitions from the + * LOCKED state + * @unlock: destroy TSM context and return device to UNLOCKED state + * + * Context: @lock and @unlock run under pci_tsm_rwsem held for write to + * sync with TSM unregistration and each other + */ + struct_group_tagged(pci_tsm_devsec_ops, devsec_ops, + struct pci_tsm *(*lock)(struct tsm_dev *tsm_dev, + struct pci_dev *pdev); + void (*unlock)(struct pci_tsm *tsm); + ); +}; + +/** + * struct pci_tsm - Core TSM context for a given PCIe endpoint + * @pdev: Back ref to device function, distinguishes type of pci_tsm context + * @dsm_dev: PCI Device Security Manager for link operations on @pdev + * @tsm_dev: PCI TEE Security Manager device for Link Confidentiality or Device + * Function Security operations + * + * This structure is wrapped by low level TSM driver data and returned by + * probe()/lock(), it is freed by the corresponding remove()/unlock(). + * + * For link operations it serves to cache the association between a Device + * Security Manager (DSM) and the functions that manager can assign to a TVM. + * That can be "self", for assigning function0 of a TEE I/O device, a + * sub-function (SR-IOV virtual function, or non-function0 + * multifunction-device), or a downstream endpoint (PCIe upstream switch-port as + * DSM). + */ +struct pci_tsm { + struct pci_dev *pdev; + struct pci_dev *dsm_dev; + struct tsm_dev *tsm_dev; +}; + +/** + * struct pci_tsm_pf0 - Physical Function 0 TDISP link context + * @base_tsm: generic core "tsm" context + * @lock: mutual exclustion for pci_tsm_ops invocation + * @doe_mb: PCIe Data Object Exchange mailbox + */ +struct pci_tsm_pf0 { + struct pci_tsm base_tsm; + struct mutex lock; + struct pci_doe_mb *doe_mb; +}; + +/* physical function0 and capable of 'connect' */ +static inline bool is_pci_tsm_pf0(struct pci_dev *pdev) +{ + if (!pdev) + return false; + + if (!pci_is_pcie(pdev)) + return false; + + if (pdev->is_virtfn) + return false; + + /* + * Allow for a Device Security Manager (DSM) associated with function0 + * of an Endpoint to coordinate TDISP requests for other functions + * (physical or virtual) of the device, or allow for an Upstream Port + * DSM to accept TDISP requests for the Endpoints downstream of the + * switch. + */ + switch (pci_pcie_type(pdev)) { + case PCI_EXP_TYPE_ENDPOINT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_RC_END: + if (pdev->ide_cap || (pdev->devcap & PCI_EXP_DEVCAP_TEE)) + break; + fallthrough; + default: + return false; + } + + return PCI_FUNC(pdev->devfn) == 0; +} + +#ifdef CONFIG_PCI_TSM +int pci_tsm_register(struct tsm_dev *tsm_dev); +void pci_tsm_unregister(struct tsm_dev *tsm_dev); +int pci_tsm_link_constructor(struct pci_dev *pdev, struct pci_tsm *tsm, + struct tsm_dev *tsm_dev); +int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm, + struct tsm_dev *tsm_dev); +void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *tsm); +int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req, + size_t req_sz, void *resp, size_t resp_sz); +#else +static inline int pci_tsm_register(struct tsm_dev *tsm_dev) +{ + return 0; +} +static inline void pci_tsm_unregister(struct tsm_dev *tsm_dev) +{ +} +static inline int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, + const void *req, size_t req_sz, + void *resp, size_t resp_sz) +{ + return -ENXIO; +} +#endif +#endif /*__PCI_TSM_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index b6a12a82be12..2f9c0cb6a50a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -546,6 +546,9 @@ struct pci_dev { u8 nr_link_ide; /* Link Stream count (Selective Stream offset) */ unsigned int ide_cfg:1; /* Config cycles over IDE */ unsigned int ide_tee_limit:1; /* Disallow T=0 traffic over IDE */ +#endif +#ifdef CONFIG_PCI_TSM + struct pci_tsm *tsm; /* TSM operation state */ #endif u16 acs_cap; /* ACS Capability offset */ u8 supported_speeds; /* Supported Link Speeds Vector */ diff --git a/include/linux/tsm.h b/include/linux/tsm.h index cd97c63ffa32..22e05b2aac69 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -108,9 +108,11 @@ struct tsm_report_ops { bool (*report_bin_attr_visible)(int n); }; +struct pci_tsm_ops; struct tsm_dev { struct device dev; int id; + const struct pci_tsm_ops *pci_ops; }; DEFINE_FREE(put_tsm_dev, struct tsm_dev *, @@ -118,6 +120,7 @@ DEFINE_FREE(put_tsm_dev, struct tsm_dev *, int tsm_report_register(const struct tsm_report_ops *ops, void *priv); int tsm_report_unregister(const struct tsm_report_ops *ops); -struct tsm_dev *tsm_register(struct device *parent); +struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *ops); void tsm_unregister(struct tsm_dev *tsm_dev); +struct tsm_dev *find_tsm_dev(int id); #endif /* __TSM_H */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 05bd22d9e352..f2759c1097bc 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -503,6 +503,7 @@ #define PCI_EXP_DEVCAP_PWR_VAL 0x03fc0000 /* Slot Power Limit Value */ #define PCI_EXP_DEVCAP_PWR_SCL 0x0c000000 /* Slot Power Limit Scale */ #define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCAP_TEE 0x40000000 /* TEE I/O (TDISP) Support */ #define PCI_EXP_DEVCTL 0x08 /* Device Control */ #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ #define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ -- cgit v1.2.3 From c0c1262fbfbafe943dbccd5f97b500b72dbd2205 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 31 Oct 2025 14:28:57 -0700 Subject: PCI: Add PCIe Device 3 Extended Capability enumeration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCIe r7.0 Section 7.7.9 Device 3 Extended Capability Structure, defines the canonical location for determining the Flit Mode of a device. This status is a dependency for PCIe IDE enabling. Add a new fm_enabled flag to 'struct pci_dev'. Cc: Lukas Wunner Cc: Ilpo Järvinen Cc: Bjorn Helgaas Cc: Samuel Ortiz Cc: Alexey Kardashevskiy Cc: Xu Yilun Acked-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20251031212902.2256310-6-dan.j.williams@intel.com Signed-off-by: Dan Williams --- drivers/pci/probe.c | 12 ++++++++++++ include/linux/pci.h | 1 + include/uapi/linux/pci_regs.h | 7 +++++++ 3 files changed, 20 insertions(+) (limited to 'include/uapi/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index d1467348c169..3b54f1720be5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2283,6 +2283,17 @@ int pci_configure_extended_tags(struct pci_dev *dev, void *ign) return 0; } +static void pci_dev3_init(struct pci_dev *pdev) +{ + u16 cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DEV3); + u32 val = 0; + + if (!cap) + return; + pci_read_config_dword(pdev, cap + PCI_DEV3_STA, &val); + pdev->fm_enabled = !!(val & PCI_DEV3_STA_SEGMENT); +} + /** * pcie_relaxed_ordering_enabled - Probe for PCIe relaxed ordering enable * @dev: PCI device to query @@ -2667,6 +2678,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_doe_init(dev); /* Data Object Exchange */ pci_tph_init(dev); /* TLP Processing Hints */ pci_rebar_init(dev); /* Resizable BAR */ + pci_dev3_init(dev); /* Device 3 capabilities */ pci_ide_init(dev); /* Link Integrity and Data Encryption */ pcie_report_downtraining(dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 2f9c0cb6a50a..ea94799c81b0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -450,6 +450,7 @@ struct pci_dev { unsigned int pasid_enabled:1; /* Process Address Space ID */ unsigned int pri_enabled:1; /* Page Request Interface */ unsigned int tph_enabled:1; /* TLP Processing Hints */ + unsigned int fm_enabled:1; /* Flit Mode (segment captured) */ unsigned int is_managed:1; /* Managed via devres */ unsigned int is_msi_managed:1; /* MSI release via devres installed */ unsigned int needs_freset:1; /* Requires fundamental reset */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index f2759c1097bc..3add74ae2594 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -755,6 +755,7 @@ #define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */ #define PCI_EXT_CAP_ID_PL_32GT 0x2A /* Physical Layer 32.0 GT/s */ #define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */ +#define PCI_EXT_CAP_ID_DEV3 0x2F /* Device 3 Capability/Control/Status */ #define PCI_EXT_CAP_ID_IDE 0x30 /* Integrity and Data Encryption */ #define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */ #define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT @@ -1246,6 +1247,12 @@ /* Deprecated old name, replaced with PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE */ #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE +/* Device 3 Extended Capability */ +#define PCI_DEV3_CAP 0x04 /* Device 3 Capabilities Register */ +#define PCI_DEV3_CTL 0x08 /* Device 3 Control Register */ +#define PCI_DEV3_STA 0x0c /* Device 3 Status Register */ +#define PCI_DEV3_STA_SEGMENT 0x8 /* Segment Captured (end-to-end flit-mode detected) */ + /* Compute Express Link (CXL r3.1, sec 8.1.5) */ #define PCI_DVSEC_CXL_PORT 3 #define PCI_DVSEC_CXL_PORT_CTL 0x0c -- cgit v1.2.3 From ad9c62bd8946621ed02ac94131a921222508a8bc Mon Sep 17 00:00:00 2001 From: Jiaqi Yan Date: Mon, 13 Oct 2025 18:59:01 +0000 Subject: KVM: arm64: VM exit to userspace to handle SEA When APEI fails to handle a stage-2 synchronous external abort (SEA), today KVM injects an asynchronous SError to the VCPU then resumes it, which usually results in unpleasant guest kernel panic. One major situation of guest SEA is when vCPU consumes recoverable uncorrected memory error (UER). Although SError and guest kernel panic effectively stops the propagation of corrupted memory, guest may re-use the corrupted memory if auto-rebooted; in worse case, guest boot may run into poisoned memory. So there is room to recover from an UER in a more graceful manner. Alternatively KVM can redirect the synchronous SEA event to VMM to - Reduce blast radius if possible. VMM can inject a SEA to VCPU via KVM's existing KVM_SET_VCPU_EVENTS API. If the memory poison consumption or fault is not from guest kernel, blast radius can be limited to the triggering thread in guest userspace, so VM can keep running. - Allow VMM to protect from future memory poison consumption by unmapping the page from stage-2, or to interrupt guest of the poisoned page so guest kernel can unmap it from stage-1 page table. - Allow VMM to track SEA events that VM customers care about, to restart VM when certain number of distinct poison events have happened, to provide observability to customers in log management UI. Introduce an userspace-visible feature to enable VMM handle SEA: - KVM_CAP_ARM_SEA_TO_USER. As the alternative fallback behavior when host APEI fails to claim a SEA, userspace can opt in this new capability to let KVM exit to userspace during SEA if it is not owned by host. - KVM_EXIT_ARM_SEA. A new exit reason is introduced for this. KVM fills kvm_run.arm_sea with as much as possible information about the SEA, enabling VMM to emulate SEA to guest by itself. - Sanitized ESR_EL2. The general rule is to keep only the bits useful for userspace and relevant to guest memory. - Flags indicating if faulting guest physical address is valid. - Faulting guest physical and virtual addresses if valid. Signed-off-by: Jiaqi Yan Co-developed-by: Oliver Upton Signed-off-by: Oliver Upton Link: https://msgid.link/20251013185903.1372553-2-jiaqiyan@google.com Signed-off-by: Oliver Upton --- arch/arm64/include/asm/kvm_host.h | 2 ++ arch/arm64/kvm/arm.c | 5 +++ arch/arm64/kvm/mmu.c | 68 ++++++++++++++++++++++++++++++++++++++- include/uapi/linux/kvm.h | 10 ++++++ 4 files changed, 84 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 64302c438355..366bf337ef64 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -350,6 +350,8 @@ struct kvm_arch { #define KVM_ARCH_FLAG_GUEST_HAS_SVE 9 /* MIDR_EL1, REVIDR_EL1, and AIDR_EL1 are writable from userspace */ #define KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS 10 + /* Unhandled SEAs are taken to userspace */ +#define KVM_ARCH_FLAG_EXIT_SEA 11 unsigned long flags; /* VM-wide vCPU feature set */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 870953b4a8a7..511d2e8ef6c7 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -132,6 +132,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, } mutex_unlock(&kvm->lock); break; + case KVM_CAP_ARM_SEA_TO_USER: + r = 0; + set_bit(KVM_ARCH_FLAG_EXIT_SEA, &kvm->arch.flags); + break; default: break; } @@ -327,6 +331,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_IRQFD_RESAMPLE: case KVM_CAP_COUNTER_OFFSET: case KVM_CAP_ARM_WRITABLE_IMP_ID_REGS: + case KVM_CAP_ARM_SEA_TO_USER: r = 1; break; case KVM_CAP_SET_GUEST_DEBUG2: diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 7cc964af8d30..58cb169727a6 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1899,8 +1899,48 @@ static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa) read_unlock(&vcpu->kvm->mmu_lock); } +/* + * Returns true if the SEA should be handled locally within KVM if the abort + * is caused by a kernel memory allocation (e.g. stage-2 table memory). + */ +static bool host_owns_sea(struct kvm_vcpu *vcpu, u64 esr) +{ + /* + * Without FEAT_RAS HCR_EL2.TEA is RES0, meaning any external abort + * taken from a guest EL to EL2 is due to a host-imposed access (e.g. + * stage-2 PTW). + */ + if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) + return true; + + /* KVM owns the VNCR when the vCPU isn't in a nested context. */ + if (is_hyp_ctxt(vcpu) && !kvm_vcpu_trap_is_iabt(vcpu) && (esr & ESR_ELx_VNCR)) + return true; + + /* + * Determining if an external abort during a table walk happened at + * stage-2 is only possible with S1PTW is set. Otherwise, since KVM + * sets HCR_EL2.TEA, SEAs due to a stage-1 walk (i.e. accessing the + * PA of the stage-1 descriptor) can reach here and are reported + * with a TTW ESR value. + */ + return (esr_fsc_is_sea_ttw(esr) && (esr & ESR_ELx_S1PTW)); +} + int kvm_handle_guest_sea(struct kvm_vcpu *vcpu) { + struct kvm *kvm = vcpu->kvm; + struct kvm_run *run = vcpu->run; + u64 esr = kvm_vcpu_get_esr(vcpu); + u64 esr_mask = ESR_ELx_EC_MASK | + ESR_ELx_IL | + ESR_ELx_FnV | + ESR_ELx_EA | + ESR_ELx_CM | + ESR_ELx_WNR | + ESR_ELx_FSC; + u64 ipa; + /* * Give APEI the opportunity to claim the abort before handling it * within KVM. apei_claim_sea() expects to be called with IRQs enabled. @@ -1909,7 +1949,33 @@ int kvm_handle_guest_sea(struct kvm_vcpu *vcpu) if (apei_claim_sea(NULL) == 0) return 1; - return kvm_inject_serror(vcpu); + if (host_owns_sea(vcpu, esr) || + !test_bit(KVM_ARCH_FLAG_EXIT_SEA, &vcpu->kvm->arch.flags)) + return kvm_inject_serror(vcpu); + + /* ESR_ELx.SET is RES0 when FEAT_RAS isn't implemented. */ + if (kvm_has_ras(kvm)) + esr_mask |= ESR_ELx_SET_MASK; + + /* + * Exit to userspace, and provide faulting guest virtual and physical + * addresses in case userspace wants to emulate SEA to guest by + * writing to FAR_ELx and HPFAR_ELx registers. + */ + memset(&run->arm_sea, 0, sizeof(run->arm_sea)); + run->exit_reason = KVM_EXIT_ARM_SEA; + run->arm_sea.esr = esr & esr_mask; + + if (!(esr & ESR_ELx_FnV)) + run->arm_sea.gva = kvm_vcpu_get_hfar(vcpu); + + ipa = kvm_vcpu_get_fault_ipa(vcpu); + if (ipa != INVALID_GPA) { + run->arm_sea.flags |= KVM_EXIT_ARM_SEA_FLAG_GPA_VALID; + run->arm_sea.gpa = ipa; + } + + return 0; } /** diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 52f6000ab020..1e541193e98d 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -179,6 +179,7 @@ struct kvm_xen_exit { #define KVM_EXIT_LOONGARCH_IOCSR 38 #define KVM_EXIT_MEMORY_FAULT 39 #define KVM_EXIT_TDX 40 +#define KVM_EXIT_ARM_SEA 41 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -473,6 +474,14 @@ struct kvm_run { } setup_event_notify; }; } tdx; + /* KVM_EXIT_ARM_SEA */ + struct { +#define KVM_EXIT_ARM_SEA_FLAG_GPA_VALID (1ULL << 0) + __u64 flags; + __u64 esr; + __u64 gva; + __u64 gpa; + } arm_sea; /* Fix the size of the union. */ char padding[256]; }; @@ -963,6 +972,7 @@ struct kvm_enable_cap { #define KVM_CAP_RISCV_MP_STATE_RESET 242 #define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 #define KVM_CAP_GUEST_MEMFD_FLAGS 244 +#define KVM_CAP_ARM_SEA_TO_USER 245 struct kvm_irq_routing_irqchip { __u32 irqchip; -- cgit v1.2.3 From f91bc8f61abf0e1d23108ae9871c60d7612a09b2 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Thu, 6 Nov 2025 14:13:31 -0800 Subject: mshv: Allow mappings that overlap in uaddr Currently the MSHV driver rejects mappings that would overlap in userspace. Some VMMs require the same memory to be mapped to different parts of the guest's address space, and so working around this restriction is difficult. The hypervisor itself doesn't prohibit mappings that overlap in uaddr, (really in SPA; system physical addresses), so supporting this in the driver doesn't require any extra work: only the checks need to be removed. Since no userspace code until now has been able to overlap regions in userspace, relaxing this constraint can't break any existing code. Signed-off-by: Magnus Kulke Signed-off-by: Nuno Das Neves Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 8 ++------ include/uapi/linux/mshv.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index a8f3c5f3ce34..3f73c468e975 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1188,12 +1188,8 @@ static int mshv_partition_create_region(struct mshv_partition *partition, /* Reject overlapping regions */ hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) { - u64 rg_size = rg->nr_pages << HV_HYP_PAGE_SHIFT; - - if ((mem->guest_pfn + nr_pages <= rg->start_gfn || - rg->start_gfn + rg->nr_pages <= mem->guest_pfn) && - (mem->userspace_addr + mem->size <= rg->start_uaddr || - rg->start_uaddr + rg_size <= mem->userspace_addr)) + if (mem->guest_pfn + nr_pages <= rg->start_gfn || + rg->start_gfn + rg->nr_pages <= mem->guest_pfn) continue; return -EEXIST; diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index 876bfe4e4227..374f75e198bc 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -89,7 +89,7 @@ enum { * @rsvd: MBZ * * Map or unmap a region of userspace memory to Guest Physical Addresses (GPA). - * Mappings can't overlap in GPA space or userspace. + * Mappings can't overlap in GPA space. * To unmap, these fields must match an existing mapping. */ struct mshv_user_mem_region { -- cgit v1.2.3 From c91fe5f162f278d4aa960d06d2dbc42f9857593a Mon Sep 17 00:00:00 2001 From: Muminul Islam Date: Thu, 13 Nov 2025 11:45:33 -0800 Subject: mshv: Extend create partition ioctl to support cpu features The existing mshv create partition ioctl does not provide a way to specify which cpu features are enabled in the guest. Instead, it attempts to enable all features and those that are not supported are silently disabled by the hypervisor. This was done to reduce unnecessary complexity and is sufficient for many cases. However, new scenarios require fine-grained control over these features. Define a new mshv_create_partition_v2 structure which supports passing the disabled processor and xsave feature bits through to the create partition hypercall directly. Introduce a new flag MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES which enables the new structure. If unset, the original mshv_create_partition struct is used, with the old behavior of enabling all features. Co-developed-by: Jinank Jain Signed-off-by: Jinank Jain Signed-off-by: Muminul Islam Signed-off-by: Nuno Das Neves Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 118 ++++++++++++++++++++++++++++++++++++-------- include/uapi/linux/mshv.h | 34 +++++++++++++ 2 files changed, 131 insertions(+), 21 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 3f73c468e975..45c7a5fea1cf 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1855,43 +1855,119 @@ add_partition(struct mshv_partition *partition) return 0; } -static long -mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev) +static_assert(MSHV_NUM_CPU_FEATURES_BANKS == + HV_PARTITION_PROCESSOR_FEATURES_BANKS); + +static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags, + struct hv_partition_creation_properties *cr_props, + union hv_partition_isolation_properties *isol_props) { - struct mshv_create_partition args; - u64 creation_flags; - struct hv_partition_creation_properties creation_properties = {}; - union hv_partition_isolation_properties isolation_properties = {}; - struct mshv_partition *partition; - struct file *file; - int fd; - long ret; + int i; + struct mshv_create_partition_v2 args; + union hv_partition_processor_features *disabled_procs; + union hv_partition_processor_xsave_features *disabled_xsave; - if (copy_from_user(&args, user_arg, sizeof(args))) + /* First, copy v1 struct in case user is on previous versions */ + if (copy_from_user(&args, user_arg, + sizeof(struct mshv_create_partition))) return -EFAULT; if ((args.pt_flags & ~MSHV_PT_FLAGS_MASK) || args.pt_isolation >= MSHV_PT_ISOLATION_COUNT) return -EINVAL; + disabled_procs = &cr_props->disabled_processor_features; + disabled_xsave = &cr_props->disabled_processor_xsave_features; + + /* Check if user provided newer struct with feature fields */ + if (args.pt_flags & BIT_ULL(MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES)) { + if (copy_from_user(&args, user_arg, sizeof(args))) + return -EFAULT; + + /* Re-validate v1 fields after second copy_from_user() */ + if ((args.pt_flags & ~MSHV_PT_FLAGS_MASK) || + args.pt_isolation >= MSHV_PT_ISOLATION_COUNT) + return -EINVAL; + + if (args.pt_num_cpu_fbanks != MSHV_NUM_CPU_FEATURES_BANKS || + mshv_field_nonzero(args, pt_rsvd) || + mshv_field_nonzero(args, pt_rsvd1)) + return -EINVAL; + + /* + * Note this assumes MSHV_NUM_CPU_FEATURES_BANKS will never + * change and equals HV_PARTITION_PROCESSOR_FEATURES_BANKS + * (i.e. 2). + * + * Further banks (index >= 2) will be modifiable as 'early' + * properties via the set partition property hypercall. + */ + for (i = 0; i < HV_PARTITION_PROCESSOR_FEATURES_BANKS; i++) + disabled_procs->as_uint64[i] = args.pt_cpu_fbanks[i]; + +#if IS_ENABLED(CONFIG_X86_64) + disabled_xsave->as_uint64 = args.pt_disabled_xsave; +#else + /* + * In practice this field is ignored on arm64, but safer to + * zero it in case it is ever used. + */ + disabled_xsave->as_uint64 = 0; + + if (mshv_field_nonzero(args, pt_rsvd2)) + return -EINVAL; +#endif + } else { + /* + * v1 behavior: try to enable everything. The hypervisor will + * disable features that are not supported. The banks can be + * queried via the get partition property hypercall. + */ + for (i = 0; i < HV_PARTITION_PROCESSOR_FEATURES_BANKS; i++) + disabled_procs->as_uint64[i] = 0; + + disabled_xsave->as_uint64 = 0; + } + /* Only support EXO partitions */ - creation_flags = HV_PARTITION_CREATION_FLAG_EXO_PARTITION | - HV_PARTITION_CREATION_FLAG_INTERCEPT_MESSAGE_PAGE_ENABLED; + *pt_flags = HV_PARTITION_CREATION_FLAG_EXO_PARTITION | + HV_PARTITION_CREATION_FLAG_INTERCEPT_MESSAGE_PAGE_ENABLED; + + if (args.pt_flags & BIT_ULL(MSHV_PT_BIT_LAPIC)) + *pt_flags |= HV_PARTITION_CREATION_FLAG_LAPIC_ENABLED; + if (args.pt_flags & BIT_ULL(MSHV_PT_BIT_X2APIC)) + *pt_flags |= HV_PARTITION_CREATION_FLAG_X2APIC_CAPABLE; + if (args.pt_flags & BIT_ULL(MSHV_PT_BIT_GPA_SUPER_PAGES)) + *pt_flags |= HV_PARTITION_CREATION_FLAG_GPA_SUPER_PAGES_ENABLED; - if (args.pt_flags & BIT(MSHV_PT_BIT_LAPIC)) - creation_flags |= HV_PARTITION_CREATION_FLAG_LAPIC_ENABLED; - if (args.pt_flags & BIT(MSHV_PT_BIT_X2APIC)) - creation_flags |= HV_PARTITION_CREATION_FLAG_X2APIC_CAPABLE; - if (args.pt_flags & BIT(MSHV_PT_BIT_GPA_SUPER_PAGES)) - creation_flags |= HV_PARTITION_CREATION_FLAG_GPA_SUPER_PAGES_ENABLED; + isol_props->as_uint64 = 0; switch (args.pt_isolation) { case MSHV_PT_ISOLATION_NONE: - isolation_properties.isolation_type = - HV_PARTITION_ISOLATION_TYPE_NONE; + isol_props->isolation_type = HV_PARTITION_ISOLATION_TYPE_NONE; break; } + return 0; +} + +static long +mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev) +{ + u64 creation_flags; + struct hv_partition_creation_properties creation_properties; + union hv_partition_isolation_properties isolation_properties; + struct mshv_partition *partition; + struct file *file; + int fd; + long ret; + + ret = mshv_ioctl_process_pt_flags(user_arg, &creation_flags, + &creation_properties, + &isolation_properties); + if (ret) + return ret; + partition = kzalloc(sizeof(*partition), GFP_KERNEL); if (!partition) return -ENOMEM; diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index 374f75e198bc..b645d17cc531 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -26,6 +26,7 @@ enum { MSHV_PT_BIT_LAPIC, MSHV_PT_BIT_X2APIC, MSHV_PT_BIT_GPA_SUPER_PAGES, + MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES, MSHV_PT_BIT_COUNT, }; @@ -41,6 +42,8 @@ enum { * @pt_flags: Bitmask of 1 << MSHV_PT_BIT_* * @pt_isolation: MSHV_PT_ISOLATION_* * + * This is the initial/v1 version for backward compatibility. + * * Returns a file descriptor to act as a handle to a guest partition. * At this point the partition is not yet initialized in the hypervisor. * Some operations must be done with the partition in this state, e.g. setting @@ -52,6 +55,37 @@ struct mshv_create_partition { __u64 pt_isolation; }; +#define MSHV_NUM_CPU_FEATURES_BANKS 2 + +/** + * struct mshv_create_partition_v2 + * + * This is extended version of the above initial MSHV_CREATE_PARTITION + * ioctl and allows for following additional parameters: + * + * @pt_num_cpu_fbanks: Must be set to MSHV_NUM_CPU_FEATURES_BANKS. + * @pt_cpu_fbanks: Disabled processor feature banks array. + * @pt_disabled_xsave: Disabled xsave feature bits. + * + * pt_cpu_fbanks and pt_disabled_xsave are passed through as-is to the create + * partition hypercall. + * + * Returns : same as above original mshv_create_partition + */ +struct mshv_create_partition_v2 { + __u64 pt_flags; + __u64 pt_isolation; + __u16 pt_num_cpu_fbanks; + __u8 pt_rsvd[6]; /* MBZ */ + __u64 pt_cpu_fbanks[MSHV_NUM_CPU_FEATURES_BANKS]; + __u64 pt_rsvd1[2]; /* MBZ */ +#if defined(__x86_64__) + __u64 pt_disabled_xsave; +#else + __u64 pt_rsvd2; /* MBZ */ +#endif +} __packed; + /* /dev/mshv */ #define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition) -- cgit v1.2.3 From 5d74781ebc86c5fa9e9d6934024c505412de9b52 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 20 Nov 2025 11:28:29 +0200 Subject: vfio/pci: Add dma-buf export support for MMIO regions Add support for exporting PCI device MMIO regions through dma-buf, enabling safe sharing of non-struct page memory with controlled lifetime management. This allows RDMA and other subsystems to import dma-buf FDs and build them into memory regions for PCI P2P operations. The implementation provides a revocable attachment mechanism using dma-buf move operations. MMIO regions are normally pinned as BARs don't change physical addresses, but access is revoked when the VFIO device is closed or a PCI reset is issued. This ensures kernel self-defense against potentially hostile userspace. Currently VFIO can take MMIO regions from the device's BAR and map them into a PFNMAP VMA with special PTEs. This mapping type ensures the memory cannot be used with things like pin_user_pages(), hmm, and so on. In practice only the user process CPU and KVM can safely make use of these VMA. When VFIO shuts down these VMAs are cleaned by unmap_mapping_range() to prevent any UAF of the MMIO beyond driver unbind. However, VFIO type 1 has an insecure behavior where it uses follow_pfnmap_*() to fish a MMIO PFN out of a VMA and program it back into the IOMMU. This has a long history of enabling P2P DMA inside VMs, but has serious lifetime problems by allowing a UAF of the MMIO after the VFIO driver has been unbound. Introduce DMABUF as a new safe way to export a FD based handle for the MMIO regions. This can be consumed by existing DMABUF importers like RDMA or DRM without opening an UAF. A following series will add an importer to iommufd to obsolete the type 1 code and allow safe UAF-free MMIO P2P in VM cases. DMABUF has a built in synchronous invalidation mechanism called move_notify. VFIO keeps track of all drivers importing its MMIO and can invoke a synchronous invalidation callback to tell the importing drivers to DMA unmap and forget about the MMIO pfns. This process is being called revoke. This synchronous invalidation fully prevents any lifecycle problems. VFIO will do this before unbinding its driver ensuring there is no UAF of the MMIO beyond the driver lifecycle. Further, VFIO has additional behavior to block access to the MMIO during things like Function Level Reset. This is because some poor platforms may experience a MCE type crash when touching MMIO of a PCI device that is undergoing a reset. Today this is done by using unmap_mapping_range() on the VMAs. Extend that into the DMABUF world and temporarily revoke the MMIO from the DMABUF importers during FLR as well. This will more robustly prevent an errant P2P from possibly upsetting the platform. A DMABUF FD is a preferred handle for MMIO compared to using something like a pgmap because: - VFIO is supported, including its P2P feature, on archs that don't support pgmap - PCI devices have all sorts of BAR sizes, including ones smaller than a section so a pgmap cannot always be created - It is undesirable to waste a lot of memory for struct pages, especially for a case like a GPU with ~100GB of BAR size - We want a synchronous revoke semantic to support FLR with light hardware requirements Use the P2P subsystem to help generate the DMA mapping. This is a significant upgrade over the abuse of dma_map_resource() that has historically been used by DMABUF exporters. Experience with an OOT version of this patch shows that real systems do need this. This approach deals with all the P2P scenarios: - Non-zero PCI bus_offset - ACS flags routing traffic to the IOMMU - ACS flags that bypass the IOMMU - though vfio noiommu is required to hit this. There will be further work to formalize the revoke semantic in DMABUF. For now this acts like a move_notify dynamic exporter where importer fault handling will get a failure when they attempt to map. This means that only fully restartable fault capable importers can import the VFIO DMABUFs. A future revoke semantic should open this up to more HW as the HW only needs to invalidate, not handle restartable faults. Signed-off-by: Jason Gunthorpe Signed-off-by: Vivek Kasireddy Reviewed-by: Kevin Tian Signed-off-by: Leon Romanovsky Acked-by: Ankit Agrawal Link: https://lore.kernel.org/r/20251120-dmabuf-vfio-v9-10-d7f71607f371@nvidia.com Signed-off-by: Alex Williamson --- drivers/vfio/pci/Kconfig | 3 + drivers/vfio/pci/Makefile | 1 + drivers/vfio/pci/vfio_pci.c | 5 + drivers/vfio/pci/vfio_pci_config.c | 22 ++- drivers/vfio/pci/vfio_pci_core.c | 18 ++- drivers/vfio/pci/vfio_pci_dmabuf.c | 316 +++++++++++++++++++++++++++++++++++++ drivers/vfio/pci/vfio_pci_priv.h | 23 +++ include/linux/vfio_pci_core.h | 42 +++++ include/uapi/linux/vfio.h | 28 ++++ 9 files changed, 453 insertions(+), 5 deletions(-) create mode 100644 drivers/vfio/pci/vfio_pci_dmabuf.c (limited to 'include/uapi/linux') diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index 2b0172f54665..2b9fca00e9e8 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -55,6 +55,9 @@ config VFIO_PCI_ZDEV_KVM To enable s390x KVM vfio-pci extensions, say Y. +config VFIO_PCI_DMABUF + def_bool y if VFIO_PCI_CORE && PCI_P2PDMA && DMA_SHARED_BUFFER + source "drivers/vfio/pci/mlx5/Kconfig" source "drivers/vfio/pci/hisilicon/Kconfig" diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile index cf00c0a7e55c..53f59226ae01 100644 --- a/drivers/vfio/pci/Makefile +++ b/drivers/vfio/pci/Makefile @@ -2,6 +2,7 @@ vfio-pci-core-y := vfio_pci_core.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o vfio-pci-core-$(CONFIG_VFIO_PCI_ZDEV_KVM) += vfio_pci_zdev.o +vfio-pci-core-$(CONFIG_VFIO_PCI_DMABUF) += vfio_pci_dmabuf.o obj-$(CONFIG_VFIO_PCI_CORE) += vfio-pci-core.o vfio-pci-y := vfio_pci.o diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index ac10f14417f2..6d41cf26b539 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -147,6 +147,10 @@ static const struct vfio_device_ops vfio_pci_ops = { .pasid_detach_ioas = vfio_iommufd_physical_pasid_detach_ioas, }; +static const struct vfio_pci_device_ops vfio_pci_dev_ops = { + .get_dmabuf_phys = vfio_pci_core_get_dmabuf_phys, +}; + static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct vfio_pci_core_device *vdev; @@ -161,6 +165,7 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return PTR_ERR(vdev); dev_set_drvdata(&pdev->dev, vdev); + vdev->pci_ops = &vfio_pci_dev_ops; ret = vfio_pci_core_register_device(vdev); if (ret) goto out_put_vdev; diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 8f02f236b5b4..1f6008eabf23 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -589,10 +589,12 @@ static int vfio_basic_config_write(struct vfio_pci_core_device *vdev, int pos, virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY); new_mem = !!(new_cmd & PCI_COMMAND_MEMORY); - if (!new_mem) + if (!new_mem) { vfio_pci_zap_and_down_write_memory_lock(vdev); - else + vfio_pci_dma_buf_move(vdev, true); + } else { down_write(&vdev->memory_lock); + } /* * If the user is writing mem/io enable (new_mem/io) and we @@ -627,6 +629,8 @@ static int vfio_basic_config_write(struct vfio_pci_core_device *vdev, int pos, *virt_cmd &= cpu_to_le16(~mask); *virt_cmd |= cpu_to_le16(new_cmd & mask); + if (__vfio_pci_memory_enabled(vdev)) + vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); } @@ -707,12 +711,16 @@ static int __init init_pci_cap_basic_perm(struct perm_bits *perm) static void vfio_lock_and_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t state) { - if (state >= PCI_D3hot) + if (state >= PCI_D3hot) { vfio_pci_zap_and_down_write_memory_lock(vdev); - else + vfio_pci_dma_buf_move(vdev, true); + } else { down_write(&vdev->memory_lock); + } vfio_pci_set_power_state(vdev, state); + if (__vfio_pci_memory_enabled(vdev)) + vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); } @@ -900,7 +908,10 @@ static int vfio_exp_config_write(struct vfio_pci_core_device *vdev, int pos, if (!ret && (cap & PCI_EXP_DEVCAP_FLR)) { vfio_pci_zap_and_down_write_memory_lock(vdev); + vfio_pci_dma_buf_move(vdev, true); pci_try_reset_function(vdev->pdev); + if (__vfio_pci_memory_enabled(vdev)) + vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); } } @@ -982,7 +993,10 @@ static int vfio_af_config_write(struct vfio_pci_core_device *vdev, int pos, if (!ret && (cap & PCI_AF_CAP_FLR) && (cap & PCI_AF_CAP_TP)) { vfio_pci_zap_and_down_write_memory_lock(vdev); + vfio_pci_dma_buf_move(vdev, true); pci_try_reset_function(vdev->pdev); + if (__vfio_pci_memory_enabled(vdev)) + vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); } } diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 142b84b3f225..9449cf44c18a 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -287,6 +287,8 @@ static int vfio_pci_runtime_pm_entry(struct vfio_pci_core_device *vdev, * semaphore. */ vfio_pci_zap_and_down_write_memory_lock(vdev); + vfio_pci_dma_buf_move(vdev, true); + if (vdev->pm_runtime_engaged) { up_write(&vdev->memory_lock); return -EINVAL; @@ -370,6 +372,8 @@ static void vfio_pci_runtime_pm_exit(struct vfio_pci_core_device *vdev) */ down_write(&vdev->memory_lock); __vfio_pci_runtime_pm_exit(vdev); + if (__vfio_pci_memory_enabled(vdev)) + vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); } @@ -690,6 +694,8 @@ void vfio_pci_core_close_device(struct vfio_device *core_vdev) #endif vfio_pci_core_disable(vdev); + vfio_pci_dma_buf_cleanup(vdev); + mutex_lock(&vdev->igate); if (vdev->err_trigger) { eventfd_ctx_put(vdev->err_trigger); @@ -1222,7 +1228,10 @@ static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev, */ vfio_pci_set_power_state(vdev, PCI_D0); + vfio_pci_dma_buf_move(vdev, true); ret = pci_try_reset_function(vdev->pdev); + if (__vfio_pci_memory_enabled(vdev)) + vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); return ret; @@ -1511,6 +1520,8 @@ int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags, return vfio_pci_core_pm_exit(vdev, flags, arg, argsz); case VFIO_DEVICE_FEATURE_PCI_VF_TOKEN: return vfio_pci_core_feature_token(vdev, flags, arg, argsz); + case VFIO_DEVICE_FEATURE_DMA_BUF: + return vfio_pci_core_feature_dma_buf(vdev, flags, arg, argsz); default: return -ENOTTY; } @@ -2095,6 +2106,7 @@ int vfio_pci_core_init_dev(struct vfio_device *core_vdev) ret = pcim_p2pdma_init(vdev->pdev); if (ret && ret != -EOPNOTSUPP) return ret; + INIT_LIST_HEAD(&vdev->dmabufs); init_rwsem(&vdev->memory_lock); xa_init(&vdev->ctx); @@ -2459,6 +2471,7 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set, break; } + vfio_pci_dma_buf_move(vdev, true); vfio_pci_zap_bars(vdev); } @@ -2487,8 +2500,11 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set, err_undo: list_for_each_entry_from_reverse(vdev, &dev_set->device_list, - vdev.dev_set_list) + vdev.dev_set_list) { + if (vdev->vdev.open_count && __vfio_pci_memory_enabled(vdev)) + vfio_pci_dma_buf_move(vdev, false); up_write(&vdev->memory_lock); + } list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list) pm_runtime_put(&vdev->pdev->dev); diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c new file mode 100644 index 000000000000..6698f540bdac --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_dmabuf.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. + */ +#include +#include +#include + +#include "vfio_pci_priv.h" + +MODULE_IMPORT_NS("DMA_BUF"); + +struct vfio_pci_dma_buf { + struct dma_buf *dmabuf; + struct vfio_pci_core_device *vdev; + struct list_head dmabufs_elm; + size_t size; + struct dma_buf_phys_vec *phys_vec; + struct p2pdma_provider *provider; + u32 nr_ranges; + u8 revoked : 1; +}; + +static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct vfio_pci_dma_buf *priv = dmabuf->priv; + + if (!attachment->peer2peer) + return -EOPNOTSUPP; + + if (priv->revoked) + return -ENODEV; + + return 0; +} + +static struct sg_table * +vfio_pci_dma_buf_map(struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct vfio_pci_dma_buf *priv = attachment->dmabuf->priv; + + dma_resv_assert_held(priv->dmabuf->resv); + + if (priv->revoked) + return ERR_PTR(-ENODEV); + + return dma_buf_phys_vec_to_sgt(attachment, priv->provider, + priv->phys_vec, priv->nr_ranges, + priv->size, dir); +} + +static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + dma_buf_free_sgt(attachment, sgt, dir); +} + +static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf) +{ + struct vfio_pci_dma_buf *priv = dmabuf->priv; + + /* + * Either this or vfio_pci_dma_buf_cleanup() will remove from the list. + * The refcount prevents both. + */ + if (priv->vdev) { + down_write(&priv->vdev->memory_lock); + list_del_init(&priv->dmabufs_elm); + up_write(&priv->vdev->memory_lock); + vfio_device_put_registration(&priv->vdev->vdev); + } + kfree(priv->phys_vec); + kfree(priv); +} + +static const struct dma_buf_ops vfio_pci_dmabuf_ops = { + .attach = vfio_pci_dma_buf_attach, + .map_dma_buf = vfio_pci_dma_buf_map, + .unmap_dma_buf = vfio_pci_dma_buf_unmap, + .release = vfio_pci_dma_buf_release, +}; + +int vfio_pci_core_fill_phys_vec(struct dma_buf_phys_vec *phys_vec, + struct vfio_region_dma_range *dma_ranges, + size_t nr_ranges, phys_addr_t start, + phys_addr_t len) +{ + phys_addr_t max_addr; + unsigned int i; + + max_addr = start + len; + for (i = 0; i < nr_ranges; i++) { + phys_addr_t end; + + if (!dma_ranges[i].length) + return -EINVAL; + + if (check_add_overflow(start, dma_ranges[i].offset, + &phys_vec[i].paddr) || + check_add_overflow(phys_vec[i].paddr, + dma_ranges[i].length, &end)) + return -EOVERFLOW; + if (end > max_addr) + return -EINVAL; + + phys_vec[i].len = dma_ranges[i].length; + } + return 0; +} +EXPORT_SYMBOL_GPL(vfio_pci_core_fill_phys_vec); + +int vfio_pci_core_get_dmabuf_phys(struct vfio_pci_core_device *vdev, + struct p2pdma_provider **provider, + unsigned int region_index, + struct dma_buf_phys_vec *phys_vec, + struct vfio_region_dma_range *dma_ranges, + size_t nr_ranges) +{ + struct pci_dev *pdev = vdev->pdev; + + *provider = pcim_p2pdma_provider(pdev, region_index); + if (!*provider) + return -EINVAL; + + return vfio_pci_core_fill_phys_vec( + phys_vec, dma_ranges, nr_ranges, + pci_resource_start(pdev, region_index), + pci_resource_len(pdev, region_index)); +} +EXPORT_SYMBOL_GPL(vfio_pci_core_get_dmabuf_phys); + +static int validate_dmabuf_input(struct vfio_device_feature_dma_buf *dma_buf, + struct vfio_region_dma_range *dma_ranges, + size_t *lengthp) +{ + size_t length = 0; + u32 i; + + for (i = 0; i < dma_buf->nr_ranges; i++) { + u64 offset = dma_ranges[i].offset; + u64 len = dma_ranges[i].length; + + if (!len || !PAGE_ALIGNED(offset) || !PAGE_ALIGNED(len)) + return -EINVAL; + + if (check_add_overflow(length, len, &length)) + return -EINVAL; + } + + /* + * dma_iova_try_alloc() will WARN on if userspace proposes a size that + * is too big, eg with lots of ranges. + */ + if ((u64)(length) & DMA_IOVA_USE_SWIOTLB) + return -EINVAL; + + *lengthp = length; + return 0; +} + +int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, + struct vfio_device_feature_dma_buf __user *arg, + size_t argsz) +{ + struct vfio_device_feature_dma_buf get_dma_buf = {}; + struct vfio_region_dma_range *dma_ranges; + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct vfio_pci_dma_buf *priv; + size_t length; + int ret; + + if (!vdev->pci_ops || !vdev->pci_ops->get_dmabuf_phys) + return -EOPNOTSUPP; + + ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET, + sizeof(get_dma_buf)); + if (ret != 1) + return ret; + + if (copy_from_user(&get_dma_buf, arg, sizeof(get_dma_buf))) + return -EFAULT; + + if (!get_dma_buf.nr_ranges || get_dma_buf.flags) + return -EINVAL; + + /* + * For PCI the region_index is the BAR number like everything else. + */ + if (get_dma_buf.region_index >= VFIO_PCI_ROM_REGION_INDEX) + return -ENODEV; + + dma_ranges = memdup_array_user(&arg->dma_ranges, get_dma_buf.nr_ranges, + sizeof(*dma_ranges)); + if (IS_ERR(dma_ranges)) + return PTR_ERR(dma_ranges); + + ret = validate_dmabuf_input(&get_dma_buf, dma_ranges, &length); + if (ret) + goto err_free_ranges; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err_free_ranges; + } + priv->phys_vec = kcalloc(get_dma_buf.nr_ranges, sizeof(*priv->phys_vec), + GFP_KERNEL); + if (!priv->phys_vec) { + ret = -ENOMEM; + goto err_free_priv; + } + + priv->vdev = vdev; + priv->nr_ranges = get_dma_buf.nr_ranges; + priv->size = length; + ret = vdev->pci_ops->get_dmabuf_phys(vdev, &priv->provider, + get_dma_buf.region_index, + priv->phys_vec, dma_ranges, + priv->nr_ranges); + if (ret) + goto err_free_phys; + + kfree(dma_ranges); + dma_ranges = NULL; + + if (!vfio_device_try_get_registration(&vdev->vdev)) { + ret = -ENODEV; + goto err_free_phys; + } + + exp_info.ops = &vfio_pci_dmabuf_ops; + exp_info.size = priv->size; + exp_info.flags = get_dma_buf.open_flags; + exp_info.priv = priv; + + priv->dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(priv->dmabuf)) { + ret = PTR_ERR(priv->dmabuf); + goto err_dev_put; + } + + /* dma_buf_put() now frees priv */ + INIT_LIST_HEAD(&priv->dmabufs_elm); + down_write(&vdev->memory_lock); + dma_resv_lock(priv->dmabuf->resv, NULL); + priv->revoked = !__vfio_pci_memory_enabled(vdev); + list_add_tail(&priv->dmabufs_elm, &vdev->dmabufs); + dma_resv_unlock(priv->dmabuf->resv); + up_write(&vdev->memory_lock); + + /* + * dma_buf_fd() consumes the reference, when the file closes the dmabuf + * will be released. + */ + ret = dma_buf_fd(priv->dmabuf, get_dma_buf.open_flags); + if (ret < 0) + goto err_dma_buf; + return ret; + +err_dma_buf: + dma_buf_put(priv->dmabuf); +err_dev_put: + vfio_device_put_registration(&vdev->vdev); +err_free_phys: + kfree(priv->phys_vec); +err_free_priv: + kfree(priv); +err_free_ranges: + kfree(dma_ranges); + return ret; +} + +void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked) +{ + struct vfio_pci_dma_buf *priv; + struct vfio_pci_dma_buf *tmp; + + lockdep_assert_held_write(&vdev->memory_lock); + + list_for_each_entry_safe(priv, tmp, &vdev->dmabufs, dmabufs_elm) { + if (!get_file_active(&priv->dmabuf->file)) + continue; + + if (priv->revoked != revoked) { + dma_resv_lock(priv->dmabuf->resv, NULL); + priv->revoked = revoked; + dma_buf_move_notify(priv->dmabuf); + dma_resv_unlock(priv->dmabuf->resv); + } + fput(priv->dmabuf->file); + } +} + +void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev) +{ + struct vfio_pci_dma_buf *priv; + struct vfio_pci_dma_buf *tmp; + + down_write(&vdev->memory_lock); + list_for_each_entry_safe(priv, tmp, &vdev->dmabufs, dmabufs_elm) { + if (!get_file_active(&priv->dmabuf->file)) + continue; + + dma_resv_lock(priv->dmabuf->resv, NULL); + list_del_init(&priv->dmabufs_elm); + priv->vdev = NULL; + priv->revoked = true; + dma_buf_move_notify(priv->dmabuf); + dma_resv_unlock(priv->dmabuf->resv); + vfio_device_put_registration(&vdev->vdev); + fput(priv->dmabuf->file); + } + up_write(&vdev->memory_lock); +} diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h index a9972eacb293..28a405f8b97c 100644 --- a/drivers/vfio/pci/vfio_pci_priv.h +++ b/drivers/vfio/pci/vfio_pci_priv.h @@ -107,4 +107,27 @@ static inline bool vfio_pci_is_vga(struct pci_dev *pdev) return (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA; } +#ifdef CONFIG_VFIO_PCI_DMABUF +int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, + struct vfio_device_feature_dma_buf __user *arg, + size_t argsz); +void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev); +void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked); +#else +static inline int +vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, + struct vfio_device_feature_dma_buf __user *arg, + size_t argsz) +{ + return -ENOTTY; +} +static inline void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev) +{ +} +static inline void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, + bool revoked) +{ +} +#endif + #endif diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index f541044e42a2..c9466ba323fa 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -26,6 +26,8 @@ struct vfio_pci_core_device; struct vfio_pci_region; +struct p2pdma_provider; +struct dma_buf_phys_vec; struct vfio_pci_regops { ssize_t (*rw)(struct vfio_pci_core_device *vdev, char __user *buf, @@ -49,9 +51,48 @@ struct vfio_pci_region { u32 flags; }; +struct vfio_pci_device_ops { + int (*get_dmabuf_phys)(struct vfio_pci_core_device *vdev, + struct p2pdma_provider **provider, + unsigned int region_index, + struct dma_buf_phys_vec *phys_vec, + struct vfio_region_dma_range *dma_ranges, + size_t nr_ranges); +}; + +#if IS_ENABLED(CONFIG_VFIO_PCI_DMABUF) +int vfio_pci_core_fill_phys_vec(struct dma_buf_phys_vec *phys_vec, + struct vfio_region_dma_range *dma_ranges, + size_t nr_ranges, phys_addr_t start, + phys_addr_t len); +int vfio_pci_core_get_dmabuf_phys(struct vfio_pci_core_device *vdev, + struct p2pdma_provider **provider, + unsigned int region_index, + struct dma_buf_phys_vec *phys_vec, + struct vfio_region_dma_range *dma_ranges, + size_t nr_ranges); +#else +static inline int +vfio_pci_core_fill_phys_vec(struct dma_buf_phys_vec *phys_vec, + struct vfio_region_dma_range *dma_ranges, + size_t nr_ranges, phys_addr_t start, + phys_addr_t len) +{ + return -EINVAL; +} +static inline int vfio_pci_core_get_dmabuf_phys( + struct vfio_pci_core_device *vdev, struct p2pdma_provider **provider, + unsigned int region_index, struct dma_buf_phys_vec *phys_vec, + struct vfio_region_dma_range *dma_ranges, size_t nr_ranges) +{ + return -EOPNOTSUPP; +} +#endif + struct vfio_pci_core_device { struct vfio_device vdev; struct pci_dev *pdev; + const struct vfio_pci_device_ops *pci_ops; void __iomem *barmap[PCI_STD_NUM_BARS]; bool bar_mmap_supported[PCI_STD_NUM_BARS]; u8 *pci_config_map; @@ -94,6 +135,7 @@ struct vfio_pci_core_device { struct vfio_pci_core_device *sriov_pf_core_dev; struct notifier_block nb; struct rw_semaphore memory_lock; + struct list_head dmabufs; }; /* Will be exported for vfio pci drivers usage */ diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 75100bf009ba..ac2329f24141 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -14,6 +14,7 @@ #include #include +#include #define VFIO_API_VERSION 0 @@ -1478,6 +1479,33 @@ struct vfio_device_feature_bus_master { }; #define VFIO_DEVICE_FEATURE_BUS_MASTER 10 +/** + * Upon VFIO_DEVICE_FEATURE_GET create a dma_buf fd for the + * regions selected. + * + * open_flags are the typical flags passed to open(2), eg O_RDWR, O_CLOEXEC, + * etc. offset/length specify a slice of the region to create the dmabuf from. + * nr_ranges is the total number of (P2P DMA) ranges that comprise the dmabuf. + * + * flags should be 0. + * + * Return: The fd number on success, -1 and errno is set on failure. + */ +#define VFIO_DEVICE_FEATURE_DMA_BUF 11 + +struct vfio_region_dma_range { + __u64 offset; + __u64 length; +}; + +struct vfio_device_feature_dma_buf { + __u32 region_index; + __u32 open_flags; + __u32 flags; + __u32 nr_ranges; + struct vfio_region_dma_range dma_ranges[] __counted_by(nr_ranges); +}; + /* -------- API for Type1 VFIO IOMMU -------- */ /** -- cgit v1.2.3 From 8e8678e740ecde2ae4a0404fd9b4ed2b726e236d Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Tue, 8 Jul 2025 12:57:57 +0000 Subject: KVM: s390: Add capability that forwards operation exceptions Setting KVM_CAP_S390_USER_OPEREXEC will forward all operation exceptions to user space. This also includes the 0x0000 instructions managed by KVM_CAP_S390_USER_INSTR0. It's helpful if user space wants to emulate instructions which do not (yet) have an opcode. While we're at it refine the documentation for KVM_CAP_S390_USER_INSTR0. Signed-off-by: Janosch Frank Reviewed-by: Claudio Imbrenda Acked-by: Christian Borntraeger Signed-off-by: Janosch Frank --- Documentation/virt/kvm/api.rst | 17 ++- arch/s390/include/asm/kvm_host.h | 1 + arch/s390/kvm/intercept.c | 3 + arch/s390/kvm/kvm-s390.c | 7 ++ include/uapi/linux/kvm.h | 1 + tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/s390/user_operexec.c | 140 +++++++++++++++++++++++ 7 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/kvm/s390/user_operexec.c (limited to 'include/uapi/linux') diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 72b2fae99a83..1bc2a84c59ee 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -7820,7 +7820,7 @@ where 0xff represents CPUs 0-7 in cluster 0. :Architectures: s390 :Parameters: none -With this capability enabled, all illegal instructions 0x0000 (2 bytes) will +With this capability enabled, the illegal instruction 0x0000 (2 bytes) will be intercepted and forwarded to user space. User space can use this mechanism e.g. to realize 2-byte software breakpoints. The kernel will not inject an operating exception for these instructions, user space has @@ -8703,6 +8703,21 @@ This capability indicate to the userspace whether a PFNMAP memory region can be safely mapped as cacheable. This relies on the presence of force write back (FWB) feature support on the hardware. +7.45 KVM_CAP_S390_USER_OPEREXEC +------------------------------- + +:Architectures: s390 +:Parameters: none + +When this capability is enabled KVM forwards all operation exceptions +that it doesn't handle itself to user space. This also includes the +0x0000 instructions managed by KVM_CAP_S390_USER_INSTR0. This is +helpful if user space wants to emulate instructions which are not +(yet) implemented in hardware. + +This capability can be enabled dynamically even if VCPUs were already +created and are running. + 8. Other capabilities. ====================== diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 22cedcaea475..1e4829c70216 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -648,6 +648,7 @@ struct kvm_arch { int user_sigp; int user_stsi; int user_instr0; + int user_operexec; struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; wait_queue_head_t ipte_wq; int ipte_lock_count; diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index c7908950c1f4..420ae62977e2 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -471,6 +471,9 @@ static int handle_operexc(struct kvm_vcpu *vcpu) if (vcpu->arch.sie_block->ipa == 0xb256) return handle_sthyi(vcpu); + if (vcpu->kvm->arch.user_operexec) + return -EOPNOTSUPP; + if (vcpu->arch.sie_block->ipa == 0 && vcpu->kvm->arch.user_instr0) return -EOPNOTSUPP; rc = read_guest_lc(vcpu, __LC_PGM_NEW_PSW, &newpsw, sizeof(psw_t)); diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 70ebc54b1bb1..56d4730b7c41 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -606,6 +606,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_SET_GUEST_DEBUG: case KVM_CAP_S390_DIAG318: case KVM_CAP_IRQFD_RESAMPLE: + case KVM_CAP_S390_USER_OPEREXEC: r = 1; break; case KVM_CAP_SET_GUEST_DEBUG2: @@ -921,6 +922,12 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) VM_EVENT(kvm, 3, "ENABLE: CAP_S390_CPU_TOPOLOGY %s", r ? "(not available)" : "(success)"); break; + case KVM_CAP_S390_USER_OPEREXEC: + VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_OPEREXEC"); + kvm->arch.user_operexec = 1; + icpt_operexc_on_all_vcpus(kvm); + r = 0; + break; default: r = -EINVAL; break; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 52f6000ab020..8ab07396ce3b 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -963,6 +963,7 @@ struct kvm_enable_cap { #define KVM_CAP_RISCV_MP_STATE_RESET 242 #define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 #define KVM_CAP_GUEST_MEMFD_FLAGS 244 +#define KVM_CAP_S390_USER_OPEREXEC 245 struct kvm_irq_routing_irqchip { __u32 irqchip; diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 148d427ff24b..87e429206bb8 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -194,6 +194,7 @@ TEST_GEN_PROGS_s390 += s390/debug_test TEST_GEN_PROGS_s390 += s390/cpumodel_subfuncs_test TEST_GEN_PROGS_s390 += s390/shared_zeropage_test TEST_GEN_PROGS_s390 += s390/ucontrol_test +TEST_GEN_PROGS_s390 += s390/user_operexec TEST_GEN_PROGS_s390 += rseq_test TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON) diff --git a/tools/testing/selftests/kvm/s390/user_operexec.c b/tools/testing/selftests/kvm/s390/user_operexec.c new file mode 100644 index 000000000000..714906c1d12a --- /dev/null +++ b/tools/testing/selftests/kvm/s390/user_operexec.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Test operation exception forwarding. + * + * Copyright IBM Corp. 2025 + * + * Authors: + * Janosch Frank + */ +#include "kselftest.h" +#include "kvm_util.h" +#include "test_util.h" +#include "sie.h" + +#include + +static void guest_code_instr0(void) +{ + asm(".word 0x0000"); +} + +static void test_user_instr0(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int rc; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); + TEST_ASSERT_EQ(0, rc); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); + + kvm_vm_free(vm); +} + +static void guest_code_user_operexec(void) +{ + asm(".word 0x0807"); +} + +static void test_user_operexec(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int rc; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); + TEST_ASSERT_EQ(0, rc); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); + + kvm_vm_free(vm); + + /* + * Since user_operexec is the superset it can be used for the + * 0 instruction. + */ + vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); + TEST_ASSERT_EQ(0, rc); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); + + kvm_vm_free(vm); +} + +/* combine user_instr0 and user_operexec */ +static void test_user_operexec_combined(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int rc; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); + TEST_ASSERT_EQ(0, rc); + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); + TEST_ASSERT_EQ(0, rc); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); + + kvm_vm_free(vm); + + /* Reverse enablement order */ + vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); + TEST_ASSERT_EQ(0, rc); + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); + TEST_ASSERT_EQ(0, rc); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); + + kvm_vm_free(vm); +} + +/* + * Run all tests above. + * + * Enablement after VCPU has been added is automatically tested since + * we enable the capability after VCPU creation. + */ +static struct testdef { + const char *name; + void (*test)(void); +} testlist[] = { + { "instr0", test_user_instr0 }, + { "operexec", test_user_operexec }, + { "operexec_combined", test_user_operexec_combined}, +}; + +int main(int argc, char *argv[]) +{ + int idx; + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_USER_INSTR0)); + + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(testlist)); + for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { + testlist[idx].test(); + ksft_test_result_pass("%s\n", testlist[idx].name); + } + ksft_finished(); +} -- cgit v1.2.3 From a67df6d1b939ca98e1ad403f53e3ee57299b8c44 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 11 Nov 2025 14:46:10 +0100 Subject: uapi: cdc.h: cleanly provide for more interfaces and countries The spec requires at least one interface respectively country. It allows multiple ones. This needs to be clearly said in the UAPI. This is subject to sanity checking in cdc_parse_cdc_header(), thus we can trust the length. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20251111134641.4118827-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- include/uapi/linux/usb/cdc.h | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 73f9476774ae..54be4aa1dcb2 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1475,7 +1475,7 @@ made_compressed_probe: if (!acm->country_codes) goto skip_countries; acm->country_code_size = cfd->bLength - 4; - memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, + memcpy(acm->country_codes, cfd->wCountryCodes, cfd->bLength - 4); acm->country_rel_date = cfd->iCountryCodeRelDate; diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h index 1924cf665448..7bd5d12d8b26 100644 --- a/include/uapi/linux/usb/cdc.h +++ b/include/uapi/linux/usb/cdc.h @@ -104,8 +104,10 @@ struct usb_cdc_union_desc { __u8 bDescriptorSubType; __u8 bMasterInterface0; - __u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ + union { + __u8 bSlaveInterface0; + __DECLARE_FLEX_ARRAY(__u8, bSlaveInterfaces); + }; } __attribute__ ((packed)); /* "Country Selection Functional Descriptor" from CDC spec 5.2.3.9 */ @@ -115,8 +117,10 @@ struct usb_cdc_country_functional_desc { __u8 bDescriptorSubType; __u8 iCountryCodeRelDate; - __le16 wCountyCode0; - /* ... and there can be a lot of country codes */ + union { + __le16 wCountryCode0; + __DECLARE_FLEX_ARRAY(__le16, wCountryCodes); + }; } __attribute__ ((packed)); /* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */ -- cgit v1.2.3 From e6ab504633e4c06e35377ecf3c8cbc304de79858 Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Mon, 17 Nov 2025 15:40:21 +0100 Subject: staging: gpib: Destage gpib Move the gpib drivers out of staging and into the "real" part of the kernel. This entails: - Remove the gpib Kconfig menu and Makefile build rule from staging. - Remove gpib/uapi from the header file search path in subdir-ccflags of the gpib Makefile - move the gpib/uapi files to include/uapi/linux - Move the gpib tree out of staging to drivers. - Remove the word "Linux" from the gpib Kconfig file. - Add the gpib Kconfig menu and Makefile build rule to drivers Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20251117144021.23569-5-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 4 +- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/gpib/Kconfig | 255 ++ drivers/gpib/Makefile | 20 + drivers/gpib/TODO | 10 + drivers/gpib/agilent_82350b/Makefile | 2 + drivers/gpib/agilent_82350b/agilent_82350b.c | 896 +++++++ drivers/gpib/agilent_82350b/agilent_82350b.h | 157 ++ drivers/gpib/agilent_82357a/Makefile | 4 + drivers/gpib/agilent_82357a/agilent_82357a.c | 1691 ++++++++++++ drivers/gpib/agilent_82357a/agilent_82357a.h | 182 ++ drivers/gpib/cb7210/Makefile | 3 + drivers/gpib/cb7210/cb7210.c | 1598 ++++++++++++ drivers/gpib/cb7210/cb7210.h | 203 ++ drivers/gpib/cec/Makefile | 3 + drivers/gpib/cec/cec.h | 20 + drivers/gpib/cec/cec_gpib.c | 393 +++ drivers/gpib/common/Makefile | 6 + drivers/gpib/common/gpib_os.c | 2271 +++++++++++++++++ drivers/gpib/common/iblib.c | 717 ++++++ drivers/gpib/common/ibsys.h | 34 + drivers/gpib/eastwood/Makefile | 3 + drivers/gpib/eastwood/fluke_gpib.c | 1180 +++++++++ drivers/gpib/eastwood/fluke_gpib.h | 146 ++ drivers/gpib/fmh_gpib/Makefile | 2 + drivers/gpib/fmh_gpib/fmh_gpib.c | 1754 +++++++++++++ drivers/gpib/fmh_gpib/fmh_gpib.h | 177 ++ drivers/gpib/gpio/Makefile | 4 + drivers/gpib/gpio/gpib_bitbang.c | 1469 +++++++++++ drivers/gpib/hp_82335/Makefile | 4 + drivers/gpib/hp_82335/hp82335.c | 371 +++ drivers/gpib/hp_82335/hp82335.h | 52 + drivers/gpib/hp_82341/Makefile | 2 + drivers/gpib/hp_82341/hp_82341.c | 907 +++++++ drivers/gpib/hp_82341/hp_82341.h | 165 ++ drivers/gpib/include/amcc5920.h | 49 + drivers/gpib/include/amccs5933.h | 59 + drivers/gpib/include/gpibP.h | 41 + drivers/gpib/include/gpib_cmd.h | 112 + drivers/gpib/include/gpib_pci_ids.h | 23 + drivers/gpib/include/gpib_proto.h | 49 + drivers/gpib/include/gpib_state_machines.h | 23 + drivers/gpib/include/gpib_types.h | 381 +++ drivers/gpib/include/nec7210.h | 141 ++ drivers/gpib/include/nec7210_registers.h | 218 ++ drivers/gpib/include/plx9050.h | 72 + drivers/gpib/include/quancom_pci.h | 22 + drivers/gpib/include/tms9914.h | 280 ++ drivers/gpib/include/tnt4882_registers.h | 192 ++ drivers/gpib/ines/Makefile | 3 + drivers/gpib/ines/ines.h | 165 ++ drivers/gpib/ines/ines_gpib.c | 1500 +++++++++++ drivers/gpib/lpvo_usb_gpib/Makefile | 3 + drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c | 2025 +++++++++++++++ drivers/gpib/nec7210/Makefile | 4 + drivers/gpib/nec7210/board.h | 19 + drivers/gpib/nec7210/nec7210.c | 1121 ++++++++ drivers/gpib/ni_usb/Makefile | 4 + drivers/gpib/ni_usb/ni_usb_gpib.c | 2678 ++++++++++++++++++++ drivers/gpib/ni_usb/ni_usb_gpib.h | 226 ++ drivers/gpib/pc2/Makefile | 5 + drivers/gpib/pc2/pc2_gpib.c | 684 +++++ drivers/gpib/tms9914/Makefile | 6 + drivers/gpib/tms9914/tms9914.c | 914 +++++++ drivers/gpib/tnt4882/Makefile | 6 + drivers/gpib/tnt4882/mite.c | 133 + drivers/gpib/tnt4882/mite.h | 234 ++ drivers/gpib/tnt4882/tnt4882_gpib.c | 1838 ++++++++++++++ drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/gpib/Kconfig | 255 -- drivers/staging/gpib/Makefile | 20 - drivers/staging/gpib/TODO | 10 - drivers/staging/gpib/agilent_82350b/Makefile | 2 - .../staging/gpib/agilent_82350b/agilent_82350b.c | 896 ------- .../staging/gpib/agilent_82350b/agilent_82350b.h | 157 -- drivers/staging/gpib/agilent_82357a/Makefile | 4 - .../staging/gpib/agilent_82357a/agilent_82357a.c | 1691 ------------ .../staging/gpib/agilent_82357a/agilent_82357a.h | 182 -- drivers/staging/gpib/cb7210/Makefile | 3 - drivers/staging/gpib/cb7210/cb7210.c | 1598 ------------ drivers/staging/gpib/cb7210/cb7210.h | 203 -- drivers/staging/gpib/cec/Makefile | 3 - drivers/staging/gpib/cec/cec.h | 20 - drivers/staging/gpib/cec/cec_gpib.c | 393 --- drivers/staging/gpib/common/Makefile | 6 - drivers/staging/gpib/common/gpib_os.c | 2271 ----------------- drivers/staging/gpib/common/iblib.c | 717 ------ drivers/staging/gpib/common/ibsys.h | 34 - drivers/staging/gpib/eastwood/Makefile | 3 - drivers/staging/gpib/eastwood/fluke_gpib.c | 1180 --------- drivers/staging/gpib/eastwood/fluke_gpib.h | 146 -- drivers/staging/gpib/fmh_gpib/Makefile | 2 - drivers/staging/gpib/fmh_gpib/fmh_gpib.c | 1754 ------------- drivers/staging/gpib/fmh_gpib/fmh_gpib.h | 177 -- drivers/staging/gpib/gpio/Makefile | 4 - drivers/staging/gpib/gpio/gpib_bitbang.c | 1469 ----------- drivers/staging/gpib/hp_82335/Makefile | 4 - drivers/staging/gpib/hp_82335/hp82335.c | 371 --- drivers/staging/gpib/hp_82335/hp82335.h | 52 - drivers/staging/gpib/hp_82341/Makefile | 2 - drivers/staging/gpib/hp_82341/hp_82341.c | 907 ------- drivers/staging/gpib/hp_82341/hp_82341.h | 165 -- drivers/staging/gpib/include/amcc5920.h | 49 - drivers/staging/gpib/include/amccs5933.h | 59 - drivers/staging/gpib/include/gpibP.h | 41 - drivers/staging/gpib/include/gpib_cmd.h | 112 - drivers/staging/gpib/include/gpib_pci_ids.h | 23 - drivers/staging/gpib/include/gpib_proto.h | 49 - drivers/staging/gpib/include/gpib_state_machines.h | 23 - drivers/staging/gpib/include/gpib_types.h | 381 --- drivers/staging/gpib/include/nec7210.h | 141 -- drivers/staging/gpib/include/nec7210_registers.h | 218 -- drivers/staging/gpib/include/plx9050.h | 72 - drivers/staging/gpib/include/quancom_pci.h | 22 - drivers/staging/gpib/include/tms9914.h | 280 -- drivers/staging/gpib/include/tnt4882_registers.h | 192 -- drivers/staging/gpib/ines/Makefile | 3 - drivers/staging/gpib/ines/ines.h | 165 -- drivers/staging/gpib/ines/ines_gpib.c | 1500 ----------- drivers/staging/gpib/lpvo_usb_gpib/Makefile | 3 - drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c | 2025 --------------- drivers/staging/gpib/nec7210/Makefile | 4 - drivers/staging/gpib/nec7210/board.h | 19 - drivers/staging/gpib/nec7210/nec7210.c | 1121 -------- drivers/staging/gpib/ni_usb/Makefile | 4 - drivers/staging/gpib/ni_usb/ni_usb_gpib.c | 2678 -------------------- drivers/staging/gpib/ni_usb/ni_usb_gpib.h | 226 -- drivers/staging/gpib/pc2/Makefile | 5 - drivers/staging/gpib/pc2/pc2_gpib.c | 684 ----- drivers/staging/gpib/tms9914/Makefile | 6 - drivers/staging/gpib/tms9914/tms9914.c | 914 ------- drivers/staging/gpib/tnt4882/Makefile | 6 - drivers/staging/gpib/tnt4882/mite.c | 133 - drivers/staging/gpib/tnt4882/mite.h | 234 -- drivers/staging/gpib/tnt4882/tnt4882_gpib.c | 1838 -------------- drivers/staging/gpib/uapi/gpib.h | 104 - drivers/staging/gpib/uapi/gpib_ioctl.h | 167 -- include/uapi/linux/gpib.h | 104 + include/uapi/linux/gpib_ioctl.h | 167 ++ 141 files changed, 28208 insertions(+), 28206 deletions(-) create mode 100644 drivers/gpib/Kconfig create mode 100644 drivers/gpib/Makefile create mode 100644 drivers/gpib/TODO create mode 100644 drivers/gpib/agilent_82350b/Makefile create mode 100644 drivers/gpib/agilent_82350b/agilent_82350b.c create mode 100644 drivers/gpib/agilent_82350b/agilent_82350b.h create mode 100644 drivers/gpib/agilent_82357a/Makefile create mode 100644 drivers/gpib/agilent_82357a/agilent_82357a.c create mode 100644 drivers/gpib/agilent_82357a/agilent_82357a.h create mode 100644 drivers/gpib/cb7210/Makefile create mode 100644 drivers/gpib/cb7210/cb7210.c create mode 100644 drivers/gpib/cb7210/cb7210.h create mode 100644 drivers/gpib/cec/Makefile create mode 100644 drivers/gpib/cec/cec.h create mode 100644 drivers/gpib/cec/cec_gpib.c create mode 100644 drivers/gpib/common/Makefile create mode 100644 drivers/gpib/common/gpib_os.c create mode 100644 drivers/gpib/common/iblib.c create mode 100644 drivers/gpib/common/ibsys.h create mode 100644 drivers/gpib/eastwood/Makefile create mode 100644 drivers/gpib/eastwood/fluke_gpib.c create mode 100644 drivers/gpib/eastwood/fluke_gpib.h create mode 100644 drivers/gpib/fmh_gpib/Makefile create mode 100644 drivers/gpib/fmh_gpib/fmh_gpib.c create mode 100644 drivers/gpib/fmh_gpib/fmh_gpib.h create mode 100644 drivers/gpib/gpio/Makefile create mode 100644 drivers/gpib/gpio/gpib_bitbang.c create mode 100644 drivers/gpib/hp_82335/Makefile create mode 100644 drivers/gpib/hp_82335/hp82335.c create mode 100644 drivers/gpib/hp_82335/hp82335.h create mode 100644 drivers/gpib/hp_82341/Makefile create mode 100644 drivers/gpib/hp_82341/hp_82341.c create mode 100644 drivers/gpib/hp_82341/hp_82341.h create mode 100644 drivers/gpib/include/amcc5920.h create mode 100644 drivers/gpib/include/amccs5933.h create mode 100644 drivers/gpib/include/gpibP.h create mode 100644 drivers/gpib/include/gpib_cmd.h create mode 100644 drivers/gpib/include/gpib_pci_ids.h create mode 100644 drivers/gpib/include/gpib_proto.h create mode 100644 drivers/gpib/include/gpib_state_machines.h create mode 100644 drivers/gpib/include/gpib_types.h create mode 100644 drivers/gpib/include/nec7210.h create mode 100644 drivers/gpib/include/nec7210_registers.h create mode 100644 drivers/gpib/include/plx9050.h create mode 100644 drivers/gpib/include/quancom_pci.h create mode 100644 drivers/gpib/include/tms9914.h create mode 100644 drivers/gpib/include/tnt4882_registers.h create mode 100644 drivers/gpib/ines/Makefile create mode 100644 drivers/gpib/ines/ines.h create mode 100644 drivers/gpib/ines/ines_gpib.c create mode 100644 drivers/gpib/lpvo_usb_gpib/Makefile create mode 100644 drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c create mode 100644 drivers/gpib/nec7210/Makefile create mode 100644 drivers/gpib/nec7210/board.h create mode 100644 drivers/gpib/nec7210/nec7210.c create mode 100644 drivers/gpib/ni_usb/Makefile create mode 100644 drivers/gpib/ni_usb/ni_usb_gpib.c create mode 100644 drivers/gpib/ni_usb/ni_usb_gpib.h create mode 100644 drivers/gpib/pc2/Makefile create mode 100644 drivers/gpib/pc2/pc2_gpib.c create mode 100644 drivers/gpib/tms9914/Makefile create mode 100644 drivers/gpib/tms9914/tms9914.c create mode 100644 drivers/gpib/tnt4882/Makefile create mode 100644 drivers/gpib/tnt4882/mite.c create mode 100644 drivers/gpib/tnt4882/mite.h create mode 100644 drivers/gpib/tnt4882/tnt4882_gpib.c delete mode 100644 drivers/staging/gpib/Kconfig delete mode 100644 drivers/staging/gpib/Makefile delete mode 100644 drivers/staging/gpib/TODO delete mode 100644 drivers/staging/gpib/agilent_82350b/Makefile delete mode 100644 drivers/staging/gpib/agilent_82350b/agilent_82350b.c delete mode 100644 drivers/staging/gpib/agilent_82350b/agilent_82350b.h delete mode 100644 drivers/staging/gpib/agilent_82357a/Makefile delete mode 100644 drivers/staging/gpib/agilent_82357a/agilent_82357a.c delete mode 100644 drivers/staging/gpib/agilent_82357a/agilent_82357a.h delete mode 100644 drivers/staging/gpib/cb7210/Makefile delete mode 100644 drivers/staging/gpib/cb7210/cb7210.c delete mode 100644 drivers/staging/gpib/cb7210/cb7210.h delete mode 100644 drivers/staging/gpib/cec/Makefile delete mode 100644 drivers/staging/gpib/cec/cec.h delete mode 100644 drivers/staging/gpib/cec/cec_gpib.c delete mode 100644 drivers/staging/gpib/common/Makefile delete mode 100644 drivers/staging/gpib/common/gpib_os.c delete mode 100644 drivers/staging/gpib/common/iblib.c delete mode 100644 drivers/staging/gpib/common/ibsys.h delete mode 100644 drivers/staging/gpib/eastwood/Makefile delete mode 100644 drivers/staging/gpib/eastwood/fluke_gpib.c delete mode 100644 drivers/staging/gpib/eastwood/fluke_gpib.h delete mode 100644 drivers/staging/gpib/fmh_gpib/Makefile delete mode 100644 drivers/staging/gpib/fmh_gpib/fmh_gpib.c delete mode 100644 drivers/staging/gpib/fmh_gpib/fmh_gpib.h delete mode 100644 drivers/staging/gpib/gpio/Makefile delete mode 100644 drivers/staging/gpib/gpio/gpib_bitbang.c delete mode 100644 drivers/staging/gpib/hp_82335/Makefile delete mode 100644 drivers/staging/gpib/hp_82335/hp82335.c delete mode 100644 drivers/staging/gpib/hp_82335/hp82335.h delete mode 100644 drivers/staging/gpib/hp_82341/Makefile delete mode 100644 drivers/staging/gpib/hp_82341/hp_82341.c delete mode 100644 drivers/staging/gpib/hp_82341/hp_82341.h delete mode 100644 drivers/staging/gpib/include/amcc5920.h delete mode 100644 drivers/staging/gpib/include/amccs5933.h delete mode 100644 drivers/staging/gpib/include/gpibP.h delete mode 100644 drivers/staging/gpib/include/gpib_cmd.h delete mode 100644 drivers/staging/gpib/include/gpib_pci_ids.h delete mode 100644 drivers/staging/gpib/include/gpib_proto.h delete mode 100644 drivers/staging/gpib/include/gpib_state_machines.h delete mode 100644 drivers/staging/gpib/include/gpib_types.h delete mode 100644 drivers/staging/gpib/include/nec7210.h delete mode 100644 drivers/staging/gpib/include/nec7210_registers.h delete mode 100644 drivers/staging/gpib/include/plx9050.h delete mode 100644 drivers/staging/gpib/include/quancom_pci.h delete mode 100644 drivers/staging/gpib/include/tms9914.h delete mode 100644 drivers/staging/gpib/include/tnt4882_registers.h delete mode 100644 drivers/staging/gpib/ines/Makefile delete mode 100644 drivers/staging/gpib/ines/ines.h delete mode 100644 drivers/staging/gpib/ines/ines_gpib.c delete mode 100644 drivers/staging/gpib/lpvo_usb_gpib/Makefile delete mode 100644 drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c delete mode 100644 drivers/staging/gpib/nec7210/Makefile delete mode 100644 drivers/staging/gpib/nec7210/board.h delete mode 100644 drivers/staging/gpib/nec7210/nec7210.c delete mode 100644 drivers/staging/gpib/ni_usb/Makefile delete mode 100644 drivers/staging/gpib/ni_usb/ni_usb_gpib.c delete mode 100644 drivers/staging/gpib/ni_usb/ni_usb_gpib.h delete mode 100644 drivers/staging/gpib/pc2/Makefile delete mode 100644 drivers/staging/gpib/pc2/pc2_gpib.c delete mode 100644 drivers/staging/gpib/tms9914/Makefile delete mode 100644 drivers/staging/gpib/tms9914/tms9914.c delete mode 100644 drivers/staging/gpib/tnt4882/Makefile delete mode 100644 drivers/staging/gpib/tnt4882/mite.c delete mode 100644 drivers/staging/gpib/tnt4882/mite.h delete mode 100644 drivers/staging/gpib/tnt4882/tnt4882_gpib.c delete mode 100644 drivers/staging/gpib/uapi/gpib.h delete mode 100644 drivers/staging/gpib/uapi/gpib_ioctl.h create mode 100644 include/uapi/linux/gpib.h create mode 100644 include/uapi/linux/gpib_ioctl.h (limited to 'include/uapi/linux') diff --git a/MAINTAINERS b/MAINTAINERS index df07d1a3c28d..a6055a910be6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10628,7 +10628,9 @@ F: drivers/platform/x86/gpd-pocket-fan.c GPIB DRIVERS M: Dave Penkler S: Maintained -F: drivers/staging/gpib/ +F: drivers/gpib/ +F: include/uapi/linux/gpib.h +F: include/uapi/linux/gpib_ioctl.h GPIO ACPI SUPPORT M: Mika Westerberg diff --git a/drivers/Kconfig b/drivers/Kconfig index 4915a63866b0..01602581b880 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -161,6 +161,8 @@ source "drivers/greybus/Kconfig" source "drivers/comedi/Kconfig" +source "drivers/gpib/Kconfig" + source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 8e1ffa4358d5..d275b1526cdd 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -150,6 +150,7 @@ obj-$(CONFIG_VHOST_IOTLB) += vhost/ obj-$(CONFIG_VHOST) += vhost/ obj-$(CONFIG_GREYBUS) += greybus/ obj-$(CONFIG_COMEDI) += comedi/ +obj-$(CONFIG_GPIB) += gpib/ obj-$(CONFIG_STAGING) += staging/ obj-y += platform/ diff --git a/drivers/gpib/Kconfig b/drivers/gpib/Kconfig new file mode 100644 index 000000000000..eeb50956ce85 --- /dev/null +++ b/drivers/gpib/Kconfig @@ -0,0 +1,255 @@ +# SPDX-License-Identifier: GPL-2.0 +menuconfig GPIB + tristate "GPIB drivers" + help + Enable support for GPIB cards and dongles. GPIB is the + General Purpose Interface Bus which conforms to the IEEE488 + standard. + + This set of drivers can be used with the corresponding user + space library that can be found on Sourceforge under linux-gpib. + Select the drivers for your hardware from the list. + +if GPIB + +config GPIB_COMMON + tristate "GPIB core" + help + + Core common driver for all GPIB drivers. It provides the + interface for the userland library + + To compile this driver as a module, choose M here: the module will be + called gpib_common + +config GPIB_AGILENT_82350B + tristate "Agilent 8235xx PCI(e) adapters" + depends on PCI + select GPIB_COMMON + select GPIB_TMS9914 + help + Enable support for HP/Agilent/Keysight boards + 82350A + 82350B + 82351A + + To compile this driver as a module, choose M here: the module will be + called agilent_82350b. + +config GPIB_AGILENT_82357A + tristate "Agilent 82357a/b USB dongles" + select GPIB_COMMON + depends on USB + help + Enable support for Agilent/Keysight 82357x USB dongles. + + To compile this driver as a module, choose M here: the module will be + called agilent_82357a. + +config GPIB_CEC_PCI + tristate "CEC PCI board" + depends on PCI + depends on HAS_IOPORT + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for Capital Equipment Corporation PCI-488 + and Keithly KPCI-488 boards. + + To compile this driver as a module, choose M here: the module will be + called cec_gpib. + +config GPIB_NI_PCI_ISA + tristate "NI PCI/ISA compatible boards" + depends on ISA_BUS || PCI || PCMCIA + depends on HAS_IOPORT + depends on PCMCIA || !PCMCIA + depends on HAS_IOPORT_MAP + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for National Instruments boards based + on TNT4882 chips: + AT-GPIB (with NAT4882 chip) + AT-GPIB (with NEC7210 chip) + AT-GPIB/TNT + PCI-GPIB + PCIe-GPIB + PCI-GPIB+ + PCM-GPIB + PXI-GPIB + PCMCIA-GPIB + and Capital Equipment Corporation CEC-488 board. + + To compile this driver as a module, choose M here: the module will be + called tnt4882. + +config GPIB_CB7210 + tristate "Measurement Computing compatible boards" + depends on HAS_IOPORT + depends on ISA_BUS || PCI || PCMCIA + depends on PCMCIA || !PCMCIA + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for Measurement Computing (Computer Boards): + CPCI_GPIB, ISA-GPIB, ISA-GPIB/LC, PCI-GPIB/1M, PCI-GPIB/300K and + PCMCIA-GPIB + Quancom PCIGPIB-1 with MC cb7210 chip + + To compile this driver as a module, choose M here: the module will be + +config GPIB_NI_USB + tristate "NI USB dongles" + select GPIB_COMMON + depends on USB + help + Enable support for National Instruments + GPIB-USB-B + GPIB-USB-HS + GPIB-USB-HS+ + Keithly + KUSB-488 + KUSB-488A + Measurement Computing (Computer Boards) + USB-488 + + To compile this driver as a module, choose M here: the module will be + called ni_usb. + +config GPIB_FLUKE + tristate "Fluke" + depends on OF + select GPIB_COMMON + select GPIB_NEC7210 + help + GPIB driver for Fluke based cda devices. + + To compile this driver as a module, choose M here: the module will be + called fluke_gpib + +config GPIB_FMH + tristate "FMH FPGA based devices" + select GPIB_COMMON + select GPIB_NEC7210 + depends on !PPC + depends on OF && PCI + help + GPIB driver for fmhess FPGA based devices + + To compile this driver as a module, choose M here: the module will be + called fmh_gpib + +config GPIB_GPIO + tristate "RPi GPIO bitbang" + depends on ARCH_BCM2835 || COMPILE_TEST + select GPIB_COMMON + help + GPIB bitbang driver Raspberry Pi GPIO adapters + + To compile this driver as a module, choose M here: the module will be + called gpib_bitbang + +config GPIB_HP82335 + tristate "HP82335/HP27209" + depends on ISA_BUS + select GPIB_COMMON + select GPIB_TMS9914 + help + GPIB driver for HP82335 and HP27209 boards + + To compile this driver as a module, choose M here: the module will be + called hp82335 + + +config GPIB_HP82341 + tristate "HP82341x" + select GPIB_COMMON + select GPIB_TMS9914 + depends on ISA_BUS || EISA + help + GPIB driver for HP82341 A/B/C/D boards + + To compile this driver as a module, choose M here: the module will be + called hp82341 + +config GPIB_INES + tristate "INES" + depends on PCI || ISA_BUS || PCMCIA + depends on PCMCIA || !PCMCIA + depends on HAS_IOPORT + select GPIB_COMMON + select GPIB_NEC7210 + help + GPIB driver for Ines compatible boards + Ines + GPIB-HS-NT + GPIB for Compact PCI + GPIB for PCI + GPIB for PCMCIA + GPIB PC/104 + Hameg + HO80-2 + Quancom + PCIGPIB-1 based on Ines iGPIB 72010 chip + + To compile this driver as a module, choose M here: the module will be + called ines_gpib + called cb7210. + +config GPIB_PCMCIA + def_bool y + depends on PCMCIA && (GPIB_NI_PCI_ISA || GPIB_CB7210 || GPIB_INES) + help + Enable PCMCIA/CArdbus support for National Instruments, + measurement computing boards and Ines boards. + +config GPIB_LPVO + tristate "LPVO DIY USB GPIB" + select GPIB_COMMON + depends on USB + help + Enable support for LPVO Self-made usb-gpib adapter + + To compile this driver as a module, choose M here: the module will be + called lpvo_usb_gpib + +config GPIB_PC2 + tristate "PC2 PC2a" + depends on ISA_BUS + depends on HAS_IOPORT + select GPIB_COMMON + select GPIB_NEC7210 + help + Enable support for pc2 and pc2a compatible adapters + Capital Equipment Corporation PC-488 + CONTEC GP-IB(PC) + Hameg HO80 + Iotech GP488B + Keithly MBC-488 + Measurement Computing ISA-GPIB-PCA2 + National Instruments PCII, PCIIa and PCII/IIa + + To compile this driver as a module, choose M here: the module will be + called pc2_gpib + + +config GPIB_TMS9914 + tristate + select GPIB_COMMON + help + Enable support for TMS 9914 chip. + + To compile this driver as a module, choose M here: the module will be + called tms9914 + +config GPIB_NEC7210 + tristate + select GPIB_COMMON + help + Enable support for NEC 7210 compatible chips. + + To compile this driver as a module, choose M here: the module will be + called nec7210 + +endif # GPIB diff --git a/drivers/gpib/Makefile b/drivers/gpib/Makefile new file mode 100644 index 000000000000..2d44fed2a743 --- /dev/null +++ b/drivers/gpib/Makefile @@ -0,0 +1,20 @@ + +subdir-ccflags-y += -I$(src)/include + +obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b/ +obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a/ +obj-$(CONFIG_GPIB_CB7210) += cb7210/ +obj-$(CONFIG_GPIB_CEC_PCI) += cec/ +obj-$(CONFIG_GPIB_COMMON) += common/ +obj-$(CONFIG_GPIB_FLUKE) += eastwood/ +obj-$(CONFIG_GPIB_FMH) += fmh_gpib/ +obj-$(CONFIG_GPIB_GPIO) += gpio/ +obj-$(CONFIG_GPIB_HP82335) += hp_82335/ +obj-$(CONFIG_GPIB_HP82341) += hp_82341/ +obj-$(CONFIG_GPIB_INES) += ines/ +obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib/ +obj-$(CONFIG_GPIB_NEC7210) += nec7210/ +obj-$(CONFIG_GPIB_NI_USB) += ni_usb/ +obj-$(CONFIG_GPIB_PC2) += pc2/ +obj-$(CONFIG_GPIB_TMS9914) += tms9914/ +obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882/ diff --git a/drivers/gpib/TODO b/drivers/gpib/TODO new file mode 100644 index 000000000000..ac07dd90b4ef --- /dev/null +++ b/drivers/gpib/TODO @@ -0,0 +1,10 @@ +TODO: +- checkpatch.pl fixes + These checks should be ignored: + CHECK:ALLOC_SIZEOF_STRUCT: Prefer kmalloc(sizeof(*board->private_data)...) over kmalloc(sizeof(struct xxx_priv)...) + ./gpio/gpib_bitbang.c:50: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parenthese + This warning will be addressed later: WARNING:UNDOCUMENTED_DT_STRING: DT compatible string +- resolve XXX notes where possible +- fix FIXME notes +- clean-up commented-out code +- fix typos diff --git a/drivers/gpib/agilent_82350b/Makefile b/drivers/gpib/agilent_82350b/Makefile new file mode 100644 index 000000000000..f24e1e713a63 --- /dev/null +++ b/drivers/gpib/agilent_82350b/Makefile @@ -0,0 +1,2 @@ + +obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b.o diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.c b/drivers/gpib/agilent_82350b/agilent_82350b.c new file mode 100644 index 000000000000..01a5bb43cd2d --- /dev/null +++ b/drivers/gpib/agilent_82350b/agilent_82350b.c @@ -0,0 +1,896 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2002, 2004 by Frank Mori Hess * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "agilent_82350b.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for Agilent 82350b"); + +static int read_transfer_counter(struct agilent_82350b_priv *a_priv); +static unsigned short read_and_clear_event_status(struct gpib_board *board); +static void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count); +static int agilent_82350b_write(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written); + +static int agilent_82350b_accel_read(struct gpib_board *board, u8 *buffer, + size_t length, int *end, size_t *bytes_read) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; + int retval = 0; + unsigned short event_status; + int i, num_fifo_bytes; + /* hardware doesn't support checking for end-of-string character when using fifo */ + if (tms_priv->eos_flags & REOS) + return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + read_and_clear_event_status(board); + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + /* disable fifo for the moment */ + writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + /* handle corner case of board not in holdoff and one byte might slip in early */ + if (tms_priv->holdoff_active == 0 && length > 1) { + size_t num_bytes; + + retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0 || *end) + return retval; + ++buffer; + --length; + } + tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); + tms9914_release_holdoff(tms_priv); + i = 0; + num_fifo_bytes = length - 1; + /* disable BI interrupts */ + write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BIIE, IMR0); + while (i < num_fifo_bytes && *end == 0) { + int block_size; + int j; + int count; + + block_size = min(num_fifo_bytes - i, agilent_82350b_fifo_size); + set_transfer_counter(a_priv, block_size); + writeb(ENABLE_TI_TO_SRAM | DIRECTION_GPIB_TO_HOST, + a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + if (agilent_82350b_fifo_is_halted(a_priv)) + writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG); + + clear_bit(READ_READY_BN, &tms_priv->state); + + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + (TERM_COUNT_STATUS_BIT | + BUFFER_END_STATUS_BIT)) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + if (retval) { + retval = -ERESTARTSYS; + break; + } + count = block_size - read_transfer_counter(a_priv); + for (j = 0; j < count && i < num_fifo_bytes; ++j) + buffer[i++] = readb(a_priv->sram_base + j); + if (event_status & BUFFER_END_STATUS_BIT) { + clear_bit(RECEIVED_END_BN, &tms_priv->state); + + tms_priv->holdoff_active = 1; + *end = 1; + } + if (test_bit(TIMO_NUM, &board->status)) { + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { + retval = -EINTR; + break; + } + } + /* re-enable BI interrupts */ + write_byte(tms_priv, tms_priv->imr0_bits, IMR0); + *bytes_read += i; + buffer += i; + length -= i; + writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + if (retval < 0) + return retval; + /* read last bytes if we havn't received an END yet */ + if (*end == 0) { + size_t num_bytes; + /* try to make sure we holdoff after last byte read */ + retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +static int translate_wait_return_value(struct gpib_board *board, int retval) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; + + if (retval) + return -ERESTARTSYS; + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) + return -EINTR; + return 0; +} + +static int agilent_82350b_accel_write(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, + size_t *bytes_written) +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; + int i, j; + unsigned short event_status; + int retval = 0; + int fifotransferlength = length; + int block_size = 0; + size_t num_bytes; + + *bytes_written = 0; + if (send_eoi) + --fifotransferlength; + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + + event_status = read_and_clear_event_status(board); + +#ifdef EXPERIMENTAL + /* wait for previous BO to complete if any */ + retval = wait_event_interruptible(board->wait, + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(WRITE_READY_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + retval = translate_wait_return_value(board, retval); + + if (retval) + return retval; +#endif + + if (fifotransferlength > 0) { + retval = agilent_82350b_write(board, buffer, 1, 0, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + + write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BOIE, IMR0); + for (i = 1; i < fifotransferlength;) { + clear_bit(WRITE_READY_BN, &tms_priv->state); + + block_size = min(fifotransferlength - i, agilent_82350b_fifo_size); + set_transfer_counter(a_priv, block_size); + for (j = 0; j < block_size; ++j, ++i) { + /* load data into board's sram */ + writeb(buffer[i], a_priv->sram_base + j); + } + writeb(ENABLE_TI_TO_SRAM, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + + if (agilent_82350b_fifo_is_halted(a_priv)) + writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG); + + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + TERM_COUNT_STATUS_BIT) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + num_bytes = block_size - read_transfer_counter(a_priv); + + *bytes_written += num_bytes; + retval = translate_wait_return_value(board, retval); + if (retval) + break; + } + write_byte(tms_priv, tms_priv->imr0_bits, IMR0); + if (retval < 0) + return retval; + + if (send_eoi) { + retval = agilent_82350b_write(board, buffer + fifotransferlength, 1, send_eoi, + &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +static unsigned short read_and_clear_event_status(struct gpib_board *board) +{ + struct agilent_82350b_priv *a_priv = board->private_data; + unsigned long flags; + unsigned short status; + + spin_lock_irqsave(&board->spinlock, flags); + status = a_priv->event_status_bits; + a_priv->event_status_bits = 0; + spin_unlock_irqrestore(&board->spinlock, flags); + return status; +} + +static irqreturn_t agilent_82350b_interrupt(int irq, void *arg) + +{ + int tms9914_status1 = 0, tms9914_status2 = 0; + int event_status; + struct gpib_board *board = arg; + struct agilent_82350b_priv *a_priv = board->private_data; + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + + spin_lock_irqsave(&board->spinlock, flags); + event_status = readb(a_priv->gpib_base + EVENT_STATUS_REG); + if (event_status & IRQ_STATUS_BIT) + retval = IRQ_HANDLED; + + if (event_status & TMS9914_IRQ_STATUS_BIT) { + tms9914_status1 = read_byte(&a_priv->tms9914_priv, ISR0); + tms9914_status2 = read_byte(&a_priv->tms9914_priv, ISR1); + tms9914_interrupt_have_status(board, &a_priv->tms9914_priv, tms9914_status1, + tms9914_status2); + } + /* write-clear status bits */ + if (event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT)) { + writeb(event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT), + a_priv->gpib_base + EVENT_STATUS_REG); + a_priv->event_status_bits |= event_status; + wake_up_interruptible(&board->wait); + } + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +static void agilent_82350b_detach(struct gpib_board *board); + +static int read_transfer_counter(struct agilent_82350b_priv *a_priv) +{ + int lo, mid, value; + + lo = readb(a_priv->gpib_base + XFER_COUNT_LO_REG); + mid = readb(a_priv->gpib_base + XFER_COUNT_MID_REG); + value = (lo & 0xff) | ((mid << 8) & 0x7f00); + value = ~(value - 1) & 0x7fff; + return value; +} + +static void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count) +{ + int complement = -count; + + writeb(complement & 0xff, a_priv->gpib_base + XFER_COUNT_LO_REG); + writeb((complement >> 8) & 0xff, a_priv->gpib_base + XFER_COUNT_MID_REG); + /* I don't think the hi count reg is even used, but oh well */ + writeb((complement >> 16) & 0xf, a_priv->gpib_base + XFER_COUNT_HI_REG); +} + +/* wrappers for interface functions */ +static int agilent_82350b_read(struct gpib_board *board, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); +} + +static int agilent_82350b_write(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); +} + +static int agilent_82350b_command(struct gpib_board *board, u8 *buffer, + size_t length, size_t *bytes_written) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); +} + +static int agilent_82350b_take_control(struct gpib_board *board, int synchronous) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_take_control_workaround(board, &priv->tms9914_priv, synchronous); +} + +static int agilent_82350b_go_to_standby(struct gpib_board *board) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_go_to_standby(board, &priv->tms9914_priv); +} + +static int agilent_82350b_request_system_control(struct gpib_board *board, int request_control) +{ + struct agilent_82350b_priv *a_priv = board->private_data; + + if (request_control) { + a_priv->card_mode_bits |= CM_SYSTEM_CONTROLLER_BIT; + if (a_priv->model != MODEL_82350A) + writeb(IC_SYSTEM_CONTROLLER_BIT, a_priv->gpib_base + INTERNAL_CONFIG_REG); + } else { + a_priv->card_mode_bits &= ~CM_SYSTEM_CONTROLLER_BIT; + if (a_priv->model != MODEL_82350A) + writeb(0, a_priv->gpib_base + INTERNAL_CONFIG_REG); + } + writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG); + return tms9914_request_system_control(board, &a_priv->tms9914_priv, request_control); +} + +static void agilent_82350b_interface_clear(struct gpib_board *board, int assert) + +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_interface_clear(board, &priv->tms9914_priv, assert); +} + +static void agilent_82350b_remote_enable(struct gpib_board *board, int enable) +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_remote_enable(board, &priv->tms9914_priv, enable); +} + +static int agilent_82350b_enable_eos(struct gpib_board *board, u8 eos_byte, + int compare_8_bits) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); +} + +static void agilent_82350b_disable_eos(struct gpib_board *board) +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_disable_eos(board, &priv->tms9914_priv); +} + +static unsigned int agilent_82350b_update_status(struct gpib_board *board, + unsigned int clear_mask) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); +} + +static int agilent_82350b_primary_address(struct gpib_board *board, + unsigned int address) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_primary_address(board, &priv->tms9914_priv, address); +} + +static int agilent_82350b_secondary_address(struct gpib_board *board, + unsigned int address, int enable) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); +} + +static int agilent_82350b_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_parallel_poll(board, &priv->tms9914_priv, result); +} + +static void agilent_82350b_parallel_poll_configure(struct gpib_board *board, + u8 config) +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); +} + +static void agilent_82350b_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); +} + +static void agilent_82350b_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_serial_poll_response(board, &priv->tms9914_priv, status); +} + +static u8 agilent_82350b_serial_poll_status(struct gpib_board *board) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_serial_poll_status(board, &priv->tms9914_priv); +} + +static int agilent_82350b_line_status(const struct gpib_board *board) +{ + struct agilent_82350b_priv *priv = board->private_data; + + return tms9914_line_status(board, &priv->tms9914_priv); +} + +static int agilent_82350b_t1_delay(struct gpib_board *board, unsigned int nanosec) +{ + struct agilent_82350b_priv *a_priv = board->private_data; + static const int nanosec_per_clock = 30; + unsigned int value; + + tms9914_t1_delay(board, &a_priv->tms9914_priv, nanosec); + + value = (nanosec + nanosec_per_clock - 1) / nanosec_per_clock; + if (value > 0xff) + value = 0xff; + writeb(value, a_priv->gpib_base + T1_DELAY_REG); + return value * nanosec_per_clock; +} + +static void agilent_82350b_return_to_local(struct gpib_board *board) +{ + struct agilent_82350b_priv *priv = board->private_data; + + tms9914_return_to_local(board, &priv->tms9914_priv); +} + +static int agilent_82350b_allocate_private(struct gpib_board *board) +{ + board->private_data = kzalloc(sizeof(struct agilent_82350b_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + return 0; +} + +static void agilent_82350b_free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int init_82350a_hardware(struct gpib_board *board, + const struct gpib_board_config *config) +{ + struct agilent_82350b_priv *a_priv = board->private_data; + static const unsigned int firmware_length = 5302; + unsigned int borg_status; + static const unsigned int timeout = 1000; + int i, j; + const char *firmware_data = config->init_data; + const unsigned int plx_cntrl_static_bits = PLX9050_WAITO_NOT_USER0_SELECT_BIT | + PLX9050_USER0_OUTPUT_BIT | + PLX9050_LLOCK_NOT_USER1_SELECT_BIT | + PLX9050_USER1_OUTPUT_BIT | + PLX9050_USER2_OUTPUT_BIT | + PLX9050_USER3_OUTPUT_BIT | + PLX9050_PCI_READ_MODE_BIT | + PLX9050_PCI_WRITE_MODE_BIT | + PLX9050_PCI_RETRY_DELAY_BITS(64) | + PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT; + + /* load borg data */ + borg_status = readb(a_priv->borg_base); + if ((borg_status & BORG_DONE_BIT)) + return 0; + /* need to programme borg */ + if (!config->init_data || config->init_data_length != firmware_length) { + dev_err(board->gpib_dev, "the 82350A board requires firmware after powering on.\n"); + return -EIO; + } + dev_dbg(board->gpib_dev, "Loading firmware...\n"); + + /* tickle the borg */ + writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT, + a_priv->plx_base + PLX9050_CNTRL_REG); + usleep_range(1000, 2000); + writel(plx_cntrl_static_bits, a_priv->plx_base + PLX9050_CNTRL_REG); + usleep_range(1000, 2000); + writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT, + a_priv->plx_base + PLX9050_CNTRL_REG); + usleep_range(1000, 2000); + + for (i = 0; i < config->init_data_length; ++i) { + for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_READY_BIT) == 0; ++j) { + if (need_resched()) + schedule(); + usleep_range(10, 20); + } + if (j == timeout) { + dev_err(board->gpib_dev, "timed out loading firmware.\n"); + return -ETIMEDOUT; + } + writeb(firmware_data[i], a_priv->gpib_base + CONFIG_DATA_REG); + } + for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_DONE_BIT) == 0; ++j) { + if (need_resched()) + schedule(); + usleep_range(10, 20); + } + if (j == timeout) { + dev_err(board->gpib_dev, "timed out waiting for firmware load to complete.\n"); + return -ETIMEDOUT; + } + dev_dbg(board->gpib_dev, " ...done.\n"); + return 0; +} + +static int test_sram(struct gpib_board *board) + +{ + struct agilent_82350b_priv *a_priv = board->private_data; + unsigned int i; + const unsigned int sram_length = pci_resource_len(a_priv->pci_device, SRAM_82350A_REGION); + /* test SRAM */ + const unsigned int byte_mask = 0xff; + + for (i = 0; i < sram_length; ++i) { + writeb(i & byte_mask, a_priv->sram_base + i); + if (need_resched()) + schedule(); + } + for (i = 0; i < sram_length; ++i) { + unsigned int read_value = readb(a_priv->sram_base + i); + + if ((i & byte_mask) != read_value) { + dev_err(board->gpib_dev, "SRAM test failed at %d wanted %d got %d\n", + i, (i & byte_mask), read_value); + return -EIO; + } + if (need_resched()) + schedule(); + } + dev_dbg(board->gpib_dev, "SRAM test passed 0x%x bytes checked\n", sram_length); + return 0; +} + +static int agilent_82350b_generic_attach(struct gpib_board *board, + const struct gpib_board_config *config, + int use_fifos) + +{ + struct agilent_82350b_priv *a_priv; + struct tms9914_priv *tms_priv; + int retval; + + board->status = 0; + + if (agilent_82350b_allocate_private(board)) + return -ENOMEM; + a_priv = board->private_data; + a_priv->using_fifos = use_fifos; + tms_priv = &a_priv->tms9914_priv; + tms_priv->read_byte = tms9914_iomem_read_byte; + tms_priv->write_byte = tms9914_iomem_write_byte; + tms_priv->offset = 1; + + /* find board */ + a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT, + PCI_DEVICE_ID_82350B, NULL); + if (a_priv->pci_device) { + a_priv->model = MODEL_82350B; + dev_dbg(board->gpib_dev, "Agilent 82350B board found\n"); + + } else { + a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT, + PCI_DEVICE_ID_82351A, NULL); + if (a_priv->pci_device) { + a_priv->model = MODEL_82351A; + dev_dbg(board->gpib_dev, "Agilent 82351B board found\n"); + + } else { + a_priv->pci_device = gpib_pci_get_subsys(config, PCI_VENDOR_ID_PLX, + PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_HP, + PCI_SUBDEVICE_ID_82350A, + a_priv->pci_device); + if (a_priv->pci_device) { + a_priv->model = MODEL_82350A; + dev_dbg(board->gpib_dev, "HP/Agilent 82350A board found\n"); + } else { + dev_err(board->gpib_dev, "no 82350/82351 board found\n"); + return -ENODEV; + } + } + } + if (pci_enable_device(a_priv->pci_device)) { + dev_err(board->gpib_dev, "error enabling pci device\n"); + return -EIO; + } + if (pci_request_regions(a_priv->pci_device, DRV_NAME)) + return -ENOMEM; + switch (a_priv->model) { + case MODEL_82350A: + a_priv->plx_base = ioremap(pci_resource_start(a_priv->pci_device, PLX_MEM_REGION), + pci_resource_len(a_priv->pci_device, PLX_MEM_REGION)); + dev_dbg(board->gpib_dev, "plx base address remapped to 0x%p\n", a_priv->plx_base); + a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, + GPIB_82350A_REGION), + pci_resource_len(a_priv->pci_device, + GPIB_82350A_REGION)); + dev_dbg(board->gpib_dev, "chip base address remapped to 0x%p\n", a_priv->gpib_base); + tms_priv->mmiobase = a_priv->gpib_base + TMS9914_BASE_REG; + a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, + SRAM_82350A_REGION), + pci_resource_len(a_priv->pci_device, + SRAM_82350A_REGION)); + dev_dbg(board->gpib_dev, "sram base address remapped to 0x%p\n", a_priv->sram_base); + a_priv->borg_base = ioremap(pci_resource_start(a_priv->pci_device, + BORG_82350A_REGION), + pci_resource_len(a_priv->pci_device, + BORG_82350A_REGION)); + dev_dbg(board->gpib_dev, "borg base address remapped to 0x%p\n", a_priv->borg_base); + + retval = init_82350a_hardware(board, config); + if (retval < 0) + return retval; + break; + case MODEL_82350B: + case MODEL_82351A: + a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, GPIB_REGION), + pci_resource_len(a_priv->pci_device, GPIB_REGION)); + dev_dbg(board->gpib_dev, "chip base address remapped to 0x%p\n", a_priv->gpib_base); + tms_priv->mmiobase = a_priv->gpib_base + TMS9914_BASE_REG; + a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, SRAM_REGION), + pci_resource_len(a_priv->pci_device, SRAM_REGION)); + dev_dbg(board->gpib_dev, "sram base address remapped to 0x%p\n", a_priv->sram_base); + a_priv->misc_base = ioremap(pci_resource_start(a_priv->pci_device, MISC_REGION), + pci_resource_len(a_priv->pci_device, MISC_REGION)); + dev_dbg(board->gpib_dev, "misc base address remapped to 0x%p\n", a_priv->misc_base); + break; + default: + dev_err(board->gpib_dev, "invalid board\n"); + return -ENODEV; + } + + retval = test_sram(board); + if (retval < 0) + return retval; + + if (request_irq(a_priv->pci_device->irq, agilent_82350b_interrupt, + IRQF_SHARED, DRV_NAME, board)) { + dev_err(board->gpib_dev, "failed to obtain irq %d\n", a_priv->pci_device->irq); + return -EIO; + } + a_priv->irq = a_priv->pci_device->irq; + dev_dbg(board->gpib_dev, " IRQ %d\n", a_priv->irq); + + writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); + a_priv->card_mode_bits = ENABLE_PCI_IRQ_BIT; + writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG); + + if (a_priv->model == MODEL_82350A) { + /* enable PCI interrupts for 82350a */ + writel(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR2_POLARITY_BIT | + PLX9050_PCI_INTR_EN_BIT, + a_priv->plx_base + PLX9050_INTCSR_REG); + } + + if (use_fifos) { + writeb(ENABLE_BUFFER_END_EVENTS_BIT | ENABLE_TERM_COUNT_EVENTS_BIT, + a_priv->gpib_base + EVENT_ENABLE_REG); + writeb(ENABLE_TERM_COUNT_INTERRUPT_BIT | ENABLE_BUFFER_END_INTERRUPT_BIT | + ENABLE_TMS9914_INTERRUPTS_BIT, a_priv->gpib_base + INTERRUPT_ENABLE_REG); + /* write-clear event status bits */ + writeb(BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT, + a_priv->gpib_base + EVENT_STATUS_REG); + } else { + writeb(0, a_priv->gpib_base + EVENT_ENABLE_REG); + writeb(ENABLE_TMS9914_INTERRUPTS_BIT, + a_priv->gpib_base + INTERRUPT_ENABLE_REG); + } + board->t1_nano_sec = agilent_82350b_t1_delay(board, 2000); + tms9914_board_reset(tms_priv); + + tms9914_online(board, tms_priv); + + return 0; +} + +static int agilent_82350b_unaccel_attach(struct gpib_board *board, + const struct gpib_board_config *config) +{ + return agilent_82350b_generic_attach(board, config, 0); +} + +static int agilent_82350b_accel_attach(struct gpib_board *board, + const struct gpib_board_config *config) +{ + return agilent_82350b_generic_attach(board, config, 1); +} + +static void agilent_82350b_detach(struct gpib_board *board) +{ + struct agilent_82350b_priv *a_priv = board->private_data; + struct tms9914_priv *tms_priv; + + if (a_priv) { + if (a_priv->plx_base) /* disable interrupts */ + writel(0, a_priv->plx_base + PLX9050_INTCSR_REG); + + tms_priv = &a_priv->tms9914_priv; + if (a_priv->irq) + free_irq(a_priv->irq, board); + if (a_priv->gpib_base) { + tms9914_board_reset(tms_priv); + if (a_priv->misc_base) + iounmap(a_priv->misc_base); + if (a_priv->borg_base) + iounmap(a_priv->borg_base); + if (a_priv->sram_base) + iounmap(a_priv->sram_base); + if (a_priv->gpib_base) + iounmap(a_priv->gpib_base); + if (a_priv->plx_base) + iounmap(a_priv->plx_base); + pci_release_regions(a_priv->pci_device); + } + if (a_priv->pci_device) + pci_dev_put(a_priv->pci_device); + } + agilent_82350b_free_private(board); +} + +static struct gpib_interface agilent_82350b_unaccel_interface = { + .name = "agilent_82350b_unaccel", + .attach = agilent_82350b_unaccel_attach, + .detach = agilent_82350b_detach, + .read = agilent_82350b_read, + .write = agilent_82350b_write, + .command = agilent_82350b_command, + .request_system_control = agilent_82350b_request_system_control, + .take_control = agilent_82350b_take_control, + .go_to_standby = agilent_82350b_go_to_standby, + .interface_clear = agilent_82350b_interface_clear, + .remote_enable = agilent_82350b_remote_enable, + .enable_eos = agilent_82350b_enable_eos, + .disable_eos = agilent_82350b_disable_eos, + .parallel_poll = agilent_82350b_parallel_poll, + .parallel_poll_configure = agilent_82350b_parallel_poll_configure, + .parallel_poll_response = agilent_82350b_parallel_poll_response, + .local_parallel_poll_mode = NULL, /* XXX */ + .line_status = agilent_82350b_line_status, + .update_status = agilent_82350b_update_status, + .primary_address = agilent_82350b_primary_address, + .secondary_address = agilent_82350b_secondary_address, + .serial_poll_response = agilent_82350b_serial_poll_response, + .serial_poll_status = agilent_82350b_serial_poll_status, + .t1_delay = agilent_82350b_t1_delay, + .return_to_local = agilent_82350b_return_to_local, +}; + +static struct gpib_interface agilent_82350b_interface = { + .name = "agilent_82350b", + .attach = agilent_82350b_accel_attach, + .detach = agilent_82350b_detach, + .read = agilent_82350b_accel_read, + .write = agilent_82350b_accel_write, + .command = agilent_82350b_command, + .request_system_control = agilent_82350b_request_system_control, + .take_control = agilent_82350b_take_control, + .go_to_standby = agilent_82350b_go_to_standby, + .interface_clear = agilent_82350b_interface_clear, + .remote_enable = agilent_82350b_remote_enable, + .enable_eos = agilent_82350b_enable_eos, + .disable_eos = agilent_82350b_disable_eos, + .parallel_poll = agilent_82350b_parallel_poll, + .parallel_poll_configure = agilent_82350b_parallel_poll_configure, + .parallel_poll_response = agilent_82350b_parallel_poll_response, + .local_parallel_poll_mode = NULL, /* XXX */ + .line_status = agilent_82350b_line_status, + .update_status = agilent_82350b_update_status, + .primary_address = agilent_82350b_primary_address, + .secondary_address = agilent_82350b_secondary_address, + .serial_poll_response = agilent_82350b_serial_poll_response, + .serial_poll_status = agilent_82350b_serial_poll_status, + .t1_delay = agilent_82350b_t1_delay, + .return_to_local = agilent_82350b_return_to_local, +}; + +static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) + +{ + return 0; +} + +static const struct pci_device_id agilent_82350b_pci_table[] = { + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, + PCI_SUBDEVICE_ID_82350A, 0, 0, 0 }, + { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table); + +static struct pci_driver agilent_82350b_pci_driver = { + .name = DRV_NAME, + .id_table = agilent_82350b_pci_table, + .probe = &agilent_82350b_pci_probe +}; + +static int __init agilent_82350b_init_module(void) +{ + int result; + + result = pci_register_driver(&agilent_82350b_pci_driver); + if (result) { + pr_err("pci_register_driver failed: error = %d\n", result); + return result; + } + + result = gpib_register_driver(&agilent_82350b_unaccel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_unaccel; + } + + result = gpib_register_driver(&agilent_82350b_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_interface; + } + + return 0; + +err_interface: + gpib_unregister_driver(&agilent_82350b_unaccel_interface); +err_unaccel: + pci_unregister_driver(&agilent_82350b_pci_driver); + + return result; +} + +static void __exit agilent_82350b_exit_module(void) +{ + gpib_unregister_driver(&agilent_82350b_interface); + gpib_unregister_driver(&agilent_82350b_unaccel_interface); + + pci_unregister_driver(&agilent_82350b_pci_driver); +} + +module_init(agilent_82350b_init_module); +module_exit(agilent_82350b_exit_module); diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.h b/drivers/gpib/agilent_82350b/agilent_82350b.h new file mode 100644 index 000000000000..ef841957297f --- /dev/null +++ b/drivers/gpib/agilent_82350b/agilent_82350b.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002, 2004 by Frank Mori Hess * + ***************************************************************************/ + +#include "gpibP.h" +#include "plx9050.h" +#include "tms9914.h" + +enum pci_vendor_ids { + PCI_VENDOR_ID_AGILENT = 0x15bc, +}; + +enum pci_device_ids { + PCI_DEVICE_ID_82350B = 0x0b01, + PCI_DEVICE_ID_82351A = 0x1218 +}; + +enum pci_subdevice_ids { + PCI_SUBDEVICE_ID_82350A = 0x10b0, +}; + +enum pci_regions_82350a { + PLX_MEM_REGION = 0, + PLX_IO_REGION = 1, + GPIB_82350A_REGION = 2, + SRAM_82350A_REGION = 3, + BORG_82350A_REGION = 4 +}; + +enum pci_regions_82350b { + GPIB_REGION = 0, + SRAM_REGION = 1, + MISC_REGION = 2, +}; + +enum board_model { + MODEL_82350A, + MODEL_82350B, + MODEL_82351A +}; + +/* struct which defines private_data for board */ +struct agilent_82350b_priv { + struct tms9914_priv tms9914_priv; + struct pci_dev *pci_device; + void __iomem *plx_base; /* 82350a only */ + void __iomem *gpib_base; + void __iomem *sram_base; + void __iomem *misc_base; + void __iomem *borg_base; + int irq; + unsigned short card_mode_bits; + unsigned short event_status_bits; + enum board_model model; + bool using_fifos; +}; + +/* registers */ +enum agilent_82350b_gpib_registers + +{ + CARD_MODE_REG = 0x1, + CONFIG_DATA_REG = 0x2, /* 82350A specific */ + INTERRUPT_ENABLE_REG = 0x3, + EVENT_STATUS_REG = 0x4, + EVENT_ENABLE_REG = 0x5, + STREAM_STATUS_REG = 0x7, + DEBUG_RAM0_REG = 0x8, + DEBUG_RAM1_REG = 0x9, + DEBUG_RAM2_REG = 0xa, + DEBUG_RAM3_REG = 0xb, + XFER_COUNT_LO_REG = 0xc, + XFER_COUNT_MID_REG = 0xd, + XFER_COUNT_HI_REG = 0xe, + TMS9914_BASE_REG = 0x10, + INTERNAL_CONFIG_REG = 0x18, + IMR0_READ_REG = 0x19, /* read */ + T1_DELAY_REG = 0x19, /* write */ + IMR1_READ_REG = 0x1a, + ADR_READ_REG = 0x1b, + SPMR_READ_REG = 0x1c, + PPR_READ_REG = 0x1d, + CDOR_READ_REG = 0x1e, + SRAM_ACCESS_CONTROL_REG = 0x1f, +}; + +enum card_mode_bits + +{ + ACTIVE_CONTROLLER_BIT = 0x2, /* read-only */ + CM_SYSTEM_CONTROLLER_BIT = 0x8, + ENABLE_BUS_MONITOR_BIT = 0x10, + ENABLE_PCI_IRQ_BIT = 0x20, +}; + +enum interrupt_enable_bits + +{ + ENABLE_TMS9914_INTERRUPTS_BIT = 0x1, + ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10, + ENABLE_TERM_COUNT_INTERRUPT_BIT = 0x20, +}; + +enum event_enable_bits + +{ + ENABLE_BUFFER_END_EVENTS_BIT = 0x10, + ENABLE_TERM_COUNT_EVENTS_BIT = 0x20, +}; + +enum event_status_bits + +{ + TMS9914_IRQ_STATUS_BIT = 0x1, + IRQ_STATUS_BIT = 0x2, + BUFFER_END_STATUS_BIT = 0x10, /* write-clear */ + TERM_COUNT_STATUS_BIT = 0x20, /* write-clear */ +}; + +enum stream_status_bits + +{ + HALTED_STATUS_BIT = 0x1, /* read */ + RESTART_STREAM_BIT = 0x1, /* write */ +}; + +enum internal_config_bits + +{ + IC_SYSTEM_CONTROLLER_BIT = 0x80, +}; + +enum sram_access_control_bits + +{ + DIRECTION_GPIB_TO_HOST = 0x20, /* transfer direction */ + ENABLE_TI_TO_SRAM = 0x40, /* enable fifo */ + ENABLE_FAST_TALKER = 0x80 /* added for 82350A (not used) */ +}; + +enum borg_bits + +{ + BORG_READY_BIT = 0x40, + BORG_DONE_BIT = 0x80 +}; + +static const int agilent_82350b_fifo_size = 0x8000; + +static inline int agilent_82350b_fifo_is_halted(struct agilent_82350b_priv *a_priv) + +{ + return readb(a_priv->gpib_base + STREAM_STATUS_REG) & HALTED_STATUS_BIT; +} + diff --git a/drivers/gpib/agilent_82357a/Makefile b/drivers/gpib/agilent_82357a/Makefile new file mode 100644 index 000000000000..81a55c257a6e --- /dev/null +++ b/drivers/gpib/agilent_82357a/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a.o + + diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c new file mode 100644 index 000000000000..77c8e549b208 --- /dev/null +++ b/drivers/gpib/agilent_82357a/agilent_82357a.c @@ -0,0 +1,1691 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * driver for Agilent 82357A/B usb to gpib adapters * + * copyright : (C) 2004 by Frank Mori Hess * + ***************************************************************************/ + +#define _GNU_SOURCE + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include +#include +#include +#include "agilent_82357a.h" +#include "gpibP.h" +#include "tms9914.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for Agilent 82357A/B usb adapters"); + +#define MAX_NUM_82357A_INTERFACES 128 +static struct usb_interface *agilent_82357a_driver_interfaces[MAX_NUM_82357A_INTERFACES]; +static DEFINE_MUTEX(agilent_82357a_hotplug_lock); // protect board insertion and removal + +static unsigned int agilent_82357a_update_status(struct gpib_board *board, + unsigned int clear_mask); + +static int agilent_82357a_take_control_internal(struct gpib_board *board, int synchronous); + +static void agilent_82357a_bulk_complete(struct urb *urb) +{ + struct agilent_82357a_urb_ctx *context = urb->context; + + complete(&context->complete); +} + +static void agilent_82357a_timeout_handler(struct timer_list *t) +{ + struct agilent_82357a_priv *a_priv = timer_container_of(a_priv, t, + bulk_timer); + struct agilent_82357a_urb_ctx *context = &a_priv->context; + + context->timed_out = 1; + complete(&context->complete); +} + +static int agilent_82357a_send_bulk_msg(struct agilent_82357a_priv *a_priv, void *data, + int data_length, int *actual_data_length, + int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int out_pipe; + struct agilent_82357a_urb_ctx *context = &a_priv->context; + + *actual_data_length = 0; + retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENODEV; + } + if (a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -EAGAIN; + } + a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + out_pipe = usb_sndbulkpipe(usb_dev, a_priv->bulk_out_endpoint); + init_completion(&context->complete); + context->timed_out = 0; + usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, out_pipe, data, data_length, + &agilent_82357a_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "failed to submit bulk out urb, retval=%i\n", retval); + mutex_unlock(&a_priv->bulk_alloc_lock); + goto cleanup; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + if (wait_for_completion_interruptible(&context->complete)) { + retval = -ERESTARTSYS; + goto cleanup; + } + if (context->timed_out) { + retval = -ETIMEDOUT; + } else { + retval = a_priv->bulk_urb->status; + *actual_data_length = a_priv->bulk_urb->actual_length; + } +cleanup: + if (timeout_msecs) { + if (timer_pending(&a_priv->bulk_timer)) + timer_delete_sync(&a_priv->bulk_timer); + } + mutex_lock(&a_priv->bulk_alloc_lock); + if (a_priv->bulk_urb) { + usb_kill_urb(a_priv->bulk_urb); + usb_free_urb(a_priv->bulk_urb); + a_priv->bulk_urb = NULL; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + return retval; +} + +static int agilent_82357a_receive_bulk_msg(struct agilent_82357a_priv *a_priv, void *data, + int data_length, int *actual_data_length, + int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + struct agilent_82357a_urb_ctx *context = &a_priv->context; + + *actual_data_length = 0; + retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENODEV; + } + if (a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -EAGAIN; + } + a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!a_priv->bulk_urb) { + mutex_unlock(&a_priv->bulk_alloc_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + in_pipe = usb_rcvbulkpipe(usb_dev, AGILENT_82357_BULK_IN_ENDPOINT); + init_completion(&context->complete); + context->timed_out = 0; + usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, in_pipe, data, data_length, + &agilent_82357a_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "failed to submit bulk in urb, retval=%i\n", retval); + mutex_unlock(&a_priv->bulk_alloc_lock); + goto cleanup; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + if (wait_for_completion_interruptible(&context->complete)) { + retval = -ERESTARTSYS; + goto cleanup; + } + if (context->timed_out) { + retval = -ETIMEDOUT; + goto cleanup; + } + retval = a_priv->bulk_urb->status; + *actual_data_length = a_priv->bulk_urb->actual_length; +cleanup: + if (timeout_msecs) + timer_delete_sync(&a_priv->bulk_timer); + + mutex_lock(&a_priv->bulk_alloc_lock); + if (a_priv->bulk_urb) { + usb_kill_urb(a_priv->bulk_urb); + usb_free_urb(a_priv->bulk_urb); + a_priv->bulk_urb = NULL; + } + mutex_unlock(&a_priv->bulk_alloc_lock); + return retval; +} + +static int agilent_82357a_receive_control_msg(struct agilent_82357a_priv *a_priv, __u8 request, + __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + + retval = mutex_lock_interruptible(&a_priv->control_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + mutex_unlock(&a_priv->control_alloc_lock); + return -ENODEV; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + in_pipe = usb_rcvctrlpipe(usb_dev, AGILENT_82357_CONTROL_ENDPOINT); + retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data, + size, timeout_msecs); + mutex_unlock(&a_priv->control_alloc_lock); + return retval; +} + +static void agilent_82357a_dump_raw_block(const u8 *raw_data, int length) +{ + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true); +} + +static int agilent_82357a_write_registers(struct agilent_82357a_priv *a_priv, + const struct agilent_82357a_register_pairlet *writes, + int num_writes) +{ + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval; + u8 *out_data, *in_data; + int out_data_length, in_data_length; + int bytes_written, bytes_read; + int i = 0; + int j; + static const int bytes_per_write = 2; + static const int header_length = 2; + static const int max_writes = 31; + + if (num_writes > max_writes) { + dev_err(&usb_dev->dev, "bug! num_writes=%i too large\n", num_writes); + return -EIO; + } + out_data_length = num_writes * bytes_per_write + header_length; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = DATA_PIPE_CMD_WR_REGS; + out_data[i++] = num_writes; + for (j = 0; j < num_writes; j++) { + out_data[i++] = writes[j].address; + out_data[i++] = writes[j].value; + } + + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval) { + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + return retval; + } + in_data_length = 0x20; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, + &bytes_read, 1000); + mutex_unlock(&a_priv->bulk_transfer_lock); + + if (retval) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + agilent_82357a_dump_raw_block(in_data, bytes_read); + kfree(in_data); + return -EIO; + } + if (in_data[0] != (0xff & ~DATA_PIPE_CMD_WR_REGS)) { + dev_err(&usb_dev->dev, "bulk command=0x%x != ~DATA_PIPE_CMD_WR_REGS\n", in_data[0]); + return -EIO; + } + if (in_data[1]) { + dev_err(&usb_dev->dev, "nonzero error code 0x%x in DATA_PIPE_CMD_WR_REGS response\n", + in_data[1]); + return -EIO; + } + kfree(in_data); + return 0; +} + +static int agilent_82357a_read_registers(struct agilent_82357a_priv *a_priv, + struct agilent_82357a_register_pairlet *reads, + int num_reads, int blocking) +{ + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval; + u8 *out_data, *in_data; + int out_data_length, in_data_length; + int bytes_written, bytes_read; + int i = 0; + int j; + static const int header_length = 2; + static const int max_reads = 62; + + if (num_reads > max_reads) { + dev_err(&usb_dev->dev, "bug! num_reads=%i too large\n", num_reads); + return -EIO; + } + out_data_length = num_reads + header_length; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = DATA_PIPE_CMD_RD_REGS; + out_data[i++] = num_reads; + for (j = 0; j < num_reads; j++) + out_data[i++] = reads[j].address; + + if (blocking) { + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + } else { + retval = mutex_trylock(&a_priv->bulk_transfer_lock); + if (retval == 0) { + kfree(out_data); + return -EAGAIN; + } + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval) { + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + return retval; + } + in_data_length = 0x20; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, + &bytes_read, 10000); + mutex_unlock(&a_priv->bulk_transfer_lock); + + if (retval) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + agilent_82357a_dump_raw_block(in_data, bytes_read); + kfree(in_data); + return -EIO; + } + i = 0; + if (in_data[i++] != (0xff & ~DATA_PIPE_CMD_RD_REGS)) { + dev_err(&usb_dev->dev, "bulk command=0x%x != ~DATA_PIPE_CMD_RD_REGS\n", in_data[0]); + return -EIO; + } + if (in_data[i++]) { + dev_err(&usb_dev->dev, "nonzero error code 0x%x in DATA_PIPE_CMD_RD_REGS response\n", + in_data[1]); + return -EIO; + } + for (j = 0; j < num_reads; j++) + reads[j].value = in_data[i++]; + kfree(in_data); + return 0; +} + +static int agilent_82357a_abort(struct agilent_82357a_priv *a_priv, int flush) +{ + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval = 0; + int receive_control_retval; + u16 wIndex = 0; + u8 *status_data; + static const unsigned int status_data_len = 2; + + status_data = kmalloc(status_data_len, GFP_KERNEL); + if (!status_data) + return -ENOMEM; + + if (flush) + wIndex |= XA_FLUSH; + receive_control_retval = agilent_82357a_receive_control_msg(a_priv, + agilent_82357a_control_request, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, XFER_ABORT, + wIndex, status_data, + status_data_len, 100); + if (receive_control_retval < 0) { + dev_err(&usb_dev->dev, "82357a_receive_control_msg() returned %i\n", + receive_control_retval); + retval = -EIO; + goto cleanup; + } + if (status_data[0] != (~XFER_ABORT & 0xff)) { + dev_err(&usb_dev->dev, "major code=0x%x != ~XFER_ABORT\n", status_data[0]); + retval = -EIO; + goto cleanup; + } + switch (status_data[1]) { + case UGP_SUCCESS: + retval = 0; + break; + case UGP_ERR_FLUSHING: + if (flush) { + retval = 0; + break; + } + fallthrough; + case UGP_ERR_FLUSHING_ALREADY: + default: + dev_err(&usb_dev->dev, "abort returned error code=0x%x\n", status_data[1]); + retval = -EIO; + break; + } + +cleanup: + kfree(status_data); + return retval; +} + +// interface functions +int agilent_82357a_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written); + +static int agilent_82357a_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *nbytes) +{ + int retval; + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + int out_data_length, in_data_length; + int bytes_written, bytes_read; + int i = 0; + u8 trailing_flags; + unsigned long start_jiffies = jiffies; + int msec_timeout; + + *nbytes = 0; + *end = 0; + + if (!a_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + out_data_length = 0x9; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = DATA_PIPE_CMD_READ; + out_data[i++] = 0; // primary address when ARF_NO_ADDR is not set + out_data[i++] = 0; // secondary address when ARF_NO_ADDR is not set + out_data[i] = ARF_NO_ADDRESS | ARF_END_ON_EOI; + if (a_priv->eos_mode & REOS) + out_data[i] |= ARF_END_ON_EOS_CHAR; + ++i; + out_data[i++] = length & 0xff; + out_data[i++] = (length >> 8) & 0xff; + out_data[i++] = (length >> 16) & 0xff; + out_data[i++] = (length >> 24) & 0xff; + out_data[i++] = a_priv->eos_char; + msec_timeout = (board->usec_timeout + 999) / 1000; + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, msec_timeout); + kfree(out_data); + if (retval || bytes_written != i) { + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + if (retval < 0) + return retval; + return -EIO; + } + in_data_length = length + 1; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + if (board->usec_timeout != 0) + msec_timeout -= jiffies_to_msecs(jiffies - start_jiffies) - 1; + if (msec_timeout >= 0) { + retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, + &bytes_read, msec_timeout); + } else { + retval = -ETIMEDOUT; + bytes_read = 0; + } + if (retval == -ETIMEDOUT) { + int extra_bytes_read; + int extra_bytes_retval; + + agilent_82357a_abort(a_priv, 1); + extra_bytes_retval = agilent_82357a_receive_bulk_msg(a_priv, in_data + bytes_read, + in_data_length - bytes_read, + &extra_bytes_read, 100); + bytes_read += extra_bytes_read; + if (extra_bytes_retval) { + dev_err(&usb_dev->dev, "extra_bytes_retval=%i, bytes_read=%i\n", + extra_bytes_retval, bytes_read); + agilent_82357a_abort(a_priv, 0); + } + } else if (retval) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + agilent_82357a_abort(a_priv, 0); + } + mutex_unlock(&a_priv->bulk_transfer_lock); + if (bytes_read > length + 1) { + bytes_read = length + 1; + dev_warn(&usb_dev->dev, "bytes_read > length? truncating"); + } + + if (bytes_read >= 1) { + memcpy(buffer, in_data, bytes_read - 1); + trailing_flags = in_data[bytes_read - 1]; + *nbytes = bytes_read - 1; + if (trailing_flags & (ATRF_EOI | ATRF_EOS)) + *end = 1; + } + kfree(in_data); + + /* + * Fix for a bug in 9914A that does not return the contents of ADSR + * when the board is in listener active state and ATN is not asserted. + * Set ATN here to obtain a valid board level ibsta + */ + agilent_82357a_take_control_internal(board, 0); + + // FIXME check trailing flags for error + return retval; +} + +static ssize_t agilent_82357a_generic_write(struct gpib_board *board, + u8 *buffer, size_t length, + int send_commands, int send_eoi, + size_t *bytes_written) +{ + int retval; + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data = NULL; + u8 *status_data = NULL; + int out_data_length; + int raw_bytes_written; + int i = 0, j; + int msec_timeout; + unsigned short bsr, adsr; + struct agilent_82357a_register_pairlet read_reg; + + *bytes_written = 0; + if (!a_priv->bus_interface) + return -ENODEV; + + usb_dev = interface_to_usbdev(a_priv->bus_interface); + out_data_length = length + 0x8; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = DATA_PIPE_CMD_WRITE; + out_data[i++] = 0; // primary address when AWF_NO_ADDRESS is not set + out_data[i++] = 0; // secondary address when AWF_NO_ADDRESS is not set + out_data[i] = AWF_NO_ADDRESS | AWF_NO_FAST_TALKER_FIRST_BYTE; + if (send_commands) + out_data[i] |= AWF_ATN | AWF_NO_FAST_TALKER; + if (send_eoi) + out_data[i] |= AWF_SEND_EOI; + ++i; + out_data[i++] = length & 0xff; + out_data[i++] = (length >> 8) & 0xff; + out_data[i++] = (length >> 16) & 0xff; + out_data[i++] = (length >> 24) & 0xff; + for (j = 0; j < length; j++) + out_data[i++] = buffer[j]; + + clear_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags); + + msec_timeout = (board->usec_timeout + 999) / 1000; + retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); + if (retval) { + kfree(out_data); + return retval; + } + retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &raw_bytes_written, + msec_timeout); + kfree(out_data); + if (retval || raw_bytes_written != i) { + agilent_82357a_abort(a_priv, 0); + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, raw_bytes_written=%i, i=%i\n", + retval, raw_bytes_written, i); + mutex_unlock(&a_priv->bulk_transfer_lock); + if (retval < 0) + return retval; + return -EIO; + } + + retval = wait_event_interruptible(board->wait, + test_bit(AIF_WRITE_COMPLETE_BN, + &a_priv->interrupt_flags) || + test_bit(TIMO_NUM, &board->status)); + if (retval) { + dev_dbg(&usb_dev->dev, "wait write complete interrupted\n"); + agilent_82357a_abort(a_priv, 0); + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ERESTARTSYS; + } + + if (test_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags) == 0) { + dev_dbg(&usb_dev->dev, "write timed out ibs %i, tmo %i\n", + test_bit(TIMO_NUM, &board->status), msec_timeout); + + agilent_82357a_abort(a_priv, 0); + + mutex_unlock(&a_priv->bulk_transfer_lock); + + read_reg.address = BSR; + retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "read_registers() returned error\n"); + return -ETIMEDOUT; + } + + bsr = read_reg.value; + dev_dbg(&usb_dev->dev, "write aborted bsr 0x%x\n", bsr); + + if (send_commands) {/* check for no listeners */ + if ((bsr & BSR_ATN_BIT) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) { + dev_dbg(&usb_dev->dev, "No listener on command\n"); + clear_bit(TIMO_NUM, &board->status); + return -ENOTCONN; // no listener on bus + } + } else { + read_reg.address = ADSR; + retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "read_registers() returned error\n"); + return -ETIMEDOUT; + } + adsr = read_reg.value; + if ((adsr & HR_TA) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) { + dev_dbg(&usb_dev->dev, "No listener on write\n"); + clear_bit(TIMO_NUM, &board->status); + return -ECOMM; + } + } + + return -ETIMEDOUT; + } + + status_data = kmalloc(STATUS_DATA_LEN, GFP_KERNEL); + if (!status_data) { + mutex_unlock(&a_priv->bulk_transfer_lock); + return -ENOMEM; + } + + retval = agilent_82357a_receive_control_msg(a_priv, agilent_82357a_control_request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + XFER_STATUS, 0, status_data, STATUS_DATA_LEN, + 100); + mutex_unlock(&a_priv->bulk_transfer_lock); + if (retval < 0) { + dev_err(&usb_dev->dev, "receive_control_msg() returned %i\n", retval); + kfree(status_data); + return -EIO; + } + *bytes_written = (u32)status_data[2]; + *bytes_written |= (u32)status_data[3] << 8; + *bytes_written |= (u32)status_data[4] << 16; + *bytes_written |= (u32)status_data[5] << 24; + + kfree(status_data); + return 0; +} + +static int agilent_82357a_write(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written) +{ + return agilent_82357a_generic_write(board, buffer, length, 0, send_eoi, bytes_written); +} + +int agilent_82357a_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + return agilent_82357a_generic_write(board, buffer, length, 1, 0, bytes_written); +} + +int agilent_82357a_take_control_internal(struct gpib_board *board, int synchronous) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + write.address = AUXCR; + if (synchronous) + write.value = AUX_TCS; + else + write.value = AUX_TCA; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + + return retval; +} + +static int agilent_82357a_take_control(struct gpib_board *board, int synchronous) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + const int timeout = 10; + int i; + + if (!a_priv->bus_interface) + return -ENODEV; + +/* + * It looks like the 9914 does not handle tcs properly. + * See comment above tms9914_take_control_workaround() in + * drivers/gpib/tms9914/tms9914_aux.c + */ + if (synchronous) + return -ETIMEDOUT; + + agilent_82357a_take_control_internal(board, synchronous); + // busy wait until ATN is asserted + for (i = 0; i < timeout; ++i) { + agilent_82357a_update_status(board, 0); + if (test_bit(ATN_NUM, &board->status)) + break; + udelay(1); + } + if (i == timeout) + return -ETIMEDOUT; + return 0; +} + +static int agilent_82357a_go_to_standby(struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet write; + int retval; + + if (!a_priv->bus_interface) + return -ENODEV; + + usb_dev = interface_to_usbdev(a_priv->bus_interface); + write.address = AUXCR; + write.value = AUX_GTS; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return 0; +} + +static int agilent_82357a_request_system_control(struct gpib_board *board, int request_control) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet writes[2]; + int retval; + int i = 0; + + if (!a_priv->bus_interface) + return -ENODEV; + + usb_dev = interface_to_usbdev(a_priv->bus_interface); + /* 82357B needs bit to be set in 9914 AUXCR register */ + writes[i].address = AUXCR; + if (request_control) { + writes[i].value = AUX_RQC; + a_priv->hw_control_bits |= SYSTEM_CONTROLLER; + } else { + return -EINVAL; + } + ++i; + writes[i].address = HW_CONTROL; + writes[i].value = a_priv->hw_control_bits; + ++i; + retval = agilent_82357a_write_registers(a_priv, writes, i); + if (retval) + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return retval; +} + +static void agilent_82357a_interface_clear(struct gpib_board *board, int assert) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet write; + int retval; + + if (!a_priv->bus_interface) + return; // -ENODEV; + + usb_dev = interface_to_usbdev(a_priv->bus_interface); + write.address = AUXCR; + write.value = AUX_SIC; + if (assert) { + write.value |= AUX_CS; + a_priv->is_cic = 1; + } + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "write_registers() returned error\n"); +} + +static void agilent_82357a_remote_enable(struct gpib_board *board, int enable) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet write; + int retval; + + if (!a_priv->bus_interface) + return; //-ENODEV; + + usb_dev = interface_to_usbdev(a_priv->bus_interface); + write.address = AUXCR; + write.value = AUX_SRE; + if (enable) + write.value |= AUX_CS; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + a_priv->ren_state = enable; + return;// 0; +} + +static int agilent_82357a_enable_eos(struct gpib_board *board, u8 eos_byte, + int compare_8_bits) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + + if (!a_priv->bus_interface) + return -ENODEV; + if (compare_8_bits == 0) + return -EOPNOTSUPP; + + a_priv->eos_char = eos_byte; + a_priv->eos_mode = REOS | BIN; + return 0; +} + +static void agilent_82357a_disable_eos(struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + + a_priv->eos_mode &= ~REOS; +} + +static unsigned int agilent_82357a_update_status(struct gpib_board *board, + unsigned int clear_mask) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet address_status, bus_status; + int retval; + + if (!a_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + board->status &= ~clear_mask; + if (a_priv->is_cic) + set_bit(CIC_NUM, &board->status); + else + clear_bit(CIC_NUM, &board->status); + address_status.address = ADSR; + retval = agilent_82357a_read_registers(a_priv, &address_status, 1, 0); + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "read_registers() returned error\n"); + return board->status; + } + // check for remote/local + if (address_status.value & HR_REM) + set_bit(REM_NUM, &board->status); + else + clear_bit(REM_NUM, &board->status); + // check for lockout + if (address_status.value & HR_LLO) + set_bit(LOK_NUM, &board->status); + else + clear_bit(LOK_NUM, &board->status); + // check for ATN + if (address_status.value & HR_ATN) + set_bit(ATN_NUM, &board->status); + else + clear_bit(ATN_NUM, &board->status); + // check for talker/listener addressed + if (address_status.value & HR_TA) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + if (address_status.value & HR_LA) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + + bus_status.address = BSR; + retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0); + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "read_registers() returned error\n"); + return board->status; + } + if (bus_status.value & BSR_SRQ_BIT) + set_bit(SRQI_NUM, &board->status); + else + clear_bit(SRQI_NUM, &board->status); + + return board->status; +} + +static int agilent_82357a_primary_address(struct gpib_board *board, unsigned int address) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet write; + int retval; + + if (!a_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + // put primary address in address0 + write.address = ADR; + write.value = address & ADDRESS_MASK; + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) { + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return retval; + } + return retval; +} + +static int agilent_82357a_secondary_address(struct gpib_board *board, + unsigned int address, int enable) +{ + if (enable) + return -EOPNOTSUPP; + return 0; +} + +static int agilent_82357a_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet writes[2]; + struct agilent_82357a_register_pairlet read; + int retval; + + if (!a_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + // execute parallel poll + writes[0].address = AUXCR; + writes[0].value = AUX_CS | AUX_RPP; + writes[1].address = HW_CONTROL; + writes[1].value = a_priv->hw_control_bits & ~NOT_PARALLEL_POLL; + retval = agilent_82357a_write_registers(a_priv, writes, 2); + if (retval) { + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return retval; + } + udelay(2); // silly, since usb write will take way longer + read.address = CPTR; + retval = agilent_82357a_read_registers(a_priv, &read, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "read_registers() returned error\n"); + return retval; + } + *result = read.value; + // clear parallel poll state + writes[0].address = HW_CONTROL; + writes[0].value = a_priv->hw_control_bits | NOT_PARALLEL_POLL; + writes[1].address = AUXCR; + writes[1].value = AUX_RPP; + retval = agilent_82357a_write_registers(a_priv, writes, 2); + if (retval) { + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return retval; + } + return 0; +} + +static void agilent_82357a_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + // board can only be system controller + return;// 0; +} + +static void agilent_82357a_parallel_poll_response(struct gpib_board *board, int ist) +{ + // board can only be system controller + return;// 0; +} + +static void agilent_82357a_serial_poll_response(struct gpib_board *board, u8 status) +{ + // board can only be system controller + return;// 0; +} + +static u8 agilent_82357a_serial_poll_status(struct gpib_board *board) +{ + // board can only be system controller + return 0; +} + +static void agilent_82357a_return_to_local(struct gpib_board *board) +{ + // board can only be system controller + return;// 0; +} + +static int agilent_82357a_line_status(const struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet bus_status; + int retval; + int status = VALID_ALL; + + if (!a_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + bus_status.address = BSR; + retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0); + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "read_registers() returned error\n"); + return retval; + } + if (bus_status.value & BSR_REN_BIT) + status |= BUS_REN; + if (bus_status.value & BSR_IFC_BIT) + status |= BUS_IFC; + if (bus_status.value & BSR_SRQ_BIT) + status |= BUS_SRQ; + if (bus_status.value & BSR_EOI_BIT) + status |= BUS_EOI; + if (bus_status.value & BSR_NRFD_BIT) + status |= BUS_NRFD; + if (bus_status.value & BSR_NDAC_BIT) + status |= BUS_NDAC; + if (bus_status.value & BSR_DAV_BIT) + status |= BUS_DAV; + if (bus_status.value & BSR_ATN_BIT) + status |= BUS_ATN; + return status; +} + +static unsigned short nanosec_to_fast_talker_bits(unsigned int *nanosec) +{ + static const int nanosec_per_bit = 21; + static const int max_value = 0x72; + static const int min_value = 0x11; + unsigned short bits; + + bits = (*nanosec + nanosec_per_bit / 2) / nanosec_per_bit; + if (bits < min_value) + bits = min_value; + if (bits > max_value) + bits = max_value; + *nanosec = bits * nanosec_per_bit; + return bits; +} + +static int agilent_82357a_t1_delay(struct gpib_board *board, unsigned int nanosec) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + struct agilent_82357a_register_pairlet write; + int retval; + + if (!a_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(a_priv->bus_interface); + write.address = FAST_TALKER_T1; + write.value = nanosec_to_fast_talker_bits(&nanosec); + retval = agilent_82357a_write_registers(a_priv, &write, 1); + if (retval) + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return nanosec; +} + +static void agilent_82357a_interrupt_complete(struct urb *urb) +{ + struct gpib_board *board = urb->context; + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + int retval; + u8 *transfer_buffer = urb->transfer_buffer; + unsigned long interrupt_flags; + + switch (urb->status) { + /* success */ + case 0: + break; + /* unlinked, don't resubmit */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* other error, resubmit */ + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); + return; + } + + interrupt_flags = transfer_buffer[0]; + if (test_bit(AIF_READ_COMPLETE_BN, &interrupt_flags)) + set_bit(AIF_READ_COMPLETE_BN, &a_priv->interrupt_flags); + if (test_bit(AIF_WRITE_COMPLETE_BN, &interrupt_flags)) + set_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags); + if (test_bit(AIF_SRQ_BN, &interrupt_flags)) + set_bit(SRQI_NUM, &board->status); + + wake_up_interruptible(&board->wait); + + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); +} + +static int agilent_82357a_setup_urbs(struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev; + int int_pipe; + int retval; + + retval = mutex_lock_interruptible(&a_priv->interrupt_alloc_lock); + if (retval) + return retval; + if (!a_priv->bus_interface) { + retval = -ENODEV; + goto setup_exit; + } + + a_priv->interrupt_buffer = kmalloc(INTERRUPT_BUF_LEN, GFP_KERNEL); + if (!a_priv->interrupt_buffer) { + retval = -ENOMEM; + goto setup_exit; + } + a_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!a_priv->interrupt_urb) { + retval = -ENOMEM; + goto setup_exit; + } + usb_dev = interface_to_usbdev(a_priv->bus_interface); + int_pipe = usb_rcvintpipe(usb_dev, a_priv->interrupt_in_endpoint); + usb_fill_int_urb(a_priv->interrupt_urb, usb_dev, int_pipe, a_priv->interrupt_buffer, + INTERRUPT_BUF_LEN, &agilent_82357a_interrupt_complete, board, 1); + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL); + if (retval) { + usb_free_urb(a_priv->interrupt_urb); + a_priv->interrupt_urb = NULL; + dev_err(&usb_dev->dev, "failed to submit first interrupt urb, retval=%i\n", retval); + goto setup_exit; + } + mutex_unlock(&a_priv->interrupt_alloc_lock); + return 0; + +setup_exit: + kfree(a_priv->interrupt_buffer); + mutex_unlock(&a_priv->interrupt_alloc_lock); + return retval; +} + +static void agilent_82357a_cleanup_urbs(struct agilent_82357a_priv *a_priv) +{ + if (a_priv && a_priv->bus_interface) { + if (a_priv->interrupt_urb) + usb_kill_urb(a_priv->interrupt_urb); + if (a_priv->bulk_urb) + usb_kill_urb(a_priv->bulk_urb); + } +}; + +static void agilent_82357a_release_urbs(struct agilent_82357a_priv *a_priv) +{ + if (a_priv) { + usb_free_urb(a_priv->interrupt_urb); + a_priv->interrupt_urb = NULL; + kfree(a_priv->interrupt_buffer); + } +} + +static int agilent_82357a_allocate_private(struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv; + + board->private_data = kzalloc(sizeof(struct agilent_82357a_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + a_priv = board->private_data; + mutex_init(&a_priv->bulk_transfer_lock); + mutex_init(&a_priv->bulk_alloc_lock); + mutex_init(&a_priv->control_alloc_lock); + mutex_init(&a_priv->interrupt_alloc_lock); + return 0; +} + +static void agilent_82357a_free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +#define INIT_NUM_REG_WRITES 18 +static int agilent_82357a_init(struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet hw_control; + struct agilent_82357a_register_pairlet writes[INIT_NUM_REG_WRITES]; + int retval; + unsigned int nanosec; + + writes[0].address = LED_CONTROL; + writes[0].value = FAIL_LED_ON; + writes[1].address = RESET_TO_POWERUP; + writes[1].value = RESET_SPACEBALL; + retval = agilent_82357a_write_registers(a_priv, writes, 2); + if (retval) { + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return -EIO; + } + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(usec_to_jiffies(2000))) + return -ERESTARTSYS; + writes[0].address = AUXCR; + writes[0].value = AUX_NBAF; + writes[1].address = AUXCR; + writes[1].value = AUX_HLDE; + writes[2].address = AUXCR; + writes[2].value = AUX_TON; + writes[3].address = AUXCR; + writes[3].value = AUX_LON; + writes[4].address = AUXCR; + writes[4].value = AUX_RSV2; + writes[5].address = AUXCR; + writes[5].value = AUX_INVAL; + writes[6].address = AUXCR; + writes[6].value = AUX_RPP; + writes[7].address = AUXCR; + writes[7].value = AUX_STDL; + writes[8].address = AUXCR; + writes[8].value = AUX_VSTDL; + writes[9].address = FAST_TALKER_T1; + nanosec = board->t1_nano_sec; + writes[9].value = nanosec_to_fast_talker_bits(&nanosec); + board->t1_nano_sec = nanosec; + writes[10].address = ADR; + writes[10].value = board->pad & ADDRESS_MASK; + writes[11].address = PPR; + writes[11].value = 0; + writes[12].address = SPMR; + writes[12].value = 0; + writes[13].address = PROTOCOL_CONTROL; + writes[13].value = WRITE_COMPLETE_INTERRUPT_EN; + writes[14].address = IMR0; + writes[14].value = HR_BOIE | HR_BIIE; + writes[15].address = IMR1; + writes[15].value = HR_SRQIE; + // turn off reset state + writes[16].address = AUXCR; + writes[16].value = AUX_CHIP_RESET; + writes[17].address = LED_CONTROL; + writes[17].value = FIRMWARE_LED_CONTROL; + retval = agilent_82357a_write_registers(a_priv, writes, INIT_NUM_REG_WRITES); + if (retval) { + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return -EIO; + } + hw_control.address = HW_CONTROL; + retval = agilent_82357a_read_registers(a_priv, &hw_control, 1, 1); + if (retval) { + dev_err(&usb_dev->dev, "read_registers() returned error\n"); + return -EIO; + } + a_priv->hw_control_bits = (hw_control.value & ~0x7) | NOT_TI_RESET | NOT_PARALLEL_POLL; + + return 0; +} + +static inline int agilent_82357a_device_match(struct usb_interface *interface, + const struct gpib_board_config *config) +{ + struct usb_device * const usbdev = interface_to_usbdev(interface); + + if (gpib_match_device_path(&interface->dev, config->device_path) == 0) + return 0; + if (config->serial_number && + strcmp(usbdev->serial, config->serial_number) != 0) + return 0; + + return 1; +} + +static int agilent_82357a_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + int retval; + int i; + unsigned int product_id; + struct agilent_82357a_priv *a_priv; + struct usb_device *usb_dev; + + if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) + return -ERESTARTSYS; + + retval = agilent_82357a_allocate_private(board); + if (retval < 0) { + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + a_priv = board->private_data; + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] && + !usb_get_intfdata(agilent_82357a_driver_interfaces[i]) && + agilent_82357a_device_match(agilent_82357a_driver_interfaces[i], config)) { + a_priv->bus_interface = agilent_82357a_driver_interfaces[i]; + usb_set_intfdata(agilent_82357a_driver_interfaces[i], board); + usb_dev = interface_to_usbdev(a_priv->bus_interface); + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) { + dev_err(board->gpib_dev, + "No supported adapters found, have you loaded its firmware?\n"); + retval = -ENODEV; + goto attach_fail; + } + product_id = le16_to_cpu(interface_to_usbdev(a_priv->bus_interface)->descriptor.idProduct); + switch (product_id) { + case USB_DEVICE_ID_AGILENT_82357A: + a_priv->bulk_out_endpoint = AGILENT_82357A_BULK_OUT_ENDPOINT; + a_priv->interrupt_in_endpoint = AGILENT_82357A_INTERRUPT_IN_ENDPOINT; + break; + case USB_DEVICE_ID_AGILENT_82357B: + a_priv->bulk_out_endpoint = AGILENT_82357B_BULK_OUT_ENDPOINT; + a_priv->interrupt_in_endpoint = AGILENT_82357B_INTERRUPT_IN_ENDPOINT; + break; + default: + dev_err(&usb_dev->dev, "bug, unhandled product_id in switch?\n"); + retval = -EIO; + goto attach_fail; + } + + retval = agilent_82357a_setup_urbs(board); + if (retval < 0) + goto attach_fail; + + timer_setup(&a_priv->bulk_timer, agilent_82357a_timeout_handler, 0); + + board->t1_nano_sec = 800; + + retval = agilent_82357a_init(board); + + if (retval < 0) { + agilent_82357a_cleanup_urbs(a_priv); + agilent_82357a_release_urbs(a_priv); + goto attach_fail; + } + + dev_info(&usb_dev->dev, "bus %d dev num %d attached to gpib%d, interface %i\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + +attach_fail: + agilent_82357a_free_private(board); + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; +} + +static int agilent_82357a_go_idle(struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); + struct agilent_82357a_register_pairlet writes[0x20]; + int retval; + + // turn on tms9914 reset state + writes[0].address = AUXCR; + writes[0].value = AUX_CS | AUX_CHIP_RESET; + a_priv->hw_control_bits &= ~NOT_TI_RESET; + writes[1].address = HW_CONTROL; + writes[1].value = a_priv->hw_control_bits; + writes[2].address = PROTOCOL_CONTROL; + writes[2].value = 0; + writes[3].address = IMR0; + writes[3].value = 0; + writes[4].address = IMR1; + writes[4].value = 0; + writes[5].address = LED_CONTROL; + writes[5].value = 0; + retval = agilent_82357a_write_registers(a_priv, writes, 6); + if (retval) { + dev_err(&usb_dev->dev, "write_registers() returned error\n"); + return -EIO; + } + return 0; +} + +static void agilent_82357a_detach(struct gpib_board *board) +{ + struct agilent_82357a_priv *a_priv; + + mutex_lock(&agilent_82357a_hotplug_lock); + + a_priv = board->private_data; + if (a_priv) { + if (a_priv->bus_interface) { + agilent_82357a_go_idle(board); + usb_set_intfdata(a_priv->bus_interface, NULL); + } + mutex_lock(&a_priv->control_alloc_lock); + mutex_lock(&a_priv->bulk_alloc_lock); + mutex_lock(&a_priv->interrupt_alloc_lock); + agilent_82357a_cleanup_urbs(a_priv); + agilent_82357a_release_urbs(a_priv); + agilent_82357a_free_private(board); + } + mutex_unlock(&agilent_82357a_hotplug_lock); +} + +static struct gpib_interface agilent_82357a_gpib_interface = { + .name = "agilent_82357a", + .attach = agilent_82357a_attach, + .detach = agilent_82357a_detach, + .read = agilent_82357a_read, + .write = agilent_82357a_write, + .command = agilent_82357a_command, + .take_control = agilent_82357a_take_control, + .go_to_standby = agilent_82357a_go_to_standby, + .request_system_control = agilent_82357a_request_system_control, + .interface_clear = agilent_82357a_interface_clear, + .remote_enable = agilent_82357a_remote_enable, + .enable_eos = agilent_82357a_enable_eos, + .disable_eos = agilent_82357a_disable_eos, + .parallel_poll = agilent_82357a_parallel_poll, + .parallel_poll_configure = agilent_82357a_parallel_poll_configure, + .parallel_poll_response = agilent_82357a_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = agilent_82357a_line_status, + .update_status = agilent_82357a_update_status, + .primary_address = agilent_82357a_primary_address, + .secondary_address = agilent_82357a_secondary_address, + .serial_poll_response = agilent_82357a_serial_poll_response, + .serial_poll_status = agilent_82357a_serial_poll_status, + .t1_delay = agilent_82357a_t1_delay, + .return_to_local = agilent_82357a_return_to_local, + .no_7_bit_eos = 1, + .skip_check_for_command_acceptors = 1 +}; + +// Table with the USB-devices: just now only testing IDs +static struct usb_device_id agilent_82357a_driver_device_table[] = { + {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357A)}, + {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357B)}, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, agilent_82357a_driver_device_table); + +static int agilent_82357a_driver_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int i; + char *path; + static const int path_length = 1024; + struct usb_device *usb_dev; + + if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) + return -ERESTARTSYS; + usb_dev = usb_get_dev(interface_to_usbdev(interface)); + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (!agilent_82357a_driver_interfaces[i]) { + agilent_82357a_driver_interfaces[i] = interface; + usb_set_intfdata(interface, NULL); + dev_dbg(&usb_dev->dev, "set bus interface %i to address 0x%p\n", + i, interface); + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) { + usb_put_dev(usb_dev); + mutex_unlock(&agilent_82357a_hotplug_lock); + dev_err(&usb_dev->dev, "out of space in agilent_82357a_driver_interfaces[]\n"); + return -1; + } + path = kmalloc(path_length, GFP_KERNEL); + if (!path) { + usb_put_dev(usb_dev); + mutex_unlock(&agilent_82357a_hotplug_lock); + return -ENOMEM; + } + usb_make_path(usb_dev, path, path_length); + dev_info(&usb_dev->dev, "probe succeeded for path: %s\n", path); + kfree(path); + mutex_unlock(&agilent_82357a_hotplug_lock); + return 0; +} + +static void agilent_82357a_driver_disconnect(struct usb_interface *interface) +{ + int i; + struct usb_device *usb_dev = interface_to_usbdev(interface); + + mutex_lock(&agilent_82357a_hotplug_lock); + + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] == interface) { + struct gpib_board *board = usb_get_intfdata(interface); + + if (board) { + struct agilent_82357a_priv *a_priv = board->private_data; + + if (a_priv) { + mutex_lock(&a_priv->control_alloc_lock); + mutex_lock(&a_priv->bulk_alloc_lock); + mutex_lock(&a_priv->interrupt_alloc_lock); + agilent_82357a_cleanup_urbs(a_priv); + a_priv->bus_interface = NULL; + mutex_unlock(&a_priv->interrupt_alloc_lock); + mutex_unlock(&a_priv->bulk_alloc_lock); + mutex_unlock(&a_priv->control_alloc_lock); + } + } + agilent_82357a_driver_interfaces[i] = NULL; + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) + dev_err(&usb_dev->dev, "unable to find interface - bug?\n"); + usb_put_dev(usb_dev); + + mutex_unlock(&agilent_82357a_hotplug_lock); +} + +static int agilent_82357a_driver_suspend(struct usb_interface *interface, pm_message_t message) +{ + int i, retval; + struct usb_device *usb_dev = interface_to_usbdev(interface); + + mutex_lock(&agilent_82357a_hotplug_lock); + + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] == interface) { + struct gpib_board *board = usb_get_intfdata(interface); + + if (board) { + struct agilent_82357a_priv *a_priv = board->private_data; + + if (a_priv) { + agilent_82357a_abort(a_priv, 0); + agilent_82357a_abort(a_priv, 0); + retval = agilent_82357a_go_idle(board); + if (retval) { + dev_err(&usb_dev->dev, "failed to go idle, retval=%i\n", + retval); + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + mutex_lock(&a_priv->interrupt_alloc_lock); + agilent_82357a_cleanup_urbs(a_priv); + mutex_unlock(&a_priv->interrupt_alloc_lock); + dev_dbg(&usb_dev->dev, + "bus %d dev num %d gpib %d, interface %i suspended\n", + usb_dev->bus->busnum, usb_dev->devnum, + board->minor, i); + } + } + break; + } + } + + mutex_unlock(&agilent_82357a_hotplug_lock); + + return 0; +} + +static int agilent_82357a_driver_resume(struct usb_interface *interface) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + struct gpib_board *board; + int i, retval = 0; + + mutex_lock(&agilent_82357a_hotplug_lock); + + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { + if (agilent_82357a_driver_interfaces[i] == interface) { + board = usb_get_intfdata(interface); + if (board) + break; + } + } + if (i == MAX_NUM_82357A_INTERFACES) { + retval = -ENOENT; + goto resume_exit; + } + + struct agilent_82357a_priv *a_priv = board->private_data; + + if (a_priv) { + if (a_priv->interrupt_urb) { + mutex_lock(&a_priv->interrupt_alloc_lock); + retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "failed to resubmit interrupt urb in resume, retval=%i\n", + retval); + mutex_unlock(&a_priv->interrupt_alloc_lock); + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + mutex_unlock(&a_priv->interrupt_alloc_lock); + } + retval = agilent_82357a_init(board); + if (retval < 0) { + mutex_unlock(&agilent_82357a_hotplug_lock); + return retval; + } + // set/unset system controller + retval = agilent_82357a_request_system_control(board, board->master); + // toggle ifc if master + if (board->master) { + agilent_82357a_interface_clear(board, 1); + usleep_range(200, 250); + agilent_82357a_interface_clear(board, 0); + } + // assert/unassert REN + agilent_82357a_remote_enable(board, a_priv->ren_state); + + dev_dbg(&usb_dev->dev, + "bus %d dev num %d gpib%d, interface %i resumed\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + } + +resume_exit: + mutex_unlock(&agilent_82357a_hotplug_lock); + + return retval; +} + +static struct usb_driver agilent_82357a_bus_driver = { + .name = DRV_NAME, + .probe = agilent_82357a_driver_probe, + .disconnect = agilent_82357a_driver_disconnect, + .suspend = agilent_82357a_driver_suspend, + .resume = agilent_82357a_driver_resume, + .id_table = agilent_82357a_driver_device_table, +}; + +static int __init agilent_82357a_init_module(void) +{ + int i; + int ret; + + for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) + agilent_82357a_driver_interfaces[i] = NULL; + + ret = usb_register(&agilent_82357a_bus_driver); + if (ret) { + pr_err("usb_register failed: error = %d\n", ret); + return ret; + } + + ret = gpib_register_driver(&agilent_82357a_gpib_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + usb_deregister(&agilent_82357a_bus_driver); + return ret; + } + + return 0; +} + +static void __exit agilent_82357a_exit_module(void) +{ + gpib_unregister_driver(&agilent_82357a_gpib_interface); + usb_deregister(&agilent_82357a_bus_driver); +} + +module_init(agilent_82357a_init_module); +module_exit(agilent_82357a_exit_module); diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.h b/drivers/gpib/agilent_82357a/agilent_82357a.h new file mode 100644 index 000000000000..33ac558e5552 --- /dev/null +++ b/drivers/gpib/agilent_82357a/agilent_82357a.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2004 by Frank Mori Hess * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "gpibP.h" +#include "tms9914.h" + +enum usb_vendor_ids { + USB_VENDOR_ID_AGILENT = 0x0957 +}; + +enum usb_device_ids { + USB_DEVICE_ID_AGILENT_82357A = 0x0107, + USB_DEVICE_ID_AGILENT_82357A_PREINIT = 0x0007, // device id before firmware is loaded + USB_DEVICE_ID_AGILENT_82357B = 0x0718, // device id before firmware is loaded + USB_DEVICE_ID_AGILENT_82357B_PREINIT = 0x0518, // device id before firmware is loaded +}; + +enum endpoint_addresses { + AGILENT_82357_CONTROL_ENDPOINT = 0x0, + AGILENT_82357_BULK_IN_ENDPOINT = 0x2, + AGILENT_82357A_BULK_OUT_ENDPOINT = 0x4, + AGILENT_82357A_INTERRUPT_IN_ENDPOINT = 0x6, + AGILENT_82357B_BULK_OUT_ENDPOINT = 0x6, + AGILENT_82357B_INTERRUPT_IN_ENDPOINT = 0x8, +}; + +enum bulk_commands { + DATA_PIPE_CMD_WRITE = 0x1, + DATA_PIPE_CMD_READ = 0x3, + DATA_PIPE_CMD_WR_REGS = 0x4, + DATA_PIPE_CMD_RD_REGS = 0x5 +}; + +enum agilent_82357a_read_flags { + ARF_END_ON_EOI = 0x1, + ARF_NO_ADDRESS = 0x2, + ARF_END_ON_EOS_CHAR = 0x4, + ARF_SPOLL = 0x8 +}; + +enum agilent_82357a_trailing_read_flags { + ATRF_EOI = 0x1, + ATRF_ATN = 0x2, + ATRF_IFC = 0x4, + ATRF_EOS = 0x8, + ATRF_ABORT = 0x10, + ATRF_COUNT = 0x20, + ATRF_DEAD_BUS = 0x40, + ATRF_UNADDRESSED = 0x80 +}; + +enum agilent_82357a_write_flags { + AWF_SEND_EOI = 0x1, + AWF_NO_FAST_TALKER_FIRST_BYTE = 0x2, + AWF_NO_FAST_TALKER = 0x4, + AWF_NO_ADDRESS = 0x8, + AWF_ATN = 0x10, + AWF_SEPARATE_HEADER = 0x80 +}; + +enum agilent_82357a_interrupt_flag_bit_numbers { + AIF_SRQ_BN = 0, + AIF_WRITE_COMPLETE_BN = 1, + AIF_READ_COMPLETE_BN = 2, +}; + +enum agilent_82357_error_codes { + UGP_SUCCESS = 0, + UGP_ERR_INVALID_CMD = 1, + UGP_ERR_INVALID_PARAM = 2, + UGP_ERR_INVALID_REG = 3, + UGP_ERR_GPIB_READ = 4, + UGP_ERR_GPIB_WRITE = 5, + UGP_ERR_FLUSHING = 6, + UGP_ERR_FLUSHING_ALREADY = 7, + UGP_ERR_UNSUPPORTED = 8, + UGP_ERR_OTHER = 9 +}; + +enum agilent_82357_control_values { + XFER_ABORT = 0xa0, + XFER_STATUS = 0xb0, +}; + +enum xfer_status_bits { + XS_COMPLETED = 0x1, + XS_READ = 0x2, +}; + +enum xfer_status_completion_bits { + XSC_EOI = 0x1, + XSC_ATN = 0x2, + XSC_IFC = 0x4, + XSC_EOS = 0x8, + XSC_ABORT = 0x10, + XSC_COUNT = 0x20, + XSC_DEAD_BUS = 0x40, + XSC_BUS_NOT_ADDRESSED = 0x80 +}; + +enum xfer_abort_type { + XA_FLUSH = 0x1 +}; + +#define STATUS_DATA_LEN 8 +#define INTERRUPT_BUF_LEN 8 + +struct agilent_82357a_urb_ctx { + struct completion complete; + unsigned timed_out : 1; +}; + +// struct which defines local data for each 82357 device +struct agilent_82357a_priv { + struct usb_interface *bus_interface; + unsigned short eos_char; + unsigned short eos_mode; + unsigned short hw_control_bits; + unsigned long interrupt_flags; + struct urb *bulk_urb; + struct urb *interrupt_urb; + u8 *interrupt_buffer; + struct mutex bulk_transfer_lock; // bulk transfer lock + struct mutex bulk_alloc_lock; // bulk transfer allocation lock + struct mutex interrupt_alloc_lock; // interrupt allocation lock + struct mutex control_alloc_lock; // control message allocation lock + struct timer_list bulk_timer; + struct agilent_82357a_urb_ctx context; + unsigned int bulk_out_endpoint; + unsigned int interrupt_in_endpoint; + unsigned is_cic : 1; + unsigned ren_state : 1; +}; + +struct agilent_82357a_register_pairlet { + short address; + unsigned short value; +}; + +enum firmware_registers { + HW_CONTROL = 0xa, + LED_CONTROL = 0xb, + RESET_TO_POWERUP = 0xc, + PROTOCOL_CONTROL = 0xd, + FAST_TALKER_T1 = 0xe +}; + +enum hardware_control_bits { + NOT_TI_RESET = 0x1, + SYSTEM_CONTROLLER = 0x2, + NOT_PARALLEL_POLL = 0x4, + OSCILLATOR_5V_ON = 0x8, + OUTPUT_5V_ON = 0x20, + CPLD_3V_ON = 0x80, +}; + +enum led_control_bits { + FIRMWARE_LED_CONTROL = 0x1, + FAIL_LED_ON = 0x20, + READY_LED_ON = 0x40, + ACCESS_LED_ON = 0x80 +}; + +enum reset_to_powerup_bits { + RESET_SPACEBALL = 0x1, // wait 2 millisec after sending +}; + +enum protocol_control_bits { + WRITE_COMPLETE_INTERRUPT_EN = 0x1, +}; + +static const int agilent_82357a_control_request = 0x4; + diff --git a/drivers/gpib/cb7210/Makefile b/drivers/gpib/cb7210/Makefile new file mode 100644 index 000000000000..d239ae80b415 --- /dev/null +++ b/drivers/gpib/cb7210/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_GPIB_CB7210) += cb7210.o + + diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c new file mode 100644 index 000000000000..3e2397898a9b --- /dev/null +++ b/drivers/gpib/cb7210/cb7210.c @@ -0,0 +1,1598 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * Measurement Computing boards using cb7210.2 and cbi488.2 chips + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "cb7210.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gpib_pci_ids.h" +#include "quancom_pci.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver Measurement Computing boards using cb7210.2 and cbi488.2"); + +static int cb7210_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read); + + static inline int have_fifo_word(const struct cb7210_priv *cb_priv) +{ + if (((cb7210_read_byte(cb_priv, HS_STATUS)) & + (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY)) == + (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY)) + return 1; + else + return 0; +} + +static inline void input_fifo_enable(struct gpib_board *board, int enable) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + if (enable) { + cb_priv->in_fifo_half_full = 0; + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + + cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits, + HS_MODE); + + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); + + cb_priv->hs_mode_bits |= HS_RX_ENABLE; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + } else { + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, nec7210_iobase(cb_priv) + + HS_MODE); + + clear_bit(READ_READY_BN, &nec_priv->state); + } + + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fifo_read(struct gpib_board *board, struct cb7210_priv *cb_priv, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + int hs_status; + u16 word; + unsigned long flags; + + *bytes_read = 0; + if (cb_priv->fifo_iobase == 0) { + dev_err(board->gpib_dev, "fifo iobase is zero!\n"); + return -EIO; + } + *end = 0; + if (length <= cb7210_fifo_size) { + dev_err(board->gpib_dev, " bug! fifo read length < fifo size\n"); + return -EINVAL; + } + + input_fifo_enable(board, 1); + + while (*bytes_read + cb7210_fifo_size < length) { + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + if (wait_event_interruptible(board->wait, + (cb_priv->in_fifo_half_full && + have_fifo_word(cb_priv)) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + break; + } + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + + while (have_fifo_word(cb_priv)) { + word = inw(cb_priv->fifo_iobase + DIR); + buffer[(*bytes_read)++] = word & 0xff; + buffer[(*bytes_read)++] = (word >> 8) & 0xff; + } + + cb_priv->in_fifo_half_full = 0; + + hs_status = cb7210_read_byte(cb_priv, HS_STATUS); + + spin_unlock_irqrestore(&board->spinlock, flags); + + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) { + *end = 1; + break; + } + if (hs_status & HS_FIFO_FULL) + break; + if (test_bit(TIMO_NUM, &board->status)) { + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + retval = -EINTR; + break; + } + } + hs_status = cb7210_read_byte(cb_priv, HS_STATUS); + if (hs_status & HS_RX_LSB_NOT_EMPTY) { + word = inw(cb_priv->fifo_iobase + DIR); + buffer[(*bytes_read)++] = word & 0xff; + } + + input_fifo_enable(board, 0); + + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &nec_priv->state) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_bit(READ_READY_BN, &nec_priv->state)) { + nec7210_set_handshake_mode(board, nec_priv, HR_HLDA); + buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end); + } + + return retval; +} + +static int cb7210_accel_read(struct gpib_board *board, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval; + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + size_t num_bytes; + + *bytes_read = 0; + // deal with limitations of fifo + if (length < cb7210_fifo_size + 3 || (nec_priv->auxa_bits & HR_REOS)) + return cb7210_read(board, buffer, length, end, bytes_read); + *end = 0; + + nec7210_release_rfd_holdoff(board, nec_priv); + + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + return -EINTR; + + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end); + if (*end) + return 0; + + nec7210_release_rfd_holdoff(board, nec_priv); + + retval = fifo_read(board, cb_priv, &buffer[*bytes_read], length - *bytes_read - 1, + end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + if (*end) + return 0; + + retval = cb7210_read(board, &buffer[*bytes_read], 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + + return 0; +} + +static int output_fifo_empty(const struct cb7210_priv *cb_priv) +{ + if ((cb7210_read_byte(cb_priv, HS_STATUS) & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY)) + == 0) + return 1; + else + return 0; +} + +static inline void output_fifo_enable(struct gpib_board *board, int enable) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + if (enable) { + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + + cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits, + HS_MODE); + + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb_priv->hs_mode_bits |= HS_TX_ENABLE; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); + + clear_bit(WRITE_READY_BN, &nec_priv->state); + + } else { + cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE); + } + + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fifo_write(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + size_t count = 0; + ssize_t retval = 0; + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned int num_bytes, i; + unsigned long flags; + + *bytes_written = 0; + if (cb_priv->fifo_iobase == 0) { + dev_err(board->gpib_dev, "fifo iobase is zero!\n"); + return -EINVAL; + } + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + clear_bit(BUS_ERROR_BN, &nec_priv->state); + + output_fifo_enable(board, 1); + + while (count < length) { + // wait until byte is ready to be sent + if (wait_event_interruptible(board->wait, + cb_priv->out_fifo_half_empty || + output_fifo_empty(cb_priv) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(BUS_ERROR_BN, &nec_priv->state)) + break; + + if (output_fifo_empty(cb_priv)) + num_bytes = cb7210_fifo_size - cb7210_fifo_width; + else + num_bytes = cb7210_fifo_size / 2; + if (num_bytes + count > length) + num_bytes = length - count; + if (num_bytes % cb7210_fifo_width) { + dev_err(board->gpib_dev, " bug! fifo write with odd number of bytes\n"); + retval = -EINVAL; + break; + } + + spin_lock_irqsave(&board->spinlock, flags); + for (i = 0; i < num_bytes / cb7210_fifo_width; i++) { + u16 word; + + word = buffer[count++] & 0xff; + word |= (buffer[count++] << 8) & 0xff00; + outw(word, cb_priv->fifo_iobase + CDOR); + } + cb_priv->out_fifo_half_empty = 0; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE); + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + spin_unlock_irqrestore(&board->spinlock, flags); + } + // wait last byte has been sent + if (wait_event_interruptible(board->wait, + output_fifo_empty(cb_priv) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + + output_fifo_enable(board, 0); + + *bytes_written = count; + return retval; +} + +static int cb7210_accel_write(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long fast_chunk_size, leftover; + int retval; + size_t num_bytes; + + *bytes_written = 0; + if (length > cb7210_fifo_width) + fast_chunk_size = length - 1; + else + fast_chunk_size = 0; + fast_chunk_size -= fast_chunk_size % cb7210_fifo_width; + leftover = length - fast_chunk_size; + + retval = fifo_write(board, buffer, fast_chunk_size, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + + retval = nec7210_write(board, nec_priv, buffer + fast_chunk_size, leftover, + send_eoi, &num_bytes); + *bytes_written += num_bytes; + return retval; +} + +static int cb7210_line_status(const struct gpib_board *board) +{ + int status = VALID_ALL; + int bsr_bits; + struct cb7210_priv *cb_priv; + + cb_priv = board->private_data; + + bsr_bits = cb7210_paged_read_byte(cb_priv, BUS_STATUS, BUS_STATUS_PAGE); + + if ((bsr_bits & BSR_REN_BIT) == 0) + status |= BUS_REN; + if ((bsr_bits & BSR_IFC_BIT) == 0) + status |= BUS_IFC; + if ((bsr_bits & BSR_SRQ_BIT) == 0) + status |= BUS_SRQ; + if ((bsr_bits & BSR_EOI_BIT) == 0) + status |= BUS_EOI; + if ((bsr_bits & BSR_NRFD_BIT) == 0) + status |= BUS_NRFD; + if ((bsr_bits & BSR_NDAC_BIT) == 0) + status |= BUS_NDAC; + if ((bsr_bits & BSR_DAV_BIT) == 0) + status |= BUS_DAV; + if ((bsr_bits & BSR_ATN_BIT) == 0) + status |= BUS_ATN; + + return status; +} + +static int cb7210_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 350) { + write_byte(nec_priv, AUX_HI_SPEED, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + } + return retval; +} + +static irqreturn_t cb7210_locked_internal_interrupt(struct gpib_board *board); + +/* + * GPIB interrupt service routines + */ + +static irqreturn_t cb_pci_interrupt(int irq, void *arg) +{ + int bits; + struct gpib_board *board = arg; + struct cb7210_priv *priv = board->private_data; + + // first task check if this is really our interrupt in a shared irq environment + switch (priv->pci_chip) { + case PCI_CHIP_AMCC_S5933: + if ((inl(priv->amcc_iobase + INTCSR_REG) & + (INBOX_INTR_CS_BIT | INTR_ASSERTED_BIT)) == 0) + return IRQ_NONE; + + // read incoming mailbox to clear mailbox full flag + inl(priv->amcc_iobase + INCOMING_MAILBOX_REG(3)); + // clear amccs5933 interrupt + bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) | + INBOX_SELECT_BITS(3) | INBOX_INTR_CS_BIT; + outl(bits, priv->amcc_iobase + INTCSR_REG); + break; + case PCI_CHIP_QUANCOM: + if ((inb(nec7210_iobase(priv) + QUANCOM_IRQ_CONTROL_STATUS_REG) & + QUANCOM_IRQ_ASSERTED_BIT)) + outb(QUANCOM_IRQ_ENABLE_BIT, nec7210_iobase(priv) + + QUANCOM_IRQ_CONTROL_STATUS_REG); + break; + default: + break; + } + return cb7210_locked_internal_interrupt(arg); +} + +static irqreturn_t cb7210_internal_interrupt(struct gpib_board *board) +{ + int hs_status, status1, status2; + struct cb7210_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int clear_bits; + + if ((priv->hs_mode_bits & HS_ENABLE_MASK)) { + status1 = 0; + hs_status = cb7210_read_byte(priv, HS_STATUS); + } else { + hs_status = 0; + status1 = read_byte(nec_priv, ISR1); + } + status2 = read_byte(nec_priv, ISR2); + nec7210_interrupt_have_status(board, nec_priv, status1, status2); + + dev_dbg(board->gpib_dev, "status 0x%x, mode 0x%x\n", hs_status, priv->hs_mode_bits); + + clear_bits = 0; + + if (hs_status & HS_HALF_FULL) { + if (priv->hs_mode_bits & HS_TX_ENABLE) + priv->out_fifo_half_empty = 1; + else if (priv->hs_mode_bits & HS_RX_ENABLE) + priv->in_fifo_half_full = 1; + clear_bits |= HS_CLR_HF_INT; + } + + if (hs_status & HS_SRQ_INT) { + set_bit(SRQI_NUM, &board->status); + clear_bits |= HS_CLR_SRQ_INT; + } + + if ((hs_status & HS_EOI_INT)) { + clear_bits |= HS_CLR_EOI_EMPTY_INT; + set_bit(RECEIVED_END_BN, &nec_priv->state); + if ((nec_priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE) + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + + if ((priv->hs_mode_bits & HS_TX_ENABLE) && + (hs_status & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY)) == 0) + clear_bits |= HS_CLR_EOI_EMPTY_INT; + + if (clear_bits) { + cb7210_write_byte(priv, priv->hs_mode_bits | clear_bits, HS_MODE); + cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE); + wake_up_interruptible(&board->wait); + } + + return IRQ_HANDLED; +} + +static irqreturn_t cb7210_locked_internal_interrupt(struct gpib_board *board) +{ + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = cb7210_internal_interrupt(board); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +static irqreturn_t cb7210_interrupt(int irq, void *arg) +{ + return cb7210_internal_interrupt(arg); +} + +static int cb_pci_attach(struct gpib_board *board, const struct gpib_board_config *config); +static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_config *config); + +static void cb_pci_detach(struct gpib_board *board); +static void cb_isa_detach(struct gpib_board *board); + +// wrappers for interface functions +static int cb7210_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int cb7210_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int cb7210_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int cb7210_take_control(struct gpib_board *board, int synchronous) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int cb7210_go_to_standby(struct gpib_board *board) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static int cb7210_request_system_control(struct gpib_board *board, int request_control) +{ + struct cb7210_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + if (request_control) + priv->hs_mode_bits |= HS_SYS_CONTROL; + else + priv->hs_mode_bits &= ~HS_SYS_CONTROL; + + cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE); + return nec7210_request_system_control(board, nec_priv, request_control); +} + +static void cb7210_interface_clear(struct gpib_board *board, int assert) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void cb7210_remote_enable(struct gpib_board *board, int enable) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int cb7210_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void cb7210_disable_eos(struct gpib_board *board) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int cb7210_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int cb7210_primary_address(struct gpib_board *board, unsigned int address) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int cb7210_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int cb7210_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void cb7210_parallel_poll_configure(struct gpib_board *board, u8 configuration) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); +} + +static void cb7210_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void cb7210_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct cb7210_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static u8 cb7210_serial_poll_status(struct gpib_board *board) +{ + struct cb7210_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static void cb7210_return_to_local(struct gpib_board *board) +{ + struct cb7210_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + write_byte(nec_priv, AUX_RTL2, AUXMR); + udelay(1); + write_byte(nec_priv, AUX_RTL, AUXMR); +} + +static struct gpib_interface cb_pci_unaccel_interface = { + .name = "cbi_pci_unaccel", + .attach = cb_pci_attach, + .detach = cb_pci_detach, + .read = cb7210_read, + .write = cb7210_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static struct gpib_interface cb_pci_accel_interface = { + .name = "cbi_pci_accel", + .attach = cb_pci_attach, + .detach = cb_pci_detach, + .read = cb7210_accel_read, + .write = cb7210_accel_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static struct gpib_interface cb_pci_interface = { + .name = "cbi_pci", + .attach = cb_pci_attach, + .detach = cb_pci_detach, + .read = cb7210_accel_read, + .write = cb7210_accel_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static struct gpib_interface cb_isa_unaccel_interface = { + .name = "cbi_isa_unaccel", + .attach = cb_isa_attach, + .detach = cb_isa_detach, + .read = cb7210_read, + .write = cb7210_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static struct gpib_interface cb_isa_interface = { + .name = "cbi_isa", + .attach = cb_isa_attach, + .detach = cb_isa_detach, + .read = cb7210_accel_read, + .write = cb7210_accel_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static struct gpib_interface cb_isa_accel_interface = { + .name = "cbi_isa_accel", + .attach = cb_isa_attach, + .detach = cb_isa_detach, + .read = cb7210_accel_read, + .write = cb7210_accel_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static int cb7210_allocate_private(struct gpib_board *board) +{ + struct cb7210_priv *priv; + + board->private_data = kmalloc(sizeof(struct cb7210_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + priv = board->private_data; + memset(priv, 0, sizeof(struct cb7210_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +static void cb7210_generic_detach(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +// generic part of attach functions shared by all cb7210 boards +static int cb7210_generic_attach(struct gpib_board *board) +{ + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + + if (cb7210_allocate_private(board)) + return -ENOMEM; + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + nec_priv->read_byte = nec7210_locking_ioport_read_byte; + nec_priv->write_byte = nec7210_locking_ioport_write_byte; + nec_priv->offset = cb7210_reg_offset; + nec_priv->type = CB7210; + return 0; +} + +static int cb7210_init(struct cb7210_priv *cb_priv, struct gpib_board *board) +{ + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + + cb7210_write_byte(cb_priv, HS_RESET7210, HS_INT_LEVEL); + cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); + + nec7210_board_reset(nec_priv, board); + cb7210_write_byte(cb_priv, HS_TX_ENABLE | HS_RX_ENABLE | HS_CLR_SRQ_INT | + HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE); + + cb_priv->hs_mode_bits = HS_HF_INT_EN; + cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); + + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + /* + * set clock register for maximum (20 MHz) driving frequency + * ICR should be set to clock in megahertz (1-15) and to zero + * for clocks faster than 15 MHz (max 20MHz) + */ + write_byte(nec_priv, ICR | 0, AUXMR); + + if (cb_priv->pci_chip == PCI_CHIP_QUANCOM) { + /* change interrupt polarity */ + nec_priv->auxb_bits |= HR_INV; + write_byte(nec_priv, nec_priv->auxb_bits, AUXMR); + } + nec7210_board_online(nec_priv, board); + + /* poll so we can detect assertion of ATN */ + if (gpib_request_pseudo_irq(board, cb_pci_interrupt)) { + pr_err("failed to allocate pseudo_irq\n"); + return -1; + } + return 0; +} + +static int cb_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int bits; + int retval; + + retval = cb7210_generic_attach(board); + if (retval) + return retval; + + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + + cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS, + PCI_DEVICE_ID_CBOARDS_PCI_GPIB, NULL); + if (cb_priv->pci_device) + cb_priv->pci_chip = PCI_CHIP_AMCC_S5933; + if (!cb_priv->pci_device) { + cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS, + PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, NULL); + if (cb_priv->pci_device) + cb_priv->pci_chip = PCI_CHIP_AMCC_S5933; + } + if (!cb_priv->pci_device) { + cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_QUANCOM, + PCI_DEVICE_ID_QUANCOM_GPIB, NULL); + if (cb_priv->pci_device) { + cb_priv->pci_chip = PCI_CHIP_QUANCOM; + nec_priv->offset = 4; + } + } + if (!cb_priv->pci_device) { + dev_err(board->gpib_dev, "no supported boards found.\n"); + return -ENODEV; + } + + if (pci_enable_device(cb_priv->pci_device)) { + dev_err(board->gpib_dev, "error enabling pci device\n"); + return -EIO; + } + + if (pci_request_regions(cb_priv->pci_device, DRV_NAME)) + return -EBUSY; + switch (cb_priv->pci_chip) { + case PCI_CHIP_AMCC_S5933: + cb_priv->amcc_iobase = pci_resource_start(cb_priv->pci_device, 0); + nec_priv->iobase = pci_resource_start(cb_priv->pci_device, 1); + cb_priv->fifo_iobase = pci_resource_start(cb_priv->pci_device, 2); + break; + case PCI_CHIP_QUANCOM: + nec_priv->iobase = pci_resource_start(cb_priv->pci_device, 0); + cb_priv->fifo_iobase = nec_priv->iobase; + break; + default: + dev_err(board->gpib_dev, "bug! unhandled pci_chip=%i\n", cb_priv->pci_chip); + return -EIO; + } + isr_flags |= IRQF_SHARED; + if (request_irq(cb_priv->pci_device->irq, cb_pci_interrupt, isr_flags, DRV_NAME, board)) { + dev_err(board->gpib_dev, "can't request IRQ %d\n", + cb_priv->pci_device->irq); + return -EBUSY; + } + cb_priv->irq = cb_priv->pci_device->irq; + + switch (cb_priv->pci_chip) { + case PCI_CHIP_AMCC_S5933: + // make sure mailbox flags are clear + inl(cb_priv->amcc_iobase + INCOMING_MAILBOX_REG(3)); + // enable interrupts on amccs5933 chip + bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) | INBOX_SELECT_BITS(3) | + INBOX_INTR_CS_BIT; + outl(bits, cb_priv->amcc_iobase + INTCSR_REG); + break; + default: + break; + } + return cb7210_init(cb_priv, board); +} + +static void cb_pci_detach(struct gpib_board *board) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cb_priv) { + gpib_free_pseudo_irq(board); + nec_priv = &cb_priv->nec7210_priv; + if (cb_priv->irq) { + // disable amcc interrupts + outl(0, cb_priv->amcc_iobase + INTCSR_REG); + free_irq(cb_priv->irq, board); + } + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + pci_release_regions(cb_priv->pci_device); + } + if (cb_priv->pci_device) + pci_dev_put(cb_priv->pci_device); + } + cb7210_generic_detach(board); +} + +static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + int isr_flags = 0; + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + unsigned int bits; + int retval; + + retval = cb7210_generic_attach(board); + if (retval) + return retval; + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + if (!request_region(config->ibbase, cb7210_iosize, DRV_NAME)) { + dev_err(board->gpib_dev, "ioports starting at 0x%x are already in use\n", + config->ibbase); + return -EBUSY; + } + nec_priv->iobase = config->ibbase; + cb_priv->fifo_iobase = nec7210_iobase(cb_priv); + + bits = irq_bits(config->ibirq); + if (bits == 0) + dev_err(board->gpib_dev, "board incapable of using irq %i, try 2-5, 7, 10, or 11\n", + config->ibirq); + + // install interrupt handler + if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { + dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); + return -EBUSY; + } + cb_priv->irq = config->ibirq; + + return cb7210_init(cb_priv, board); +} + +static void cb_isa_detach(struct gpib_board *board) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cb_priv) { + gpib_free_pseudo_irq(board); + nec_priv = &cb_priv->nec7210_priv; + if (cb_priv->irq) + free_irq(cb_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region(nec7210_iobase(cb_priv), cb7210_iosize); + } + } + cb7210_generic_detach(board); +} + +static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static const struct pci_device_id cb7210_pci_table[] = { + {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, cb7210_pci_table); + +static struct pci_driver cb7210_pci_driver = { + .name = DRV_NAME, + .id_table = cb7210_pci_table, + .probe = &cb7210_pci_probe +}; + +/*************************************************************************** + * Support for computer boards pcmcia-gpib card + * + * Based on gpib PCMCIA client driver written by Claus Schroeter + * (clausi@chemie.fu-berlin.de), which was adapted from the + * pcmcia skeleton example (presumably David Hinds) + ***************************************************************************/ + +#ifdef CONFIG_GPIB_PCMCIA + +#include +#include +#include +#include + +#include +#include + +/* + * The event() function is this driver's Card Services event handler. + * It will be called by Card Services when an appropriate card status + * event is received. The config() and release() entry points are + * used to configure or release a socket, in response to card insertion + * and ejection events. They are invoked from the gpib event + * handler. + */ + +static int cb_gpib_config(struct pcmcia_device *link); +static void cb_gpib_release(struct pcmcia_device *link); +static int cb_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config); +static void cb_pcmcia_detach(struct gpib_board *board); + +/* + * A linked list of "instances" of the gpib device. Each actual + * PCMCIA card corresponds to one device instance, and is described + * by one dev_link_t structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the + * memory card driver uses an array of dev_link_t pointers, where minor + * device numbers are used to derive the corresponding array index. + */ + +static struct pcmcia_device *curr_dev; + +/* + * A dev_link_t structure has fields for most things that are needed + * to keep track of a socket, but there will usually be some device + * specific information that also needs to be kept track of. The + * 'priv' pointer in a dev_link_t structure can be used to point to + * a device-specific private data structure, like this. + * + * A driver needs to provide a dev_node_t structure for each device + * on a card. In some cases, there is only one device per card (for + * example, ethernet cards, modems). In other cases, there may be + * many actual or logical devices (SCSI adapters, memory cards with + * multiple partitions). The dev_node_t structures need to be kept + * in a linked list starting at the 'dev' field of a dev_link_t + * structure. We allocate them in the card's private data structure, + * because they generally can't be allocated dynamically. + */ + +struct local_info { + struct pcmcia_device *p_dev; + struct gpib_board *dev; +}; + +/* + * gpib_attach() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ + +static int cb_gpib_probe(struct pcmcia_device *link) +{ + struct local_info *info; + int ret; + + /* Allocate space for private device-specific data */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + + /* The io structure describes IO port mapping */ + link->resource[0]->end = 16; + link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + link->resource[1]->end = 16; + link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; + link->io_lines = 10; + + /* General socket configuration */ + link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + link->config_index = 1; + link->config_regs = PRESENT_OPTION; + + /* Register with Card Services */ + curr_dev = link; + ret = cb_gpib_config(link); + if (ret) + goto free_info; + + return 0; + +free_info: + kfree(info); + return ret; +} + +/* + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ + +static void cb_gpib_remove(struct pcmcia_device *link) +{ + struct local_info *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + if (info->dev) + cb_pcmcia_detach(info->dev); + cb_gpib_release(link); + + //free_netdev(dev); + kfree(info); +} + +static int cb_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) +{ + return pcmcia_request_io(link); +} + +/* + * gpib_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * ethernet device available to the system. + */ + +static int cb_gpib_config(struct pcmcia_device *link) +{ + int retval; + + retval = pcmcia_loop_config(link, &cb_gpib_config_iteration, NULL); + if (retval) { + dev_warn(&link->dev, "no configuration found\n"); + cb_gpib_release(link); + return -ENODEV; + } + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + retval = pcmcia_enable_device(link); + if (retval) { + dev_warn(&link->dev, "pcmcia_enable_device failed\n"); + cb_gpib_release(link); + return -ENODEV; + } + + return 0; +} /* gpib_config */ + +/* + * After a card is removed, gpib_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ + +static void cb_gpib_release(struct pcmcia_device *link) +{ + pcmcia_disable_device(link); +} + +static int cb_gpib_suspend(struct pcmcia_device *link) +{ + //struct local_info *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + if (link->open) + dev_warn(&link->dev, "Device still open\n"); + //netif_device_detach(dev); + + return 0; +} + +static int cb_gpib_resume(struct pcmcia_device *link) +{ + //struct local_info *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + /*if (link->open) { + * ni_gpib_probe(dev); / really? + * //netif_device_attach(dev); + * + */ + return cb_gpib_config(link); +} + +/*====================================================================*/ + +static struct pcmcia_device_id cb_pcmcia_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0005), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, cb_pcmcia_ids); + +static struct pcmcia_driver cb_gpib_cs_driver = { + .name = "cb_gpib_cs", + .owner = THIS_MODULE, + .id_table = cb_pcmcia_ids, + .probe = cb_gpib_probe, + .remove = cb_gpib_remove, + .suspend = cb_gpib_suspend, + .resume = cb_gpib_resume, +}; + +static void cb_pcmcia_cleanup_module(void) +{ + pcmcia_unregister_driver(&cb_gpib_cs_driver); +} + +static struct gpib_interface cb_pcmcia_unaccel_interface = { + .name = "cbi_pcmcia_unaccel", + .attach = cb_pcmcia_attach, + .detach = cb_pcmcia_detach, + .read = cb7210_read, + .write = cb7210_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static struct gpib_interface cb_pcmcia_interface = { + .name = "cbi_pcmcia", + .attach = cb_pcmcia_attach, + .detach = cb_pcmcia_detach, + .read = cb7210_accel_read, + .write = cb7210_accel_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static struct gpib_interface cb_pcmcia_accel_interface = { + .name = "cbi_pcmcia_accel", + .attach = cb_pcmcia_attach, + .detach = cb_pcmcia_detach, + .read = cb7210_accel_read, + .write = cb7210_accel_write, + .command = cb7210_command, + .take_control = cb7210_take_control, + .go_to_standby = cb7210_go_to_standby, + .request_system_control = cb7210_request_system_control, + .interface_clear = cb7210_interface_clear, + .remote_enable = cb7210_remote_enable, + .enable_eos = cb7210_enable_eos, + .disable_eos = cb7210_disable_eos, + .parallel_poll = cb7210_parallel_poll, + .parallel_poll_configure = cb7210_parallel_poll_configure, + .parallel_poll_response = cb7210_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = cb7210_line_status, + .update_status = cb7210_update_status, + .primary_address = cb7210_primary_address, + .secondary_address = cb7210_secondary_address, + .serial_poll_response = cb7210_serial_poll_response, + .serial_poll_status = cb7210_serial_poll_status, + .t1_delay = cb7210_t1_delay, + .return_to_local = cb7210_return_to_local, +}; + +static int cb_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct cb7210_priv *cb_priv; + struct nec7210_priv *nec_priv; + int retval; + + if (!curr_dev) { + dev_err(board->gpib_dev, "no cb pcmcia cards found\n"); + return -ENODEV; + } + + retval = cb7210_generic_attach(board); + if (retval) + return retval; + + cb_priv = board->private_data; + nec_priv = &cb_priv->nec7210_priv; + + if (!request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]), + DRV_NAME)) { + dev_err(board->gpib_dev, "ioports starting at 0x%lx are already in use\n", + (unsigned long)curr_dev->resource[0]->start); + return -EBUSY; + } + nec_priv->iobase = curr_dev->resource[0]->start; + cb_priv->fifo_iobase = curr_dev->resource[0]->start; + + if (request_irq(curr_dev->irq, cb7210_interrupt, IRQF_SHARED, DRV_NAME, board)) { + dev_err(board->gpib_dev, "failed to request IRQ %d\n", curr_dev->irq); + return -EBUSY; + } + cb_priv->irq = curr_dev->irq; + + return cb7210_init(cb_priv, board); +} + +static void cb_pcmcia_detach(struct gpib_board *board) +{ + struct cb7210_priv *cb_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cb_priv) { + nec_priv = &cb_priv->nec7210_priv; + gpib_free_pseudo_irq(board); + if (cb_priv->irq) + free_irq(cb_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region(nec7210_iobase(cb_priv), cb7210_iosize); + } + } + cb7210_generic_detach(board); +} + +#endif /* CONFIG_GPIB_PCMCIA */ + +static int __init cb7210_init_module(void) +{ + int ret; + + ret = pci_register_driver(&cb7210_pci_driver); + if (ret) { + pr_err("pci_register_driver failed: error = %d\n", ret); + return ret; + } + + ret = gpib_register_driver(&cb_pci_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pci; + } + + ret = gpib_register_driver(&cb_isa_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_isa; + } + + ret = gpib_register_driver(&cb_pci_accel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pci_accel; + } + + ret = gpib_register_driver(&cb_pci_unaccel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pci_unaccel; + } + + ret = gpib_register_driver(&cb_isa_accel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_isa_accel; + } + + ret = gpib_register_driver(&cb_isa_unaccel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_isa_unaccel; + } + +#ifdef CONFIG_GPIB_PCMCIA + ret = gpib_register_driver(&cb_pcmcia_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pcmcia; + } + + ret = gpib_register_driver(&cb_pcmcia_accel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pcmcia_accel; + } + + ret = gpib_register_driver(&cb_pcmcia_unaccel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pcmcia_unaccel; + } + + ret = pcmcia_register_driver(&cb_gpib_cs_driver); + if (ret) { + pr_err("pcmcia_register_driver failed: error = %d\n", ret); + goto err_pcmcia_driver; + } +#endif + + return 0; + +#ifdef CONFIG_GPIB_PCMCIA +err_pcmcia_driver: + gpib_unregister_driver(&cb_pcmcia_unaccel_interface); +err_pcmcia_unaccel: + gpib_unregister_driver(&cb_pcmcia_accel_interface); +err_pcmcia_accel: + gpib_unregister_driver(&cb_pcmcia_interface); +err_pcmcia: +#endif + gpib_unregister_driver(&cb_isa_unaccel_interface); +err_isa_unaccel: + gpib_unregister_driver(&cb_isa_accel_interface); +err_isa_accel: + gpib_unregister_driver(&cb_pci_unaccel_interface); +err_pci_unaccel: + gpib_unregister_driver(&cb_pci_accel_interface); +err_pci_accel: + gpib_unregister_driver(&cb_isa_interface); +err_isa: + gpib_unregister_driver(&cb_pci_interface); +err_pci: + pci_unregister_driver(&cb7210_pci_driver); + + return ret; +} + +static void __exit cb7210_exit_module(void) +{ + gpib_unregister_driver(&cb_pci_interface); + gpib_unregister_driver(&cb_isa_interface); + gpib_unregister_driver(&cb_pci_accel_interface); + gpib_unregister_driver(&cb_pci_unaccel_interface); + gpib_unregister_driver(&cb_isa_accel_interface); + gpib_unregister_driver(&cb_isa_unaccel_interface); +#ifdef CONFIG_GPIB_PCMCIA + gpib_unregister_driver(&cb_pcmcia_interface); + gpib_unregister_driver(&cb_pcmcia_accel_interface); + gpib_unregister_driver(&cb_pcmcia_unaccel_interface); + cb_pcmcia_cleanup_module(); +#endif + + pci_unregister_driver(&cb7210_pci_driver); +} + +module_init(cb7210_init_module); +module_exit(cb7210_exit_module); diff --git a/drivers/gpib/cb7210/cb7210.h b/drivers/gpib/cb7210/cb7210.h new file mode 100644 index 000000000000..ddc841ff87ae --- /dev/null +++ b/drivers/gpib/cb7210/cb7210.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "nec7210.h" +#include "gpibP.h" +#include "amccs5933.h" + +#include +#include + +enum { + PCI_DEVICE_ID_CBOARDS_PCI_GPIB = 0x6, + PCI_DEVICE_ID_CBOARDS_CPCI_GPIB = 0xe, +}; + +enum pci_chip { + PCI_CHIP_NONE = 0, + PCI_CHIP_AMCC_S5933, + PCI_CHIP_QUANCOM +}; + +// struct which defines private_data for cb7210 boards +struct cb7210_priv { + struct nec7210_priv nec7210_priv; + struct pci_dev *pci_device; + // base address of amccs5933 pci chip + unsigned long amcc_iobase; + unsigned long fifo_iobase; + unsigned int irq; + enum pci_chip pci_chip; + u8 hs_mode_bits; + unsigned out_fifo_half_empty : 1; + unsigned in_fifo_half_full : 1; +}; + +// pci-gpib register offset +static const int cb7210_reg_offset = 1; + +// uses 10 ioports +static const int cb7210_iosize = 10; + +// fifo size in bytes +static const int cb7210_fifo_size = 2048; +static const int cb7210_fifo_width = 2; + +// cb7210 specific registers and bits +enum cb7210_regs { + BUS_STATUS = 0x7, +}; + +enum cb7210_page_in { + BUS_STATUS_PAGE = 1, +}; + +enum hs_regs { + // write registers + HS_MODE = 0x8, /* HS_MODE register */ + HS_INT_LEVEL = 0x9, /* HS_INT_LEVEL register */ + // read registers + HS_STATUS = 0x8, /* HS_STATUS register */ +}; + +static inline u32 nec7210_iobase(const struct cb7210_priv *cb_priv) +{ + return cb_priv->nec7210_priv.iobase; +} + +static inline int cb7210_page_in_bits(unsigned int page) +{ + return 0x50 | (page & 0xf); +} + +static inline u8 cb7210_paged_read_byte(struct cb7210_priv *cb_priv, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset); + udelay(1); + retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); + return retval; +} + +// don't use for register_num < 8, since it doesn't lock +static inline u8 cb7210_read_byte(const struct cb7210_priv *cb_priv, + enum hs_regs register_num) +{ + const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + u8 retval; + + retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset); + return retval; +} + +static inline void cb7210_paged_write_byte(struct cb7210_priv *cb_priv, u8 data, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset); + udelay(1); + outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); +} + +// don't use for register_num < 8, since it doesn't lock +static inline void cb7210_write_byte(const struct cb7210_priv *cb_priv, u8 data, + enum hs_regs register_num) +{ + const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; + + outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset); +} + +enum bus_status_bits { + BSR_ATN_BIT = 0x1, + BSR_EOI_BIT = 0x2, + BSR_SRQ_BIT = 0x4, + BSR_IFC_BIT = 0x8, + BSR_REN_BIT = 0x10, + BSR_DAV_BIT = 0x20, + BSR_NRFD_BIT = 0x40, + BSR_NDAC_BIT = 0x80, +}; + +/* CBI 488.2 HS control */ + +/* + * when both bit 0 and 1 are set, it + * 1 clears the transmit state machine to an initial condition + * 2 clears any residual interrupts left latched on cbi488.2 + * 3 resets all control bits in HS_MODE to zero + * 4 enables TX empty interrupts + * when both bit 0 and 1 are zero, then the high speed mode is disabled + */ +enum hs_mode_bits { + HS_ENABLE_MASK = 0x3, + HS_TX_ENABLE = (1 << 0), + HS_RX_ENABLE = (1 << 1), + HS_HF_INT_EN = (1 << 3), + HS_CLR_SRQ_INT = (1 << 4), + HS_CLR_EOI_EMPTY_INT = (1 << 5), + HS_CLR_HF_INT = (1 << 6), + HS_SYS_CONTROL = (1 << 7), +}; + +/* CBI 488.2 status */ +enum hs_status_bits { + HS_FIFO_FULL = (1 << 0), + HS_HALF_FULL = (1 << 1), + HS_SRQ_INT = (1 << 2), + HS_EOI_INT = (1 << 3), + HS_TX_MSB_NOT_EMPTY = (1 << 4), + HS_RX_MSB_NOT_EMPTY = (1 << 5), + HS_TX_LSB_NOT_EMPTY = (1 << 6), + HS_RX_LSB_NOT_EMPTY = (1 << 7), +}; + +/* CBI488.2 hs_int_level register */ +enum hs_int_level_bits { + HS_RESET7210 = (1 << 7), +}; + +static inline unsigned int irq_bits(unsigned int irq) +{ + switch (irq) { + case 2: + case 3: + case 4: + case 5: + return irq - 1; + case 7: + return 0x5; + case 10: + return 0x6; + case 11: + return 0x7; + default: + return 0; + } +} + +enum cb7210_aux_cmds { +/* + * AUX_RTL2 is an undocumented aux command which causes cb7210 to assert + * (and keep asserted) local rtl message. This is used in conjunction + * with the (stupid) cb7210 implementation + * of the normal nec7210 AUX_RTL aux command, which + * causes the rtl message to toggle between on and off. + */ + AUX_RTL2 = 0xd, + AUX_LO_SPEED = 0x40, + AUX_HI_SPEED = 0x41, +}; diff --git a/drivers/gpib/cec/Makefile b/drivers/gpib/cec/Makefile new file mode 100644 index 000000000000..b7141e23d4e0 --- /dev/null +++ b/drivers/gpib/cec/Makefile @@ -0,0 +1,3 @@ + +obj-$(CONFIG_GPIB_CEC_PCI) += cec_gpib.o + diff --git a/drivers/gpib/cec/cec.h b/drivers/gpib/cec/cec.h new file mode 100644 index 000000000000..3ce2869c7429 --- /dev/null +++ b/drivers/gpib/cec/cec.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#include "nec7210.h" +#include "gpibP.h" +#include "plx9050.h" + +struct cec_priv { + struct nec7210_priv nec7210_priv; + struct pci_dev *pci_device; + // base address for plx9052 pci chip + unsigned long plx_iobase; + unsigned int irq; +}; + +// offset between consecutive nec7210 registers +static const int cec_reg_offset = 1; diff --git a/drivers/gpib/cec/cec_gpib.c b/drivers/gpib/cec/cec_gpib.c new file mode 100644 index 000000000000..dbf9b95baabc --- /dev/null +++ b/drivers/gpib/cec/cec_gpib.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "cec.h" +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for CEC PCI and PCMCIA boards"); + +/* + * GPIB interrupt service routines + */ + +static irqreturn_t cec_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + struct cec_priv *priv = board->private_data; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = nec7210_interrupt(board, &priv->nec7210_priv); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +#define CEC_VENDOR_ID 0x12fc +#define CEC_DEV_ID 0x5cec +#define CEC_SUBID 0x9050 + +static int cec_pci_attach(struct gpib_board *board, const struct gpib_board_config *config); + +static void cec_pci_detach(struct gpib_board *board); + +// wrappers for interface functions +static int cec_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int cec_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int cec_command(struct gpib_board *board, u8 *buffer, + size_t length, size_t *bytes_written) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int cec_take_control(struct gpib_board *board, int synchronous) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int cec_go_to_standby(struct gpib_board *board) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static int cec_request_system_control(struct gpib_board *board, int request_control) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_request_system_control(board, &priv->nec7210_priv, request_control); +} + +static void cec_interface_clear(struct gpib_board *board, int assert) +{ + struct cec_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void cec_remote_enable(struct gpib_board *board, int enable) +{ + struct cec_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int cec_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void cec_disable_eos(struct gpib_board *board) +{ + struct cec_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int cec_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int cec_primary_address(struct gpib_board *board, unsigned int address) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int cec_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int cec_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void cec_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + struct cec_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); +} + +static void cec_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct cec_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void cec_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct cec_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static u8 cec_serial_poll_status(struct gpib_board *board) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static int cec_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct cec_priv *priv = board->private_data; + + return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec); +} + +static void cec_return_to_local(struct gpib_board *board) +{ + struct cec_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +static struct gpib_interface cec_pci_interface = { + .name = "cec_pci", + .attach = cec_pci_attach, + .detach = cec_pci_detach, + .read = cec_read, + .write = cec_write, + .command = cec_command, + .take_control = cec_take_control, + .go_to_standby = cec_go_to_standby, + .request_system_control = cec_request_system_control, + .interface_clear = cec_interface_clear, + .remote_enable = cec_remote_enable, + .enable_eos = cec_enable_eos, + .disable_eos = cec_disable_eos, + .parallel_poll = cec_parallel_poll, + .parallel_poll_configure = cec_parallel_poll_configure, + .parallel_poll_response = cec_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = NULL, // XXX + .update_status = cec_update_status, + .primary_address = cec_primary_address, + .secondary_address = cec_secondary_address, + .serial_poll_response = cec_serial_poll_response, + .serial_poll_status = cec_serial_poll_status, + .t1_delay = cec_t1_delay, + .return_to_local = cec_return_to_local, +}; + +static int cec_allocate_private(struct gpib_board *board) +{ + struct cec_priv *priv; + + board->private_data = kmalloc(sizeof(struct cec_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + priv = board->private_data; + memset(priv, 0, sizeof(struct cec_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +static void cec_free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int cec_generic_attach(struct gpib_board *board) +{ + struct cec_priv *cec_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + + if (cec_allocate_private(board)) + return -ENOMEM; + cec_priv = board->private_data; + nec_priv = &cec_priv->nec7210_priv; + nec_priv->read_byte = nec7210_ioport_read_byte; + nec_priv->write_byte = nec7210_ioport_write_byte; + nec_priv->offset = cec_reg_offset; + nec_priv->type = NEC7210; // guess + return 0; +} + +static void cec_init(struct cec_priv *cec_priv, const struct gpib_board *board) +{ + struct nec7210_priv *nec_priv = &cec_priv->nec7210_priv; + + nec7210_board_reset(nec_priv, board); + + /* set internal counter register for 8 MHz input clock */ + write_byte(nec_priv, ICR | 8, AUXMR); + + nec7210_board_online(nec_priv, board); +} + +static int cec_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct cec_priv *cec_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + + retval = cec_generic_attach(board); + if (retval) + return retval; + + cec_priv = board->private_data; + nec_priv = &cec_priv->nec7210_priv; + + // find board + cec_priv->pci_device = NULL; + while ((cec_priv->pci_device = + gpib_pci_get_device(config, CEC_VENDOR_ID, + CEC_DEV_ID, cec_priv->pci_device))) { + // check for board with plx9050 controller + if (cec_priv->pci_device->subsystem_device == CEC_SUBID) + break; + } + if (!cec_priv->pci_device) { + dev_err(board->gpib_dev, "no cec PCI board found\n"); + return -ENODEV; + } + + if (pci_enable_device(cec_priv->pci_device)) { + dev_err(board->gpib_dev, "error enabling pci device\n"); + return -EIO; + } + + if (pci_request_regions(cec_priv->pci_device, "cec-gpib")) + return -EBUSY; + + cec_priv->plx_iobase = pci_resource_start(cec_priv->pci_device, 1); + nec_priv->iobase = pci_resource_start(cec_priv->pci_device, 3); + + isr_flags |= IRQF_SHARED; + if (request_irq(cec_priv->pci_device->irq, cec_interrupt, isr_flags, DRV_NAME, board)) { + dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", cec_priv->pci_device->irq); + return -EBUSY; + } + cec_priv->irq = cec_priv->pci_device->irq; + if (gpib_request_pseudo_irq(board, cec_interrupt)) { + dev_err(board->gpib_dev, "failed to allocate pseudo irq\n"); + return -1; + } + cec_init(cec_priv, board); + + // enable interrupts on plx chip + outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT, + cec_priv->plx_iobase + PLX9050_INTCSR_REG); + + return 0; +} + +static void cec_pci_detach(struct gpib_board *board) +{ + struct cec_priv *cec_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (cec_priv) { + nec_priv = &cec_priv->nec7210_priv; + gpib_free_pseudo_irq(board); + if (cec_priv->irq) { + // disable plx9050 interrupts + outl(0, cec_priv->plx_iobase + PLX9050_INTCSR_REG); + free_irq(cec_priv->irq, board); + } + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + pci_release_regions(cec_priv->pci_device); + } + if (cec_priv->pci_device) + pci_dev_put(cec_priv->pci_device); + } + cec_free_private(board); +} + +static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static const struct pci_device_id cec_pci_table[] = { + {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 }, + {0} +}; +MODULE_DEVICE_TABLE(pci, cec_pci_table); + +static struct pci_driver cec_pci_driver = { + .name = DRV_NAME, + .id_table = cec_pci_table, + .probe = &cec_pci_probe +}; + +static int __init cec_init_module(void) +{ + int result; + + result = pci_register_driver(&cec_pci_driver); + if (result) { + pr_err("pci_register_driver failed: error = %d\n", result); + return result; + } + + result = gpib_register_driver(&cec_pci_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + return result; + } + + return 0; +} + +static void cec_exit_module(void) +{ + gpib_unregister_driver(&cec_pci_interface); + + pci_unregister_driver(&cec_pci_driver); +} + +module_init(cec_init_module); +module_exit(cec_exit_module); diff --git a/drivers/gpib/common/Makefile b/drivers/gpib/common/Makefile new file mode 100644 index 000000000000..460586edb574 --- /dev/null +++ b/drivers/gpib/common/Makefile @@ -0,0 +1,6 @@ + +obj-$(CONFIG_GPIB_COMMON) += gpib_common.o + +gpib_common-objs := gpib_os.o iblib.o + + diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c new file mode 100644 index 000000000000..9dbbac8b8436 --- /dev/null +++ b/drivers/gpib/common/gpib_os.c @@ -0,0 +1,2271 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2004 by Frank Mori Hess + *************************************************************************** + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt + +#include "ibsys.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB base support"); +MODULE_ALIAS_CHARDEV_MAJOR(GPIB_CODE); + +static int board_type_ioctl(struct gpib_file_private *file_priv, + struct gpib_board *board, unsigned long arg); +static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, + unsigned long arg); +static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, + unsigned long arg); +static int command_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, + unsigned long arg); +static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg); +static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg); +static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg); +static int wait_ioctl(struct gpib_file_private *file_priv, + struct gpib_board *board, unsigned long arg); +static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg); +static int online_ioctl(struct gpib_board *board, unsigned long arg); +static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg); +static int take_control_ioctl(struct gpib_board *board, unsigned long arg); +static int line_status_ioctl(struct gpib_board *board, unsigned long arg); +static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg); +static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg); +static int eos_ioctl(struct gpib_board *board, unsigned long arg); +static int request_service_ioctl(struct gpib_board *board, unsigned long arg); +static int request_service2_ioctl(struct gpib_board *board, unsigned long arg); +static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg); +static int irq_ioctl(struct gpib_board_config *config, unsigned long arg); +static int dma_ioctl(struct gpib_board_config *config, unsigned long arg); +static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg); +static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg); +static int timeout_ioctl(struct gpib_board *board, unsigned long arg); +static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg); +static int board_info_ioctl(const struct gpib_board *board, unsigned long arg); +static int ppc_ioctl(struct gpib_board *board, unsigned long arg); +static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg); +static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg); +static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg); +static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg); +static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg); +static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg); +static int event_ioctl(struct gpib_board *board, unsigned long arg); +static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg); +static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg); + +static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board); + +static int pop_gpib_event_nolock(struct gpib_board *board, + struct gpib_event_queue *queue, short *event_type); + +/* + * Timer functions + */ + +/* Watchdog timeout routine */ + +static void watchdog_timeout(struct timer_list *t) +{ + struct gpib_board *board = timer_container_of(board, t, timer); + + set_bit(TIMO_NUM, &board->status); + wake_up_interruptible(&board->wait); +} + +/* install timer interrupt handler */ +void os_start_timer(struct gpib_board *board, unsigned int usec_timeout) +/* Starts the timeout task */ +{ + if (timer_pending(&board->timer)) { + dev_err(board->gpib_dev, "bug! timer already running?\n"); + return; + } + clear_bit(TIMO_NUM, &board->status); + + if (usec_timeout > 0) { + board->timer.function = watchdog_timeout; + /* set number of ticks */ + mod_timer(&board->timer, jiffies + usec_to_jiffies(usec_timeout)); + } +} + +void os_remove_timer(struct gpib_board *board) +/* Removes the timeout task */ +{ + if (timer_pending(&board->timer)) + timer_delete_sync(&board->timer); +} + +int io_timed_out(struct gpib_board *board) +{ + if (test_bit(TIMO_NUM, &board->status)) + return 1; + return 0; +} + +/* + * this is a function instead of a constant because of Suse + * defining HZ to be a function call to get_hz() + */ +static inline int pseudo_irq_period(void) +{ + return (HZ + 99) / 100; +} + +static void pseudo_irq_handler(struct timer_list *t) +{ + struct gpib_pseudo_irq *pseudo_irq = timer_container_of(pseudo_irq, t, + timer); + + if (pseudo_irq->handler) + pseudo_irq->handler(0, pseudo_irq->board); + else + pr_err("gpib: bug! pseudo_irq.handler is NULL\n"); + + if (atomic_read(&pseudo_irq->active)) + mod_timer(&pseudo_irq->timer, jiffies + pseudo_irq_period()); +} + +int gpib_request_pseudo_irq(struct gpib_board *board, irqreturn_t (*handler)(int, void *)) +{ + if (timer_pending(&board->pseudo_irq.timer) || board->pseudo_irq.handler) { + dev_err(board->gpib_dev, "only one pseudo interrupt per board allowed\n"); + return -1; + } + + board->pseudo_irq.handler = handler; + board->pseudo_irq.timer.function = pseudo_irq_handler; + board->pseudo_irq.board = board; + + atomic_set(&board->pseudo_irq.active, 1); + + mod_timer(&board->pseudo_irq.timer, jiffies + pseudo_irq_period()); + + return 0; +} +EXPORT_SYMBOL(gpib_request_pseudo_irq); + +void gpib_free_pseudo_irq(struct gpib_board *board) +{ + atomic_set(&board->pseudo_irq.active, 0); + + timer_delete_sync(&board->pseudo_irq.timer); + board->pseudo_irq.handler = NULL; +} +EXPORT_SYMBOL(gpib_free_pseudo_irq); + +static const unsigned int serial_timeout = 1000000; + +unsigned int num_status_bytes(const struct gpib_status_queue *dev) +{ + if (!dev) + return 0; + return dev->num_status_bytes; +} + +// push status byte onto back of status byte fifo +int push_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 poll_byte) +{ + struct list_head *head = &device->status_bytes; + struct gpib_status_byte *status; + static const unsigned int max_num_status_bytes = 1024; + int retval; + + if (num_status_bytes(device) >= max_num_status_bytes) { + u8 lost_byte; + + device->dropped_byte = 1; + retval = pop_status_byte(board, device, &lost_byte); + if (retval < 0) + return retval; + } + + status = kmalloc(sizeof(*status), GFP_KERNEL); + if (!status) + return -ENOMEM; + + INIT_LIST_HEAD(&status->list); + status->poll_byte = poll_byte; + + list_add_tail(&status->list, head); + + device->num_status_bytes++; + + dev_dbg(board->gpib_dev, "pushed status byte 0x%x, %i in queue\n", + (int)poll_byte, num_status_bytes(device)); + + return 0; +} + +// pop status byte from front of status byte fifo +int pop_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 *poll_byte) +{ + struct list_head *head = &device->status_bytes; + struct list_head *front = head->next; + struct gpib_status_byte *status; + + if (num_status_bytes(device) == 0) + return -EIO; + + if (front == head) + return -EIO; + + if (device->dropped_byte) { + device->dropped_byte = 0; + return -EPIPE; + } + + status = list_entry(front, struct gpib_status_byte, list); + *poll_byte = status->poll_byte; + + list_del(front); + kfree(status); + + device->num_status_bytes--; + + dev_dbg(board->gpib_dev, "popped status byte 0x%x, %i in queue\n", + (int)*poll_byte, num_status_bytes(device)); + + return 0; +} + +struct gpib_status_queue *get_gpib_status_queue(struct gpib_board *board, unsigned int pad, int sad) +{ + struct gpib_status_queue *device; + struct list_head *list_ptr; + const struct list_head *head = &board->device_list; + + for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { + device = list_entry(list_ptr, struct gpib_status_queue, list); + if (gpib_address_equal(device->pad, device->sad, pad, sad)) + return device; + } + + return NULL; +} + +int get_serial_poll_byte(struct gpib_board *board, unsigned int pad, int sad, + unsigned int usec_timeout, u8 *poll_byte) +{ + struct gpib_status_queue *device; + + device = get_gpib_status_queue(board, pad, sad); + if (num_status_bytes(device)) + return pop_status_byte(board, device, poll_byte); + else + return dvrsp(board, pad, sad, usec_timeout, poll_byte); +} + +int autopoll_all_devices(struct gpib_board *board) +{ + int retval; + + if (mutex_lock_interruptible(&board->user_mutex)) + return -ERESTARTSYS; + if (mutex_lock_interruptible(&board->big_gpib_mutex)) { + mutex_unlock(&board->user_mutex); + return -ERESTARTSYS; + } + + dev_dbg(board->gpib_dev, "autopoll has board lock\n"); + + retval = serial_poll_all(board, serial_timeout); + if (retval < 0) { + mutex_unlock(&board->big_gpib_mutex); + mutex_unlock(&board->user_mutex); + return retval; + } + + dev_dbg(board->gpib_dev, "complete\n"); + /* + * need to wake wait queue in case someone is + * waiting on RQS + */ + wake_up_interruptible(&board->wait); + mutex_unlock(&board->big_gpib_mutex); + mutex_unlock(&board->user_mutex); + + return retval; +} + +static int setup_serial_poll(struct gpib_board *board, unsigned int usec_timeout) +{ + u8 cmd_string[8]; + int i; + size_t bytes_written; + int ret; + + os_start_timer(board, usec_timeout); + ret = ibcac(board, 1, 1); + if (ret < 0) { + os_remove_timer(board); + return ret; + } + + i = 0; + cmd_string[i++] = UNL; + cmd_string[i++] = MLA(board->pad); /* controller's listen address */ + if (board->sad >= 0) + cmd_string[i++] = MSA(board->sad); + cmd_string[i++] = SPE; // serial poll enable + + ret = board->interface->command(board, cmd_string, i, &bytes_written); + if (ret < 0 || bytes_written < i) { + dev_dbg(board->gpib_dev, "failed to setup serial poll\n"); + os_remove_timer(board); + return -EIO; + } + os_remove_timer(board); + + return 0; +} + +static int read_serial_poll_byte(struct gpib_board *board, unsigned int pad, + int sad, unsigned int usec_timeout, u8 *result) +{ + u8 cmd_string[8]; + int end_flag; + int ret; + int i; + size_t nbytes; + + dev_dbg(board->gpib_dev, "entering pad=%i sad=%i\n", pad, sad); + + os_start_timer(board, usec_timeout); + ret = ibcac(board, 1, 1); + if (ret < 0) { + os_remove_timer(board); + return ret; + } + + i = 0; + // send talk address + cmd_string[i++] = MTA(pad); + if (sad >= 0) + cmd_string[i++] = MSA(sad); + + ret = board->interface->command(board, cmd_string, i, &nbytes); + if (ret < 0 || nbytes < i) { + dev_err(board->gpib_dev, "failed to setup serial poll\n"); + os_remove_timer(board); + return -EIO; + } + + ibgts(board); + + // read poll result + ret = board->interface->read(board, result, 1, &end_flag, &nbytes); + if (ret < 0 || nbytes < 1) { + dev_err(board->gpib_dev, "serial poll failed\n"); + os_remove_timer(board); + return -EIO; + } + os_remove_timer(board); + + return 0; +} + +static int cleanup_serial_poll(struct gpib_board *board, unsigned int usec_timeout) +{ + u8 cmd_string[8]; + int ret; + size_t bytes_written; + + os_start_timer(board, usec_timeout); + ret = ibcac(board, 1, 1); + if (ret < 0) { + os_remove_timer(board); + return ret; + } + + cmd_string[0] = SPD; /* disable serial poll bytes */ + cmd_string[1] = UNT; + ret = board->interface->command(board, cmd_string, 2, &bytes_written); + if (ret < 0 || bytes_written < 2) { + dev_err(board->gpib_dev, "failed to disable serial poll\n"); + os_remove_timer(board); + return -EIO; + } + os_remove_timer(board); + + return 0; +} + +static int serial_poll_single(struct gpib_board *board, unsigned int pad, int sad, + unsigned int usec_timeout, u8 *result) +{ + int retval, cleanup_retval; + + retval = setup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + retval = read_serial_poll_byte(board, pad, sad, usec_timeout, result); + cleanup_retval = cleanup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + if (cleanup_retval < 0) + return retval; + + return 0; +} + +int serial_poll_all(struct gpib_board *board, unsigned int usec_timeout) +{ + int retval = 0; + struct list_head *cur; + const struct list_head *head = NULL; + struct gpib_status_queue *device; + u8 result; + unsigned int num_bytes = 0; + + head = &board->device_list; + if (head->next == head) + return 0; + + retval = setup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + + for (cur = head->next; cur != head; cur = cur->next) { + device = list_entry(cur, struct gpib_status_queue, list); + retval = read_serial_poll_byte(board, + device->pad, device->sad, usec_timeout, &result); + if (retval < 0) + continue; + if (result & request_service_bit) { + retval = push_status_byte(board, device, result); + if (retval < 0) + continue; + num_bytes++; + } + } + + retval = cleanup_serial_poll(board, usec_timeout); + if (retval < 0) + return retval; + + return num_bytes; +} + +/* + * DVRSP + * This function performs a serial poll of the device with primary + * address pad and secondary address sad. If the device has no + * secondary address, pass a negative number in for this argument. At the + * end of a successful serial poll the response is returned in result. + * SPD and UNT are sent at the completion of the poll. + */ + +int dvrsp(struct gpib_board *board, unsigned int pad, int sad, + unsigned int usec_timeout, u8 *result) +{ + int status = ibstatus(board); + int retval; + + if ((status & CIC) == 0) { + dev_err(board->gpib_dev, "not CIC during serial poll\n"); + return -1; + } + + if (pad > MAX_GPIB_PRIMARY_ADDRESS || sad > MAX_GPIB_SECONDARY_ADDRESS || sad < -1) { + dev_err(board->gpib_dev, "bad address for serial poll"); + return -1; + } + + retval = serial_poll_single(board, pad, sad, usec_timeout, result); + if (io_timed_out(board)) + retval = -ETIMEDOUT; + + return retval; +} + +static struct gpib_descriptor *handle_to_descriptor(const struct gpib_file_private *file_priv, + int handle) +{ + if (handle < 0 || handle >= GPIB_MAX_NUM_DESCRIPTORS) { + pr_err("gpib: invalid handle %i\n", handle); + return NULL; + } + + return file_priv->descriptors[handle]; +} + +static int init_gpib_file_private(struct gpib_file_private *priv) +{ + memset(priv, 0, sizeof(*priv)); + atomic_set(&priv->holding_mutex, 0); + priv->descriptors[0] = kmalloc(sizeof(struct gpib_descriptor), GFP_KERNEL); + if (!priv->descriptors[0]) { + pr_err("gpib: failed to allocate default board descriptor\n"); + return -ENOMEM; + } + init_gpib_descriptor(priv->descriptors[0]); + priv->descriptors[0]->is_board = 1; + mutex_init(&priv->descriptors_mutex); + return 0; +} + +int ibopen(struct inode *inode, struct file *filep) +{ + unsigned int minor = iminor(inode); + struct gpib_board *board; + struct gpib_file_private *priv; + + if (minor >= GPIB_MAX_NUM_BOARDS) { + pr_err("gpib: invalid minor number of device file\n"); + return -ENXIO; + } + + board = &board_array[minor]; + + filep->private_data = kmalloc(sizeof(struct gpib_file_private), GFP_KERNEL); + if (!filep->private_data) + return -ENOMEM; + + priv = filep->private_data; + init_gpib_file_private((struct gpib_file_private *)filep->private_data); + + if (board->use_count == 0) { + int retval; + + retval = request_module("gpib%i", minor); + if (retval) + dev_dbg(board->gpib_dev, "request module returned %i\n", retval); + } + if (board->interface) { + if (!try_module_get(board->provider_module)) { + dev_err(board->gpib_dev, "try_module_get() failed\n"); + return -EIO; + } + board->use_count++; + priv->got_module = 1; + } + return 0; +} + +int ibclose(struct inode *inode, struct file *filep) +{ + unsigned int minor = iminor(inode); + struct gpib_board *board; + struct gpib_file_private *priv = filep->private_data; + struct gpib_descriptor *desc; + + if (minor >= GPIB_MAX_NUM_BOARDS) { + pr_err("gpib: invalid minor number of device file\n"); + return -ENODEV; + } + + board = &board_array[minor]; + + if (priv) { + desc = handle_to_descriptor(priv, 0); + if (desc) { + if (desc->autopoll_enabled) { + dev_dbg(board->gpib_dev, "decrementing autospollers\n"); + if (board->autospollers > 0) + board->autospollers--; + else + dev_err(board->gpib_dev, + "Attempt to decrement zero autospollers\n"); + } + } else { + dev_err(board->gpib_dev, "Unexpected null gpib_descriptor\n"); + } + + cleanup_open_devices(priv, board); + + if (atomic_read(&priv->holding_mutex)) + mutex_unlock(&board->user_mutex); + + if (priv->got_module && board->use_count) { + module_put(board->provider_module); + --board->use_count; + } + + kfree(filep->private_data); + filep->private_data = NULL; + } + + return 0; +} + +long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + unsigned int minor = iminor(file_inode(filep)); + struct gpib_board *board; + struct gpib_file_private *file_priv = filep->private_data; + long retval = -ENOTTY; + + if (minor >= GPIB_MAX_NUM_BOARDS) { + pr_err("gpib: invalid minor number of device file\n"); + return -ENODEV; + } + board = &board_array[minor]; + + if (mutex_lock_interruptible(&board->big_gpib_mutex)) + return -ERESTARTSYS; + + dev_dbg(board->gpib_dev, "ioctl %d, interface=%s, use=%d, onl=%d\n", + cmd & 0xff, + board->interface ? board->interface->name : "", + board->use_count, + board->online); + + switch (cmd) { + case CFCBOARDTYPE: + retval = board_type_ioctl(file_priv, board, arg); + goto done; + case IBONL: + retval = online_ioctl(board, arg); + goto done; + default: + break; + } + if (!board->interface) { + dev_err(board->gpib_dev, "no gpib board configured\n"); + retval = -ENODEV; + goto done; + } + if (file_priv->got_module == 0) { + if (!try_module_get(board->provider_module)) { + dev_err(board->gpib_dev, "try_module_get() failed\n"); + retval = -EIO; + goto done; + } + file_priv->got_module = 1; + board->use_count++; + } + switch (cmd) { + case CFCBASE: + retval = iobase_ioctl(&board->config, arg); + goto done; + case CFCIRQ: + retval = irq_ioctl(&board->config, arg); + goto done; + case CFCDMA: + retval = dma_ioctl(&board->config, arg); + goto done; + case IBAUTOSPOLL: + retval = autospoll_ioctl(board, file_priv, arg); + goto done; + case IBBOARD_INFO: + retval = board_info_ioctl(board, arg); + goto done; + case IBMUTEX: + /* + * Need to unlock board->big_gpib_mutex before potentially locking board->user_mutex + * to maintain consistent locking order + */ + mutex_unlock(&board->big_gpib_mutex); + return mutex_ioctl(board, file_priv, arg); + case IBPAD: + retval = pad_ioctl(board, file_priv, arg); + goto done; + case IBSAD: + retval = sad_ioctl(board, file_priv, arg); + goto done; + case IBSELECT_PCI: + retval = select_pci_ioctl(&board->config, arg); + goto done; + case IBSELECT_DEVICE_PATH: + retval = select_device_path_ioctl(&board->config, arg); + goto done; + default: + break; + } + + if (!board->online) { + retval = -EINVAL; + goto done; + } + + switch (cmd) { + case IBEVENT: + retval = event_ioctl(board, arg); + goto done; + case IBCLOSEDEV: + retval = close_dev_ioctl(filep, board, arg); + goto done; + case IBOPENDEV: + retval = open_dev_ioctl(filep, board, arg); + goto done; + case IBSPOLL_BYTES: + retval = status_bytes_ioctl(board, arg); + goto done; + case IBWAIT: + retval = wait_ioctl(file_priv, board, arg); + if (retval == -ERESTARTSYS) + return retval; + goto done; + case IBLINES: + retval = line_status_ioctl(board, arg); + goto done; + case IBLOC: + board->interface->return_to_local(board); + retval = 0; + goto done; + default: + break; + } + + spin_lock(&board->locking_pid_spinlock); + if (current->pid != board->locking_pid) { + spin_unlock(&board->locking_pid_spinlock); + retval = -EPERM; + goto done; + } + spin_unlock(&board->locking_pid_spinlock); + + switch (cmd) { + case IB_T1_DELAY: + retval = t1_delay_ioctl(board, arg); + goto done; + case IBCAC: + retval = take_control_ioctl(board, arg); + goto done; + case IBCMD: + /* + * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex + * before we call them. + */ + mutex_unlock(&board->big_gpib_mutex); + return command_ioctl(file_priv, board, arg); + case IBEOS: + retval = eos_ioctl(board, arg); + goto done; + case IBGTS: + retval = ibgts(board); + goto done; + case IBPPC: + retval = ppc_ioctl(board, arg); + goto done; + case IBPP2_SET: + retval = set_local_ppoll_mode_ioctl(board, arg); + goto done; + case IBPP2_GET: + retval = get_local_ppoll_mode_ioctl(board, arg); + goto done; + case IBQUERY_BOARD_RSV: + retval = query_board_rsv_ioctl(board, arg); + goto done; + case IBRD: + /* + * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex + * before we call them. + */ + mutex_unlock(&board->big_gpib_mutex); + return read_ioctl(file_priv, board, arg); + case IBRPP: + retval = parallel_poll_ioctl(board, arg); + goto done; + case IBRSC: + retval = request_system_control_ioctl(board, arg); + goto done; + case IBRSP: + retval = serial_poll_ioctl(board, arg); + goto done; + case IBRSV: + retval = request_service_ioctl(board, arg); + goto done; + case IBRSV2: + retval = request_service2_ioctl(board, arg); + goto done; + case IBSIC: + retval = interface_clear_ioctl(board, arg); + goto done; + case IBSRE: + retval = remote_enable_ioctl(board, arg); + goto done; + case IBTMO: + retval = timeout_ioctl(board, arg); + goto done; + case IBWRT: + /* + * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex + * before we call them. + */ + mutex_unlock(&board->big_gpib_mutex); + return write_ioctl(file_priv, board, arg); + default: + retval = -ENOTTY; + goto done; + } + +done: + mutex_unlock(&board->big_gpib_mutex); + dev_dbg(board->gpib_dev, "ioctl done status = 0x%lx\n", board->status); + return retval; +} + +static int board_type_ioctl(struct gpib_file_private *file_priv, + struct gpib_board *board, unsigned long arg) +{ + struct list_head *list_ptr; + struct gpib_board_type_ioctl cmd; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (board->online) + return -EBUSY; + + retval = copy_from_user(&cmd, (void __user *)arg, + sizeof(struct gpib_board_type_ioctl)); + if (retval) + return -EFAULT; + + for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers; + list_ptr = list_ptr->next) { + struct gpib_interface_list *entry; + + entry = list_entry(list_ptr, struct gpib_interface_list, list); + if (strcmp(entry->interface->name, cmd.name) == 0) { + int i; + int had_module = file_priv->got_module; + + if (board->use_count) { + for (i = 0; i < board->use_count; ++i) + module_put(board->provider_module); + board->interface = NULL; + file_priv->got_module = 0; + } + board->interface = entry->interface; + board->provider_module = entry->module; + for (i = 0; i < board->use_count; ++i) { + if (!try_module_get(entry->module)) { + board->use_count = i; + return -EIO; + } + } + if (had_module == 0) { + if (!try_module_get(entry->module)) + return -EIO; + ++board->use_count; + } + file_priv->got_module = 1; + return 0; + } + } + + return -EINVAL; +} + +static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, + unsigned long arg) +{ + struct gpib_read_write_ioctl read_cmd; + u8 __user *userbuf; + unsigned long remain; + int end_flag = 0; + int retval; + ssize_t read_ret = 0; + struct gpib_descriptor *desc; + size_t nbytes; + + retval = copy_from_user(&read_cmd, (void __user *)arg, sizeof(read_cmd)); + if (retval) + return -EFAULT; + + if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count) + return -EINVAL; + + desc = handle_to_descriptor(file_priv, read_cmd.handle); + if (!desc) + return -EINVAL; + + if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr))) + return -EFAULT; + + userbuf = (u8 __user *)(unsigned long)read_cmd.buffer_ptr; + userbuf += read_cmd.completed_transfer_count; + + remain = read_cmd.requested_transfer_count - read_cmd.completed_transfer_count; + + /* Check write access to buffer */ + if (!access_ok(userbuf, remain)) + return -EFAULT; + + atomic_set(&desc->io_in_progress, 1); + + /* Read buffer loads till we fill the user supplied buffer */ + while (remain > 0 && end_flag == 0) { + nbytes = 0; + read_ret = ibrd(board, board->buffer, (board->buffer_length < remain) ? + board->buffer_length : remain, &end_flag, &nbytes); + if (nbytes == 0) + break; + retval = copy_to_user(userbuf, board->buffer, nbytes); + if (retval) { + retval = -EFAULT; + break; + } + remain -= nbytes; + userbuf += nbytes; + if (read_ret < 0) + break; + } + read_cmd.completed_transfer_count = read_cmd.requested_transfer_count - remain; + read_cmd.end = end_flag; + /* + * suppress errors (for example due to timeout or interruption by device clear) + * if all bytes got sent. This prevents races that can occur in the various drivers + * if a device receives a device clear immediately after a transfer completes and + * the driver code wasn't careful enough to handle that case. + */ + if (remain == 0 || end_flag) + read_ret = 0; + if (retval == 0) + retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd)); + + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + if (retval) + return -EFAULT; + + return read_ret; +} + +static int command_ioctl(struct gpib_file_private *file_priv, + struct gpib_board *board, unsigned long arg) +{ + struct gpib_read_write_ioctl cmd; + u8 __user *userbuf; + unsigned long remain; + int retval; + int fault = 0; + struct gpib_descriptor *desc; + size_t bytes_written; + int no_clear_io_in_prog; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (cmd.completed_transfer_count > cmd.requested_transfer_count) + return -EINVAL; + + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) + return -EINVAL; + + userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr; + userbuf += cmd.completed_transfer_count; + + no_clear_io_in_prog = cmd.end; + cmd.end = 0; + + remain = cmd.requested_transfer_count - cmd.completed_transfer_count; + + /* Check read access to buffer */ + if (!access_ok(userbuf, remain)) + return -EFAULT; + + /* + * Write buffer loads till we empty the user supplied buffer. + * Call drivers at least once, even if remain is zero, in + * order to allow them to insure previous commands were + * completely finished, in the case of a restarted ioctl. + */ + + atomic_set(&desc->io_in_progress, 1); + + do { + fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? + board->buffer_length : remain); + if (fault) { + retval = -EFAULT; + bytes_written = 0; + } else { + retval = ibcmd(board, board->buffer, (board->buffer_length < remain) ? + board->buffer_length : remain, &bytes_written); + } + remain -= bytes_written; + userbuf += bytes_written; + if (retval < 0) { + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + break; + } + } while (remain > 0); + + cmd.completed_transfer_count = cmd.requested_transfer_count - remain; + + if (fault == 0) + fault = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); + + /* + * no_clear_io_in_prog (cmd.end) is true when io_in_progress should + * not be set to zero because the cmd in progress is the address setup + * operation for an async read or write. This causes CMPL not to be set + * in general_ibstatus until the async read or write completes. + */ + if (!no_clear_io_in_prog || fault) + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + if (fault) + return -EFAULT; + + return retval; +} + +static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, + unsigned long arg) +{ + struct gpib_read_write_ioctl write_cmd; + u8 __user *userbuf; + unsigned long remain; + int retval = 0; + int fault; + struct gpib_descriptor *desc; + + fault = copy_from_user(&write_cmd, (void __user *)arg, sizeof(write_cmd)); + if (fault) + return -EFAULT; + + if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count) + return -EINVAL; + + desc = handle_to_descriptor(file_priv, write_cmd.handle); + if (!desc) + return -EINVAL; + + userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr; + userbuf += write_cmd.completed_transfer_count; + + remain = write_cmd.requested_transfer_count - write_cmd.completed_transfer_count; + + /* Check read access to buffer */ + if (!access_ok(userbuf, remain)) + return -EFAULT; + + atomic_set(&desc->io_in_progress, 1); + + /* Write buffer loads till we empty the user supplied buffer */ + while (remain > 0) { + int send_eoi; + size_t bytes_written = 0; + + send_eoi = remain <= board->buffer_length && write_cmd.end; + fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? + board->buffer_length : remain); + if (fault) { + retval = -EFAULT; + break; + } + retval = ibwrt(board, board->buffer, (board->buffer_length < remain) ? + board->buffer_length : remain, send_eoi, &bytes_written); + remain -= bytes_written; + userbuf += bytes_written; + if (retval < 0) + break; + } + write_cmd.completed_transfer_count = write_cmd.requested_transfer_count - remain; + /* + * suppress errors (for example due to timeout or interruption by device clear) + * if all bytes got sent. This prevents races that can occur in the various drivers + * if a device receives a device clear immediately after a transfer completes and + * the driver code wasn't careful enough to handle that case. + */ + if (remain == 0) + retval = 0; + if (fault == 0) + fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd)); + + atomic_set(&desc->io_in_progress, 0); + + wake_up_interruptible(&board->wait); + if (fault) + return -EFAULT; + + return retval; +} + +static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg) +{ + struct gpib_status_queue *device; + struct gpib_spoll_bytes_ioctl cmd; + int retval; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + device = get_gpib_status_queue(board, cmd.pad, cmd.sad); + if (!device) + cmd.num_bytes = 0; + else + cmd.num_bytes = num_status_bytes(device); + + retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int increment_open_device_count(struct gpib_board *board, struct list_head *head, + unsigned int pad, int sad) +{ + struct list_head *list_ptr; + struct gpib_status_queue *device; + + /* + * first see if address has already been opened, then increment + * open count + */ + for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { + device = list_entry(list_ptr, struct gpib_status_queue, list); + if (gpib_address_equal(device->pad, device->sad, pad, sad)) { + dev_dbg(board->gpib_dev, "incrementing open count for pad %i, sad %i\n", + device->pad, device->sad); + device->reference_count++; + return 0; + } + } + + /* otherwise we need to allocate a new struct gpib_status_queue */ + device = kmalloc(sizeof(struct gpib_status_queue), GFP_ATOMIC); + if (!device) + return -ENOMEM; + init_gpib_status_queue(device); + device->pad = pad; + device->sad = sad; + device->reference_count = 1; + + list_add(&device->list, head); + + dev_dbg(board->gpib_dev, "opened pad %i, sad %i\n", device->pad, device->sad); + + return 0; +} + +static int subtract_open_device_count(struct gpib_board *board, struct list_head *head, + unsigned int pad, int sad, unsigned int count) +{ + struct gpib_status_queue *device; + struct list_head *list_ptr; + + for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { + device = list_entry(list_ptr, struct gpib_status_queue, list); + if (gpib_address_equal(device->pad, device->sad, pad, sad)) { + dev_dbg(board->gpib_dev, "decrementing open count for pad %i, sad %i\n", + device->pad, device->sad); + if (count > device->reference_count) { + dev_err(board->gpib_dev, "bug! in %s()\n", __func__); + return -EINVAL; + } + device->reference_count -= count; + if (device->reference_count == 0) { + dev_dbg(board->gpib_dev, "closing pad %i, sad %i\n", + device->pad, device->sad); + list_del(list_ptr); + kfree(device); + } + return 0; + } + } + dev_err(board->gpib_dev, "bug! tried to close address that was never opened!\n"); + return -EINVAL; +} + +static inline int decrement_open_device_count(struct gpib_board *board, struct list_head *head, + unsigned int pad, int sad) +{ + return subtract_open_device_count(board, head, pad, sad, 1); +} + +static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board) +{ + int retval = 0; + int i; + + for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) { + struct gpib_descriptor *desc; + + desc = file_priv->descriptors[i]; + if (!desc) + continue; + + if (desc->is_board == 0) { + retval = decrement_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + } + kfree(desc); + file_priv->descriptors[i] = NULL; + } + + return 0; +} + +static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg) +{ + struct gpib_open_dev_ioctl open_dev_cmd; + int retval; + struct gpib_file_private *file_priv = filep->private_data; + int i; + + retval = copy_from_user(&open_dev_cmd, (void __user *)arg, sizeof(open_dev_cmd)); + if (retval) + return -EFAULT; + + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) + if (!file_priv->descriptors[i]) + break; + if (i == GPIB_MAX_NUM_DESCRIPTORS) { + mutex_unlock(&file_priv->descriptors_mutex); + return -ERANGE; + } + file_priv->descriptors[i] = kmalloc(sizeof(struct gpib_descriptor), GFP_KERNEL); + if (!file_priv->descriptors[i]) { + mutex_unlock(&file_priv->descriptors_mutex); + return -ENOMEM; + } + init_gpib_descriptor(file_priv->descriptors[i]); + + file_priv->descriptors[i]->pad = open_dev_cmd.pad; + file_priv->descriptors[i]->sad = open_dev_cmd.sad; + file_priv->descriptors[i]->is_board = open_dev_cmd.is_board; + mutex_unlock(&file_priv->descriptors_mutex); + + retval = increment_open_device_count(board, &board->device_list, open_dev_cmd.pad, + open_dev_cmd.sad); + if (retval < 0) + return retval; + + /* + * clear stuck srq state, since we may be able to find service request on + * the new device + */ + atomic_set(&board->stuck_srq, 0); + + open_dev_cmd.handle = i; + retval = copy_to_user((void __user *)arg, &open_dev_cmd, sizeof(open_dev_cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg) +{ + struct gpib_close_dev_ioctl cmd; + struct gpib_file_private *file_priv = filep->private_data; + int retval; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS) + return -EINVAL; + if (!file_priv->descriptors[cmd.handle]) + return -EINVAL; + + retval = decrement_open_device_count(board, &board->device_list, + file_priv->descriptors[cmd.handle]->pad, + file_priv->descriptors[cmd.handle]->sad); + if (retval < 0) + return retval; + + kfree(file_priv->descriptors[cmd.handle]); + file_priv->descriptors[cmd.handle] = NULL; + + return 0; +} + +static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg) +{ + struct gpib_serial_poll_ioctl serial_cmd; + int retval; + + retval = copy_from_user(&serial_cmd, (void __user *)arg, sizeof(serial_cmd)); + if (retval) + return -EFAULT; + + retval = get_serial_poll_byte(board, serial_cmd.pad, serial_cmd.sad, board->usec_timeout, + &serial_cmd.status_byte); + if (retval < 0) + return retval; + + retval = copy_to_user((void __user *)arg, &serial_cmd, sizeof(serial_cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, + unsigned long arg) +{ + struct gpib_wait_ioctl wait_cmd; + int retval; + struct gpib_descriptor *desc; + + retval = copy_from_user(&wait_cmd, (void __user *)arg, sizeof(wait_cmd)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, wait_cmd.handle); + if (!desc) + return -EINVAL; + + retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask, + wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc); + if (retval < 0) + return retval; + + retval = copy_to_user((void __user *)arg, &wait_cmd, sizeof(wait_cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg) +{ + u8 poll_byte; + int retval; + + retval = ibrpp(board, &poll_byte); + if (retval < 0) + return retval; + + retval = copy_to_user((void __user *)arg, &poll_byte, sizeof(poll_byte)); + if (retval) + return -EFAULT; + + return 0; +} + +static int online_ioctl(struct gpib_board *board, unsigned long arg) +{ + struct gpib_online_ioctl online_cmd; + int retval; + void __user *init_data = NULL; + + board->config.init_data = NULL; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&online_cmd, (void __user *)arg, sizeof(online_cmd)); + if (retval) + return -EFAULT; + if (online_cmd.init_data_length > 0) { + board->config.init_data = vmalloc(online_cmd.init_data_length); + if (!board->config.init_data) + return -ENOMEM; + if (WARN_ON_ONCE(sizeof(init_data) > sizeof(online_cmd.init_data_ptr))) + return -EFAULT; + init_data = (void __user *)(unsigned long)(online_cmd.init_data_ptr); + retval = copy_from_user(board->config.init_data, init_data, + online_cmd.init_data_length); + if (retval) { + vfree(board->config.init_data); + return -EFAULT; + } + board->config.init_data_length = online_cmd.init_data_length; + } else { + board->config.init_data = NULL; + board->config.init_data_length = 0; + } + if (online_cmd.online) + retval = ibonline(board); + else + retval = iboffline(board); + if (board->config.init_data) { + vfree(board->config.init_data); + board->config.init_data = NULL; + board->config.init_data_length = 0; + } + return retval; +} + +static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg) +{ + int enable; + int retval; + + retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable)); + if (retval) + return -EFAULT; + + return ibsre(board, enable); +} + +static int take_control_ioctl(struct gpib_board *board, unsigned long arg) +{ + int synchronous; + int retval; + + retval = copy_from_user(&synchronous, (void __user *)arg, sizeof(synchronous)); + if (retval) + return -EFAULT; + + return ibcac(board, synchronous, 1); +} + +static int line_status_ioctl(struct gpib_board *board, unsigned long arg) +{ + short lines; + int retval; + + retval = iblines(board, &lines); + if (retval < 0) + return retval; + + retval = copy_to_user((void __user *)arg, &lines, sizeof(lines)); + if (retval) + return -EFAULT; + + return 0; +} + +static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg) +{ + struct gpib_pad_ioctl cmd; + int retval; + struct gpib_descriptor *desc; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) + return -EINVAL; + + if (desc->is_board) { + retval = ibpad(board, cmd.pad); + if (retval < 0) + return retval; + } else { + retval = decrement_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + + desc->pad = cmd.pad; + + retval = increment_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + } + + return 0; +} + +static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg) +{ + struct gpib_sad_ioctl cmd; + int retval; + struct gpib_descriptor *desc; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) + return -EINVAL; + + if (desc->is_board) { + retval = ibsad(board, cmd.sad); + if (retval < 0) + return retval; + } else { + retval = decrement_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + + desc->sad = cmd.sad; + + retval = increment_open_device_count(board, &board->device_list, desc->pad, + desc->sad); + if (retval < 0) + return retval; + } + return 0; +} + +static int eos_ioctl(struct gpib_board *board, unsigned long arg) +{ + struct gpib_eos_ioctl eos_cmd; + int retval; + + retval = copy_from_user(&eos_cmd, (void __user *)arg, sizeof(eos_cmd)); + if (retval) + return -EFAULT; + + return ibeos(board, eos_cmd.eos, eos_cmd.eos_flags); +} + +static int request_service_ioctl(struct gpib_board *board, unsigned long arg) +{ + u8 status_byte; + int retval; + + retval = copy_from_user(&status_byte, (void __user *)arg, sizeof(status_byte)); + if (retval) + return -EFAULT; + + return ibrsv2(board, status_byte, status_byte & request_service_bit); +} + +static int request_service2_ioctl(struct gpib_board *board, unsigned long arg) +{ + struct gpib_request_service2 request_service2_cmd; + int retval; + + retval = copy_from_user(&request_service2_cmd, (void __user *)arg, + sizeof(struct gpib_request_service2)); + if (retval) + return -EFAULT; + + return ibrsv2(board, request_service2_cmd.status_byte, + request_service2_cmd.new_reason_for_service); +} + +static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg) +{ + u64 base_addr; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&base_addr, (void __user *)arg, sizeof(base_addr)); + if (retval) + return -EFAULT; + + if (WARN_ON_ONCE(sizeof(void *) > sizeof(base_addr))) + return -EFAULT; + config->ibbase = base_addr; + + return 0; +} + +static int irq_ioctl(struct gpib_board_config *config, unsigned long arg) +{ + unsigned int irq; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&irq, (void __user *)arg, sizeof(irq)); + if (retval) + return -EFAULT; + + config->ibirq = irq; + + return 0; +} + +static int dma_ioctl(struct gpib_board_config *config, unsigned long arg) +{ + unsigned int dma_channel; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&dma_channel, (void __user *)arg, sizeof(dma_channel)); + if (retval) + return -EFAULT; + + config->ibdma = dma_channel; + + return 0; +} + +static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg) +{ + short enable; + int retval; + struct gpib_descriptor *desc; + + retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable)); + if (retval) + return -EFAULT; + + desc = handle_to_descriptor(file_priv, 0); /* board handle is 0 */ + + if (enable) { + if (!desc->autopoll_enabled) { + board->autospollers++; + desc->autopoll_enabled = 1; + } + retval = 0; + } else { + if (desc->autopoll_enabled) { + desc->autopoll_enabled = 0; + if (board->autospollers > 0) { + board->autospollers--; + retval = 0; + } else { + dev_err(board->gpib_dev, + "tried to set number of autospollers negative\n"); + retval = -EINVAL; + } + } else { + dev_err(board->gpib_dev, "autopoll disable requested before enable\n"); + retval = -EINVAL; + } + } + return retval; +} + +static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, + unsigned long arg) +{ + int retval, lock_mutex; + + retval = copy_from_user(&lock_mutex, (void __user *)arg, sizeof(lock_mutex)); + if (retval) + return -EFAULT; + + if (lock_mutex) { + retval = mutex_lock_interruptible(&board->user_mutex); + if (retval) + return -ERESTARTSYS; + + spin_lock(&board->locking_pid_spinlock); + board->locking_pid = current->pid; + spin_unlock(&board->locking_pid_spinlock); + + atomic_set(&file_priv->holding_mutex, 1); + + dev_dbg(board->gpib_dev, "locked board mutex\n"); + } else { + spin_lock(&board->locking_pid_spinlock); + if (current->pid != board->locking_pid) { + dev_err(board->gpib_dev, "bug! pid %i tried to release mutex held by pid %i\n", + current->pid, board->locking_pid); + spin_unlock(&board->locking_pid_spinlock); + return -EPERM; + } + board->locking_pid = 0; + spin_unlock(&board->locking_pid_spinlock); + + atomic_set(&file_priv->holding_mutex, 0); + + mutex_unlock(&board->user_mutex); + dev_dbg(board->gpib_dev, "unlocked board mutex\n"); + } + return 0; +} + +static int timeout_ioctl(struct gpib_board *board, unsigned long arg) +{ + unsigned int timeout; + int retval; + + retval = copy_from_user(&timeout, (void __user *)arg, sizeof(timeout)); + if (retval) + return -EFAULT; + + board->usec_timeout = timeout; + dev_dbg(board->gpib_dev, "timeout set to %i usec\n", timeout); + + return 0; +} + +static int ppc_ioctl(struct gpib_board *board, unsigned long arg) +{ + struct gpib_ppoll_config_ioctl cmd; + int retval; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (cmd.set_ist) { + board->ist = 1; + board->interface->parallel_poll_response(board, board->ist); + } else if (cmd.clear_ist) { + board->ist = 0; + board->interface->parallel_poll_response(board, board->ist); + } + + if (cmd.config) { + retval = ibppc(board, cmd.config); + if (retval < 0) + return retval; + } + + return 0; +} + +static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg) +{ + short cmd; + int retval; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + if (!board->interface->local_parallel_poll_mode) + return -ENOENT; + board->local_ppoll_mode = cmd != 0; + board->interface->local_parallel_poll_mode(board, board->local_ppoll_mode); + + return 0; +} + +static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg) +{ + short cmd; + int retval; + + cmd = board->local_ppoll_mode; + retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); + if (retval) + return -EFAULT; + + return 0; +} + +static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg) +{ + int status; + int retval; + + status = board->interface->serial_poll_status(board); + + retval = copy_to_user((void __user *)arg, &status, sizeof(status)); + if (retval) + return -EFAULT; + + return 0; +} + +static int board_info_ioctl(const struct gpib_board *board, unsigned long arg) +{ + struct gpib_board_info_ioctl info = { }; + int retval; + + info.pad = board->pad; + info.sad = board->sad; + info.parallel_poll_configuration = board->parallel_poll_configuration; + info.is_system_controller = board->master; + if (board->autospollers) + info.autopolling = 1; + else + info.autopolling = 0; + info.t1_delay = board->t1_nano_sec; + info.ist = board->ist; + info.no_7_bit_eos = board->interface->no_7_bit_eos; + retval = copy_to_user((void __user *)arg, &info, sizeof(info)); + if (retval) + return -EFAULT; + + return 0; +} + +static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg) +{ + unsigned int usec_duration; + int retval; + + retval = copy_from_user(&usec_duration, (void __user *)arg, sizeof(usec_duration)); + if (retval) + return -EFAULT; + + return ibsic(board, usec_duration); +} + +static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg) +{ + struct gpib_select_pci_ioctl selection; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + retval = copy_from_user(&selection, (void __user *)arg, sizeof(selection)); + if (retval) + return -EFAULT; + + config->pci_bus = selection.pci_bus; + config->pci_slot = selection.pci_slot; + + return 0; +} + +static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg) +{ + struct gpib_select_device_path_ioctl *selection; + int retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + selection = vmalloc(sizeof(struct gpib_select_device_path_ioctl)); + if (!selection) + return -ENOMEM; + + retval = copy_from_user(selection, (void __user *)arg, + sizeof(struct gpib_select_device_path_ioctl)); + if (retval) { + vfree(selection); + return -EFAULT; + } + + selection->device_path[sizeof(selection->device_path) - 1] = '\0'; + kfree(config->device_path); + config->device_path = NULL; + if (strlen(selection->device_path) > 0) + config->device_path = kstrdup(selection->device_path, GFP_KERNEL); + + vfree(selection); + return 0; +} + +unsigned int num_gpib_events(const struct gpib_event_queue *queue) +{ + return queue->num_events; +} + +static int push_gpib_event_nolock(struct gpib_board *board, short event_type) +{ + struct gpib_event_queue *queue = &board->event_queue; + struct list_head *head = &queue->event_head; + struct gpib_event *event; + static const unsigned int max_num_events = 1024; + int retval; + + if (num_gpib_events(queue) >= max_num_events) { + short lost_event; + + queue->dropped_event = 1; + retval = pop_gpib_event_nolock(board, queue, &lost_event); + if (retval < 0) + return retval; + } + + event = kmalloc(sizeof(struct gpib_event), GFP_ATOMIC); + if (!event) { + queue->dropped_event = 1; + dev_err(board->gpib_dev, "failed to allocate memory for event\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&event->list); + event->event_type = event_type; + + list_add_tail(&event->list, head); + + queue->num_events++; + + dev_dbg(board->gpib_dev, "pushed event %i, %i in queue\n", + (int)event_type, num_gpib_events(queue)); + + return 0; +} + +// push event onto back of event queue +int push_gpib_event(struct gpib_board *board, short event_type) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&board->event_queue.lock, flags); + retval = push_gpib_event_nolock(board, event_type); + spin_unlock_irqrestore(&board->event_queue.lock, flags); + + if (event_type == EVENT_DEV_TRG) + board->status |= DTAS; + if (event_type == EVENT_DEV_CLR) + board->status |= DCAS; + + return retval; +} +EXPORT_SYMBOL(push_gpib_event); + +static int pop_gpib_event_nolock(struct gpib_board *board, + struct gpib_event_queue *queue, short *event_type) +{ + struct list_head *head = &queue->event_head; + struct list_head *front = head->next; + struct gpib_event *event; + + if (num_gpib_events(queue) == 0) { + *event_type = EVENT_NONE; + return 0; + } + + if (front == head) + return -EIO; + + if (queue->dropped_event) { + queue->dropped_event = 0; + return -EPIPE; + } + + event = list_entry(front, struct gpib_event, list); + *event_type = event->event_type; + + list_del(front); + kfree(event); + + queue->num_events--; + + dev_dbg(board->gpib_dev, "popped event %i, %i in queue\n", + (int)*event_type, num_gpib_events(queue)); + + return 0; +} + +// pop event from front of event queue +int pop_gpib_event(struct gpib_board *board, struct gpib_event_queue *queue, short *event_type) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&queue->lock, flags); + retval = pop_gpib_event_nolock(board, queue, event_type); + spin_unlock_irqrestore(&queue->lock, flags); + return retval; +} + +static int event_ioctl(struct gpib_board *board, unsigned long arg) +{ + short user_event; + int retval; + short event; + + retval = pop_gpib_event(board, &board->event_queue, &event); + if (retval < 0) + return retval; + + user_event = event; + + retval = copy_to_user((void __user *)arg, &user_event, sizeof(user_event)); + if (retval) + return -EFAULT; + + return 0; +} + +static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg) +{ + int request_control; + int retval; + + retval = copy_from_user(&request_control, (void __user *)arg, sizeof(request_control)); + if (retval) + return -EFAULT; + + return ibrsc(board, request_control); +} + +static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg) +{ + unsigned int cmd; + unsigned int delay; + int retval; + + if (!board->interface->t1_delay) + return -ENOENT; + + retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); + if (retval) + return -EFAULT; + + delay = cmd; + + retval = board->interface->t1_delay(board, delay); + if (retval < 0) + return retval; + + board->t1_nano_sec = retval; + return 0; +} + +static const struct file_operations ib_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .unlocked_ioctl = &ibioctl, + .compat_ioctl = &ibioctl, + .open = &ibopen, + .release = &ibclose, +}; + +struct gpib_board board_array[GPIB_MAX_NUM_BOARDS]; + +LIST_HEAD(registered_drivers); + +void init_gpib_descriptor(struct gpib_descriptor *desc) +{ + desc->pad = 0; + desc->sad = -1; + desc->is_board = 0; + desc->autopoll_enabled = 0; + atomic_set(&desc->io_in_progress, 0); +} + +int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module) +{ + struct gpib_interface_list *entry; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->interface = interface; + entry->module = provider_module; + list_add(&entry->list, ®istered_drivers); + + return 0; +} +EXPORT_SYMBOL(gpib_register_driver); + +void gpib_unregister_driver(struct gpib_interface *interface) +{ + int i; + struct list_head *list_ptr; + + for (i = 0; i < GPIB_MAX_NUM_BOARDS; i++) { + struct gpib_board *board = &board_array[i]; + + if (board->interface == interface) { + if (board->use_count > 0) + pr_warn("gpib: Warning: deregistered interface %s in use\n", + interface->name); + iboffline(board); + board->interface = NULL; + } + } + for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers;) { + struct gpib_interface_list *entry; + + entry = list_entry(list_ptr, struct gpib_interface_list, list); + list_ptr = list_ptr->next; + if (entry->interface == interface) { + list_del(&entry->list); + kfree(entry); + } + } +} +EXPORT_SYMBOL(gpib_unregister_driver); + +static void init_gpib_board_config(struct gpib_board_config *config) +{ + memset(config, 0, sizeof(struct gpib_board_config)); + config->pci_bus = -1; + config->pci_slot = -1; +} + +void init_gpib_board(struct gpib_board *board) +{ + board->interface = NULL; + board->provider_module = NULL; + board->buffer = NULL; + board->buffer_length = 0; + board->status = 0; + init_waitqueue_head(&board->wait); + mutex_init(&board->user_mutex); + mutex_init(&board->big_gpib_mutex); + board->locking_pid = 0; + spin_lock_init(&board->locking_pid_spinlock); + spin_lock_init(&board->spinlock); + timer_setup(&board->timer, NULL, 0); + board->dev = NULL; + board->gpib_dev = NULL; + init_gpib_board_config(&board->config); + board->private_data = NULL; + board->use_count = 0; + INIT_LIST_HEAD(&board->device_list); + board->pad = 0; + board->sad = -1; + board->usec_timeout = 3000000; + board->parallel_poll_configuration = 0; + board->online = 0; + board->autospollers = 0; + board->autospoll_task = NULL; + init_event_queue(&board->event_queue); + board->minor = -1; + init_gpib_pseudo_irq(&board->pseudo_irq); + board->master = 1; + atomic_set(&board->stuck_srq, 0); + board->local_ppoll_mode = 0; +} + +int gpib_allocate_board(struct gpib_board *board) +{ + if (!board->buffer) { + board->buffer_length = 0x4000; + board->buffer = vmalloc(board->buffer_length); + if (!board->buffer) { + board->buffer_length = 0; + return -ENOMEM; + } + } + return 0; +} + +void gpib_deallocate_board(struct gpib_board *board) +{ + short dummy; + + if (board->buffer) { + vfree(board->buffer); + board->buffer = NULL; + board->buffer_length = 0; + } + while (num_gpib_events(&board->event_queue)) + pop_gpib_event(board, &board->event_queue, &dummy); +} + +static void init_board_array(struct gpib_board *board_array, unsigned int length) +{ + int i; + + for (i = 0; i < length; i++) { + init_gpib_board(&board_array[i]); + board_array[i].minor = i; + } +} + +void init_gpib_status_queue(struct gpib_status_queue *device) +{ + INIT_LIST_HEAD(&device->list); + INIT_LIST_HEAD(&device->status_bytes); + device->num_status_bytes = 0; + device->reference_count = 0; + device->dropped_byte = 0; +} + +static struct class *gpib_class; + +static int __init gpib_common_init_module(void) +{ + int i; + + pr_info("GPIB core driver\n"); + init_board_array(board_array, GPIB_MAX_NUM_BOARDS); + if (register_chrdev(GPIB_CODE, "gpib", &ib_fops)) { + pr_err("gpib: can't get major %d\n", GPIB_CODE); + return -EIO; + } + gpib_class = class_create("gpib_common"); + if (IS_ERR(gpib_class)) { + pr_err("gpib: failed to create gpib class\n"); + unregister_chrdev(GPIB_CODE, "gpib"); + return PTR_ERR(gpib_class); + } + for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) + board_array[i].gpib_dev = device_create(gpib_class, NULL, + MKDEV(GPIB_CODE, i), NULL, "gpib%i", i); + + return 0; +} + +static void __exit gpib_common_exit_module(void) +{ + int i; + + for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) + device_destroy(gpib_class, MKDEV(GPIB_CODE, i)); + + class_destroy(gpib_class); + unregister_chrdev(GPIB_CODE, "gpib"); +} + +int gpib_match_device_path(struct device *dev, const char *device_path_in) +{ + if (device_path_in) { + char *device_path; + + device_path = kobject_get_path(&dev->kobj, GFP_KERNEL); + if (!device_path) { + dev_err(dev, "kobject_get_path returned NULL."); + return 0; + } + if (strcmp(device_path_in, device_path) != 0) { + kfree(device_path); + return 0; + } + kfree(device_path); + } + return 1; +} +EXPORT_SYMBOL(gpib_match_device_path); + +struct pci_dev *gpib_pci_get_device(const struct gpib_board_config *config, unsigned int vendor_id, + unsigned int device_id, struct pci_dev *from) +{ + struct pci_dev *pci_device = from; + + while ((pci_device = pci_get_device(vendor_id, device_id, pci_device))) { + if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != + PCI_SLOT(pci_device->devfn)) + continue; + if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) + continue; + return pci_device; + } + return NULL; +} +EXPORT_SYMBOL(gpib_pci_get_device); + +struct pci_dev *gpib_pci_get_subsys(const struct gpib_board_config *config, unsigned int vendor_id, + unsigned int device_id, unsigned int ss_vendor, + unsigned int ss_device, + struct pci_dev *from) +{ + struct pci_dev *pci_device = from; + + while ((pci_device = pci_get_subsys(vendor_id, device_id, + ss_vendor, ss_device, pci_device))) { + if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != + PCI_SLOT(pci_device->devfn)) + continue; + if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) + continue; + return pci_device; + } + return NULL; +} +EXPORT_SYMBOL(gpib_pci_get_subsys); + +module_init(gpib_common_init_module); +module_exit(gpib_common_exit_module); + diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c new file mode 100644 index 000000000000..7cbb6a467177 --- /dev/null +++ b/drivers/gpib/common/iblib.c @@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "ibsys.h" +#include +#include +#include + +/* + * IBCAC + * Return to the controller active state from the + * controller standby state, i.e., turn ATN on. Note + * that in order to enter the controller active state + * from the controller idle state, ibsic must be called. + * If sync is non-zero, attempt to take control synchronously. + * If fallback_to_async is non-zero, try to take control asynchronously + * if synchronous attempt fails. + */ +int ibcac(struct gpib_board *board, int sync, int fallback_to_async) +{ + int status = ibstatus(board); + int retval; + + if ((status & CIC) == 0) + return -EINVAL; + + if (status & ATN) + return 0; + + if (sync && (status & LACS) == 0) + /* + * tcs (take control synchronously) can only possibly work when + * controller is listener. Error code also needs to be -ETIMEDOUT + * or it will giveout without doing fallback. + */ + retval = -ETIMEDOUT; + else + retval = board->interface->take_control(board, sync); + + if (retval < 0 && fallback_to_async) { + if (sync && retval == -ETIMEDOUT) + retval = board->interface->take_control(board, 0); + } + board->interface->update_status(board, 0); + + return retval; +} + +/* + * After ATN is asserted, it should cause any connected devices + * to start listening for command bytes and leave acceptor idle state. + * So if ATN is asserted and neither NDAC or NRFD are asserted, + * then there are no devices and ibcmd should error out immediately. + * Some gpib hardware sees itself asserting NDAC/NRFD when it + * is controller in charge, in which case this check will + * do nothing useful (but shouldn't cause any harm either). + * Drivers that don't need this check (ni_usb for example) may + * set the skip_check_for_command_acceptors flag in their + * gpib_interface_struct to avoid useless overhead. + */ +static int check_for_command_acceptors(struct gpib_board *board) +{ + int lines; + + if (board->interface->skip_check_for_command_acceptors) + return 0; + if (!board->interface->line_status) + return 0; + + udelay(2); // allow time for devices to respond to ATN if it was just asserted + + lines = board->interface->line_status(board); + if (lines < 0) + return lines; + + if ((lines & VALID_NRFD) && (lines & VALID_NDAC)) { + if ((lines & BUS_NRFD) == 0 && (lines & BUS_NDAC) == 0) + return -ENOTCONN; + } + + return 0; +} + +/* + * IBCMD + * Write cnt command bytes from buf to the GPIB. The + * command operation terminates only on I/O complete. + * + * NOTE: + * 1. Prior to beginning the command, the interface is + * placed in the controller active state. + * 2. Before calling ibcmd for the first time, ibsic + * must be called to initialize the GPIB and enable + * the interface to leave the controller idle state. + */ +int ibcmd(struct gpib_board *board, u8 *buf, size_t length, size_t *bytes_written) +{ + ssize_t ret = 0; + int status; + + *bytes_written = 0; + + status = ibstatus(board); + + if ((status & CIC) == 0) + return -EINVAL; + + os_start_timer(board, board->usec_timeout); + + ret = ibcac(board, 1, 1); + if (ret == 0) { + ret = check_for_command_acceptors(board); + if (ret == 0) + ret = board->interface->command(board, buf, length, bytes_written); + } + + os_remove_timer(board); + + if (io_timed_out(board)) + ret = -ETIMEDOUT; + + return ret; +} + +/* + * IBGTS + * Go to the controller standby state from the controller + * active state, i.e., turn ATN off. + */ + +int ibgts(struct gpib_board *board) +{ + int status = ibstatus(board); + int retval; + + if ((status & CIC) == 0) + return -EINVAL; + + retval = board->interface->go_to_standby(board); /* go to standby */ + + board->interface->update_status(board, 0); + + return retval; +} + +static int autospoll_wait_should_wake_up(struct gpib_board *board) +{ + int retval; + + mutex_lock(&board->big_gpib_mutex); + + retval = board->master && board->autospollers > 0 && + !atomic_read(&board->stuck_srq) && + test_and_clear_bit(SRQI_NUM, &board->status); + + mutex_unlock(&board->big_gpib_mutex); + return retval; +} + +static int autospoll_thread(void *board_void) +{ + struct gpib_board *board = board_void; + int retval = 0; + + dev_dbg(board->gpib_dev, "entering autospoll thread\n"); + + while (1) { + wait_event_interruptible(board->wait, + kthread_should_stop() || + autospoll_wait_should_wake_up(board)); + dev_dbg(board->gpib_dev, "autospoll wait satisfied\n"); + if (kthread_should_stop()) + break; + + mutex_lock(&board->big_gpib_mutex); + /* make sure we are still good after we have lock */ + if (board->autospollers <= 0 || board->master == 0) { + mutex_unlock(&board->big_gpib_mutex); + continue; + } + mutex_unlock(&board->big_gpib_mutex); + + if (try_module_get(board->provider_module)) { + retval = autopoll_all_devices(board); + module_put(board->provider_module); + } else { + dev_err(board->gpib_dev, "try_module_get() failed!\n"); + } + if (retval <= 0) { + dev_err(board->gpib_dev, "stuck SRQ\n"); + + atomic_set(&board->stuck_srq, 1); // XXX could be better + set_bit(SRQI_NUM, &board->status); + } + } + return retval; +} + +int ibonline(struct gpib_board *board) +{ + int retval; + + if (board->online) + return -EBUSY; + if (!board->interface) + return -ENODEV; + retval = gpib_allocate_board(board); + if (retval < 0) + return retval; + + board->dev = NULL; + board->local_ppoll_mode = 0; + retval = board->interface->attach(board, &board->config); + if (retval < 0) { + board->interface->detach(board); + return retval; + } + /* + * nios2nommu on 2.6.11 uclinux kernel has weird problems + * with autospoll thread causing huge slowdowns + */ +#ifndef CONFIG_NIOS2 + board->autospoll_task = kthread_run(&autospoll_thread, board, + "gpib%d_autospoll_kthread", board->minor); + retval = IS_ERR(board->autospoll_task); + if (retval) { + dev_err(board->gpib_dev, "failed to create autospoll thread\n"); + board->interface->detach(board); + return retval; + } +#endif + board->online = 1; + dev_dbg(board->gpib_dev, "board online\n"); + + return 0; +} + +/* XXX need to make sure board is generally not in use (grab board lock?) */ +int iboffline(struct gpib_board *board) +{ + int retval; + + if (board->online == 0) + return 0; + if (!board->interface) + return -ENODEV; + + if (board->autospoll_task && !IS_ERR(board->autospoll_task)) { + retval = kthread_stop(board->autospoll_task); + if (retval) + dev_err(board->gpib_dev, "kthread_stop returned %i\n", retval); + board->autospoll_task = NULL; + } + + board->interface->detach(board); + gpib_deallocate_board(board); + board->online = 0; + dev_dbg(board->gpib_dev, "board offline\n"); + + return 0; +} + +/* + * IBLINES + * Poll the GPIB control lines and return their status in buf. + * + * LSB (bits 0-7) - VALID lines mask (lines that can be monitored). + * Next LSB (bits 8-15) - STATUS lines mask (lines that are currently set). + * + */ +int iblines(const struct gpib_board *board, short *lines) +{ + int retval; + + *lines = 0; + if (!board->interface->line_status) + return 0; + retval = board->interface->line_status(board); + if (retval < 0) + return retval; + *lines = retval; + return 0; +} + +/* + * IBRD + * Read up to 'length' bytes of data from the GPIB into buf. End + * on detection of END (EOI and or EOS) and set 'end_flag'. + * + * NOTE: + * 1. The interface is placed in the controller standby + * state prior to beginning the read. + * 2. Prior to calling ibrd, the intended devices as well + * as the interface board itself must be addressed by + * calling ibcmd. + */ + +int ibrd(struct gpib_board *board, u8 *buf, size_t length, int *end_flag, size_t *nbytes) +{ + ssize_t ret = 0; + int retval; + size_t bytes_read; + + *nbytes = 0; + *end_flag = 0; + if (length == 0) + return 0; + + if (board->master) { + retval = ibgts(board); + if (retval < 0) + return retval; + } + /* + * XXX resetting timer here could cause timeouts take longer than they should, + * since read_ioctl calls this + * function in a loop, there is probably a similar problem with writes/commands + */ + os_start_timer(board, board->usec_timeout); + + do { + ret = board->interface->read(board, buf, length - *nbytes, end_flag, &bytes_read); + if (ret < 0) + goto ibrd_out; + + buf += bytes_read; + *nbytes += bytes_read; + if (need_resched()) + schedule(); + } while (ret == 0 && *nbytes > 0 && *nbytes < length && *end_flag == 0); +ibrd_out: + os_remove_timer(board); + + return ret; +} + +/* + * IBRPP + * Conduct a parallel poll and return the byte in buf. + * + * NOTE: + * 1. Prior to conducting the poll the interface is placed + * in the controller active state. + */ +int ibrpp(struct gpib_board *board, u8 *result) +{ + int retval = 0; + + os_start_timer(board, board->usec_timeout); + retval = ibcac(board, 1, 1); + if (retval) + return -1; + + retval = board->interface->parallel_poll(board, result); + + os_remove_timer(board); + return retval; +} + +int ibppc(struct gpib_board *board, u8 configuration) +{ + configuration &= 0x1f; + board->interface->parallel_poll_configure(board, configuration); + board->parallel_poll_configuration = configuration; + + return 0; +} + +int ibrsv2(struct gpib_board *board, u8 status_byte, int new_reason_for_service) +{ + int board_status = ibstatus(board); + const unsigned int MSS = status_byte & request_service_bit; + + if ((board_status & CIC)) + return -EINVAL; + + if (MSS == 0 && new_reason_for_service) + return -EINVAL; + + if (board->interface->serial_poll_response2) { + board->interface->serial_poll_response2(board, status_byte, new_reason_for_service); + // fall back on simpler serial_poll_response if the behavior would be the same + } else if (board->interface->serial_poll_response && + (MSS == 0 || (MSS && new_reason_for_service))) { + board->interface->serial_poll_response(board, status_byte); + } else { + return -EOPNOTSUPP; + } + + return 0; +} + +/* + * IBSIC + * Send IFC for at least 100 microseconds. + * + * NOTE: + * 1. Ibsic must be called prior to the first call to + * ibcmd in order to initialize the bus and enable the + * interface to leave the controller idle state. + */ +int ibsic(struct gpib_board *board, unsigned int usec_duration) +{ + if (board->master == 0) + return -EINVAL; + + if (usec_duration < 100) + usec_duration = 100; + if (usec_duration > 1000) + usec_duration = 1000; + + dev_dbg(board->gpib_dev, "sending interface clear, delay = %ius\n", usec_duration); + board->interface->interface_clear(board, 1); + udelay(usec_duration); + board->interface->interface_clear(board, 0); + + return 0; +} + +int ibrsc(struct gpib_board *board, int request_control) +{ + int retval; + + if (!board->interface->request_system_control) + return -EPERM; + + retval = board->interface->request_system_control(board, request_control); + + if (retval) + return retval; + + board->master = request_control != 0; + + return 0; +} + +/* + * IBSRE + * Send REN true if v is non-zero or false if v is zero. + */ +int ibsre(struct gpib_board *board, int enable) +{ + if (board->master == 0) + return -EINVAL; + + board->interface->remote_enable(board, enable); /* set or clear REN */ + if (!enable) + usleep_range(100, 150); + + return 0; +} + +/* + * IBPAD + * change the GPIB address of the interface board. The address + * must be 0 through 30. ibonl resets the address to PAD. + */ +int ibpad(struct gpib_board *board, unsigned int addr) +{ + if (addr > MAX_GPIB_PRIMARY_ADDRESS) + return -EINVAL; + + board->pad = addr; + if (board->online) + board->interface->primary_address(board, board->pad); + dev_dbg(board->gpib_dev, "set primary addr to %i\n", board->pad); + return 0; +} + +/* + * IBSAD + * change the secondary GPIB address of the interface board. + * The address must be 0 through 30, or negative disables. ibonl resets the + * address to SAD. + */ +int ibsad(struct gpib_board *board, int addr) +{ + if (addr > MAX_GPIB_SECONDARY_ADDRESS) + return -EINVAL; + board->sad = addr; + if (board->online) { + if (board->sad >= 0) + board->interface->secondary_address(board, board->sad, 1); + else + board->interface->secondary_address(board, 0, 0); + } + dev_dbg(board->gpib_dev, "set secondary addr to %i\n", board->sad); + + return 0; +} + +/* + * IBEOS + * Set the end-of-string modes for I/O operations to v. + * + */ +int ibeos(struct gpib_board *board, int eos, int eosflags) +{ + int retval; + + if (eosflags & ~EOS_MASK) + return -EINVAL; + if (eosflags & REOS) { + retval = board->interface->enable_eos(board, eos, eosflags & BIN); + } else { + board->interface->disable_eos(board); + retval = 0; + } + return retval; +} + +int ibstatus(struct gpib_board *board) +{ + return general_ibstatus(board, NULL, 0, 0, NULL); +} + +int general_ibstatus(struct gpib_board *board, const struct gpib_status_queue *device, + int clear_mask, int set_mask, struct gpib_descriptor *desc) +{ + int status = 0; + short line_status; + + if (board->private_data) { + status = board->interface->update_status(board, clear_mask); + /* + * XXX should probably stop having drivers use TIMO bit in + * board->status to avoid confusion + */ + status &= ~TIMO; + /* get real SRQI status if we can */ + if (iblines(board, &line_status) == 0) { + if ((line_status & VALID_SRQ)) { + if ((line_status & BUS_SRQ)) + status |= SRQI; + else + status &= ~SRQI; + } + } + } + if (device) + if (num_status_bytes(device)) + status |= RQS; + + if (desc) { + if (set_mask & CMPL) + atomic_set(&desc->io_in_progress, 0); + else if (clear_mask & CMPL) + atomic_set(&desc->io_in_progress, 1); + + if (atomic_read(&desc->io_in_progress)) + status &= ~CMPL; + else + status |= CMPL; + } + if (num_gpib_events(&board->event_queue)) + status |= EVENT; + else + status &= ~EVENT; + + return status; +} + +struct wait_info { + struct gpib_board *board; + struct timer_list timer; + int timed_out; + unsigned long usec_timeout; +}; + +static void wait_timeout(struct timer_list *t) +{ + struct wait_info *winfo = timer_container_of(winfo, t, timer); + + winfo->timed_out = 1; + wake_up_interruptible(&winfo->board->wait); +} + +static void init_wait_info(struct wait_info *winfo) +{ + winfo->board = NULL; + winfo->timed_out = 0; + timer_setup_on_stack(&winfo->timer, wait_timeout, 0); +} + +static int wait_satisfied(struct wait_info *winfo, struct gpib_status_queue *status_queue, + int wait_mask, int *status, struct gpib_descriptor *desc) +{ + struct gpib_board *board = winfo->board; + int temp_status; + + if (mutex_lock_interruptible(&board->big_gpib_mutex)) + return -ERESTARTSYS; + + temp_status = general_ibstatus(board, status_queue, 0, 0, desc); + + mutex_unlock(&board->big_gpib_mutex); + + if (winfo->timed_out) + temp_status |= TIMO; + else + temp_status &= ~TIMO; + if (wait_mask & temp_status) { + *status = temp_status; + return 1; + } +// XXX does wait for END work? + return 0; +} + +/* install timer interrupt handler */ +static void start_wait_timer(struct wait_info *winfo) +/* Starts the timeout task */ +{ + winfo->timed_out = 0; + + if (winfo->usec_timeout > 0) + mod_timer(&winfo->timer, jiffies + usec_to_jiffies(winfo->usec_timeout)); +} + +static void remove_wait_timer(struct wait_info *winfo) +{ + timer_delete_sync(&winfo->timer); + timer_destroy_on_stack(&winfo->timer); +} + +/* + * IBWAIT + * Check or wait for a GPIB event to occur. The mask argument + * is a bit vector corresponding to the status bit vector. It + * has a bit set for each condition which can terminate the wait + * If the mask is 0 then + * no condition is waited for. + */ +int ibwait(struct gpib_board *board, int wait_mask, int clear_mask, int set_mask, + int *status, unsigned long usec_timeout, struct gpib_descriptor *desc) +{ + int retval = 0; + struct gpib_status_queue *status_queue; + struct wait_info winfo; + + if (desc->is_board) + status_queue = NULL; + else + status_queue = get_gpib_status_queue(board, desc->pad, desc->sad); + + if (wait_mask == 0) { + *status = general_ibstatus(board, status_queue, clear_mask, set_mask, desc); + return 0; + } + + mutex_unlock(&board->big_gpib_mutex); + + init_wait_info(&winfo); + winfo.board = board; + winfo.usec_timeout = usec_timeout; + start_wait_timer(&winfo); + + if (wait_event_interruptible(board->wait, wait_satisfied(&winfo, status_queue, + wait_mask, status, desc))) { + dev_dbg(board->gpib_dev, "wait interrupted\n"); + retval = -ERESTARTSYS; + } + remove_wait_timer(&winfo); + + if (retval) + return retval; + if (mutex_lock_interruptible(&board->big_gpib_mutex)) + return -ERESTARTSYS; + + /* make sure we only clear status bits that we are reporting */ + if (*status & clear_mask || set_mask) + general_ibstatus(board, status_queue, *status & clear_mask, set_mask, NULL); + + return 0; +} + +/* + * IBWRT + * Write cnt bytes of data from buf to the GPIB. The write + * operation terminates only on I/O complete. + * + * NOTE: + * 1. Prior to beginning the write, the interface is + * placed in the controller standby state. + * 2. Prior to calling ibwrt, the intended devices as + * well as the interface board itself must be + * addressed by calling ibcmd. + */ +int ibwrt(struct gpib_board *board, u8 *buf, size_t cnt, int send_eoi, size_t *bytes_written) +{ + int ret = 0; + int retval; + + if (cnt == 0) + return 0; + + if (board->master) { + retval = ibgts(board); + if (retval < 0) + return retval; + } + os_start_timer(board, board->usec_timeout); + ret = board->interface->write(board, buf, cnt, send_eoi, bytes_written); + + if (io_timed_out(board)) + ret = -ETIMEDOUT; + + os_remove_timer(board); + + return ret; +} + diff --git a/drivers/gpib/common/ibsys.h b/drivers/gpib/common/ibsys.h new file mode 100644 index 000000000000..e5a148f513a8 --- /dev/null +++ b/drivers/gpib/common/ibsys.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include "gpibP.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_GPIB_PRIMARY_ADDRESS 30 +#define MAX_GPIB_SECONDARY_ADDRESS 31 + +int gpib_allocate_board(struct gpib_board *board); +void gpib_deallocate_board(struct gpib_board *board); + +unsigned int num_status_bytes(const struct gpib_status_queue *dev); +int push_status_byte(struct gpib_board *board, struct gpib_status_queue *device, + u8 poll_byte); +int pop_status_byte(struct gpib_board *board, struct gpib_status_queue *device, + u8 *poll_byte); +struct gpib_status_queue *get_gpib_status_queue(struct gpib_board *board, + unsigned int pad, int sad); +int get_serial_poll_byte(struct gpib_board *board, unsigned int pad, int sad, + unsigned int usec_timeout, u8 *poll_byte); +int autopoll_all_devices(struct gpib_board *board); diff --git a/drivers/gpib/eastwood/Makefile b/drivers/gpib/eastwood/Makefile new file mode 100644 index 000000000000..384825195f77 --- /dev/null +++ b/drivers/gpib/eastwood/Makefile @@ -0,0 +1,3 @@ + +obj-$(CONFIG_GPIB_FLUKE) += fluke_gpib.o + diff --git a/drivers/gpib/eastwood/fluke_gpib.c b/drivers/gpib/eastwood/fluke_gpib.c new file mode 100644 index 000000000000..3ae848e3f738 --- /dev/null +++ b/drivers/gpib/eastwood/fluke_gpib.c @@ -0,0 +1,1180 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * GPIB Driver for Fluke cda devices. Basically, its a driver for a (bugfixed) + * cb7210 connected to channel 0 of a pl330 dma controller. + * Author: Frank Mori Hess + * copyright: (C) 2006, 2010, 2015 Fluke Corporation + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "fluke_gpib.h" + +#include "gpibP.h" +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB Driver for Fluke cda devices"); + +static int fluke_attach_holdoff_all(struct gpib_board *board, + const struct gpib_board_config *config); +static int fluke_attach_holdoff_end(struct gpib_board *board, + const struct gpib_board_config *config); +static void fluke_detach(struct gpib_board *board); +static int fluke_config_dma(struct gpib_board *board, int output); +static irqreturn_t fluke_gpib_internal_interrupt(struct gpib_board *board); + +static struct platform_device *fluke_gpib_pdev; + +static u8 fluke_locking_read_byte(struct nec7210_priv *nec_priv, unsigned int register_number) +{ + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + retval = fluke_read_byte_nolock(nec_priv, register_number); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); + return retval; +} + +static void fluke_locking_write_byte(struct nec7210_priv *nec_priv, u8 byte, + unsigned int register_number) +{ + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + fluke_write_byte_nolock(nec_priv, byte, register_number); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); +} + +// wrappers for interface functions +static int fluke_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int fluke_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int fluke_command(struct gpib_board *board, u8 *buffer, + size_t length, size_t *bytes_written) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int fluke_take_control(struct gpib_board *board, int synchronous) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int fluke_go_to_standby(struct gpib_board *board) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static int fluke_request_system_control(struct gpib_board *board, int request_control) +{ + struct fluke_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + return nec7210_request_system_control(board, nec_priv, request_control); +} + +static void fluke_interface_clear(struct gpib_board *board, int assert) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void fluke_remote_enable(struct gpib_board *board, int enable) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int fluke_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void fluke_disable_eos(struct gpib_board *board) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int fluke_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int fluke_primary_address(struct gpib_board *board, unsigned int address) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int fluke_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int fluke_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void fluke_parallel_poll_configure(struct gpib_board *board, u8 configuration) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); +} + +static void fluke_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void fluke_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct fluke_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static u8 fluke_serial_poll_status(struct gpib_board *board) +{ + struct fluke_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static void fluke_return_to_local(struct gpib_board *board) +{ + struct fluke_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + write_byte(nec_priv, AUX_RTL2, AUXMR); + udelay(1); + write_byte(nec_priv, AUX_RTL, AUXMR); +} + +static int fluke_line_status(const struct gpib_board *board) +{ + int status = VALID_ALL; + int bsr_bits; + struct fluke_priv *e_priv; + + e_priv = board->private_data; + + bsr_bits = fluke_paged_read_byte(e_priv, BUS_STATUS, BUS_STATUS_PAGE); + + if ((bsr_bits & BSR_REN_BIT) == 0) + status |= BUS_REN; + if ((bsr_bits & BSR_IFC_BIT) == 0) + status |= BUS_IFC; + if ((bsr_bits & BSR_SRQ_BIT) == 0) + status |= BUS_SRQ; + if ((bsr_bits & BSR_EOI_BIT) == 0) + status |= BUS_EOI; + if ((bsr_bits & BSR_NRFD_BIT) == 0) + status |= BUS_NRFD; + if ((bsr_bits & BSR_NDAC_BIT) == 0) + status |= BUS_NDAC; + if ((bsr_bits & BSR_DAV_BIT) == 0) + status |= BUS_DAV; + if ((bsr_bits & BSR_ATN_BIT) == 0) + status |= BUS_ATN; + + return status; +} + +static int fluke_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 350) { + write_byte(nec_priv, AUX_HI_SPEED, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + } + return retval; +} + +static int lacs_or_read_ready(struct gpib_board *board) +{ + const struct fluke_priv *e_priv = board->private_data; + const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + int retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = test_bit(LACS_NUM, &board->status) || test_bit(READ_READY_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +/* + * Wait until it is possible for a read to do something useful. This + * is not essential, it only exists to prevent RFD holdoff from being released pointlessly. + */ +static int wait_for_read(struct gpib_board *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + lacs_or_read_ready(board) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +/* + * Check if the SH state machine is in SGNS. We check twice since there is a very small chance + * we could be blowing through SGNS from SIDS to SDYS if there is already a + * byte available in the handshake state machine. We are interested + * in the case where the handshake is stuck in SGNS due to no byte being + * available to the chip (and thus we can be confident a dma transfer will + * result in at least one byte making it into the chip). This matters + * because we want to be confident before sending a "send eoi" auxilary + * command that we will be able to also put the associated data byte + * in the chip before any potential timeout. + */ +static int source_handshake_is_sgns(struct fluke_priv *e_priv) +{ + int i; + + for (i = 0; i < 2; ++i) { + if ((fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) & + SOURCE_HANDSHAKE_MASK) != SOURCE_HANDSHAKE_SGNS_BITS) { + return 0; + } + } + return 1; +} + +static int source_handshake_is_sids_or_sgns(struct fluke_priv *e_priv) +{ + unsigned int source_handshake_bits; + + source_handshake_bits = fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) & + SOURCE_HANDSHAKE_MASK; + + return (source_handshake_bits == SOURCE_HANDSHAKE_SGNS_BITS) || + (source_handshake_bits == SOURCE_HANDSHAKE_SIDS_BITS); +} + +/* + * Wait until the gpib chip is ready to accept a data out byte. + * If the chip is SGNS it is probably waiting for a a byte to + * be written to it. + */ +static int wait_for_data_out_ready(struct gpib_board *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + source_handshake_is_sgns(e_priv)) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +static int wait_for_sids_or_sgns(struct gpib_board *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + source_handshake_is_sids_or_sgns(e_priv) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +static void fluke_dma_callback(void *arg) +{ + struct gpib_board *board = arg; + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE); + wake_up_interruptible(&board->wait); + + fluke_gpib_internal_interrupt(board); + clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); + clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fluke_dma_write(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + int retval = 0; + dma_addr_t address; + struct dma_async_tx_descriptor *tx_desc; + + *bytes_written = 0; + + if (WARN_ON_ONCE(length > e_priv->dma_buffer_size)) + return -EFAULT; + dmaengine_terminate_all(e_priv->dma_channel); + // write-clear counter + writel(0x0, e_priv->write_transfer_counter); + + memcpy(e_priv->dma_buffer, buffer, length); + address = dma_map_single(board->dev, e_priv->dma_buffer, + length, DMA_TO_DEVICE); + /* program dma controller */ + retval = fluke_config_dma(board, 1); + if (retval) + goto cleanup; + + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); + retval = -ENOMEM; + goto cleanup; + } + tx_desc->callback = fluke_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + + clear_bit(WRITE_READY_BN, &nec_priv->state); + set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); + + // suspend until message is sent + if (wait_event_interruptible(board->wait, + ((readl(e_priv->write_transfer_counter) & + write_transfer_counter_mask) == length) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + // disable board's dma + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fluke_dma_callback got called + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state)) + fluke_dma_callback(board); + + /* + * if everything went fine, try to wait until last byte is actually + * transmitted across gpib (but don't try _too_ hard) + */ + if (retval == 0) + retval = wait_for_sids_or_sgns(board); + + *bytes_written = readl(e_priv->write_transfer_counter) & write_transfer_counter_mask; + if (WARN_ON_ONCE(*bytes_written > length)) + return -EFAULT; + +cleanup: + dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE); + return retval; +} + +static int fluke_accel_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remainder = length; + size_t transfer_size; + ssize_t retval = 0; + size_t dma_remainder = remainder; + + if (!e_priv->dma_channel) { + dev_err(board->gpib_dev, "No dma channel available, cannot do accel write."); + return -ENXIO; + } + + *bytes_written = 0; + if (length < 1) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + + if (send_eoi) + --dma_remainder; + + while (dma_remainder > 0) { + size_t num_bytes; + + retval = wait_for_data_out_ready(board); + if (retval < 0) + break; + + transfer_size = (e_priv->dma_buffer_size < dma_remainder) ? + e_priv->dma_buffer_size : dma_remainder; + retval = fluke_dma_write(board, buffer, transfer_size, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + break; + dma_remainder -= num_bytes; + remainder -= num_bytes; + buffer += num_bytes; + if (need_resched()) + schedule(); + } + if (retval < 0) + return retval; + // handle sending of last byte with eoi + if (send_eoi) { + size_t num_bytes; + + if (WARN_ON_ONCE(remainder != 1)) + return -EFAULT; + + /* + * wait until we are sure we will be able to write the data byte + * into the chip before we send AUX_SEOI. This prevents a timeout + * scenerio where we send AUX_SEOI but then timeout without getting + * any bytes into the gpib chip. This will result in the first byte + * of the next write having a spurious EOI set on the first byte. + */ + retval = wait_for_data_out_ready(board); + if (retval < 0) + return retval; + + write_byte(nec_priv, AUX_SEOI, AUXMR); + retval = fluke_dma_write(board, buffer, remainder, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + remainder -= num_bytes; + } + return 0; +} + +static int fluke_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie) +{ + struct dma_tx_state state; + int result; + + result = dmaengine_pause(chan); + if (result < 0) { + pr_err("dma pause failed?\n"); + return result; + } + dmaengine_tx_status(chan, cookie, &state); + /* + * hardware doesn't support resume, so dont call this + * method unless the dma transfer is done. + */ + return state.residue; +} + +static int fluke_dma_read(struct gpib_board *board, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned long flags; + int residue; + dma_addr_t bus_address; + struct dma_async_tx_descriptor *tx_desc; + dma_cookie_t dma_cookie; + int i; + static const int timeout = 10; + + *bytes_read = 0; + *end = 0; + if (length == 0) + return 0; + + bus_address = dma_map_single(board->dev, e_priv->dma_buffer, + length, DMA_FROM_DEVICE); + + /* program dma controller */ + retval = fluke_config_dma(board, 0); + if (retval) { + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + return retval; + } + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, + bus_address, length, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); + dma_unmap_single(NULL, bus_address, length, DMA_FROM_DEVICE); + return -EIO; + } + tx_desc->callback = fluke_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + // enable nec7210 dma + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + dma_cookie = dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + + set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); + clear_bit(READ_READY_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); + // wait for data to transfer + if (wait_event_interruptible(board->wait, + test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0 || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + + /* + * If we woke up because of end, wait until the dma transfer has pulled + * the data byte associated with the end before we cancel the dma transfer. + */ + if (test_bit(RECEIVED_END_BN, &nec_priv->state)) { + for (i = 0; i < timeout; ++i) { + if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0) + break; + if ((read_byte(nec_priv, ADR0) & DATA_IN_STATUS) == 0) + break; + usleep_range(10, 15); + } + if (i == timeout) + pr_warn("fluke_gpib: timeout waiting for dma to transfer end data byte.\n"); + } + + // stop the dma transfer + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + /* + * delay a little just to make sure any bytes in dma controller's fifo get + * written to memory before we disable it + */ + usleep_range(10, 15); + residue = fluke_get_dma_residue(e_priv->dma_channel, dma_cookie); + if (WARN_ON_ONCE(residue > length || residue < 0)) + return -EFAULT; + *bytes_read += length - residue; + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fluke_dma_callback got called + if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state)) + fluke_dma_callback(board); + + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + memcpy(buffer, e_priv->dma_buffer, *bytes_read); + + /* + * If we got an end interrupt, figure out if it was + * associated with the last byte we dma'd or with a + * byte still sitting on the cb7210. + */ + spin_lock_irqsave(&board->spinlock, flags); + if (test_bit(READ_READY_BN, &nec_priv->state) == 0) { + /* + * There is no byte sitting on the cb7210. If we + * saw an end interrupt, we need to deal with it now + */ + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + } + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} + +static int fluke_accel_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remain = length; + size_t transfer_size; + int retval = 0; + size_t dma_nbytes; + + *end = 0; + *bytes_read = 0; + + smp_mb__before_atomic(); + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + smp_mb__after_atomic(); + + retval = wait_for_read(board); + if (retval < 0) + return retval; + + nec7210_release_rfd_holdoff(board, nec_priv); + + while (remain > 0) { + transfer_size = (e_priv->dma_buffer_size < remain) ? + e_priv->dma_buffer_size : remain; + retval = fluke_dma_read(board, buffer, transfer_size, end, &dma_nbytes); + remain -= dma_nbytes; + buffer += dma_nbytes; + *bytes_read += dma_nbytes; + if (*end) + break; + if (retval < 0) + return retval; + if (need_resched()) + schedule(); + } + + return retval; +} + +static struct gpib_interface fluke_unaccel_interface = { + .name = "fluke_unaccel", + .attach = fluke_attach_holdoff_all, + .detach = fluke_detach, + .read = fluke_read, + .write = fluke_write, + .command = fluke_command, + .take_control = fluke_take_control, + .go_to_standby = fluke_go_to_standby, + .request_system_control = fluke_request_system_control, + .interface_clear = fluke_interface_clear, + .remote_enable = fluke_remote_enable, + .enable_eos = fluke_enable_eos, + .disable_eos = fluke_disable_eos, + .parallel_poll = fluke_parallel_poll, + .parallel_poll_configure = fluke_parallel_poll_configure, + .parallel_poll_response = fluke_parallel_poll_response, + .line_status = fluke_line_status, + .update_status = fluke_update_status, + .primary_address = fluke_primary_address, + .secondary_address = fluke_secondary_address, + .serial_poll_response = fluke_serial_poll_response, + .serial_poll_status = fluke_serial_poll_status, + .t1_delay = fluke_t1_delay, + .return_to_local = fluke_return_to_local, +}; + +/* + * fluke_hybrid uses dma for writes but not for reads. Added + * to deal with occasional corruption of bytes seen when doing dma + * reads. From looking at the cb7210 vhdl, I believe the corruption + * is due to a hardware bug triggered by the cpu reading a cb7210 + * } + * register just as the dma controller is also doing a read. + */ + +static struct gpib_interface fluke_hybrid_interface = { + .name = "fluke_hybrid", + .attach = fluke_attach_holdoff_all, + .detach = fluke_detach, + .read = fluke_read, + .write = fluke_accel_write, + .command = fluke_command, + .take_control = fluke_take_control, + .go_to_standby = fluke_go_to_standby, + .request_system_control = fluke_request_system_control, + .interface_clear = fluke_interface_clear, + .remote_enable = fluke_remote_enable, + .enable_eos = fluke_enable_eos, + .disable_eos = fluke_disable_eos, + .parallel_poll = fluke_parallel_poll, + .parallel_poll_configure = fluke_parallel_poll_configure, + .parallel_poll_response = fluke_parallel_poll_response, + .line_status = fluke_line_status, + .update_status = fluke_update_status, + .primary_address = fluke_primary_address, + .secondary_address = fluke_secondary_address, + .serial_poll_response = fluke_serial_poll_response, + .serial_poll_status = fluke_serial_poll_status, + .t1_delay = fluke_t1_delay, + .return_to_local = fluke_return_to_local, +}; + +static struct gpib_interface fluke_interface = { + .name = "fluke", + .attach = fluke_attach_holdoff_end, + .detach = fluke_detach, + .read = fluke_accel_read, + .write = fluke_accel_write, + .command = fluke_command, + .take_control = fluke_take_control, + .go_to_standby = fluke_go_to_standby, + .request_system_control = fluke_request_system_control, + .interface_clear = fluke_interface_clear, + .remote_enable = fluke_remote_enable, + .enable_eos = fluke_enable_eos, + .disable_eos = fluke_disable_eos, + .parallel_poll = fluke_parallel_poll, + .parallel_poll_configure = fluke_parallel_poll_configure, + .parallel_poll_response = fluke_parallel_poll_response, + .line_status = fluke_line_status, + .update_status = fluke_update_status, + .primary_address = fluke_primary_address, + .secondary_address = fluke_secondary_address, + .serial_poll_response = fluke_serial_poll_response, + .serial_poll_status = fluke_serial_poll_status, + .t1_delay = fluke_t1_delay, + .return_to_local = fluke_return_to_local, +}; + +irqreturn_t fluke_gpib_internal_interrupt(struct gpib_board *board) +{ + int status0, status1, status2; + struct fluke_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int retval = IRQ_NONE; + + if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS) + set_bit(READ_READY_BN, &nec_priv->state); + + status0 = fluke_paged_read_byte(priv, ISR0_IMR0, ISR0_IMR0_PAGE); + status1 = read_byte(nec_priv, ISR1); + status2 = read_byte(nec_priv, ISR2); + + if (status0 & FLUKE_IFCI_BIT) { + push_gpib_event(board, EVENT_IFC); + retval = IRQ_HANDLED; + } + + if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED) + retval = IRQ_HANDLED; + + if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS) { + if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state)) + set_bit(READ_READY_BN, &nec_priv->state); + else + clear_bit(READ_READY_BN, &nec_priv->state); + } + + if (retval == IRQ_HANDLED) + wake_up_interruptible(&board->wait); + + return retval; +} + +static irqreturn_t fluke_gpib_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = fluke_gpib_internal_interrupt(board); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +static int fluke_allocate_private(struct gpib_board *board) +{ + struct fluke_priv *priv; + + board->private_data = kmalloc(sizeof(struct fluke_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + priv = board->private_data; + memset(priv, 0, sizeof(struct fluke_priv)); + init_nec7210_private(&priv->nec7210_priv); + priv->dma_buffer_size = 0x7ff; + priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL); + if (!priv->dma_buffer) + return -ENOMEM; + return 0; +} + +static void fluke_generic_detach(struct gpib_board *board) +{ + if (board->private_data) { + struct fluke_priv *e_priv = board->private_data; + + kfree(e_priv->dma_buffer); + kfree(board->private_data); + board->private_data = NULL; + } +} + +// generic part of attach functions shared by all cb7210 boards +static int fluke_generic_attach(struct gpib_board *board) +{ + struct fluke_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + + board->status = 0; + + retval = fluke_allocate_private(board); + if (retval < 0) + return retval; + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + nec_priv->read_byte = fluke_locking_read_byte; + nec_priv->write_byte = fluke_locking_write_byte; + nec_priv->offset = fluke_reg_offset; + nec_priv->type = CB7210; + return 0; +} + +static int fluke_config_dma(struct gpib_board *board, int output) +{ + struct fluke_priv *e_priv = board->private_data; + struct dma_slave_config config; + + config.src_maxburst = 1; + config.dst_maxburst = 1; + config.device_fc = true; + + if (output) { + config.direction = DMA_MEM_TO_DEV; + config.src_addr = 0; + config.dst_addr = e_priv->dma_port_res->start; + config.src_addr_width = 1; + config.dst_addr_width = 1; + } else { + config.direction = DMA_DEV_TO_MEM; + config.src_addr = e_priv->dma_port_res->start; + config.dst_addr = 0; + config.src_addr_width = 1; + config.dst_addr_width = 1; + } + return dmaengine_slave_config(e_priv->dma_channel, &config); +} + +static int fluke_init(struct fluke_priv *e_priv, struct gpib_board *board, int handshake_mode) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + + nec7210_board_reset(nec_priv, board); + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + /* + * set clock register for driving frequency + * ICR should be set to clock in megahertz (1-15) and to zero + * for clocks faster than 15 MHz (max 20MHz) + */ + write_byte(nec_priv, ICR | 10, AUXMR); + nec7210_set_handshake_mode(board, nec_priv, handshake_mode); + + nec7210_board_online(nec_priv, board); + + /* poll so we can detect ATN changes */ + if (gpib_request_pseudo_irq(board, fluke_gpib_interrupt)) { + dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n"); + return -EINVAL; + } + + fluke_paged_write_byte(e_priv, FLUKE_IFCIE_BIT, ISR0_IMR0, ISR0_IMR0_PAGE); + return 0; +} + +/* + * This function is passed to dma_request_channel() in order to + * select the pl330 dma channel which has been hardwired to + * the gpib controller. + */ +static bool gpib_dma_channel_filter(struct dma_chan *chan, void *filter_param) +{ + // select the channel which is wired to the gpib chip + return chan->chan_id == 0; +} + +static int fluke_attach_impl(struct gpib_board *board, const struct gpib_board_config *config, + unsigned int handshake_mode) +{ + struct fluke_priv *e_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + int irq; + struct resource *res; + dma_cap_mask_t dma_cap; + + if (!fluke_gpib_pdev) { + dev_err(board->gpib_dev, "No fluke device was found, attach failed.\n"); + return -ENODEV; + } + + retval = fluke_generic_attach(board); + if (retval) + return retval; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + nec_priv->offset = fluke_reg_offset; + board->dev = &fluke_gpib_pdev->dev; + + res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource\n"); + return -ENODEV; + } + + if (request_mem_region(res->start, + resource_size(res), + fluke_gpib_pdev->name) == NULL) { + dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->gpib_iomem_res = res; + + nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + if (!nec_priv->mmiobase) { + dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n"); + return -ENOMEM; + } + + res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for gpib dma port\n"); + return -ENODEV; + } + if (request_mem_region(res->start, + resource_size(res), + fluke_gpib_pdev->name) == NULL) { + dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->dma_port_res = res; + + res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 2); + if (!res) { + dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for write transfer counter\n"); + return -ENODEV; + } + + if (request_mem_region(res->start, + resource_size(res), + fluke_gpib_pdev->name) == NULL) { + dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->write_transfer_counter_res = res; + + e_priv->write_transfer_counter = ioremap(e_priv->write_transfer_counter_res->start, + resource_size(e_priv->write_transfer_counter_res)); + if (!e_priv->write_transfer_counter) { + dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n"); + return -ENOMEM; + } + + irq = platform_get_irq(fluke_gpib_pdev, 0); + if (irq < 0) + return -EBUSY; + retval = request_irq(irq, fluke_gpib_interrupt, isr_flags, fluke_gpib_pdev->name, board); + if (retval) { + dev_err(&fluke_gpib_pdev->dev, + "cannot register interrupt handler err=%d\n", + retval); + return retval; + } + e_priv->irq = irq; + + dma_cap_zero(dma_cap); + dma_cap_set(DMA_SLAVE, dma_cap); + e_priv->dma_channel = dma_request_channel(dma_cap, gpib_dma_channel_filter, NULL); + if (!e_priv->dma_channel) { + dev_err(board->gpib_dev, "failed to allocate a dma channel.\n"); + /* + * we don't error out here because unaccel interface will still + * work without dma + */ + } + + return fluke_init(e_priv, board, handshake_mode); +} + +int fluke_attach_holdoff_all(struct gpib_board *board, const struct gpib_board_config *config) +{ + return fluke_attach_impl(board, config, HR_HLDA); +} + +int fluke_attach_holdoff_end(struct gpib_board *board, const struct gpib_board_config *config) +{ + return fluke_attach_impl(board, config, HR_HLDE); +} + +void fluke_detach(struct gpib_board *board) +{ + struct fluke_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (e_priv) { + if (e_priv->dma_channel) + dma_release_channel(e_priv->dma_channel); + gpib_free_pseudo_irq(board); + nec_priv = &e_priv->nec7210_priv; + + if (nec_priv->mmiobase) { + fluke_paged_write_byte(e_priv, 0, ISR0_IMR0, ISR0_IMR0_PAGE); + nec7210_board_reset(nec_priv, board); + } + if (e_priv->irq) + free_irq(e_priv->irq, board); + if (e_priv->write_transfer_counter_res) { + release_mem_region(e_priv->write_transfer_counter_res->start, + resource_size(e_priv->write_transfer_counter_res)); + } + if (e_priv->dma_port_res) { + release_mem_region(e_priv->dma_port_res->start, + resource_size(e_priv->dma_port_res)); + } + if (e_priv->gpib_iomem_res) + release_mem_region(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + } + fluke_generic_detach(board); +} + +static int fluke_gpib_probe(struct platform_device *pdev) +{ + fluke_gpib_pdev = pdev; + return 0; +} + +static const struct of_device_id fluke_gpib_of_match[] = { + { .compatible = "flk,fgpib-4.0"}, + { {0} } +}; +MODULE_DEVICE_TABLE(of, fluke_gpib_of_match); + +static struct platform_driver fluke_gpib_platform_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = fluke_gpib_of_match, + }, + .probe = &fluke_gpib_probe +}; + +static int __init fluke_init_module(void) +{ + int result; + + result = platform_driver_register(&fluke_gpib_platform_driver); + if (result) { + pr_err("platform_driver_register failed: error = %d\n", result); + return result; + } + + result = gpib_register_driver(&fluke_unaccel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_unaccel; + } + + result = gpib_register_driver(&fluke_hybrid_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_hybrid; + } + + result = gpib_register_driver(&fluke_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_interface; + } + + return 0; + +err_interface: + gpib_unregister_driver(&fluke_hybrid_interface); +err_hybrid: + gpib_unregister_driver(&fluke_unaccel_interface); +err_unaccel: + platform_driver_unregister(&fluke_gpib_platform_driver); + + return result; +} + +static void __exit fluke_exit_module(void) +{ + gpib_unregister_driver(&fluke_unaccel_interface); + gpib_unregister_driver(&fluke_hybrid_interface); + gpib_unregister_driver(&fluke_interface); + platform_driver_unregister(&fluke_gpib_platform_driver); +} + +module_init(fluke_init_module); +module_exit(fluke_exit_module); diff --git a/drivers/gpib/eastwood/fluke_gpib.h b/drivers/gpib/eastwood/fluke_gpib.h new file mode 100644 index 000000000000..493c200d0bbf --- /dev/null +++ b/drivers/gpib/eastwood/fluke_gpib.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Author: Frank Mori Hess + * copyright: (C) 2006, 2010, 2015 Fluke Corporation + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include "nec7210.h" + +struct fluke_priv { + struct nec7210_priv nec7210_priv; + struct resource *gpib_iomem_res; + struct resource *write_transfer_counter_res; + struct resource *dma_port_res; + int irq; + struct dma_chan *dma_channel; + u8 *dma_buffer; + int dma_buffer_size; + void __iomem *write_transfer_counter; +}; + +// cb7210 specific registers and bits +enum cb7210_regs { + STATE1_REG = 0x4, + ISR0_IMR0 = 0x6, + BUS_STATUS = 0x7 +}; + +enum cb7210_page_in { + ISR0_IMR0_PAGE = 1, + BUS_STATUS_PAGE = 1, + STATE1_PAGE = 1 +}; + +/* IMR0 -- Interrupt Mode Register 0 */ +enum imr0_bits { + FLUKE_IFCIE_BIT = 0x8, /* interface clear interrupt */ +}; + +/* ISR0 -- Interrupt Status Register 0 */ +enum isr0_bits { + FLUKE_IFCI_BIT = 0x8, /* interface clear interrupt */ +}; + +enum state1_bits { + SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */ + SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */ + SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */ + SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */ + SOURCE_HANDSHAKE_MASK = 0x7 +}; + +/* + * we customized the cb7210 vhdl to give the "data in" status + * on the unused bit 7 of the address0 register. + */ +enum cb7210_address0 { + DATA_IN_STATUS = 0x80 +}; + +static inline int cb7210_page_in_bits(unsigned int page) +{ + return 0x50 | (page & 0xf); +} + +// don't use without locking nec_priv->register_page_lock +static inline u8 fluke_read_byte_nolock(struct nec7210_priv *nec_priv, + int register_num) +{ + u8 retval; + + retval = readl(nec_priv->mmiobase + register_num * nec_priv->offset); + return retval; +} + +// don't use without locking nec_priv->register_page_lock +static inline void fluke_write_byte_nolock(struct nec7210_priv *nec_priv, u8 data, + int register_num) +{ + writel(data, nec_priv->mmiobase + register_num * nec_priv->offset); +} + +static inline u8 fluke_paged_read_byte(struct fluke_priv *e_priv, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR); + udelay(1); + /* chip auto clears the page after a read */ + retval = fluke_read_byte_nolock(nec_priv, register_num); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); + return retval; +} + +static inline void fluke_paged_write_byte(struct fluke_priv *e_priv, u8 data, + unsigned int register_num, unsigned int page) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&nec_priv->register_page_lock, flags); + fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR); + udelay(1); + fluke_write_byte_nolock(nec_priv, data, register_num); + spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); +} + +enum bus_status_bits { + BSR_ATN_BIT = 0x1, + BSR_EOI_BIT = 0x2, + BSR_SRQ_BIT = 0x4, + BSR_IFC_BIT = 0x8, + BSR_REN_BIT = 0x10, + BSR_DAV_BIT = 0x20, + BSR_NRFD_BIT = 0x40, + BSR_NDAC_BIT = 0x80, +}; + +enum cb7210_aux_cmds { +/* + * AUX_RTL2 is an undocumented aux command which causes cb7210 to assert + * (and keep asserted) local rtl message. This is used in conjunction + * with the (stupid) cb7210 implementation + * of the normal nec7210 AUX_RTL aux command, which + * causes the rtl message to toggle between on and off. + */ + AUX_RTL2 = 0xd, + AUX_NBAF = 0xe, // new byte available false (also clears seoi) + AUX_LO_SPEED = 0x40, + AUX_HI_SPEED = 0x41, +}; + +enum { + fluke_reg_offset = 4, + fluke_num_regs = 8, + write_transfer_counter_mask = 0x7ff, +}; diff --git a/drivers/gpib/fmh_gpib/Makefile b/drivers/gpib/fmh_gpib/Makefile new file mode 100644 index 000000000000..cc4d9e7cd5cd --- /dev/null +++ b/drivers/gpib/fmh_gpib/Makefile @@ -0,0 +1,2 @@ + +obj-$(CONFIG_GPIB_FMH) += fmh_gpib.o diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c new file mode 100644 index 000000000000..f7bfb4a8e553 --- /dev/null +++ b/drivers/gpib/fmh_gpib/fmh_gpib.c @@ -0,0 +1,1754 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * GPIB Driver for fmh_gpib_core, see + * https://github.com/fmhess/fmh_gpib_core + * + * More specifically, it is a driver for the hardware arrangement described by + * src/examples/fmh_gpib_top.vhd in the fmh_gpib_core repository. + * + * Author: Frank Mori Hess + * Copyright: (C) 2006, 2010, 2015 Fluke Corporation + * (C) 2017 Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "fmh_gpib.h" + +#include "gpibP.h" +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB Driver for fmh_gpib_core"); +MODULE_AUTHOR("Frank Mori Hess "); + +static irqreturn_t fmh_gpib_interrupt(int irq, void *arg); +static int fmh_gpib_attach_holdoff_all(struct gpib_board *board, + const struct gpib_board_config *config); +static int fmh_gpib_attach_holdoff_end(struct gpib_board *board, + const struct gpib_board_config *config); +static void fmh_gpib_detach(struct gpib_board *board); +static int fmh_gpib_pci_attach_holdoff_all(struct gpib_board *board, + const struct gpib_board_config *config); +static int fmh_gpib_pci_attach_holdoff_end(struct gpib_board *board, + const struct gpib_board_config *config); +static void fmh_gpib_pci_detach(struct gpib_board *board); +static int fmh_gpib_config_dma(struct gpib_board *board, int output); +static irqreturn_t fmh_gpib_internal_interrupt(struct gpib_board *board); +static struct platform_driver fmh_gpib_platform_driver; +static struct pci_driver fmh_gpib_pci_driver; + +// wrappers for interface functions +static int fmh_gpib_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int fmh_gpib_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int fmh_gpib_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int fmh_gpib_take_control(struct gpib_board *board, int synchronous) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int fmh_gpib_go_to_standby(struct gpib_board *board) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static int fmh_gpib_request_system_control(struct gpib_board *board, int request_control) +{ + struct fmh_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + return nec7210_request_system_control(board, nec_priv, request_control); +} + +static void fmh_gpib_interface_clear(struct gpib_board *board, int assert) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void fmh_gpib_remote_enable(struct gpib_board *board, int enable) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int fmh_gpib_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void fmh_gpib_disable_eos(struct gpib_board *board) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int fmh_gpib_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int fmh_gpib_primary_address(struct gpib_board *board, unsigned int address) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int fmh_gpib_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int fmh_gpib_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void fmh_gpib_parallel_poll_configure(struct gpib_board *board, u8 configuration) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); +} + +static void fmh_gpib_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct fmh_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void fmh_gpib_local_parallel_poll_mode(struct gpib_board *board, int local) +{ + struct fmh_priv *priv = board->private_data; + + if (local) { + write_byte(&priv->nec7210_priv, AUX_I_REG | LOCAL_PPOLL_MODE_BIT, AUXMR); + } else { + /* + * For fmh_gpib_core, remote parallel poll config mode is unaffected by the + * state of the disable bit of the parallel poll register (unlike the tnt4882). + * So, we don't need to worry about that. + */ + write_byte(&priv->nec7210_priv, AUX_I_REG | 0x0, AUXMR); + } +} + +static void fmh_gpib_serial_poll_response2(struct gpib_board *board, u8 status, + int new_reason_for_service) +{ + struct fmh_priv *priv = board->private_data; + unsigned long flags; + const int MSS = status & request_service_bit; + const int reqt = MSS && new_reason_for_service; + const int reqf = MSS == 0; + + spin_lock_irqsave(&board->spinlock, flags); + if (reqt) { + priv->nec7210_priv.srq_pending = 1; + clear_bit(SPOLL_NUM, &board->status); + } else if (reqf) { + priv->nec7210_priv.srq_pending = 0; + } + + if (reqt) { + /* + * It may seem like a race to issue reqt before updating + * the status byte, but it is not. The chip does not + * issue the reqt until the SPMR is written to at + * a later time. + */ + write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR); + } else if (reqf) { + write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR); + } + /* + * We need to always zero bit 6 of the status byte before writing it to + * the SPMR to insure we are using + * serial poll mode SP1, and not accidentally triggering mode SP3. + */ + write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR); + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static u8 fmh_gpib_serial_poll_status(struct gpib_board *board) +{ + struct fmh_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static void fmh_gpib_return_to_local(struct gpib_board *board) +{ + struct fmh_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + write_byte(nec_priv, AUX_RTL2, AUXMR); + udelay(1); + write_byte(nec_priv, AUX_RTL, AUXMR); +} + +static int fmh_gpib_line_status(const struct gpib_board *board) +{ + int status = VALID_ALL; + int bsr_bits; + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + + bsr_bits = read_byte(nec_priv, BUS_STATUS_REG); + + if ((bsr_bits & BSR_REN_BIT) == 0) + status |= BUS_REN; + if ((bsr_bits & BSR_IFC_BIT) == 0) + status |= BUS_IFC; + if ((bsr_bits & BSR_SRQ_BIT) == 0) + status |= BUS_SRQ; + if ((bsr_bits & BSR_EOI_BIT) == 0) + status |= BUS_EOI; + if ((bsr_bits & BSR_NRFD_BIT) == 0) + status |= BUS_NRFD; + if ((bsr_bits & BSR_NDAC_BIT) == 0) + status |= BUS_NDAC; + if ((bsr_bits & BSR_DAV_BIT) == 0) + status |= BUS_DAV; + if ((bsr_bits & BSR_ATN_BIT) == 0) + status |= BUS_ATN; + + return status; +} + +static int fmh_gpib_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 350) { + write_byte(nec_priv, AUX_HI_SPEED, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + } + return retval; +} + +static int lacs_or_read_ready(struct gpib_board *board) +{ + const struct fmh_priv *e_priv = board->private_data; + const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + retval = test_bit(LACS_NUM, &board->status) || + test_bit(READ_READY_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} + +static int wait_for_read(struct gpib_board *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + lacs_or_read_ready(board) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +static int wait_for_rx_fifo_half_full_or_end(struct gpib_board *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & + RX_FIFO_HALF_FULL) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + return retval; +} + +/* + * Wait until the gpib chip is ready to accept a data out byte. + */ +static int wait_for_data_out_ready(struct gpib_board *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + (read_byte(nec_priv, EXT_STATUS_1_REG) & + DATA_OUT_STATUS_BIT)) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + + return retval; +} + +static void fmh_gpib_dma_callback(void *arg) +{ + struct gpib_board *board = arg; + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE); + wake_up_interruptible(&board->wait); + + fmh_gpib_internal_interrupt(board); + + clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); + clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); +} + +/* + * returns true when all the bytes of a write have been transferred to + * the chip and successfully transferred out over the gpib bus. + */ +static int fmh_gpib_all_bytes_are_sent(struct fmh_priv *e_priv) +{ + if (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & fifo_xfer_counter_mask) + return 0; + + if ((read_byte(&e_priv->nec7210_priv, EXT_STATUS_1_REG) & DATA_OUT_STATUS_BIT) == 0) + return 0; + + return 1; +} + +static int fmh_gpib_dma_write(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + int retval = 0; + dma_addr_t address; + struct dma_async_tx_descriptor *tx_desc; + + *bytes_written = 0; + if (WARN_ON_ONCE(length > e_priv->dma_buffer_size)) + return -EFAULT; + dmaengine_terminate_all(e_priv->dma_channel); + memcpy(e_priv->dma_buffer, buffer, length); + address = dma_map_single(board->dev, e_priv->dma_buffer, length, DMA_TO_DEVICE); + if (dma_mapping_error(board->dev, address)) + dev_err(board->gpib_dev, "dma mapping error in dma write!\n"); + /* program dma controller */ + retval = fmh_gpib_config_dma(board, 1); + if (retval) + goto cleanup; + + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); + retval = -ENOMEM; + goto cleanup; + } + tx_desc->callback = fmh_gpib_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, TX_FIFO_DMA_REQUEST_ENABLE | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + + dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + clear_bit(WRITE_READY_BN, &nec_priv->state); + set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); + + // suspend until message is sent + if (wait_event_interruptible(board->wait, + fmh_gpib_all_bytes_are_sent(e_priv) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + // disable board's dma + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fmh_gpib_dma_callback got called + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state)) + fmh_gpib_dma_callback(board); + + *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & + fifo_xfer_counter_mask); + if (WARN_ON_ONCE(*bytes_written > length)) + return -EFAULT; +cleanup: + dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE); + return retval; +} + +static int fmh_gpib_accel_write(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remainder = length; + size_t transfer_size; + ssize_t retval = 0; + size_t dma_remainder = remainder; + + if (!e_priv->dma_channel) { + dev_err(board->gpib_dev, "No dma channel available, cannot do accel write."); + return -ENXIO; + } + + *bytes_written = 0; + if (length < 1) + return 0; + + smp_mb__before_atomic(); + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + smp_mb__after_atomic(); + + if (send_eoi) + --dma_remainder; + + while (dma_remainder > 0) { + size_t num_bytes; + + retval = wait_for_data_out_ready(board); + if (retval < 0) + break; + + transfer_size = (e_priv->dma_buffer_size < dma_remainder) ? + e_priv->dma_buffer_size : dma_remainder; + retval = fmh_gpib_dma_write(board, buffer, transfer_size, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + break; + dma_remainder -= num_bytes; + remainder -= num_bytes; + buffer += num_bytes; + if (need_resched()) + schedule(); + } + if (retval < 0) + return retval; + // handle sending of last byte with eoi + if (send_eoi) { + size_t num_bytes; + + if (WARN_ON_ONCE(remainder != 1)) + return -EFAULT; + + /* + * wait until we are sure we will be able to write the data byte + * into the chip before we send AUX_SEOI. This prevents a timeout + * scenario where we send AUX_SEOI but then timeout without getting + * any bytes into the gpib chip. This will result in the first byte + * of the next write having a spurious EOI set on the first byte. + */ + retval = wait_for_data_out_ready(board); + if (retval < 0) + return retval; + + write_byte(nec_priv, AUX_SEOI, AUXMR); + retval = fmh_gpib_dma_write(board, buffer, remainder, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + remainder -= num_bytes; + } + return 0; +} + +static int fmh_gpib_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie) +{ + struct dma_tx_state state; + int result; + + result = dmaengine_pause(chan); + if (result < 0) { + pr_err("dma pause failed?\n"); + return result; + } + dmaengine_tx_status(chan, cookie, &state); + /* + * dma330 hardware doesn't support resume, so dont call this + * method unless the dma transfer is done. + */ + return state.residue; +} + +static int wait_for_tx_fifo_half_empty(struct gpib_board *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & + TX_FIFO_HALF_EMPTY)) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + + return retval; +} + +/* + * supports writing a chunk of data whose length must fit into the hardware'd xfer counter, + * called in a loop by fmh_gpib_fifo_write() + */ +static int fmh_gpib_fifo_write_countable(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned int remainder; + + *bytes_written = 0; + if (WARN_ON_ONCE(length > fifo_xfer_counter_mask)) + return -EFAULT; + + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + + remainder = length; + while (remainder > 0) { + int i; + + fifos_write(e_priv, TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG); + retval = wait_for_tx_fifo_half_empty(board); + if (retval < 0) + goto cleanup; + + for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && remainder > 0; ++i) { + unsigned int data_value = *buffer; + + if (send_eoi && remainder == 1) + data_value |= FIFO_DATA_EOI_FLAG; + fifos_write(e_priv, data_value, FIFO_DATA_REG); + ++buffer; + --remainder; + } + } + + // suspend until last byte is sent + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE); + if (wait_event_interruptible(board->wait, + fmh_gpib_all_bytes_are_sent(e_priv) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) + retval = -EIO; + +cleanup: + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + + *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & + fifo_xfer_counter_mask); + if (WARN_ON_ONCE(*bytes_written > length)) + return -EFAULT; + + return retval; +} + +static int fmh_gpib_fifo_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remainder = length; + size_t transfer_size; + ssize_t retval = 0; + + *bytes_written = 0; + if (length < 1) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + + while (remainder > 0) { + size_t num_bytes; + int last_pass; + + retval = wait_for_data_out_ready(board); + if (retval < 0) + break; + + if (fifo_xfer_counter_mask < remainder) { + // round transfer size to a multiple of half fifo size + transfer_size = (fifo_xfer_counter_mask / + fmh_gpib_half_fifo_size(e_priv)) * + fmh_gpib_half_fifo_size(e_priv); + last_pass = 0; + } else { + transfer_size = remainder; + last_pass = 1; + } + retval = fmh_gpib_fifo_write_countable(board, buffer, transfer_size, + last_pass && send_eoi, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + break; + remainder -= num_bytes; + buffer += num_bytes; + if (need_resched()) + schedule(); + } + + return retval; +} + +static int fmh_gpib_dma_read(struct gpib_board *board, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + unsigned long flags; + int residue; + int wait_retval; + dma_addr_t bus_address; + struct dma_async_tx_descriptor *tx_desc; + dma_cookie_t dma_cookie; + + *bytes_read = 0; + *end = 0; + if (length == 0) + return 0; + + bus_address = dma_map_single(board->dev, e_priv->dma_buffer, + length, DMA_FROM_DEVICE); + if (dma_mapping_error(board->dev, bus_address)) + dev_err(board->gpib_dev, "dma mapping error in dma read!"); + + /* program dma controller */ + retval = fmh_gpib_config_dma(board, 0); + if (retval) { + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + return retval; + } + tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, bus_address, + length, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_desc) { + dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + return -EIO; + } + tx_desc->callback = fmh_gpib_dma_callback; + tx_desc->callback_param = board; + + spin_lock_irqsave(&board->spinlock, flags); + // enable nec7210 dma + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, RX_FIFO_DMA_REQUEST_ENABLE | RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + dma_cookie = dmaengine_submit(tx_desc); + dma_async_issue_pending(e_priv->dma_channel); + + set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); + + // wait for data to transfer + wait_retval = wait_event_interruptible(board->wait, + test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) + == 0 || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status)); + if (wait_retval) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + // stop the dma transfer + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + /* + * give time for pl330 to transfer any in-flight data, since + * pl330 will throw it away when dmaengine_pause is called. + */ + usleep_range(10, 15); + residue = fmh_gpib_get_dma_residue(e_priv->dma_channel, dma_cookie); + if (WARN_ON_ONCE(residue > length || residue < 0)) + return -EFAULT; + *bytes_read += length - residue; + dmaengine_terminate_all(e_priv->dma_channel); + // make sure fmh_gpib_dma_callback got called + if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state)) + fmh_gpib_dma_callback(board); + + dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); + memcpy(buffer, e_priv->dma_buffer, *bytes_read); + + /* Manually read any dregs out of fifo. */ + while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) { + if ((*bytes_read) >= length) { + dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d residue=%d\n", + (int)(*bytes_read), (int)length, (int)residue); + break; + } + buffer[(*bytes_read)++] = fifos_read(e_priv, FIFO_DATA_REG) & fifo_data_mask; + } + + /* + * If we got an end interrupt, figure out if it was + * associated with the last byte we dma'd or with a + * byte still sitting on the cb7210. + */ + spin_lock_irqsave(&board->spinlock, flags); + if (*bytes_read > 0 && test_bit(READ_READY_BN, &nec_priv->state) == 0) { + /* + * If there is no byte sitting on the cb7210 and we + * saw an end, we need to deal with it now + */ + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + } + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} + +static void fmh_gpib_release_rfd_holdoff(struct gpib_board *board, struct fmh_priv *e_priv) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned int ext_status_1; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG); + + /* + * if there is an end byte sitting on the chip, don't release + * holdoff. We want it left set after we read out the end + * byte. + */ + if ((ext_status_1 & (DATA_IN_STATUS_BIT | END_STATUS_BIT)) != + (DATA_IN_STATUS_BIT | END_STATUS_BIT)) { + if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT) + write_byte(nec_priv, AUX_FH, AUXMR); + + /* + * Check if an end byte raced in before we executed the AUX_FH command. + * If it did, we want to make sure the rfd holdoff is in effect. The end + * byte can arrive since + * AUX_RFD_HOLDOFF_ASAP doesn't immediately force the acceptor handshake + * to leave ACRS. + */ + if ((read_byte(nec_priv, EXT_STATUS_1_REG) & + (RFD_HOLDOFF_STATUS_BIT | DATA_IN_STATUS_BIT | END_STATUS_BIT)) == + (DATA_IN_STATUS_BIT | END_STATUS_BIT)) { + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } else { + clear_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + } + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static int fmh_gpib_accel_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remain = length; + size_t transfer_size; + int retval = 0; + size_t dma_nbytes; + unsigned long flags; + + smp_mb__before_atomic(); + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + smp_mb__after_atomic(); + *end = 0; + *bytes_read = 0; + + retval = wait_for_read(board); + if (retval < 0) + return retval; + + fmh_gpib_release_rfd_holdoff(board, e_priv); + while (remain > 0) { + transfer_size = (e_priv->dma_buffer_size < remain) ? + e_priv->dma_buffer_size : remain; + retval = fmh_gpib_dma_read(board, buffer, transfer_size, end, &dma_nbytes); + remain -= dma_nbytes; + buffer += dma_nbytes; + *bytes_read += dma_nbytes; + if (*end) + break; + if (retval < 0) + break; + if (need_resched()) + schedule(); + } + + spin_lock_irqsave(&board->spinlock, flags); + if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state) == 0) { + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} + +/* + * Read a chunk of data whose length is within the limits of the hardware's + * xfer counter. Called in a loop from fmh_gpib_fifo_read(). + */ +static int fmh_gpib_fifo_read_countable(struct gpib_board *board, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + int retval = 0; + + *bytes_read = 0; + *end = 0; + if (length == 0) + return 0; + + fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); + fifos_write(e_priv, RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + + while (*bytes_read < length && *end == 0) { + int i; + + fifos_write(e_priv, RX_FIFO_HALF_FULL_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG); + retval = wait_for_rx_fifo_half_full_or_end(board); + if (retval < 0) + goto cleanup; + + for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && *end == 0; ++i) { + unsigned int data_value; + + data_value = fifos_read(e_priv, FIFO_DATA_REG); + buffer[(*bytes_read)++] = data_value & fifo_data_mask; + if (data_value & FIFO_DATA_EOI_FLAG) + *end = 1; + } + } + +cleanup: + // stop the transfer + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + + /* Manually read any dregs out of fifo. */ + while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) { + unsigned int data_value; + + if ((*bytes_read) >= length) { + dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d\n", + (int)(*bytes_read), (int)length); + break; + } + data_value = fifos_read(e_priv, FIFO_DATA_REG); + buffer[(*bytes_read)++] = data_value & fifo_data_mask; + if (data_value & FIFO_DATA_EOI_FLAG) + *end = 1; + } + + return retval; +} + +static int fmh_gpib_fifo_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + size_t remain = length; + size_t transfer_size; + int retval = 0; + size_t nbytes; + unsigned long flags; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME + *end = 0; + *bytes_read = 0; + + /* + * Do a little prep with data in interrupt so that following wait_for_read() + * will wake up if a data byte is received. + */ + nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, HR_DIIE); + fmh_gpib_interrupt(0, board); + + retval = wait_for_read(board); + if (retval < 0) + return retval; + + fmh_gpib_release_rfd_holdoff(board, e_priv); + while (remain > 0) { + if (fifo_xfer_counter_mask < remain) { + // round transfer size to a multiple of half fifo size + transfer_size = (fifo_xfer_counter_mask / + fmh_gpib_half_fifo_size(e_priv)) * + fmh_gpib_half_fifo_size(e_priv); + } else { + transfer_size = remain; + } + retval = fmh_gpib_fifo_read_countable(board, buffer, transfer_size, end, &nbytes); + remain -= nbytes; + buffer += nbytes; + *bytes_read += nbytes; + if (*end) + break; + if (retval < 0) + break; + if (need_resched()) + schedule(); + } + + if (*end == 0) { + spin_lock_irqsave(&board->spinlock, flags); + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + } + + return retval; +} + +static struct gpib_interface fmh_gpib_unaccel_interface = { + .name = "fmh_gpib_unaccel", + .attach = fmh_gpib_attach_holdoff_all, + .detach = fmh_gpib_detach, + .read = fmh_gpib_read, + .write = fmh_gpib_write, + .command = fmh_gpib_command, + .take_control = fmh_gpib_take_control, + .go_to_standby = fmh_gpib_go_to_standby, + .request_system_control = fmh_gpib_request_system_control, + .interface_clear = fmh_gpib_interface_clear, + .remote_enable = fmh_gpib_remote_enable, + .enable_eos = fmh_gpib_enable_eos, + .disable_eos = fmh_gpib_disable_eos, + .parallel_poll = fmh_gpib_parallel_poll, + .parallel_poll_configure = fmh_gpib_parallel_poll_configure, + .parallel_poll_response = fmh_gpib_parallel_poll_response, + .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, + .line_status = fmh_gpib_line_status, + .update_status = fmh_gpib_update_status, + .primary_address = fmh_gpib_primary_address, + .secondary_address = fmh_gpib_secondary_address, + .serial_poll_response2 = fmh_gpib_serial_poll_response2, + .serial_poll_status = fmh_gpib_serial_poll_status, + .t1_delay = fmh_gpib_t1_delay, + .return_to_local = fmh_gpib_return_to_local, +}; + +static struct gpib_interface fmh_gpib_interface = { + .name = "fmh_gpib", + .attach = fmh_gpib_attach_holdoff_end, + .detach = fmh_gpib_detach, + .read = fmh_gpib_accel_read, + .write = fmh_gpib_accel_write, + .command = fmh_gpib_command, + .take_control = fmh_gpib_take_control, + .go_to_standby = fmh_gpib_go_to_standby, + .request_system_control = fmh_gpib_request_system_control, + .interface_clear = fmh_gpib_interface_clear, + .remote_enable = fmh_gpib_remote_enable, + .enable_eos = fmh_gpib_enable_eos, + .disable_eos = fmh_gpib_disable_eos, + .parallel_poll = fmh_gpib_parallel_poll, + .parallel_poll_configure = fmh_gpib_parallel_poll_configure, + .parallel_poll_response = fmh_gpib_parallel_poll_response, + .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, + .line_status = fmh_gpib_line_status, + .update_status = fmh_gpib_update_status, + .primary_address = fmh_gpib_primary_address, + .secondary_address = fmh_gpib_secondary_address, + .serial_poll_response2 = fmh_gpib_serial_poll_response2, + .serial_poll_status = fmh_gpib_serial_poll_status, + .t1_delay = fmh_gpib_t1_delay, + .return_to_local = fmh_gpib_return_to_local, +}; + +static struct gpib_interface fmh_gpib_pci_interface = { + .name = "fmh_gpib_pci", + .attach = fmh_gpib_pci_attach_holdoff_end, + .detach = fmh_gpib_pci_detach, + .read = fmh_gpib_fifo_read, + .write = fmh_gpib_fifo_write, + .command = fmh_gpib_command, + .take_control = fmh_gpib_take_control, + .go_to_standby = fmh_gpib_go_to_standby, + .request_system_control = fmh_gpib_request_system_control, + .interface_clear = fmh_gpib_interface_clear, + .remote_enable = fmh_gpib_remote_enable, + .enable_eos = fmh_gpib_enable_eos, + .disable_eos = fmh_gpib_disable_eos, + .parallel_poll = fmh_gpib_parallel_poll, + .parallel_poll_configure = fmh_gpib_parallel_poll_configure, + .parallel_poll_response = fmh_gpib_parallel_poll_response, + .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, + .line_status = fmh_gpib_line_status, + .update_status = fmh_gpib_update_status, + .primary_address = fmh_gpib_primary_address, + .secondary_address = fmh_gpib_secondary_address, + .serial_poll_response2 = fmh_gpib_serial_poll_response2, + .serial_poll_status = fmh_gpib_serial_poll_status, + .t1_delay = fmh_gpib_t1_delay, + .return_to_local = fmh_gpib_return_to_local, +}; + +static struct gpib_interface fmh_gpib_pci_unaccel_interface = { + .name = "fmh_gpib_pci_unaccel", + .attach = fmh_gpib_pci_attach_holdoff_all, + .detach = fmh_gpib_pci_detach, + .read = fmh_gpib_read, + .write = fmh_gpib_write, + .command = fmh_gpib_command, + .take_control = fmh_gpib_take_control, + .go_to_standby = fmh_gpib_go_to_standby, + .request_system_control = fmh_gpib_request_system_control, + .interface_clear = fmh_gpib_interface_clear, + .remote_enable = fmh_gpib_remote_enable, + .enable_eos = fmh_gpib_enable_eos, + .disable_eos = fmh_gpib_disable_eos, + .parallel_poll = fmh_gpib_parallel_poll, + .parallel_poll_configure = fmh_gpib_parallel_poll_configure, + .parallel_poll_response = fmh_gpib_parallel_poll_response, + .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, + .line_status = fmh_gpib_line_status, + .update_status = fmh_gpib_update_status, + .primary_address = fmh_gpib_primary_address, + .secondary_address = fmh_gpib_secondary_address, + .serial_poll_response2 = fmh_gpib_serial_poll_response2, + .serial_poll_status = fmh_gpib_serial_poll_status, + .t1_delay = fmh_gpib_t1_delay, + .return_to_local = fmh_gpib_return_to_local, +}; + +irqreturn_t fmh_gpib_internal_interrupt(struct gpib_board *board) +{ + unsigned int status0, status1, status2, ext_status_1, fifo_status; + struct fmh_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int retval = IRQ_NONE; + + status0 = read_byte(nec_priv, ISR0_IMR0_REG); + status1 = read_byte(nec_priv, ISR1); + status2 = read_byte(nec_priv, ISR2); + fifo_status = fifos_read(priv, FIFO_CONTROL_STATUS_REG); + + if (status0 & IFC_INTERRUPT_BIT) { + push_gpib_event(board, EVENT_IFC); + retval = IRQ_HANDLED; + } + + if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED) + retval = IRQ_HANDLED; + + ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG); + + if (ext_status_1 & DATA_IN_STATUS_BIT) + set_bit(READ_READY_BN, &nec_priv->state); + else + clear_bit(READ_READY_BN, &nec_priv->state); + + if (ext_status_1 & DATA_OUT_STATUS_BIT) + set_bit(WRITE_READY_BN, &nec_priv->state); + else + clear_bit(WRITE_READY_BN, &nec_priv->state); + + if (ext_status_1 & COMMAND_OUT_STATUS_BIT) + set_bit(COMMAND_READY_BN, &nec_priv->state); + else + clear_bit(COMMAND_READY_BN, &nec_priv->state); + + if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT) + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + else + clear_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + if (ext_status_1 & END_STATUS_BIT) { + /* + * only set RECEIVED_END while there is still a data + * byte sitting in the chip, to avoid spuriously + * setting it multiple times after it has been cleared + * during a read. + */ + if (ext_status_1 & DATA_IN_STATUS_BIT) + set_bit(RECEIVED_END_BN, &nec_priv->state); + } else { + clear_bit(RECEIVED_END_BN, &nec_priv->state); + } + + if ((fifo_status & TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED) && + (fifo_status & TX_FIFO_HALF_EMPTY)) { + /* + * We really only want to clear the + * TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE bit in the + * FIFO_CONTROL_STATUS_REG. Since we are not being + * careful, this also has a side effect of disabling + * DMA requests and the RX fifo interrupt. That is + * fine though, since they should never be in use at + * the same time as the TX fifo interrupt. + */ + fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG); + retval = IRQ_HANDLED; + } + + if ((fifo_status & RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED) && + (fifo_status & RX_FIFO_HALF_FULL)) { + /* + * We really only want to clear the + * RX_FIFO_HALF_FULL_INTERRUPT_ENABLE bit in the + * FIFO_CONTROL_STATUS_REG. Since we are not being + * careful, this also has a side effect of disabling + * DMA requests and the TX fifo interrupt. That is + * fine though, since they should never be in use at + * the same time as the RX fifo interrupt. + */ + fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG); + retval = IRQ_HANDLED; + } + + if (retval == IRQ_HANDLED) + wake_up_interruptible(&board->wait); + + return retval; +} + +irqreturn_t fmh_gpib_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = fmh_gpib_internal_interrupt(board); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +static int fmh_gpib_allocate_private(struct gpib_board *board) +{ + struct fmh_priv *priv; + + board->private_data = kmalloc(sizeof(struct fmh_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + priv = board->private_data; + memset(priv, 0, sizeof(struct fmh_priv)); + init_nec7210_private(&priv->nec7210_priv); + priv->dma_buffer_size = 0x800; + priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL); + if (!priv->dma_buffer) + return -ENOMEM; + return 0; +} + +static void fmh_gpib_generic_detach(struct gpib_board *board) +{ + if (board->private_data) { + struct fmh_priv *e_priv = board->private_data; + + kfree(e_priv->dma_buffer); + kfree(board->private_data); + board->private_data = NULL; + } + if (board->dev) + dev_set_drvdata(board->dev, NULL); +} + +// generic part of attach functions +static int fmh_gpib_generic_attach(struct gpib_board *board) +{ + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + + board->status = 0; + + retval = fmh_gpib_allocate_private(board); + if (retval < 0) + return retval; + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + nec_priv->read_byte = gpib_cs_read_byte; + nec_priv->write_byte = gpib_cs_write_byte; + nec_priv->offset = 1; + nec_priv->type = CB7210; + return 0; +} + +static int fmh_gpib_config_dma(struct gpib_board *board, int output) +{ + struct fmh_priv *e_priv = board->private_data; + struct dma_slave_config config; + + config.device_fc = true; + + if (e_priv->dma_burst_length < 1) { + config.src_maxburst = 1; + config.dst_maxburst = 1; + } else { + config.src_maxburst = e_priv->dma_burst_length; + config.dst_maxburst = e_priv->dma_burst_length; + } + + config.src_addr_width = 1; + config.dst_addr_width = 1; + + if (output) { + config.direction = DMA_MEM_TO_DEV; + config.src_addr = 0; + config.dst_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset; + } else { + config.direction = DMA_DEV_TO_MEM; + config.src_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset; + config.dst_addr = 0; + } + return dmaengine_slave_config(e_priv->dma_channel, &config); +} + +static int fmh_gpib_init(struct fmh_priv *e_priv, struct gpib_board *board, int handshake_mode) +{ + struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; + unsigned long flags; + unsigned int fifo_status_bits; + + fifos_write(e_priv, RX_FIFO_CLEAR | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); + + nec7210_board_reset(nec_priv, board); + write_byte(nec_priv, AUX_LO_SPEED, AUXMR); + nec7210_set_handshake_mode(board, nec_priv, handshake_mode); + + /* Hueristically check if hardware supports fifo half full/empty interrupts */ + fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG); + e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) && + (fifo_status_bits & TX_FIFO_HALF_EMPTY); + + nec7210_board_online(nec_priv, board); + + write_byte(nec_priv, IFC_INTERRUPT_ENABLE_BIT | ATN_INTERRUPT_ENABLE_BIT, ISR0_IMR0_REG); + + spin_lock_irqsave(&board->spinlock, flags); + write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + spin_unlock_irqrestore(&board->spinlock, flags); + return 0; +} + +/* Match callback for driver_find_device */ +static int fmh_gpib_device_match(struct device *dev, const void *data) +{ + const struct gpib_board_config *config = data; + + if (dev_get_drvdata(dev)) + return 0; + + if (gpib_match_device_path(dev, config->device_path) == 0) + return 0; + + // driver doesn't support selection by serial number + if (config->serial_number) + return 0; + + dev_dbg(dev, "matched: %s\n", of_node_full_name(dev_of_node((dev)))); + return 1; +} + +static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_board_config *config, + unsigned int handshake_mode, int acquire_dma) +{ + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + int irq; + struct resource *res; + struct platform_device *pdev; + + board->dev = driver_find_device(&fmh_gpib_platform_driver.driver, + NULL, (const void *)config, &fmh_gpib_device_match); + if (!board->dev) { + dev_err(board->gpib_dev, "No matching fmh_gpib_core device was found, attach failed."); + return -ENODEV; + } + // currently only used to mark the device as already attached + dev_set_drvdata(board->dev, board); + pdev = to_platform_device(board->dev); + + retval = fmh_gpib_generic_attach(board); + if (retval) + return retval; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpib_control_status"); + if (!res) { + dev_err(board->dev, "Unable to locate mmio resource\n"); + return -ENODEV; + } + + if (request_mem_region(res->start, + resource_size(res), + pdev->name) == NULL) { + dev_err(board->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->gpib_iomem_res = res; + + nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + if (!nec_priv->mmiobase) { + dev_err(board->dev, "Could not map I/O memory\n"); + return -ENOMEM; + } + dev_dbg(board->dev, "iobase %pr remapped to %p\n", + e_priv->gpib_iomem_res, nec_priv->mmiobase); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma_fifos"); + if (!res) { + dev_err(board->dev, "Unable to locate mmio resource for gpib dma port\n"); + return -ENODEV; + } + if (request_mem_region(res->start, + resource_size(res), + pdev->name) == NULL) { + dev_err(board->dev, "cannot claim registers\n"); + return -ENXIO; + } + e_priv->dma_port_res = res; + e_priv->fifo_base = ioremap(e_priv->dma_port_res->start, + resource_size(e_priv->dma_port_res)); + if (!e_priv->fifo_base) { + dev_err(board->dev, "Could not map I/O memory for fifos\n"); + return -ENOMEM; + } + dev_dbg(board->dev, "dma fifos 0x%lx remapped to %p, length=%ld\n", + (unsigned long)e_priv->dma_port_res->start, e_priv->fifo_base, + (unsigned long)resource_size(e_priv->dma_port_res)); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EBUSY; + retval = request_irq(irq, fmh_gpib_interrupt, IRQF_SHARED, pdev->name, board); + if (retval) { + dev_err(board->dev, + "cannot register interrupt handler err=%d\n", + retval); + return retval; + } + e_priv->irq = irq; + + if (acquire_dma) { + e_priv->dma_channel = dma_request_slave_channel(board->dev, "rxtx"); + if (!e_priv->dma_channel) { + dev_err(board->dev, "failed to acquire dma channel \"rxtx\".\n"); + return -EIO; + } + } + /* + * in the future we might want to know the half-fifo size + * (dma_burst_length) even when not using dma, so go ahead an + * initialize it unconditionally. + */ + e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) & + fifo_max_burst_length_mask; + + return fmh_gpib_init(e_priv, board, handshake_mode); +} + +int fmh_gpib_attach_holdoff_all(struct gpib_board *board, const struct gpib_board_config *config) +{ + return fmh_gpib_attach_impl(board, config, HR_HLDA, 0); +} + +int fmh_gpib_attach_holdoff_end(struct gpib_board *board, const struct gpib_board_config *config) +{ + return fmh_gpib_attach_impl(board, config, HR_HLDE, 1); +} + +void fmh_gpib_detach(struct gpib_board *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (e_priv) { + if (e_priv->dma_channel) + dma_release_channel(e_priv->dma_channel); + nec_priv = &e_priv->nec7210_priv; + + if (e_priv->irq) + free_irq(e_priv->irq, board); + if (e_priv->fifo_base) + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + if (nec_priv->mmiobase) { + write_byte(nec_priv, 0, ISR0_IMR0_REG); + nec7210_board_reset(nec_priv, board); + } + if (e_priv->fifo_base) + iounmap(e_priv->fifo_base); + if (nec_priv->mmiobase) + iounmap(nec_priv->mmiobase); + if (e_priv->dma_port_res) { + release_mem_region(e_priv->dma_port_res->start, + resource_size(e_priv->dma_port_res)); + } + if (e_priv->gpib_iomem_res) + release_mem_region(e_priv->gpib_iomem_res->start, + resource_size(e_priv->gpib_iomem_res)); + } + fmh_gpib_generic_detach(board); + + if (board->dev) { + put_device(board->dev); + board->dev = NULL; + } +} + +static int fmh_gpib_pci_attach_impl(struct gpib_board *board, + const struct gpib_board_config *config, + unsigned int handshake_mode) +{ + struct fmh_priv *e_priv; + struct nec7210_priv *nec_priv; + int retval; + struct pci_dev *pci_device; + + retval = fmh_gpib_generic_attach(board); + if (retval) + return retval; + + e_priv = board->private_data; + nec_priv = &e_priv->nec7210_priv; + + // find board + pci_device = gpib_pci_get_device(config, BOGUS_PCI_VENDOR_ID_FLUKE, + BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, NULL); + if (!pci_device) { + dev_err(board->gpib_dev, "No matching fmh_gpib_core pci device was found, attach failed."); + return -ENODEV; + } + board->dev = &pci_device->dev; + + // bladerunner prototype has offset of 4 between gpib control/status registers + nec_priv->offset = 4; + + if (pci_enable_device(pci_device)) { + dev_err(board->dev, "error enabling pci device\n"); + return -EIO; + } + if (pci_request_regions(pci_device, KBUILD_MODNAME)) { + dev_err(board->dev, "pci_request_regions failed\n"); + return -EIO; + } + e_priv->gpib_iomem_res = &pci_device->resource[gpib_control_status_pci_resource_index]; + e_priv->dma_port_res = &pci_device->resource[gpib_fifo_pci_resource_index]; + + nec_priv->mmiobase = ioremap(pci_resource_start(pci_device, + gpib_control_status_pci_resource_index), + pci_resource_len(pci_device, + gpib_control_status_pci_resource_index)); + dev_dbg(board->dev, "base address for gpib control/status registers remapped to 0x%p\n", + nec_priv->mmiobase); + + if (e_priv->dma_port_res->flags & IORESOURCE_MEM) { + e_priv->fifo_base = ioremap(pci_resource_start(pci_device, + gpib_fifo_pci_resource_index), + pci_resource_len(pci_device, + gpib_fifo_pci_resource_index)); + dev_dbg(board->dev, "base address for gpib fifo registers remapped to 0x%p\n", + e_priv->fifo_base); + } else { + e_priv->fifo_base = NULL; + dev_dbg(board->dev, "hardware has no gpib fifo registers.\n"); + } + + if (pci_device->irq) { + retval = request_irq(pci_device->irq, fmh_gpib_interrupt, IRQF_SHARED, + KBUILD_MODNAME, board); + if (retval) { + dev_err(board->dev, "cannot register interrupt handler err=%d\n", retval); + return retval; + } + } + e_priv->irq = pci_device->irq; + + e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) & + fifo_max_burst_length_mask; + + return fmh_gpib_init(e_priv, board, handshake_mode); +} + +int fmh_gpib_pci_attach_holdoff_all(struct gpib_board *board, + const struct gpib_board_config *config) +{ + return fmh_gpib_pci_attach_impl(board, config, HR_HLDA); +} + +int fmh_gpib_pci_attach_holdoff_end(struct gpib_board *board, + const struct gpib_board_config *config) +{ + int retval; + struct fmh_priv *e_priv; + + retval = fmh_gpib_pci_attach_impl(board, config, HR_HLDE); + e_priv = board->private_data; + if (retval == 0 && e_priv && e_priv->supports_fifo_interrupts == 0) { + dev_err(board->gpib_dev, "your fmh_gpib_core does not appear to support fifo interrupts. Try the fmh_gpib_pci_unaccel board type instead."); + return -EIO; + } + return retval; +} + +void fmh_gpib_pci_detach(struct gpib_board *board) +{ + struct fmh_priv *e_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (e_priv) { + nec_priv = &e_priv->nec7210_priv; + + if (e_priv->irq) + free_irq(e_priv->irq, board); + if (e_priv->fifo_base) + fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); + if (nec_priv->mmiobase) { + write_byte(nec_priv, 0, ISR0_IMR0_REG); + nec7210_board_reset(nec_priv, board); + } + if (e_priv->fifo_base) + iounmap(e_priv->fifo_base); + if (nec_priv->mmiobase) + iounmap(nec_priv->mmiobase); + if (e_priv->dma_port_res || e_priv->gpib_iomem_res) + pci_release_regions(to_pci_dev(board->dev)); + if (board->dev) + pci_dev_put(to_pci_dev(board->dev)); + } + fmh_gpib_generic_detach(board); +} + +static int fmh_gpib_platform_probe(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id fmh_gpib_of_match[] = { + { .compatible = "fmhess,fmh_gpib_core"}, + { {0} } +}; +MODULE_DEVICE_TABLE(of, fmh_gpib_of_match); + +static struct platform_driver fmh_gpib_platform_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = fmh_gpib_of_match, + }, + .probe = &fmh_gpib_platform_probe +}; + +static int fmh_gpib_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static const struct pci_device_id fmh_gpib_pci_match[] = { + { BOGUS_PCI_VENDOR_ID_FLUKE, BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, fmh_gpib_pci_match); + +static struct pci_driver fmh_gpib_pci_driver = { + .name = DRV_NAME, + .id_table = fmh_gpib_pci_match, + .probe = &fmh_gpib_pci_probe +}; + +static int __init fmh_gpib_init_module(void) +{ + int result; + + result = platform_driver_register(&fmh_gpib_platform_driver); + if (result) { + pr_err("platform_driver_register failed: error = %d\n", result); + return result; + } + + result = pci_register_driver(&fmh_gpib_pci_driver); + if (result) { + pr_err("pci_register_driver failed: error = %d\n", result); + goto err_pci_driver; + } + + result = gpib_register_driver(&fmh_gpib_unaccel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_unaccel; + } + + result = gpib_register_driver(&fmh_gpib_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_interface; + } + + result = gpib_register_driver(&fmh_gpib_pci_unaccel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_pci_unaccel; + } + + result = gpib_register_driver(&fmh_gpib_pci_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_pci; + } + + return 0; + +err_pci: + gpib_unregister_driver(&fmh_gpib_pci_unaccel_interface); +err_pci_unaccel: + gpib_unregister_driver(&fmh_gpib_interface); +err_interface: + gpib_unregister_driver(&fmh_gpib_unaccel_interface); +err_unaccel: + pci_unregister_driver(&fmh_gpib_pci_driver); +err_pci_driver: + platform_driver_unregister(&fmh_gpib_platform_driver); + + return result; +} + +static void __exit fmh_gpib_exit_module(void) +{ + gpib_unregister_driver(&fmh_gpib_pci_interface); + gpib_unregister_driver(&fmh_gpib_pci_unaccel_interface); + gpib_unregister_driver(&fmh_gpib_unaccel_interface); + gpib_unregister_driver(&fmh_gpib_interface); + + pci_unregister_driver(&fmh_gpib_pci_driver); + platform_driver_unregister(&fmh_gpib_platform_driver); +} + +module_init(fmh_gpib_init_module); +module_exit(fmh_gpib_exit_module); diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.h b/drivers/gpib/fmh_gpib/fmh_gpib.h new file mode 100644 index 000000000000..e7602d7e1401 --- /dev/null +++ b/drivers/gpib/fmh_gpib/fmh_gpib.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Author: Frank Mori Hess + * Copyright: (C) 2006, 2010, 2015 Fluke Corporation + * (C) 2017 Frank Mori Hess + ***************************************************************************/ + +#include +#include +#include +#include +#include "nec7210.h" + +static const int fifo_reg_offset = 2; + +static const int gpib_control_status_pci_resource_index; +static const int gpib_fifo_pci_resource_index = 1; + +/* We don't have a real pci vendor/device id, the following will need to be + * patched to match prototype hardware. + */ +#define BOGUS_PCI_VENDOR_ID_FLUKE 0xffff +#define BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER 0x0 + +struct fmh_priv { + struct nec7210_priv nec7210_priv; + struct resource *gpib_iomem_res; + struct resource *write_transfer_counter_res; + struct resource *dma_port_res; + int irq; + struct dma_chan *dma_channel; + u8 *dma_buffer; + int dma_buffer_size; + int dma_burst_length; + void __iomem *fifo_base; + unsigned supports_fifo_interrupts : 1; +}; + +static inline int fmh_gpib_half_fifo_size(struct fmh_priv *priv) +{ + return priv->dma_burst_length; +} + +// registers beyond the nec7210 register set +enum fmh_gpib_regs { + EXT_STATUS_1_REG = 0x9, + STATE1_REG = 0xc, + ISR0_IMR0_REG = 0xe, + BUS_STATUS_REG = 0xf +}; + +/* IMR0 -- Interrupt Mode Register 0 */ +enum imr0_bits { + ATN_INTERRUPT_ENABLE_BIT = 0x4, + IFC_INTERRUPT_ENABLE_BIT = 0x8 +}; + +/* ISR0 -- Interrupt Status Register 0 */ +enum isr0_bits { + ATN_INTERRUPT_BIT = 0x4, + IFC_INTERRUPT_BIT = 0x8 +}; + +enum state1_bits { + SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */ + SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */ + SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */ + SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */ + SOURCE_HANDSHAKE_MASK = 0x7 +}; + +enum fmh_gpib_auxmr_bits { + AUX_I_REG = 0xe0, +}; + +enum aux_reg_i_bits { + LOCAL_PPOLL_MODE_BIT = 0x4 +}; + +enum ext_status_1_bits { + DATA_IN_STATUS_BIT = 0x01, + DATA_OUT_STATUS_BIT = 0x02, + COMMAND_OUT_STATUS_BIT = 0x04, + RFD_HOLDOFF_STATUS_BIT = 0x08, + END_STATUS_BIT = 0x10 +}; + +/* dma fifo reg and bits */ +enum dma_fifo_regs { + FIFO_DATA_REG = 0x0, + FIFO_CONTROL_STATUS_REG = 0x1, + FIFO_XFER_COUNTER_REG = 0x2, + FIFO_MAX_BURST_LENGTH_REG = 0x3 +}; + +enum fifo_data_bits { + FIFO_DATA_EOI_FLAG = 0x100 +}; + +enum fifo_control_bits { + TX_FIFO_DMA_REQUEST_ENABLE = 0x0001, + TX_FIFO_CLEAR = 0x0002, + TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE = 0x0008, + RX_FIFO_DMA_REQUEST_ENABLE = 0x0100, + RX_FIFO_CLEAR = 0x0200, + RX_FIFO_HALF_FULL_INTERRUPT_ENABLE = 0x0800 +}; + +enum fifo_status_bits { + TX_FIFO_EMPTY = 0x0001, + TX_FIFO_FULL = 0x0002, + TX_FIFO_HALF_EMPTY = 0x0004, + TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED = 0x0008, + TX_FIFO_DMA_REQUEST_IS_ENABLED = 0x0010, + RX_FIFO_EMPTY = 0x0100, + RX_FIFO_FULL = 0x0200, + RX_FIFO_HALF_FULL = 0x0400, + RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED = 0x0800, + RX_FIFO_DMA_REQUEST_IS_ENABLED = 0x1000 +}; + +static const unsigned int fifo_data_mask = 0x00ff; +static const unsigned int fifo_xfer_counter_mask = 0x0fff; +static const unsigned int fifo_max_burst_length_mask = 0x00ff; + +static inline u8 gpib_cs_read_byte(struct nec7210_priv *nec_priv, + unsigned int register_num) +{ + return readb(nec_priv->mmiobase + register_num * nec_priv->offset); +} + +static inline void gpib_cs_write_byte(struct nec7210_priv *nec_priv, u8 data, + unsigned int register_num) +{ + writeb(data, nec_priv->mmiobase + register_num * nec_priv->offset); +} + +static inline uint16_t fifos_read(struct fmh_priv *fmh_priv, int register_num) +{ + if (!fmh_priv->fifo_base) + return 0; + return readw(fmh_priv->fifo_base + register_num * fifo_reg_offset); +} + +static inline void fifos_write(struct fmh_priv *fmh_priv, uint16_t data, int register_num) +{ + if (!fmh_priv->fifo_base) + return; + writew(data, fmh_priv->fifo_base + register_num * fifo_reg_offset); +} + +enum bus_status_bits { + BSR_ATN_BIT = 0x01, + BSR_EOI_BIT = 0x02, + BSR_SRQ_BIT = 0x04, + BSR_IFC_BIT = 0x08, + BSR_REN_BIT = 0x10, + BSR_DAV_BIT = 0x20, + BSR_NRFD_BIT = 0x40, + BSR_NDAC_BIT = 0x80, +}; + +enum fmh_gpib_aux_cmds { + /* AUX_RTL2 is an auxiliary command which causes the cb7210 to assert + * (and keep asserted) the local rtl message. This is used in conjunction + * with the normal nec7210 AUX_RTL command, which + * pulses the rtl message, having the effect of clearing rtl if it was left + * asserted by AUX_RTL2. + */ + AUX_RTL2 = 0x0d, + AUX_RFD_HOLDOFF_ASAP = 0x15, + AUX_REQT = 0x18, + AUX_REQF = 0x19, + AUX_LO_SPEED = 0x40, + AUX_HI_SPEED = 0x41 +}; diff --git a/drivers/gpib/gpio/Makefile b/drivers/gpib/gpio/Makefile new file mode 100644 index 000000000000..00ea52abdda7 --- /dev/null +++ b/drivers/gpib/gpio/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_GPIB_GPIO) += gpib_bitbang.o + + diff --git a/drivers/gpib/gpio/gpib_bitbang.c b/drivers/gpib/gpio/gpib_bitbang.c new file mode 100644 index 000000000000..374cd61355e9 --- /dev/null +++ b/drivers/gpib/gpio/gpib_bitbang.c @@ -0,0 +1,1469 @@ +// SPDX-License-Identifier: GPL-2.0 + +/************************************************************************* + * This code has been developed at the Institute of Sensor and Actuator * + * Systems (Technical University of Vienna, Austria) to enable the GPIO * + * lines (e.g. of a raspberry pi) to function as a GPIO master device * + * * + * authors : Thomas Klima * + * Marcello Carla' * + * Dave Penkler * + * * + * copyright : (C) 2016 Thomas Klima * + * * + *************************************************************************/ + +/* + * limitations: + * works only on RPi + * cannot function as non-CIC system controller with SN7516x because + * SN75161B cannot simultaneously make ATN input with IFC and REN as + * outputs. + * not implemented: + * parallel poll + * return2local + * device support (non master operation) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define NAME KBUILD_MODNAME + +#define ENABLE_IRQ(IRQ, TYPE) irq_set_irq_type(IRQ, TYPE) +#define DISABLE_IRQ(IRQ) irq_set_irq_type(IRQ, IRQ_TYPE_NONE) + +/* + * Debug print levels: + * 0 = load/unload info and errors that make the driver fail; + * 1 = + warnings for unforeseen events that may break the current + * operation and lead to a timeout, but do not affect the + * driver integrity (mainly unexpected interrupts); + * 2 = + trace of function calls; + * 3 = + trace of protocol codes; + * 4 = + trace of interrupt operation. + */ +#define dbg_printk(level, frm, ...) \ + do { if (debug >= (level)) \ + dev_dbg(board->gpib_dev, frm, ## __VA_ARGS__); } \ + while (0) + +#define LINVAL gpiod_get_value(DAV), \ + gpiod_get_value(NRFD), \ + gpiod_get_value(NDAC), \ + gpiod_get_value(SRQ) +#define LINFMT "DAV: %d NRFD:%d NDAC: %d SRQ: %d" + +#include "gpibP.h" +#include "gpib_state_machines.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int sn7516x_used = 1, sn7516x; +module_param(sn7516x_used, int, 0660); + +#define PINMAP_0 "elektronomikon" +#define PINMAP_1 "gpib4pi-1.1" +#define PINMAP_2 "yoga" +static char *pin_map = PINMAP_0; +module_param(pin_map, charp, 0660); +MODULE_PARM_DESC(pin_map, " valid values: " PINMAP_0 " " PINMAP_1 " " PINMAP_2); + +/********************************************** + * Signal pairing and pin wiring between the * + * Raspberry-Pi connector and the GPIB bus * + * * + * signal pin wiring * + * GPIB Pi-gpio GPIB -> RPi * + ********************************************** + */ +enum lines_t { + D01_pin_nr = 20, /* 1 -> 38 */ + D02_pin_nr = 26, /* 2 -> 37 */ + D03_pin_nr = 16, /* 3 -> 36 */ + D04_pin_nr = 19, /* 4 -> 35 */ + D05_pin_nr = 13, /* 13 -> 33 */ + D06_pin_nr = 12, /* 14 -> 32 */ + D07_pin_nr = 6, /* 15 -> 31 */ + D08_pin_nr = 5, /* 16 -> 29 */ + EOI_pin_nr = 9, /* 5 -> 21 */ + DAV_pin_nr = 10, /* 6 -> 19 */ + NRFD_pin_nr = 24, /* 7 -> 18 */ + NDAC_pin_nr = 23, /* 8 -> 16 */ + IFC_pin_nr = 22, /* 9 -> 15 */ + SRQ_pin_nr = 11, /* 10 -> 23 */ + _ATN_pin_nr = 25, /* 11 -> 22 */ + REN_pin_nr = 27, /* 17 -> 13 */ +/* + * GROUND PINS + * 12,18,19,20,21,22,23,24 => 14,20,25,30,34,39 + */ + +/* + * These lines are used to control the external + * SN75160/161 driver chips when used. + * When not used there is reduced fan out; + * currently tested with up to 4 devices. + */ + +/* Pi GPIO RPI 75161B 75160B Description */ + PE_pin_nr = 7, /* 26 -> nc 11 Pullup Enable */ + DC_pin_nr = 8, /* 24 -> 12 nc Direction control */ + TE_pin_nr = 18, /* 12 -> 2 1 Talk Enable */ + ACT_LED_pin_nr = 4, /* 7 -> LED */ + +/* YOGA adapter uses different pinout to ease layout */ + YOGA_D03_pin_nr = 13, + YOGA_D04_pin_nr = 12, + YOGA_D05_pin_nr = 21, + YOGA_D06_pin_nr = 19, +}; + +/* + * GPIO descriptors and pins - WARNING: STRICTLY KEEP ITEMS ORDER + */ + +#define GPIB_PINS 16 +#define SN7516X_PINS 4 +#define NUM_PINS (GPIB_PINS + SN7516X_PINS) + +#define ACT_LED_ON do { \ + if (ACT_LED) \ + gpiod_direction_output(ACT_LED, 1); \ + } while (0) +#define ACT_LED_OFF do { \ + if (ACT_LED) \ + gpiod_direction_output(ACT_LED, 0); \ + } while (0) + +static struct gpio_desc *all_descriptors[GPIB_PINS + SN7516X_PINS]; + +#define D01 all_descriptors[0] +#define D02 all_descriptors[1] +#define D03 all_descriptors[2] +#define D04 all_descriptors[3] +#define D05 all_descriptors[4] +#define D06 all_descriptors[5] +#define D07 all_descriptors[6] +#define D08 all_descriptors[7] + +#define EOI all_descriptors[8] +#define NRFD all_descriptors[9] +#define IFC all_descriptors[10] +#define _ATN all_descriptors[11] +#define REN all_descriptors[12] +#define DAV all_descriptors[13] +#define NDAC all_descriptors[14] +#define SRQ all_descriptors[15] + +#define PE all_descriptors[16] +#define DC all_descriptors[17] +#define TE all_descriptors[18] +#define ACT_LED all_descriptors[19] + +/* YOGA adapter uses a global enable for the buffer chips, re-using the TE pin */ +#define YOGA_ENABLE TE + +static int gpios_vector[] = { + D01_pin_nr, + D02_pin_nr, + D03_pin_nr, + D04_pin_nr, + D05_pin_nr, + D06_pin_nr, + D07_pin_nr, + D08_pin_nr, + + EOI_pin_nr, + NRFD_pin_nr, + IFC_pin_nr, + _ATN_pin_nr, + REN_pin_nr, + DAV_pin_nr, + NDAC_pin_nr, + SRQ_pin_nr, + + PE_pin_nr, + DC_pin_nr, + TE_pin_nr, + ACT_LED_pin_nr +}; + +/* Lookup table for general GPIOs */ + +static struct gpiod_lookup_table gpib_gpio_table_1 = { + // for bcm2835/6 + .dev_id = "", // device id of board device + .table = { + GPIO_LOOKUP_IDX("GPIO_GCLK", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_CE1_N", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_CE0_N", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_MISO", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_MOSI", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("SPI_SCLK", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table gpib_gpio_table_0 = { + .dev_id = "", // device id of board device + .table = { + // for bcm27xx based pis (b b+ 2b 3b 3b+ 4 5) + GPIO_LOOKUP_IDX("GPIO4", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO7", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO8", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO9", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO10", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO11", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table *lookup_tables[] = { + &gpib_gpio_table_0, + &gpib_gpio_table_1, + NULL +}; + +/* struct which defines private_data for gpio driver */ + +struct bb_priv { + int irq_NRFD; + int irq_NDAC; + int irq_DAV; + int irq_SRQ; + int dav_mode; /* dav interrupt mode 0/1 -> edge/levels */ + int nrfd_mode; /* nrfd interrupt mode 0/1 -> edge/levels */ + int ndac_mode; /* nrfd interrupt mode 0/1 -> edge/levels */ + int dav_tx; /* keep trace of DAV status while sending */ + int dav_rx; /* keep trace of DAV status while receiving */ + u8 eos; /* eos character */ + short eos_flags; /* eos mode */ + short eos_check; /* eos check required in current operation ... */ + short eos_check_8; /* ... with byte comparison */ + short eos_mask_7; /* ... with 7 bit masked character */ + short int end; + int request; + int count; + int direction; + int t1_delay; + u8 *rbuf; + u8 *wbuf; + int end_flag; + int r_busy; /* 0==idle 1==busy */ + int w_busy; + int write_done; + int cmd; /* 1 = cmd write in progress */ + size_t w_cnt; + size_t length; + u8 *w_buf; + spinlock_t rw_lock; /* protect mods to rw_lock */ + int phase; + int ndac_idle; + int ndac_seq; + int nrfd_idle; + int nrfd_seq; + int dav_seq; + long all_irqs; + int dav_idle; + + enum talker_function_state talker_state; + enum listener_function_state listener_state; +}; + +static inline long usec_diff(struct timespec64 *a, struct timespec64 *b); +static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length, + int cmd, int eoi); +static void set_data_lines(u8 byte); +static u8 get_data_lines(void); +static void set_data_lines_input(void); +static void set_data_lines_output(void); +static inline int check_for_eos(struct bb_priv *priv, u8 byte); +static void set_atn(struct gpib_board *board, int atn_asserted); + +static inline void SET_DIR_WRITE(struct bb_priv *priv); +static inline void SET_DIR_READ(struct bb_priv *priv); + +#define DIR_READ 0 +#define DIR_WRITE 1 + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB helper functions for bitbanging I/O"); + +/**** global variables ****/ +static int debug; +module_param(debug, int, 0644); + +static char printable(char x) +{ + if (x < 32 || x > 126) + return ' '; + return x; +} + +/*************************************************************************** + * * + * READ * + * * + ***************************************************************************/ + +static int bb_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct bb_priv *priv = board->private_data; + unsigned long flags; + int retval = 0; + + ACT_LED_ON; + SET_DIR_READ(priv); + + dbg_printk(2, "board: %p lock %d length: %zu\n", + board, mutex_is_locked(&board->user_mutex), length); + + priv->end = 0; + priv->count = 0; + priv->rbuf = buffer; + if (length == 0) + goto read_end; + priv->request = length; + priv->eos_check = (priv->eos_flags & REOS) == 0; /* do eos check */ + priv->eos_check_8 = priv->eos_flags & BIN; /* over 8 bits */ + priv->eos_mask_7 = priv->eos & 0x7f; /* with this 7 bit eos */ + + dbg_printk(3, ".........." LINFMT "\n", LINVAL); + + spin_lock_irqsave(&priv->rw_lock, flags); + priv->dav_mode = 1; + priv->dav_rx = 1; + ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_LEVEL_LOW); + priv->end_flag = 0; + gpiod_set_value(NRFD, 1); // ready for data + priv->r_busy = 1; + priv->phase = 100; + spin_unlock_irqrestore(&priv->rw_lock, flags); + + /* wait for the interrupt routines finish their work */ + + retval = wait_event_interruptible(board->wait, + (priv->end_flag || board->status & TIMO)); + + dbg_printk(3, "awake from wait queue: %d\n", retval); + + if (retval == 0 && board->status & TIMO) { + retval = -ETIMEDOUT; + dbg_printk(1, "timeout\n"); + } else if (retval) { + retval = -ERESTARTSYS; + } + + DISABLE_IRQ(priv->irq_DAV); + spin_lock_irqsave(&priv->rw_lock, flags); + gpiod_set_value(NRFD, 0); // DIR_READ line state + priv->r_busy = 0; + spin_unlock_irqrestore(&priv->rw_lock, flags); + +read_end: + ACT_LED_OFF; + *bytes_read = priv->count; + *end = priv->end; + priv->r_busy = 0; + dbg_printk(2, "return: %d eoi|eos: %d count: %d\n\n", retval, priv->end, priv->count); + return retval; +} + +/*************************************************************************** + * * + * READ interrupt routine (DAV line) * + * * + ***************************************************************************/ + +static irqreturn_t bb_DAV_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + struct bb_priv *priv = board->private_data; + int val; + unsigned long flags; + + spin_lock_irqsave(&priv->rw_lock, flags); + + priv->all_irqs++; + + if (priv->dav_mode) { + ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_EDGE_BOTH); + priv->dav_mode = 0; + } + + if (priv->r_busy == 0) { + dbg_printk(1, "interrupt while idle after %d at %d\n", + priv->count, priv->phase); + priv->dav_idle++; + priv->phase = 200; + goto dav_exit; /* idle */ + } + + val = gpiod_get_value(DAV); + if (val == priv->dav_rx) { + dbg_printk(1, "out of order DAV interrupt %d/%d after %zu/%zu at %d cmd %d " + LINFMT ".\n", val, priv->dav_rx, priv->w_cnt, priv->length, + priv->phase, priv->cmd, LINVAL); + priv->dav_seq++; + } + priv->dav_rx = val; + + dbg_printk(3, "> irq: %d DAV: %d st: %4lx dir: %d busy: %d:%d\n", + irq, val, board->status, priv->direction, priv->r_busy, priv->w_busy); + + if (val == 0) { + gpiod_set_value(NRFD, 0); // not ready for data + priv->rbuf[priv->count++] = get_data_lines(); + priv->end = !gpiod_get_value(EOI); + gpiod_set_value(NDAC, 1); // data accepted + priv->end |= check_for_eos(priv, priv->rbuf[priv->count - 1]); + priv->end_flag = ((priv->count >= priv->request) || priv->end); + priv->phase = 210; + } else { + gpiod_set_value(NDAC, 0); // data not accepted + if (priv->end_flag) { + priv->r_busy = 0; + wake_up_interruptible(&board->wait); + priv->phase = 220; + } else { + gpiod_set_value(NRFD, 1); // ready for data + priv->phase = 230; + } + } + +dav_exit: + spin_unlock_irqrestore(&priv->rw_lock, flags); + dbg_printk(3, "< irq: %d count %d\n", irq, priv->count); + return IRQ_HANDLED; +} + +/*************************************************************************** + * * + * WRITE * + * * + ***************************************************************************/ + +static int bb_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + unsigned long flags; + int retval = 0; + + struct bb_priv *priv = board->private_data; + + ACT_LED_ON; + + priv->w_cnt = 0; + priv->w_buf = buffer; + dbg_printk(2, "board %p lock %d length: %zu\n", + board, mutex_is_locked(&board->user_mutex), length); + + if (debug > 1) + bb_buffer_print(board, buffer, length, priv->cmd, send_eoi); + priv->count = 0; + priv->phase = 300; + + if (length == 0) + goto write_end; + priv->end = send_eoi; + priv->length = length; + + SET_DIR_WRITE(priv); + + dbg_printk(2, "Enabling interrupts - NRFD: %d NDAC: %d\n", + gpiod_get_value(NRFD), gpiod_get_value(NDAC)); + + if (gpiod_get_value(NRFD) && gpiod_get_value(NDAC)) { /* check for listener */ + retval = -ENOTCONN; + goto write_end; + } + + spin_lock_irqsave(&priv->rw_lock, flags); + priv->w_busy = 1; /* make the interrupt routines active */ + priv->write_done = 0; + priv->nrfd_mode = 1; + priv->ndac_mode = 1; + priv->dav_tx = 1; + ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_LEVEL_HIGH); + ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_LEVEL_HIGH); + spin_unlock_irqrestore(&priv->rw_lock, flags); + + /* wait for the interrupt routines finish their work */ + + retval = wait_event_interruptible(board->wait, + priv->write_done || (board->status & TIMO)); + + dbg_printk(3, "awake from wait queue: %d\n", retval); + + if (retval == 0) { + if (board->status & TIMO) { + retval = -ETIMEDOUT; + dbg_printk(1, "timeout after %zu/%zu at %d " LINFMT " eoi: %d\n", + priv->w_cnt, length, priv->phase, LINVAL, send_eoi); + } else { + retval = priv->w_cnt; + } + } else { + retval = -ERESTARTSYS; + } + + DISABLE_IRQ(priv->irq_NRFD); + DISABLE_IRQ(priv->irq_NDAC); + + spin_lock_irqsave(&priv->rw_lock, flags); + priv->w_busy = 0; + gpiod_set_value(DAV, 1); // DIR_WRITE line state + gpiod_set_value(EOI, 1); // De-assert EOI (in case) + spin_unlock_irqrestore(&priv->rw_lock, flags); + +write_end: + *bytes_written = priv->w_cnt; + ACT_LED_OFF; + dbg_printk(2, "sent %zu bytes\r\n\r\n", *bytes_written); + priv->phase = 310; + return retval; +} + +/*************************************************************************** + * * + * WRITE interrupt routine (NRFD line) * + * * + ***************************************************************************/ + +static irqreturn_t bb_NRFD_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + struct bb_priv *priv = board->private_data; + unsigned long flags; + int nrfd; + + spin_lock_irqsave(&priv->rw_lock, flags); + + nrfd = gpiod_get_value(NRFD); + priv->all_irqs++; + + dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n", + irq, nrfd, gpiod_get_value(NDAC), board->status, priv->direction, + priv->w_busy, priv->r_busy); + + if (priv->nrfd_mode) { + ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_EDGE_RISING); + priv->nrfd_mode = 0; + } + + if (priv->w_busy == 0) { + dbg_printk(1, "interrupt while idle after %zu/%zu at %d\n", + priv->w_cnt, priv->length, priv->phase); + priv->nrfd_idle++; + goto nrfd_exit; /* idle */ + } + if (nrfd == 0) { + dbg_printk(1, "out of order interrupt after %zu/%zu at %d cmd %d " LINFMT ".\n", + priv->w_cnt, priv->length, priv->phase, priv->cmd, LINVAL); + priv->phase = 400; + priv->nrfd_seq++; + goto nrfd_exit; + } + if (!priv->dav_tx) { + dbg_printk(1, "DAV low after %zu/%zu cmd %d " LINFMT ". No action.\n", + priv->w_cnt, priv->length, priv->cmd, LINVAL); + priv->dav_seq++; + goto nrfd_exit; + } + + if (priv->w_cnt >= priv->length) { // test for missed NDAC end of transfer + dev_err(board->gpib_dev, "Unexpected NRFD exit\n"); + priv->write_done = 1; + priv->w_busy = 0; + wake_up_interruptible(&board->wait); + goto nrfd_exit; + } + + dbg_printk(3, "sending %zu\n", priv->w_cnt); + + set_data_lines(priv->w_buf[priv->w_cnt++]); // put the data on the lines + + if (priv->w_cnt == priv->length && priv->end) { + dbg_printk(3, "Asserting EOI\n"); + gpiod_set_value(EOI, 0); // Assert EOI + } + + gpiod_set_value(DAV, 0); // Data available + priv->dav_tx = 0; + priv->phase = 410; + +nrfd_exit: + spin_unlock_irqrestore(&priv->rw_lock, flags); + + return IRQ_HANDLED; +} + +/*************************************************************************** + * * + * WRITE interrupt routine (NDAC line) * + * * + ***************************************************************************/ + +static irqreturn_t bb_NDAC_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + struct bb_priv *priv = board->private_data; + unsigned long flags; + int ndac; + + spin_lock_irqsave(&priv->rw_lock, flags); + + ndac = gpiod_get_value(NDAC); + priv->all_irqs++; + dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n", + irq, gpiod_get_value(NRFD), ndac, board->status, priv->direction, + priv->w_busy, priv->r_busy); + + if (priv->ndac_mode) { + ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_EDGE_RISING); + priv->ndac_mode = 0; + } + + if (priv->w_busy == 0) { + dbg_printk(1, "interrupt while idle.\n"); + priv->ndac_idle++; + goto ndac_exit; + } + if (ndac == 0) { + dbg_printk(1, "out of order interrupt at %zu:%d.\n", priv->w_cnt, priv->phase); + priv->phase = 500; + priv->ndac_seq++; + goto ndac_exit; + } + if (priv->dav_tx) { + dbg_printk(1, "DAV high after %zu/%zu cmd %d " LINFMT ". No action.\n", + priv->w_cnt, priv->length, priv->cmd, LINVAL); + priv->dav_seq++; + goto ndac_exit; + } + + dbg_printk(3, "accepted %zu\n", priv->w_cnt - 1); + + gpiod_set_value(DAV, 1); // Data not available + priv->dav_tx = 1; + priv->phase = 510; + + if (priv->w_cnt >= priv->length) { // test for end of transfer + priv->write_done = 1; + priv->w_busy = 0; + wake_up_interruptible(&board->wait); + } + +ndac_exit: + spin_unlock_irqrestore(&priv->rw_lock, flags); + return IRQ_HANDLED; +} + +/*************************************************************************** + * * + * interrupt routine for SRQ line * + * * + ***************************************************************************/ + +static irqreturn_t bb_SRQ_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + + int val = gpiod_get_value(SRQ); + + dbg_printk(3, "> %d st: %4lx\n", val, board->status); + + if (!val) + set_bit(SRQI_NUM, &board->status); /* set_bit() is atomic */ + + wake_up_interruptible(&board->wait); + + return IRQ_HANDLED; +} + +static int bb_command(struct gpib_board *board, u8 *buffer, + size_t length, size_t *bytes_written) +{ + int ret; + struct bb_priv *priv = board->private_data; + int i; + + dbg_printk(2, "%p %p\n", buffer, board->buffer); + + /* the _ATN line has already been asserted by bb_take_control() */ + + priv->cmd = 1; + + ret = bb_write(board, buffer, length, 0, bytes_written); // no eoi + + for (i = 0; i < length; i++) { + if (buffer[i] == UNT) { + priv->talker_state = talker_idle; + } else { + if (buffer[i] == UNL) { + priv->listener_state = listener_idle; + } else { + if (buffer[i] == (MTA(board->pad))) { + priv->talker_state = talker_addressed; + priv->listener_state = listener_idle; + } else if (buffer[i] == (MLA(board->pad))) { + priv->listener_state = listener_addressed; + priv->talker_state = talker_idle; + } + } + } + } + + /* the _ATN line will be released by bb_go_to_stby */ + + priv->cmd = 0; + + return ret; +} + +/*************************************************************************** + * * + * Buffer print with decode for debug/trace * + * * + ***************************************************************************/ + +static char *cmd_string[32] = { + "", // 0x00 + "GTL", // 0x01 + "", // 0x02 + "", // 0x03 + "SDC", // 0x04 + "PPC", // 0x05 + "", // 0x06 + "", // 0x07 + "GET", // 0x08 + "TCT", // 0x09 + "", // 0x0a + "", // 0x0b + "", // 0x0c + "", // 0x0d + "", // 0x0e + "", // 0x0f + "", // 0x10 + "LLO", // 0x11 + "", // 0x12 + "", // 0x13 + "DCL", // 0x14 + "PPU", // 0x15 + "", // 0x16 + "", // 0x17 + "SPE", // 0x18 + "SPD", // 0x19 + "", // 0x1a + "", // 0x1b + "", // 0x1c + "", // 0x1d + "", // 0x1e + "CFE" // 0x1f +}; + +static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length, + int cmd, int eoi) +{ + int i; + + if (cmd) { + dbg_printk(2, "\n", length); + for (i = 0; i < length; i++) { + if (buffer[i] < 0x20) { + dbg_printk(3, "0x%x=%s\n", buffer[i], cmd_string[buffer[i]]); + } else if (buffer[i] == 0x3f) { + dbg_printk(3, "0x%x=%s\n", buffer[i], "UNL"); + } else if (buffer[i] == 0x5f) { + dbg_printk(3, "0x%x=%s\n", buffer[i], "UNT"); + } else if (buffer[i] < 0x60) { + dbg_printk(3, "0x%x=%s%d\n", buffer[i], + (buffer[i] & 0x40) ? "TLK" : "LSN", buffer[i] & 0x1F); + } else { + dbg_printk(3, "0x%x\n", buffer[i]); + } + } + } else { + dbg_printk(2, "\n", length, (eoi) ? "w.EOI" : " "); + for (i = 0; i < length; i++) + dbg_printk(2, "%3d 0x%x->%c\n", i, buffer[i], printable(buffer[i])); + } +} + +/*************************************************************************** + * * + * STATUS Management * + * * + ***************************************************************************/ +static void set_atn(struct gpib_board *board, int atn_asserted) +{ + struct bb_priv *priv = board->private_data; + + if (priv->listener_state != listener_idle && + priv->talker_state != talker_idle) { + dev_err(board->gpib_dev, "listener/talker state machine conflict\n"); + } + if (atn_asserted) { + if (priv->listener_state == listener_active) + priv->listener_state = listener_addressed; + if (priv->talker_state == talker_active) + priv->talker_state = talker_addressed; + SET_DIR_WRITE(priv); // need to be able to read bus NRFD/NDAC + } else { + if (priv->listener_state == listener_addressed) { + priv->listener_state = listener_active; + SET_DIR_READ(priv); // make sure holdoff is active when we unassert ATN + } + if (priv->talker_state == talker_addressed) + priv->talker_state = talker_active; + } + gpiod_direction_output(_ATN, !atn_asserted); +} + +static int bb_take_control(struct gpib_board *board, int synchronous) +{ + dbg_printk(2, "%d\n", synchronous); + set_atn(board, 1); + return 0; +} + +static int bb_go_to_standby(struct gpib_board *board) +{ + dbg_printk(2, "\n"); + set_atn(board, 0); + return 0; +} + +static int bb_request_system_control(struct gpib_board *board, int request_control) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "%d\n", request_control); + if (!request_control) + return -EINVAL; + + gpiod_direction_output(REN, 1); /* user space must enable REN if needed */ + gpiod_direction_output(IFC, 1); /* user space must toggle IFC if needed */ + if (sn7516x) + gpiod_direction_output(DC, 0); /* enable ATN as output on SN75161/2 */ + + gpiod_direction_input(SRQ); + + ENABLE_IRQ(priv->irq_SRQ, IRQ_TYPE_EDGE_FALLING); + + return 0; +} + +static void bb_interface_clear(struct gpib_board *board, int assert) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "%d\n", assert); + if (assert) { + gpiod_direction_output(IFC, 0); + priv->talker_state = talker_idle; + priv->listener_state = listener_idle; + set_bit(CIC_NUM, &board->status); + } else { + gpiod_direction_output(IFC, 1); + } +} + +static void bb_remote_enable(struct gpib_board *board, int enable) +{ + dbg_printk(2, "%d\n", enable); + if (enable) { + set_bit(REM_NUM, &board->status); + gpiod_direction_output(REN, 0); + } else { + clear_bit(REM_NUM, &board->status); + gpiod_direction_output(REN, 1); + } +} + +static int bb_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "%s\n", "EOS_en"); + priv->eos = eos_byte; + priv->eos_flags = REOS; + if (compare_8_bits) + priv->eos_flags |= BIN; + + return 0; +} + +static void bb_disable_eos(struct gpib_board *board) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "\n"); + priv->eos_flags &= ~REOS; +} + +static unsigned int bb_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct bb_priv *priv = board->private_data; + + board->status &= ~clear_mask; + + if (gpiod_get_value(SRQ)) /* SRQ asserted low */ + clear_bit(SRQI_NUM, &board->status); + else + set_bit(SRQI_NUM, &board->status); + if (gpiod_get_value(_ATN)) /* ATN asserted low */ + clear_bit(ATN_NUM, &board->status); + else + set_bit(ATN_NUM, &board->status); + if (priv->talker_state == talker_active || + priv->talker_state == talker_addressed) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + + if (priv->listener_state == listener_active || + priv->listener_state == listener_addressed) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + + dbg_printk(2, "0x%lx mask 0x%x\n", board->status, clear_mask); + + return board->status; +} + +static int bb_primary_address(struct gpib_board *board, unsigned int address) +{ + dbg_printk(2, "%d\n", address); + board->pad = address; + return 0; +} + +static int bb_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + dbg_printk(2, "%d %d\n", address, enable); + if (enable) + board->sad = address; + return 0; +} + +static int bb_parallel_poll(struct gpib_board *board, u8 *result) +{ + return -ENOENT; +} + +static void bb_parallel_poll_configure(struct gpib_board *board, u8 config) +{ +} + +static void bb_parallel_poll_response(struct gpib_board *board, int ist) +{ +} + +static void bb_serial_poll_response(struct gpib_board *board, u8 status) +{ +} + +static u8 bb_serial_poll_status(struct gpib_board *board) +{ + return 0; // -ENOENT; +} + +static int bb_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct bb_priv *priv = board->private_data; + + if (nano_sec <= 350) + priv->t1_delay = 350; + else if (nano_sec <= 1100) + priv->t1_delay = 1100; + else + priv->t1_delay = 2000; + + dbg_printk(2, "t1 delay set to %d nanosec\n", priv->t1_delay); + + return priv->t1_delay; +} + +static void bb_return_to_local(struct gpib_board *board) +{ +} + +static int bb_line_status(const struct gpib_board *board) +{ + int line_status = VALID_ALL; + + if (gpiod_get_value(REN) == 0) + line_status |= BUS_REN; + if (gpiod_get_value(IFC) == 0) + line_status |= BUS_IFC; + if (gpiod_get_value(NDAC) == 0) + line_status |= BUS_NDAC; + if (gpiod_get_value(NRFD) == 0) + line_status |= BUS_NRFD; + if (gpiod_get_value(DAV) == 0) + line_status |= BUS_DAV; + if (gpiod_get_value(EOI) == 0) + line_status |= BUS_EOI; + if (gpiod_get_value(_ATN) == 0) + line_status |= BUS_ATN; + if (gpiod_get_value(SRQ) == 0) + line_status |= BUS_SRQ; + + dbg_printk(2, "status lines: %4x\n", line_status); + + return line_status; +} + +/*************************************************************************** + * * + * Module Management * + * * + ***************************************************************************/ + +static int allocate_private(struct gpib_board *board) +{ + board->private_data = kzalloc(sizeof(struct bb_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + return 0; +} + +static void free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int bb_get_irq(struct gpib_board *board, char *name, + struct gpio_desc *gpio, int *irq, + irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags) +{ + if (!gpio) + return -1; + gpiod_direction_input(gpio); + *irq = gpiod_to_irq(gpio); + dbg_printk(2, "IRQ %s: %d\n", name, *irq); + if (*irq < 0) { + dev_err(board->gpib_dev, "can't get IRQ for %s\n", name); + return -1; + } + if (request_threaded_irq(*irq, handler, thread_fn, flags, name, board)) { + dev_err(board->gpib_dev, "can't request IRQ for %s %d\n", name, *irq); + *irq = 0; + return -1; + } + DISABLE_IRQ(*irq); + return 0; +} + +static void bb_free_irq(struct gpib_board *board, int *irq, char *name) +{ + if (*irq) { + free_irq(*irq, board); + dbg_printk(2, "IRQ %d(%s) freed\n", *irq, name); + *irq = 0; + } +} + +static void release_gpios(void) +{ + int j; + + for (j = 0 ; j < NUM_PINS ; j++) { + if (all_descriptors[j]) { + gpiod_put(all_descriptors[j]); + all_descriptors[j] = NULL; + } + } +} + +static int allocate_gpios(struct gpib_board *board) +{ + int j; + int table_index = 0; + char name[256]; + struct gpio_desc *desc; + struct gpiod_lookup_table *lookup_table; + + if (!board->gpib_dev) { + pr_err("NULL gpib dev for board\n"); + return -ENOENT; + } + + lookup_table = lookup_tables[table_index]; + lookup_table->dev_id = dev_name(board->gpib_dev); + gpiod_add_lookup_table(lookup_table); + dbg_printk(1, "Allocating gpios using table index %d\n", table_index); + + for (j = 0 ; j < NUM_PINS ; j++) { + if (gpios_vector[j] < 0) + continue; + /* name not really used in gpiod_get_index() */ + sprintf(name, "GPIO%d", gpios_vector[j]); +try_again: + dbg_printk(1, "Allocating gpio %s pin no %d\n", name, gpios_vector[j]); + desc = gpiod_get_index(board->gpib_dev, name, gpios_vector[j], GPIOD_IN); + + if (IS_ERR(desc)) { + gpiod_remove_lookup_table(lookup_table); + table_index++; + lookup_table = lookup_tables[table_index]; + if (!lookup_table) { + dev_err(board->gpib_dev, "Unable to obtain gpio descriptor for pin %d error %ld\n", + gpios_vector[j], PTR_ERR(desc)); + goto alloc_gpios_fail; + } + dbg_printk(1, "Allocation failed, now using table_index %d\n", table_index); + lookup_table->dev_id = dev_name(board->gpib_dev); + gpiod_add_lookup_table(lookup_table); + goto try_again; + } + all_descriptors[j] = desc; + } + + gpiod_remove_lookup_table(lookup_table); + + return 0; + +alloc_gpios_fail: + release_gpios(); + return -1; +} + +static void bb_detach(struct gpib_board *board) +{ + struct bb_priv *priv = board->private_data; + + dbg_printk(2, "Enter with data %p\n", board->private_data); + if (!board->private_data) + return; + + bb_free_irq(board, &priv->irq_DAV, NAME "_DAV"); + bb_free_irq(board, &priv->irq_NRFD, NAME "_NRFD"); + bb_free_irq(board, &priv->irq_NDAC, NAME "_NDAC"); + bb_free_irq(board, &priv->irq_SRQ, NAME "_SRQ"); + + if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */ + gpiod_set_value(YOGA_ENABLE, 0); + } + + release_gpios(); + + dbg_printk(2, "detached board: %d\n", board->minor); + dbg_printk(0, "NRFD: idle %d, seq %d, NDAC: idle %d, seq %d DAV: idle %d seq: %d all: %ld", + priv->nrfd_idle, priv->nrfd_seq, + priv->ndac_idle, priv->ndac_seq, + priv->dav_idle, priv->dav_seq, priv->all_irqs); + + free_private(board); +} + +static int bb_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct bb_priv *priv; + int retval = 0; + + dbg_printk(2, "%s\n", "Enter ..."); + + board->status = 0; + + if (allocate_private(board)) + return -ENOMEM; + priv = board->private_data; + priv->direction = -1; + priv->t1_delay = 2000; + priv->listener_state = listener_idle; + priv->talker_state = talker_idle; + + sn7516x = sn7516x_used; + if (strcmp(PINMAP_0, pin_map) == 0) { + if (!sn7516x) { + gpios_vector[&(PE) - &all_descriptors[0]] = -1; + gpios_vector[&(DC) - &all_descriptors[0]] = -1; + gpios_vector[&(TE) - &all_descriptors[0]] = -1; + } + } else if (strcmp(PINMAP_1, pin_map) == 0) { + if (!sn7516x) { + gpios_vector[&(PE) - &all_descriptors[0]] = -1; + gpios_vector[&(DC) - &all_descriptors[0]] = -1; + gpios_vector[&(TE) - &all_descriptors[0]] = -1; + } + gpios_vector[&(REN) - &all_descriptors[0]] = 0; /* 27 -> 0 REN on GPIB pin 0 */ + } else if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */ + sn7516x = 0; + gpios_vector[&(D03) - &all_descriptors[0]] = YOGA_D03_pin_nr; + gpios_vector[&(D04) - &all_descriptors[0]] = YOGA_D04_pin_nr; + gpios_vector[&(D05) - &all_descriptors[0]] = YOGA_D05_pin_nr; + gpios_vector[&(D06) - &all_descriptors[0]] = YOGA_D06_pin_nr; + gpios_vector[&(PE) - &all_descriptors[0]] = -1; + gpios_vector[&(DC) - &all_descriptors[0]] = -1; + } else { + dev_err(board->gpib_dev, "Unrecognized pin map %s\n", pin_map); + goto bb_attach_fail; + } + dbg_printk(0, "Using pin map \"%s\" %s\n", pin_map, (sn7516x) ? + " with SN7516x driver support" : ""); + + if (allocate_gpios(board)) + goto bb_attach_fail; + +/* + * Configure SN7516X control lines. + * drive ATN, IFC and REN as outputs only when master + * i.e. system controller. In this mode can only be the CIC + * When not master then enable device mode ATN, IFC & REN as inputs + */ + if (sn7516x) { + gpiod_direction_output(DC, 0); + gpiod_direction_output(TE, 1); + gpiod_direction_output(PE, 1); + } +/* Set main control lines to a known state */ + gpiod_direction_output(IFC, 1); + gpiod_direction_output(REN, 1); + gpiod_direction_output(_ATN, 1); + + if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA: enable level shifters */ + gpiod_direction_output(YOGA_ENABLE, 1); + } + + spin_lock_init(&priv->rw_lock); + + /* request DAV interrupt for read */ + if (bb_get_irq(board, NAME "_DAV", DAV, &priv->irq_DAV, bb_DAV_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + /* request NRFD interrupt for write */ + if (bb_get_irq(board, NAME "_NRFD", NRFD, &priv->irq_NRFD, bb_NRFD_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + /* request NDAC interrupt for write */ + if (bb_get_irq(board, NAME "_NDAC", NDAC, &priv->irq_NDAC, bb_NDAC_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + /* request SRQ interrupt for Service Request */ + if (bb_get_irq(board, NAME "_SRQ", SRQ, &priv->irq_SRQ, bb_SRQ_interrupt, NULL, + IRQF_TRIGGER_NONE)) + goto bb_attach_fail_r; + + dbg_printk(0, "attached board %d\n", board->minor); + goto bb_attach_out; + +bb_attach_fail_r: + release_gpios(); +bb_attach_fail: + retval = -1; +bb_attach_out: + return retval; +} + +static struct gpib_interface bb_interface = { + .name = NAME, + .attach = bb_attach, + .detach = bb_detach, + .read = bb_read, + .write = bb_write, + .command = bb_command, + .take_control = bb_take_control, + .go_to_standby = bb_go_to_standby, + .request_system_control = bb_request_system_control, + .interface_clear = bb_interface_clear, + .remote_enable = bb_remote_enable, + .enable_eos = bb_enable_eos, + .disable_eos = bb_disable_eos, + .parallel_poll = bb_parallel_poll, + .parallel_poll_configure = bb_parallel_poll_configure, + .parallel_poll_response = bb_parallel_poll_response, + .line_status = bb_line_status, + .update_status = bb_update_status, + .primary_address = bb_primary_address, + .secondary_address = bb_secondary_address, + .serial_poll_response = bb_serial_poll_response, + .serial_poll_status = bb_serial_poll_status, + .t1_delay = bb_t1_delay, + .return_to_local = bb_return_to_local, +}; + +static int __init bb_init_module(void) +{ + int result = gpib_register_driver(&bb_interface, THIS_MODULE); + + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + return result; + } + + return 0; +} + +static void __exit bb_exit_module(void) +{ + gpib_unregister_driver(&bb_interface); +} + +module_init(bb_init_module); +module_exit(bb_exit_module); + +/*************************************************************************** + * * + * UTILITY Functions * + * * + ***************************************************************************/ +inline long usec_diff(struct timespec64 *a, struct timespec64 *b) +{ + return ((a->tv_sec - b->tv_sec) * 1000000 + + (a->tv_nsec - b->tv_nsec) / 1000); +} + +static inline int check_for_eos(struct bb_priv *priv, u8 byte) +{ + if (priv->eos_check) + return 0; + + if (priv->eos_check_8) { + if (priv->eos == byte) + return 1; + } else { + if (priv->eos_mask_7 == (byte & 0x7f)) + return 1; + } + return 0; +} + +static void set_data_lines_output(void) +{ + gpiod_direction_output(D01, 1); + gpiod_direction_output(D02, 1); + gpiod_direction_output(D03, 1); + gpiod_direction_output(D04, 1); + gpiod_direction_output(D05, 1); + gpiod_direction_output(D06, 1); + gpiod_direction_output(D07, 1); + gpiod_direction_output(D08, 1); +} + +static void set_data_lines(u8 byte) +{ + gpiod_set_value(D01, !(byte & 0x01)); + gpiod_set_value(D02, !(byte & 0x02)); + gpiod_set_value(D03, !(byte & 0x04)); + gpiod_set_value(D04, !(byte & 0x08)); + gpiod_set_value(D05, !(byte & 0x10)); + gpiod_set_value(D06, !(byte & 0x20)); + gpiod_set_value(D07, !(byte & 0x40)); + gpiod_set_value(D08, !(byte & 0x80)); +} + +static u8 get_data_lines(void) +{ + u8 ret; + + ret = gpiod_get_value(D01); + ret |= gpiod_get_value(D02) << 1; + ret |= gpiod_get_value(D03) << 2; + ret |= gpiod_get_value(D04) << 3; + ret |= gpiod_get_value(D05) << 4; + ret |= gpiod_get_value(D06) << 5; + ret |= gpiod_get_value(D07) << 6; + ret |= gpiod_get_value(D08) << 7; + return ~ret; +} + +static void set_data_lines_input(void) +{ + gpiod_direction_input(D01); + gpiod_direction_input(D02); + gpiod_direction_input(D03); + gpiod_direction_input(D04); + gpiod_direction_input(D05); + gpiod_direction_input(D06); + gpiod_direction_input(D07); + gpiod_direction_input(D08); +} + +static inline void SET_DIR_WRITE(struct bb_priv *priv) +{ + if (priv->direction == DIR_WRITE) + return; + + gpiod_direction_input(NRFD); + gpiod_direction_input(NDAC); + set_data_lines_output(); + gpiod_direction_output(DAV, 1); + gpiod_direction_output(EOI, 1); + + if (sn7516x) { + gpiod_set_value(PE, 1); /* set data lines to transmit on sn75160b */ + gpiod_set_value(TE, 1); /* set NDAC and NRFD to receive and DAV to transmit */ + } + + priv->direction = DIR_WRITE; +} + +static inline void SET_DIR_READ(struct bb_priv *priv) +{ + if (priv->direction == DIR_READ) + return; + + gpiod_direction_input(DAV); + gpiod_direction_input(EOI); + + set_data_lines_input(); + + if (sn7516x) { + gpiod_set_value(PE, 0); /* set data lines to receive on sn75160b */ + gpiod_set_value(TE, 0); /* set NDAC and NRFD to transmit and DAV to receive */ + } + + gpiod_direction_output(NRFD, 0); /* hold off the talker */ + gpiod_direction_output(NDAC, 0); /* data not accepted */ + + priv->direction = DIR_READ; +} diff --git a/drivers/gpib/hp_82335/Makefile b/drivers/gpib/hp_82335/Makefile new file mode 100644 index 000000000000..305ce44ee48a --- /dev/null +++ b/drivers/gpib/hp_82335/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_GPIB_HP82335) += hp82335.o + + diff --git a/drivers/gpib/hp_82335/hp82335.c b/drivers/gpib/hp_82335/hp82335.c new file mode 100644 index 000000000000..d0e47ef77c87 --- /dev/null +++ b/drivers/gpib/hp_82335/hp82335.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess * + ***************************************************************************/ + +/* + * should enable ATN interrupts (and update board->status on occurrence), + * implement recovery from bus errors (if necessary) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "hp82335.h" +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for HP 82335 interface cards"); + +static int hp82335_attach(struct gpib_board *board, const struct gpib_board_config *config); +static void hp82335_detach(struct gpib_board *board); +static irqreturn_t hp82335_interrupt(int irq, void *arg); + +// wrappers for interface functions +static int hp82335_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); +} + +static int hp82335_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); +} + +static int hp82335_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); +} + +static int hp82335_take_control(struct gpib_board *board, int synchronous) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_take_control(board, &priv->tms9914_priv, synchronous); +} + +static int hp82335_go_to_standby(struct gpib_board *board) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_go_to_standby(board, &priv->tms9914_priv); +} + +static int hp82335_request_system_control(struct gpib_board *board, int request_control) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_request_system_control(board, &priv->tms9914_priv, request_control); +} + +static void hp82335_interface_clear(struct gpib_board *board, int assert) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_interface_clear(board, &priv->tms9914_priv, assert); +} + +static void hp82335_remote_enable(struct gpib_board *board, int enable) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_remote_enable(board, &priv->tms9914_priv, enable); +} + +static int hp82335_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); +} + +static void hp82335_disable_eos(struct gpib_board *board) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_disable_eos(board, &priv->tms9914_priv); +} + +static unsigned int hp82335_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); +} + +static int hp82335_primary_address(struct gpib_board *board, unsigned int address) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_primary_address(board, &priv->tms9914_priv, address); +} + +static int hp82335_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); +} + +static int hp82335_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_parallel_poll(board, &priv->tms9914_priv, result); +} + +static void hp82335_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); +} + +static void hp82335_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); +} + +static void hp82335_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_serial_poll_response(board, &priv->tms9914_priv, status); +} + +static u8 hp82335_serial_poll_status(struct gpib_board *board) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_serial_poll_status(board, &priv->tms9914_priv); +} + +static int hp82335_line_status(const struct gpib_board *board) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_line_status(board, &priv->tms9914_priv); +} + +static int hp82335_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct hp82335_priv *priv = board->private_data; + + return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); +} + +static void hp82335_return_to_local(struct gpib_board *board) +{ + struct hp82335_priv *priv = board->private_data; + + tms9914_return_to_local(board, &priv->tms9914_priv); +} + +static struct gpib_interface hp82335_interface = { + .name = "hp82335", + .attach = hp82335_attach, + .detach = hp82335_detach, + .read = hp82335_read, + .write = hp82335_write, + .command = hp82335_command, + .request_system_control = hp82335_request_system_control, + .take_control = hp82335_take_control, + .go_to_standby = hp82335_go_to_standby, + .interface_clear = hp82335_interface_clear, + .remote_enable = hp82335_remote_enable, + .enable_eos = hp82335_enable_eos, + .disable_eos = hp82335_disable_eos, + .parallel_poll = hp82335_parallel_poll, + .parallel_poll_configure = hp82335_parallel_poll_configure, + .parallel_poll_response = hp82335_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = hp82335_line_status, + .update_status = hp82335_update_status, + .primary_address = hp82335_primary_address, + .secondary_address = hp82335_secondary_address, + .serial_poll_response = hp82335_serial_poll_response, + .serial_poll_status = hp82335_serial_poll_status, + .t1_delay = hp82335_t1_delay, + .return_to_local = hp82335_return_to_local, +}; + +static int hp82335_allocate_private(struct gpib_board *board) +{ + board->private_data = kzalloc(sizeof(struct hp82335_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + return 0; +} + +static void hp82335_free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static inline unsigned int tms9914_to_hp82335_offset(unsigned int register_num) +{ + return 0x1ff8 + register_num; +} + +static u8 hp82335_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return tms9914_iomem_read_byte(priv, tms9914_to_hp82335_offset(register_num)); +} + +static void hp82335_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) +{ + tms9914_iomem_write_byte(priv, data, tms9914_to_hp82335_offset(register_num)); +} + +static void hp82335_clear_interrupt(struct hp82335_priv *hp_priv) +{ + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + + writeb(0, tms_priv->mmiobase + HPREG_INTR_CLEAR); +} + +static int hp82335_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct hp82335_priv *hp_priv; + struct tms9914_priv *tms_priv; + int retval; + const unsigned long upper_iomem_base = config->ibbase + hp82335_rom_size; + + board->status = 0; + + if (hp82335_allocate_private(board)) + return -ENOMEM; + hp_priv = board->private_data; + tms_priv = &hp_priv->tms9914_priv; + tms_priv->read_byte = hp82335_read_byte; + tms_priv->write_byte = hp82335_write_byte; + tms_priv->offset = 1; + + switch (config->ibbase) { + case 0xc4000: + case 0xc8000: + case 0xcc000: + case 0xd0000: + case 0xd4000: + case 0xd8000: + case 0xdc000: + case 0xe0000: + case 0xe4000: + case 0xe8000: + case 0xec000: + case 0xf0000: + case 0xf4000: + case 0xf8000: + case 0xfc000: + break; + default: + dev_err(board->gpib_dev, "invalid base io address 0x%x\n", config->ibbase); + return -EINVAL; + } + if (!request_mem_region(upper_iomem_base, hp82335_upper_iomem_size, "hp82335")) { + dev_err(board->gpib_dev, "failed to allocate io memory region 0x%lx-0x%lx\n", + upper_iomem_base, upper_iomem_base + hp82335_upper_iomem_size - 1); + return -EBUSY; + } + hp_priv->raw_iobase = upper_iomem_base; + tms_priv->mmiobase = ioremap(upper_iomem_base, hp82335_upper_iomem_size); + + retval = request_irq(config->ibirq, hp82335_interrupt, 0, DRV_NAME, board); + if (retval) { + dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq); + return retval; + } + hp_priv->irq = config->ibirq; + + tms9914_board_reset(tms_priv); + + hp82335_clear_interrupt(hp_priv); + + writeb(INTR_ENABLE, tms_priv->mmiobase + HPREG_CCR); + + tms9914_online(board, tms_priv); + + return 0; +} + +static void hp82335_detach(struct gpib_board *board) +{ + struct hp82335_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv; + + if (hp_priv) { + tms_priv = &hp_priv->tms9914_priv; + if (hp_priv->irq) + free_irq(hp_priv->irq, board); + if (tms_priv->mmiobase) { + writeb(0, tms_priv->mmiobase + HPREG_CCR); + tms9914_board_reset(tms_priv); + iounmap(tms_priv->mmiobase); + } + if (hp_priv->raw_iobase) + release_mem_region(hp_priv->raw_iobase, hp82335_upper_iomem_size); + } + hp82335_free_private(board); +} + +static int __init hp82335_init_module(void) +{ + int result = gpib_register_driver(&hp82335_interface, THIS_MODULE); + + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + return result; + } + + return 0; +} + +static void __exit hp82335_exit_module(void) +{ + gpib_unregister_driver(&hp82335_interface); +} + +module_init(hp82335_init_module); +module_exit(hp82335_exit_module); + +/* + * GPIB interrupt service routines + */ + +static irqreturn_t hp82335_interrupt(int irq, void *arg) +{ + int status1, status2; + struct gpib_board *board = arg; + struct hp82335_priv *priv = board->private_data; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + status1 = read_byte(&priv->tms9914_priv, ISR0); + status2 = read_byte(&priv->tms9914_priv, ISR1); + hp82335_clear_interrupt(priv); + retval = tms9914_interrupt_have_status(board, &priv->tms9914_priv, status1, status2); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + diff --git a/drivers/gpib/hp_82335/hp82335.h b/drivers/gpib/hp_82335/hp82335.h new file mode 100644 index 000000000000..0c252a712ec9 --- /dev/null +++ b/drivers/gpib/hp_82335/hp82335.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess * + ***************************************************************************/ + +#ifndef _HP82335_H +#define _HP82335_H + +#include "tms9914.h" +#include "gpibP.h" + +// struct which defines private_data for board +struct hp82335_priv { + struct tms9914_priv tms9914_priv; + unsigned int irq; + unsigned long raw_iobase; +}; + +// size of io memory region used +static const int hp82335_rom_size = 0x2000; +static const int hp82335_upper_iomem_size = 0x2000; + +// hp82335 register offsets +enum hp_read_regs { + HPREG_CSR = 0x17f8, + HPREG_STATUS = 0x1ffc, +}; + +enum hp_write_regs { + HPREG_INTR_CLEAR = 0x17f7, + HPREG_CCR = HPREG_CSR, +}; + +enum ccr_bits { + DMA_ENABLE = (1 << 0), /* DMA enable */ + DMA_CHAN_SELECT = (1 << 1), /* DMA channel select O=3,1=2 */ + INTR_ENABLE = (1 << 2), /* interrupt enable */ + SYS_DISABLE = (1 << 3), /* system controller disable */ +}; + +enum csr_bits { + SWITCH6 = (1 << 0), /* switch 6 position */ + SWITCH5 = (1 << 1), /* switch 5 position */ + SYS_CONTROLLER = (1 << 2), /* system controller bit */ + DMA_ENABLE_STATUS = (1 << 4), /* DMA enabled */ + DMA_CHAN_STATUS = (1 << 5), /* DMA channel 0=3,1=2 */ + INTR_ENABLE_STATUS = (1 << 6), /* Interrupt enable */ + INTR_PENDING = (1 << 7), /* Interrupt Pending */ +}; + +#endif // _HP82335_H diff --git a/drivers/gpib/hp_82341/Makefile b/drivers/gpib/hp_82341/Makefile new file mode 100644 index 000000000000..21367310a17e --- /dev/null +++ b/drivers/gpib/hp_82341/Makefile @@ -0,0 +1,2 @@ + +obj-$(CONFIG_GPIB_HP82341) += hp_82341.o diff --git a/drivers/gpib/hp_82341/hp_82341.c b/drivers/gpib/hp_82341/hp_82341.c new file mode 100644 index 000000000000..1a2ad0560e14 --- /dev/null +++ b/drivers/gpib/hp_82341/hp_82341.c @@ -0,0 +1,907 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * Driver for hp 82341a/b/c/d boards. * + * Might be worth merging with Agilent 82350b driver. * + * copyright : (C) 2002, 2005 by Frank Mori Hess * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "hp_82341.h" +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for hp 82341a/b/c/d boards"); + +static unsigned short read_and_clear_event_status(struct gpib_board *board); +static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count); +static int read_transfer_counter(struct hp_82341_priv *hp_priv); +static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written); +static irqreturn_t hp_82341_interrupt(int irq, void *arg); + +static int hp_82341_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + int retval = 0; + unsigned short event_status; + int i; + int num_fifo_bytes; + // hardware doesn't support checking for end-of-string character when using fifo + if (tms_priv->eos_flags & REOS) + return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + read_and_clear_event_status(board); + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + // disable fifo for the moment + outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + /* + * Handle corner case of board not in holdoff and one byte has slipped in already. + * Also, board sometimes has problems (spurious 1 byte reads) when read fifo is + * started up with board in TACS under certain data holdoff conditions. + * Doing a 1 byte tms9914-style read avoids these problems. + */ + if (/*tms_priv->holdoff_active == 0 && */length > 1) { + size_t num_bytes; + + retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + dev_err(board->gpib_dev, "tms9914_read failed retval=%i\n", retval); + if (retval < 0 || *end) + return retval; + ++buffer; + --length; + } + tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); + tms9914_release_holdoff(tms_priv); + outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); + i = 0; + num_fifo_bytes = length - 1; + while (i < num_fifo_bytes && *end == 0) { + int block_size; + int j; + int count; + + block_size = min(num_fifo_bytes - i, hp_82341_fifo_size); + set_transfer_counter(hp_priv, block_size); + outb(ENABLE_TI_BUFFER_BIT | DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + + BUFFER_CONTROL_REG); + if (inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) + outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); + + clear_bit(READ_READY_BN, &tms_priv->state); + + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + (TERMINAL_COUNT_EVENT_BIT | + BUFFER_END_EVENT_BIT)) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + if (retval) { + retval = -ERESTARTSYS; + break; + } + // have to disable buffer before we can read from buffer port + outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + count = block_size - read_transfer_counter(hp_priv); + j = 0; + while (j < count && i < num_fifo_bytes) { + unsigned short data_word = inw(hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); + + buffer[i++] = data_word & 0xff; + ++j; + if (j < count && i < num_fifo_bytes) { + buffer[i++] = (data_word >> 8) & 0xff; + ++j; + } + } + if (event_status & BUFFER_END_EVENT_BIT) { + clear_bit(RECEIVED_END_BN, &tms_priv->state); + + *end = 1; + tms_priv->holdoff_active = 1; + } + if (test_bit(TIMO_NUM, &board->status)) { + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { + retval = -EINTR; + break; + } + } + *bytes_read += i; + buffer += i; + length -= i; + if (retval < 0) + return retval; + // read last byte if we havn't received an END yet + if (*end == 0) { + size_t num_bytes; + // try to make sure we holdoff after last byte read + retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +static int restart_write_fifo(struct gpib_board *board, struct hp_82341_priv *hp_priv) +{ + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + + if ((inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) == 0) + return 0; + while (1) { + int status; + + // restart doesn't work if data holdoff is in effect + status = tms9914_line_status(board, tms_priv); + if ((status & BUS_NRFD) == 0) { + outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); + return 0; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) + return -EINTR; + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (msleep_interruptible(1)) + return -EINTR; + } + return 0; +} + +static int hp_82341_accel_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + int i, j; + unsigned short event_status; + int retval = 0; + int fifo_xfer_len = length; + + *bytes_written = 0; + if (send_eoi) + --fifo_xfer_len; + + clear_bit(DEV_CLEAR_BN, &tms_priv->state); + + read_and_clear_event_status(board); + outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); + for (i = 0; i < fifo_xfer_len;) { + int block_size; + + block_size = min(fifo_xfer_len - i, hp_82341_fifo_size); + set_transfer_counter(hp_priv, block_size); + // load data into board's fifo + for (j = 0; j < block_size;) { + unsigned short data_word = buffer[i++]; + ++j; + if (j < block_size) { + data_word |= buffer[i++] << 8; + ++j; + } + outw(data_word, hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); + } + clear_bit(WRITE_READY_BN, &tms_priv->state); + outb(ENABLE_TI_BUFFER_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + retval = restart_write_fifo(board, hp_priv); + if (retval < 0) { + dev_err(board->gpib_dev, "failed to restart write stream\n"); + break; + } + retval = wait_event_interruptible(board->wait, + ((event_status = + read_and_clear_event_status(board)) & + TERMINAL_COUNT_EVENT_BIT) || + test_bit(DEV_CLEAR_BN, &tms_priv->state) || + test_bit(TIMO_NUM, &board->status)); + outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); + *bytes_written += block_size - read_transfer_counter(hp_priv); + if (retval) { + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status)) { + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { + retval = -EINTR; + break; + } + } + if (retval) + return retval; + if (send_eoi) { + size_t num_bytes; + + retval = hp_82341_write(board, buffer + fifo_xfer_len, 1, 1, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} + +static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config); + +static void hp_82341_detach(struct gpib_board *board); + +// wrappers for interface functions +static int hp_82341_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); +} + +static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); +} + +static int hp_82341_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); +} + +static int hp_82341_take_control(struct gpib_board *board, int synchronous) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_take_control(board, &priv->tms9914_priv, synchronous); +} + +static int hp_82341_go_to_standby(struct gpib_board *board) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_go_to_standby(board, &priv->tms9914_priv); +} + +static int hp_82341_request_system_control(struct gpib_board *board, int request_control) +{ + struct hp_82341_priv *priv = board->private_data; + + if (request_control) + priv->mode_control_bits |= SYSTEM_CONTROLLER_BIT; + else + priv->mode_control_bits &= ~SYSTEM_CONTROLLER_BIT; + outb(priv->mode_control_bits, priv->iobase[0] + MODE_CONTROL_STATUS_REG); + return tms9914_request_system_control(board, &priv->tms9914_priv, request_control); +} + +static void hp_82341_interface_clear(struct gpib_board *board, int assert) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_interface_clear(board, &priv->tms9914_priv, assert); +} + +static void hp_82341_remote_enable(struct gpib_board *board, int enable) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_remote_enable(board, &priv->tms9914_priv, enable); +} + +static int hp_82341_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); +} + +static void hp_82341_disable_eos(struct gpib_board *board) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_disable_eos(board, &priv->tms9914_priv); +} + +static unsigned int hp_82341_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); +} + +static int hp_82341_primary_address(struct gpib_board *board, unsigned int address) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_primary_address(board, &priv->tms9914_priv, address); +} + +static int hp_82341_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); +} + +static int hp_82341_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_parallel_poll(board, &priv->tms9914_priv, result); +} + +static void hp_82341_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); +} + +static void hp_82341_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); +} + +static void hp_82341_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_serial_poll_response(board, &priv->tms9914_priv, status); +} + +static u8 hp_82341_serial_poll_status(struct gpib_board *board) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_serial_poll_status(board, &priv->tms9914_priv); +} + +static int hp_82341_line_status(const struct gpib_board *board) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_line_status(board, &priv->tms9914_priv); +} + +static int hp_82341_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct hp_82341_priv *priv = board->private_data; + + return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); +} + +static void hp_82341_return_to_local(struct gpib_board *board) +{ + struct hp_82341_priv *priv = board->private_data; + + tms9914_return_to_local(board, &priv->tms9914_priv); +} + +static struct gpib_interface hp_82341_unaccel_interface = { + .name = "hp_82341_unaccel", + .attach = hp_82341_attach, + .detach = hp_82341_detach, + .read = hp_82341_read, + .write = hp_82341_write, + .command = hp_82341_command, + .request_system_control = hp_82341_request_system_control, + .take_control = hp_82341_take_control, + .go_to_standby = hp_82341_go_to_standby, + .interface_clear = hp_82341_interface_clear, + .remote_enable = hp_82341_remote_enable, + .enable_eos = hp_82341_enable_eos, + .disable_eos = hp_82341_disable_eos, + .parallel_poll = hp_82341_parallel_poll, + .parallel_poll_configure = hp_82341_parallel_poll_configure, + .parallel_poll_response = hp_82341_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = hp_82341_line_status, + .update_status = hp_82341_update_status, + .primary_address = hp_82341_primary_address, + .secondary_address = hp_82341_secondary_address, + .serial_poll_response = hp_82341_serial_poll_response, + .serial_poll_status = hp_82341_serial_poll_status, + .t1_delay = hp_82341_t1_delay, + .return_to_local = hp_82341_return_to_local, +}; + +static struct gpib_interface hp_82341_interface = { + .name = "hp_82341", + .attach = hp_82341_attach, + .detach = hp_82341_detach, + .read = hp_82341_accel_read, + .write = hp_82341_accel_write, + .command = hp_82341_command, + .request_system_control = hp_82341_request_system_control, + .take_control = hp_82341_take_control, + .go_to_standby = hp_82341_go_to_standby, + .interface_clear = hp_82341_interface_clear, + .remote_enable = hp_82341_remote_enable, + .enable_eos = hp_82341_enable_eos, + .disable_eos = hp_82341_disable_eos, + .parallel_poll = hp_82341_parallel_poll, + .parallel_poll_configure = hp_82341_parallel_poll_configure, + .parallel_poll_response = hp_82341_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = hp_82341_line_status, + .update_status = hp_82341_update_status, + .primary_address = hp_82341_primary_address, + .secondary_address = hp_82341_secondary_address, + .serial_poll_response = hp_82341_serial_poll_response, + .t1_delay = hp_82341_t1_delay, + .return_to_local = hp_82341_return_to_local, +}; + +static int hp_82341_allocate_private(struct gpib_board *board) +{ + board->private_data = kzalloc(sizeof(struct hp_82341_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + return 0; +} + +static void hp_82341_free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static u8 hp_82341_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return inb(priv->iobase + register_num); +} + +static void hp_82341_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) +{ + outb(data, priv->iobase + register_num); +} + +static int hp_82341_find_isapnp_board(struct pnp_dev **dev) +{ + *dev = pnp_find_dev(NULL, ISAPNP_VENDOR('H', 'W', 'P'), + ISAPNP_FUNCTION(0x1411), NULL); + if (!*dev || !(*dev)->card) { + pr_err("failed to find isapnp board\n"); + return -ENODEV; + } + if (pnp_device_attach(*dev) < 0) { + pr_err("board already active, skipping\n"); + return -EBUSY; + } + if (pnp_activate_dev(*dev) < 0) { + pnp_device_detach(*dev); + pr_err("failed to activate(), aborting\n"); + return -EAGAIN; + } + if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { + pnp_device_detach(*dev); + pr_err("invalid port or irq, aborting\n"); + return -ENOMEM; + } + return 0; +} + +static int xilinx_ready(struct hp_82341_priv *hp_priv) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & XILINX_READY_BIT) + return 1; + else + return 0; + break; + case HW_VERSION_82341D: + if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_READY_BIT) + return 1; + else + return 0; + default: + pr_err("bug! unknown hw_version\n"); + break; + } + return 0; +} + +static int xilinx_done(struct hp_82341_priv *hp_priv) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & DONE_PGL_BIT) + return 1; + else + return 0; + case HW_VERSION_82341D: + if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_DONE_BIT) + return 1; + else + return 0; + default: + pr_err("bug! unknown hw_version\n"); + break; + } + return 0; +} + +static int irq_valid(struct hp_82341_priv *hp_priv, int irq) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + switch (irq) { + case 3: + case 5: + case 7: + case 9: + case 10: + case 11: + case 12: + case 15: + return 1; + default: + pr_err("invalid irq=%i for 82341C, irq must be 3, 5, 7, 9, 10, 11, 12, or 15.\n", + irq); + return 0; + } + break; + case HW_VERSION_82341D: + return 1; + default: + pr_err("bug! unknown hw_version\n"); + break; + } + return 0; +} + +static int hp_82341_load_firmware_array(struct hp_82341_priv *hp_priv, + const unsigned char *firmware_data, + unsigned int firmware_length) +{ + int i, j; + static const int timeout = 100; + + for (i = 0; i < firmware_length; ++i) { + for (j = 0; j < timeout; ++j) { + if (need_resched()) + schedule(); + if (xilinx_ready(hp_priv)) + break; + usleep_range(10, 15); + } + if (j == timeout) { + pr_err("timed out waiting for Xilinx ready.\n"); + return -ETIMEDOUT; + } + outb(firmware_data[i], hp_priv->iobase[0] + XILINX_DATA_REG); + } + for (j = 0; j < timeout; ++j) { + if (xilinx_done(hp_priv)) + break; + if (need_resched()) + schedule(); + usleep_range(10, 15); + } + if (j == timeout) { + pr_err("timed out waiting for Xilinx done.\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int hp_82341_load_firmware(struct hp_82341_priv *hp_priv, + const struct gpib_board_config *config) +{ + if (config->init_data_length == 0) { + if (xilinx_done(hp_priv)) + return 0; + pr_err("board needs be initialized with firmware upload.\n" + "\tUse the --init-data option of gpib_config.\n"); + return -EINVAL; + } + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (config->init_data_length != hp_82341c_firmware_length) { + pr_err("bad firmware length=%i for 82341c (expected %i).\n", + config->init_data_length, hp_82341c_firmware_length); + return -EINVAL; + } + break; + case HW_VERSION_82341D: + if (config->init_data_length != hp_82341d_firmware_length) { + pr_err("bad firmware length=%i for 82341d (expected %i).\n", + config->init_data_length, hp_82341d_firmware_length); + return -EINVAL; + } + break; + default: + pr_err("bug! unknown hw_version\n"); + break; + } + return hp_82341_load_firmware_array(hp_priv, config->init_data, config->init_data_length); +} + +static void set_xilinx_not_prog(struct hp_82341_priv *hp_priv, int assert) +{ + switch (hp_priv->hw_version) { + case HW_VERSION_82341C: + if (assert) + hp_priv->config_control_bits |= DONE_PGL_BIT; + else + hp_priv->config_control_bits &= ~DONE_PGL_BIT; + outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); + break; + case HW_VERSION_82341D: + if (assert) + isapnp_write_byte(PIO_DATA_REG, HP_82341D_NOT_PROG_BIT); + else + isapnp_write_byte(PIO_DATA_REG, 0x0); + break; + default: + break; + } +} + +// clear xilinx firmware +static int clear_xilinx(struct hp_82341_priv *hp_priv) +{ + set_xilinx_not_prog(hp_priv, 1); + if (msleep_interruptible(1)) + return -EINTR; + set_xilinx_not_prog(hp_priv, 0); + if (msleep_interruptible(1)) + return -EINTR; + set_xilinx_not_prog(hp_priv, 1); + if (msleep_interruptible(1)) + return -EINTR; + return 0; +} + +static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct hp_82341_priv *hp_priv; + struct tms9914_priv *tms_priv; + u32 start_addr; + u32 iobase; + int irq; + int i; + int retval; + + board->status = 0; + if (hp_82341_allocate_private(board)) + return -ENOMEM; + hp_priv = board->private_data; + tms_priv = &hp_priv->tms9914_priv; + tms_priv->read_byte = hp_82341_read_byte; + tms_priv->write_byte = hp_82341_write_byte; + tms_priv->offset = 1; + + if (config->ibbase == 0) { + struct pnp_dev *dev; + int retval = hp_82341_find_isapnp_board(&dev); + + if (retval < 0) + return retval; + hp_priv->pnp_dev = dev; + iobase = pnp_port_start(dev, 0); + irq = pnp_irq(dev, 0); + hp_priv->hw_version = HW_VERSION_82341D; + hp_priv->io_region_offset = 0x8; + } else { + iobase = config->ibbase; + irq = config->ibirq; + hp_priv->hw_version = HW_VERSION_82341C; + hp_priv->io_region_offset = 0x400; + } + for (i = 0; i < hp_82341_num_io_regions; ++i) { + start_addr = iobase + i * hp_priv->io_region_offset; + if (!request_region(start_addr, hp_82341_region_iosize, DRV_NAME)) { + dev_err(board->gpib_dev, "failed to allocate io ports 0x%x-0x%x\n", + start_addr, + start_addr + hp_82341_region_iosize - 1); + return -EIO; + } + hp_priv->iobase[i] = start_addr; + } + tms_priv->iobase = hp_priv->iobase[2]; + if (hp_priv->hw_version == HW_VERSION_82341D) { + retval = isapnp_cfg_begin(hp_priv->pnp_dev->card->number, + hp_priv->pnp_dev->number); + if (retval < 0) { + dev_err(board->gpib_dev, "isapnp_cfg_begin returned error\n"); + return retval; + } + isapnp_write_byte(PIO_DIRECTION_REG, HP_82341D_XILINX_READY_BIT | + HP_82341D_XILINX_DONE_BIT); + } + retval = clear_xilinx(hp_priv); + if (retval < 0) + return retval; + retval = hp_82341_load_firmware(hp_priv, config); + if (hp_priv->hw_version == HW_VERSION_82341D) + isapnp_cfg_end(); + if (retval < 0) + return retval; + if (irq_valid(hp_priv, irq) == 0) + return -EINVAL; + if (request_irq(irq, hp_82341_interrupt, 0, DRV_NAME, board)) { + dev_err(board->gpib_dev, "failed to allocate IRQ %d\n", irq); + return -EIO; + } + hp_priv->irq = irq; + hp_priv->config_control_bits &= ~IRQ_SELECT_MASK; + hp_priv->config_control_bits |= IRQ_SELECT_BITS(irq); + outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); + hp_priv->mode_control_bits |= ENABLE_IRQ_CONFIG_BIT; + outb(hp_priv->mode_control_bits, hp_priv->iobase[0] + MODE_CONTROL_STATUS_REG); + tms9914_board_reset(tms_priv); + outb(ENABLE_BUFFER_END_EVENT_BIT | ENABLE_TERMINAL_COUNT_EVENT_BIT | + ENABLE_TI_INTERRUPT_EVENT_BIT, hp_priv->iobase[0] + EVENT_ENABLE_REG); + outb(ENABLE_BUFFER_END_INTERRUPT_BIT | ENABLE_TERMINAL_COUNT_INTERRUPT_BIT | + ENABLE_TI_INTERRUPT_BIT, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); + // write clear event register + outb((TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | + BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), + hp_priv->iobase[0] + EVENT_STATUS_REG); + + tms9914_online(board, tms_priv); + + return 0; +} + +static void hp_82341_detach(struct gpib_board *board) +{ + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv; + int i; + + if (hp_priv) { + tms_priv = &hp_priv->tms9914_priv; + if (hp_priv->iobase[0]) { + outb(0, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); + if (tms_priv->iobase) + tms9914_board_reset(tms_priv); + if (hp_priv->irq) + free_irq(hp_priv->irq, board); + } + for (i = 0; i < hp_82341_num_io_regions; ++i) { + if (hp_priv->iobase[i]) + release_region(hp_priv->iobase[i], hp_82341_region_iosize); + } + if (hp_priv->pnp_dev) + pnp_device_detach(hp_priv->pnp_dev); + } + hp_82341_free_private(board); +} + +#if 0 +/* unused, will be needed when the driver is turned into a pnp_driver */ +static const struct pnp_device_id hp_82341_pnp_table[] = { + {.id = "HWP1411"}, + {.id = ""} +}; +MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table); +#endif + +static int __init hp_82341_init_module(void) +{ + int ret; + + ret = gpib_register_driver(&hp_82341_unaccel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + return ret; + } + + ret = gpib_register_driver(&hp_82341_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + gpib_unregister_driver(&hp_82341_unaccel_interface); + return ret; + } + + return 0; +} + +static void __exit hp_82341_exit_module(void) +{ + gpib_unregister_driver(&hp_82341_interface); + gpib_unregister_driver(&hp_82341_unaccel_interface); +} + +module_init(hp_82341_init_module); +module_exit(hp_82341_exit_module); + +/* + * GPIB interrupt service routines + */ +static unsigned short read_and_clear_event_status(struct gpib_board *board) +{ + struct hp_82341_priv *hp_priv = board->private_data; + unsigned long flags; + unsigned short status; + + spin_lock_irqsave(&board->spinlock, flags); + status = hp_priv->event_status_bits; + hp_priv->event_status_bits = 0; + spin_unlock_irqrestore(&board->spinlock, flags); + return status; +} + +static irqreturn_t hp_82341_interrupt(int irq, void *arg) +{ + int status1, status2; + struct gpib_board *board = arg; + struct hp_82341_priv *hp_priv = board->private_data; + struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + int event_status; + + spin_lock_irqsave(&board->spinlock, flags); + event_status = inb(hp_priv->iobase[0] + EVENT_STATUS_REG); + if (event_status & INTERRUPT_PENDING_EVENT_BIT) + retval = IRQ_HANDLED; + // write-clear status bits + if (event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | + BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT)) { + outb(event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | + BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), + hp_priv->iobase[0] + EVENT_STATUS_REG); + hp_priv->event_status_bits |= event_status; + } + if (event_status & TI_INTERRUPT_EVENT_BIT) { + status1 = read_byte(tms_priv, ISR0); + status2 = read_byte(tms_priv, ISR1); + tms9914_interrupt_have_status(board, tms_priv, status1, status2); + } + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +static int read_transfer_counter(struct hp_82341_priv *hp_priv) +{ + int lo, mid, value; + + lo = inb(hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); + mid = inb(hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); + value = (lo & 0xff) | ((mid << 8) & 0x7f00); + value = ~(value - 1) & 0x7fff; + return value; +} + +static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count) +{ + int complement = -count; + + outb(complement & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); + outb((complement >> 8) & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); + // I don't think the hi count reg is even used, but oh well + outb((complement >> 16) & 0xf, hp_priv->iobase[1] + TRANSFER_COUNT_HIGH_REG); +} + diff --git a/drivers/gpib/hp_82341/hp_82341.h b/drivers/gpib/hp_82341/hp_82341.h new file mode 100644 index 000000000000..859ef2899acb --- /dev/null +++ b/drivers/gpib/hp_82341/hp_82341.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002, 2005 by Frank Mori Hess * + ***************************************************************************/ + +#include "tms9914.h" +#include "gpibP.h" + +enum hp_82341_hardware_version { + HW_VERSION_UNKNOWN, + HW_VERSION_82341C, + HW_VERSION_82341D, +}; + +// struct which defines private_data for board +struct hp_82341_priv { + struct tms9914_priv tms9914_priv; + unsigned int irq; + unsigned short config_control_bits; + unsigned short mode_control_bits; + unsigned short event_status_bits; + struct pnp_dev *pnp_dev; + unsigned long iobase[4]; + unsigned long io_region_offset; + enum hp_82341_hardware_version hw_version; +}; + +static const int hp_82341_region_iosize = 0x8; +static const int hp_82341_num_io_regions = 4; +static const int hp_82341_fifo_size = 0xffe; +static const int hp_82341c_firmware_length = 5764; +static const int hp_82341d_firmware_length = 5302; + +// hp 82341 register offsets +enum hp_82341_region_0_registers { + CONFIG_CONTROL_STATUS_REG = 0x0, + MODE_CONTROL_STATUS_REG = 0x1, + MONITOR_REG = 0x2, // after initialization + XILINX_DATA_REG = 0x2, // before initialization, write only + INTERRUPT_ENABLE_REG = 0x3, + EVENT_STATUS_REG = 0x4, + EVENT_ENABLE_REG = 0x5, + STREAM_STATUS_REG = 0x7, +}; + +enum hp_82341_region_1_registers { + ID0_REG = 0x2, + ID1_REG = 0x3, + TRANSFER_COUNT_LOW_REG = 0x4, + TRANSFER_COUNT_MID_REG = 0x5, + TRANSFER_COUNT_HIGH_REG = 0x6, +}; + +enum hp_82341_region_3_registers { + BUFFER_PORT_LOW_REG = 0x0, + BUFFER_PORT_HIGH_REG = 0x1, + ID2_REG = 0x2, + ID3_REG = 0x3, + BUFFER_FLUSH_REG = 0x4, + BUFFER_CONTROL_REG = 0x7 +}; + +enum config_control_status_bits { + IRQ_SELECT_MASK = 0x7, + DMA_CONFIG_MASK = 0x18, + ENABLE_DMA_CONFIG_BIT = 0x20, + XILINX_READY_BIT = 0x40, // read only + DONE_PGL_BIT = 0x80 +}; + +static inline unsigned int IRQ_SELECT_BITS(int irq) +{ + switch (irq) { + case 3: + return 0x3; + case 5: + return 0x2; + case 7: + return 0x1; + case 9: + return 0x0; + case 10: + return 0x7; + case 11: + return 0x6; + case 12: + return 0x5; + case 15: + return 0x4; + default: + return 0x0; + } +}; + +enum mode_control_status_bits { + SLOT8_BIT = 0x1, // read only + ACTIVE_CONTROLLER_BIT = 0x2, // read only + ENABLE_DMA_BIT = 0x4, + SYSTEM_CONTROLLER_BIT = 0x8, + MONITOR_BIT = 0x10, + ENABLE_IRQ_CONFIG_BIT = 0x20, + ENABLE_TI_STREAM_BIT = 0x40 +}; + +enum monitor_bits { + MONITOR_INTERRUPT_PENDING_BIT = 0x1, // read only + MONITOR_CLEAR_HOLDOFF_BIT = 0x2, // write only + MONITOR_PPOLL_BIT = 0x4, // write clear + MONITOR_SRQ_BIT = 0x8, // write clear + MONITOR_IFC_BIT = 0x10, // write clear + MONITOR_REN_BIT = 0x20, // write clear + MONITOR_END_BIT = 0x40, // write clear + MONITOR_DAV_BIT = 0x80 // write clear +}; + +enum interrupt_enable_bits { + ENABLE_TI_INTERRUPT_BIT = 0x1, + ENABLE_POINTERS_EQUAL_INTERRUPT_BIT = 0x4, + ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10, + ENABLE_TERMINAL_COUNT_INTERRUPT_BIT = 0x20, + ENABLE_DMA_TERMINAL_COUNT_INTERRUPT_BIT = 0x80, +}; + +enum event_status_bits { + TI_INTERRUPT_EVENT_BIT = 0x1, // write clear + INTERRUPT_PENDING_EVENT_BIT = 0x2, // read only + POINTERS_EQUAL_EVENT_BIT = 0x4, // write clear + BUFFER_END_EVENT_BIT = 0x10, // write clear + TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear + DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear +}; + +enum event_enable_bits { + ENABLE_TI_INTERRUPT_EVENT_BIT = 0x1, // write clear + ENABLE_POINTERS_EQUAL_EVENT_BIT = 0x4, // write clear + ENABLE_BUFFER_END_EVENT_BIT = 0x10, // write clear + ENABLE_TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear + ENABLE_DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear +}; + +enum stream_status_bits { + HALTED_STATUS_BIT = 0x1, // read + RESTART_STREAM_BIT = 0x1 // write +}; + +enum buffer_control_bits { + DIRECTION_GPIB_TO_HOST_BIT = 0x20, // transfer direction (set for gpib to host) + ENABLE_TI_BUFFER_BIT = 0x40, // enable fifo + FAST_WR_EN_BIT = 0x80, // 350 ns t1 delay? +}; + +// registers accessible through isapnp chip on 82341d +enum hp_82341d_pnp_registers { + PIO_DATA_REG = 0x20, // read/write pio data lines + PIO_DIRECTION_REG = 0x21, // set pio data line directions (set for input) +}; + +enum hp_82341d_pnp_pio_bits { + HP_82341D_XILINX_READY_BIT = 0x1, + HP_82341D_XILINX_DONE_BIT = 0x2, + // use register layout compatible with C and older versions instead of 32 contiguous ioports + HP_82341D_LEGACY_MODE_BIT = 0x4, + HP_82341D_NOT_PROG_BIT = 0x8, // clear to reinitialize xilinx +}; diff --git a/drivers/gpib/include/amcc5920.h b/drivers/gpib/include/amcc5920.h new file mode 100644 index 000000000000..7a88bd282feb --- /dev/null +++ b/drivers/gpib/include/amcc5920.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Header for amcc5920 pci chip + * + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +// plx pci chip registers and bits +enum amcc_registers { + AMCC_INTCS_REG = 0x38, + AMCC_PASS_THRU_REG = 0x60, +}; + +enum amcc_incsr_bits { + AMCC_ADDON_INTR_ENABLE_BIT = 0x2000, + AMCC_ADDON_INTR_ACTIVE_BIT = 0x400000, + AMCC_INTR_ACTIVE_BIT = 0x800000, +}; + +static const int bits_per_region = 8; + +static inline uint32_t amcc_wait_state_bits(unsigned int region, unsigned int num_wait_states) +{ + return (num_wait_states & 0x7) << (--region * bits_per_region); +}; + +enum amcc_prefetch_bits { + PREFETCH_DISABLED = 0x0, + PREFETCH_SMALL = 0x8, + PREFETCH_MEDIUM = 0x10, + PREFETCH_LARGE = 0x18, +}; + +static inline uint32_t amcc_prefetch_bits(unsigned int region, enum amcc_prefetch_bits prefetch) +{ + return prefetch << (--region * bits_per_region); +}; + +static inline uint32_t amcc_PTADR_mode_bit(unsigned int region) +{ + return 0x80 << (--region * bits_per_region); +}; + +static inline uint32_t amcc_disable_write_fifo_bit(unsigned int region) +{ + return 0x20 << (--region * bits_per_region); +}; + diff --git a/drivers/gpib/include/amccs5933.h b/drivers/gpib/include/amccs5933.h new file mode 100644 index 000000000000..d7f63c795096 --- /dev/null +++ b/drivers/gpib/include/amccs5933.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Registers and bits for amccs5933 pci chip + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +// register offsets +enum { + MBEF_REG = 0x34, // mailbux empty/full + INTCSR_REG = 0x38, // interrupt control and status + BMCSR_REG = 0x3c, // bus master control and status +}; + +// incoming mailbox 0-3 register offsets +extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox) +{ + return (0x10 + 4 * mailbox); +}; + +// bit definitions + +// INTCSR bits +enum { + OUTBOX_EMPTY_INTR_BIT = 0x10, // enable outbox empty interrupt + INBOX_FULL_INTR_BIT = 0x1000, // enable inbox full interrupt + INBOX_INTR_CS_BIT = 0x20000, // read, or write clear inbox full interrupt + INTR_ASSERTED_BIT = 0x800000, // read only, interrupt asserted +}; + +// select byte 0 to 3 of incoming mailbox +extern inline int INBOX_BYTE_BITS(unsigned int byte) +{ + return (byte & 0x3) << 8; +}; + +// select incoming mailbox 0 to 3 +extern inline int INBOX_SELECT_BITS(unsigned int mailbox) +{ + return (mailbox & 0x3) << 10; +}; + +// select byte 0 to 3 of outgoing mailbox +extern inline int OUTBOX_BYTE_BITS(unsigned int byte) +{ + return (byte & 0x3); +}; + +// select outgoing mailbox 0 to 3 +extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox) +{ + return (mailbox & 0x3) << 2; +}; + +// BMCSR bits +enum { + MBOX_FLAGS_RESET_BIT = 0x08000000, // resets mailbox empty/full flags +}; + diff --git a/drivers/gpib/include/gpibP.h b/drivers/gpib/include/gpibP.h new file mode 100644 index 000000000000..e3938ada3e0d --- /dev/null +++ b/drivers/gpib/include/gpibP.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002,2003 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_P_H +#define _GPIB_P_H + +#include + +#include "gpib_types.h" +#include "gpib_proto.h" +#include "gpib_cmd.h" +#include +#include + +#include +#include +#include + +int gpib_register_driver(struct gpib_interface *interface, struct module *mod); +void gpib_unregister_driver(struct gpib_interface *interface); +struct pci_dev *gpib_pci_get_device(const struct gpib_board_config *config, unsigned int vendor_id, + unsigned int device_id, struct pci_dev *from); +struct pci_dev *gpib_pci_get_subsys(const struct gpib_board_config *config, unsigned int vendor_id, + unsigned int device_id, unsigned int ss_vendor, + unsigned int ss_device, struct pci_dev *from); +unsigned int num_gpib_events(const struct gpib_event_queue *queue); +int push_gpib_event(struct gpib_board *board, short event_type); +int pop_gpib_event(struct gpib_board *board, struct gpib_event_queue *queue, short *event_type); +int gpib_request_pseudo_irq(struct gpib_board *board, irqreturn_t (*handler)(int, void *)); +void gpib_free_pseudo_irq(struct gpib_board *board); +int gpib_match_device_path(struct device *dev, const char *device_path_in); + +extern struct gpib_board board_array[GPIB_MAX_NUM_BOARDS]; + +extern struct list_head registered_drivers; + +#endif // _GPIB_P_H + diff --git a/drivers/gpib/include/gpib_cmd.h b/drivers/gpib/include/gpib_cmd.h new file mode 100644 index 000000000000..9e96a3bfa22d --- /dev/null +++ b/drivers/gpib/include/gpib_cmd.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _GPIB_CMD_H +#define _GPIB_CMD_H + +#include + +/* Command byte definitions tests and functions */ + +/* mask of bits that actually matter in a command byte */ +enum { + gpib_command_mask = 0x7f, +}; + +/* Possible GPIB command messages */ + +enum cmd_byte { + GTL = 0x1, /* go to local */ + SDC = 0x4, /* selected device clear */ + PP_CONFIG = 0x5, + GET = 0x8, /* group execute trigger */ + TCT = 0x9, /* take control */ + LLO = 0x11, /* local lockout */ + DCL = 0x14, /* device clear */ + PPU = 0x15, /* parallel poll unconfigure */ + SPE = 0x18, /* serial poll enable */ + SPD = 0x19, /* serial poll disable */ + CFE = 0x1f, /* configure enable */ + LAD = 0x20, /* value to be 'ored' in to obtain listen address */ + UNL = 0x3F, /* unlisten */ + TAD = 0x40, /* value to be 'ored' in to obtain talk address */ + UNT = 0x5F, /* untalk */ + SAD = 0x60, /* my secondary address (base) */ + PPE = 0x60, /* parallel poll enable (base) */ + PPD = 0x70 /* parallel poll disable */ +}; + +/* confine address to range 0 to 30. */ +static inline unsigned int gpib_address_restrict(u32 addr) +{ + addr &= 0x1f; + if (addr == 0x1f) + addr = 0; + return addr; +} + +static inline u8 MLA(u32 addr) +{ + return gpib_address_restrict(addr) | LAD; +} + +static inline u8 MTA(u32 addr) +{ + return gpib_address_restrict(addr) | TAD; +} + +static inline u8 MSA(u32 addr) +{ + return (addr & 0x1f) | SAD; +} + +static inline s32 gpib_address_equal(u32 pad1, s32 sad1, u32 pad2, s32 sad2) +{ + if (pad1 == pad2) { + if (sad1 == sad2) + return 1; + if (sad1 < 0 && sad2 < 0) + return 1; + } + + return 0; +} + +static inline s32 is_PPE(u8 command) +{ + return (command & 0x70) == 0x60; +} + +static inline s32 is_PPD(u8 command) +{ + return (command & 0x70) == 0x70; +} + +static inline s32 in_addressed_command_group(u8 command) +{ + return (command & 0x70) == 0x0; +} + +static inline s32 in_universal_command_group(u8 command) +{ + return (command & 0x70) == 0x10; +} + +static inline s32 in_listen_address_group(u8 command) +{ + return (command & 0x60) == 0x20; +} + +static inline s32 in_talk_address_group(u8 command) +{ + return (command & 0x60) == 0x40; +} + +static inline s32 in_primary_command_group(u8 command) +{ + return in_addressed_command_group(command) || + in_universal_command_group(command) || + in_listen_address_group(command) || + in_talk_address_group(command); +} + +#endif /* _GPIB_CMD_H */ diff --git a/drivers/gpib/include/gpib_pci_ids.h b/drivers/gpib/include/gpib_pci_ids.h new file mode 100644 index 000000000000..52dcab07a7d1 --- /dev/null +++ b/drivers/gpib/include/gpib_pci_ids.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __GPIB_PCI_IDS_H +#define __GPIB_PCI_IDS_H + +#ifndef PCI_VENDOR_ID_AMCC +#define PCI_VENDOR_ID_AMCC 0x10e8 +#endif + +#ifndef PCI_VENDOR_ID_CBOARDS +#define PCI_VENDOR_ID_CBOARDS 0x1307 +#endif + +#ifndef PCI_VENDOR_ID_QUANCOM +#define PCI_VENDOR_ID_QUANCOM 0x8008 +#endif + +#ifndef PCI_DEVICE_ID_QUANCOM_GPIB +#define PCI_DEVICE_ID_QUANCOM_GPIB 0x3302 +#endif + +#endif // __GPIB_PCI_IDS_H + diff --git a/drivers/gpib/include/gpib_proto.h b/drivers/gpib/include/gpib_proto.h new file mode 100644 index 000000000000..42e736e3b7cd --- /dev/null +++ b/drivers/gpib/include/gpib_proto.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIB_PROTO_INCLUDED +#define GPIB_PROTO_INCLUDED + +#include + +int ibopen(struct inode *inode, struct file *filep); +int ibclose(struct inode *inode, struct file *file); +long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg); +void os_start_timer(struct gpib_board *board, unsigned int usec_timeout); +void os_remove_timer(struct gpib_board *board); +void init_gpib_board(struct gpib_board *board); +static inline unsigned long usec_to_jiffies(unsigned int usec) +{ + unsigned long usec_per_jiffy = 1000000 / HZ; + + return 1 + (usec + usec_per_jiffy - 1) / usec_per_jiffy; +}; + +int serial_poll_all(struct gpib_board *board, unsigned int usec_timeout); +void init_gpib_descriptor(struct gpib_descriptor *desc); +int dvrsp(struct gpib_board *board, unsigned int pad, int sad, + unsigned int usec_timeout, u8 *result); +int ibcac(struct gpib_board *board, int sync, int fallback_to_async); +int ibcmd(struct gpib_board *board, u8 *buf, size_t length, size_t *bytes_written); +int ibgts(struct gpib_board *board); +int ibonline(struct gpib_board *board); +int iboffline(struct gpib_board *board); +int iblines(const struct gpib_board *board, short *lines); +int ibrd(struct gpib_board *board, u8 *buf, size_t length, int *end_flag, size_t *bytes_read); +int ibrpp(struct gpib_board *board, u8 *buf); +int ibrsv2(struct gpib_board *board, u8 status_byte, int new_reason_for_service); +int ibrsc(struct gpib_board *board, int request_control); +int ibsic(struct gpib_board *board, unsigned int usec_duration); +int ibsre(struct gpib_board *board, int enable); +int ibpad(struct gpib_board *board, unsigned int addr); +int ibsad(struct gpib_board *board, int addr); +int ibeos(struct gpib_board *board, int eos, int eosflags); +int ibwait(struct gpib_board *board, int wait_mask, int clear_mask, int set_mask, + int *status, unsigned long usec_timeout, struct gpib_descriptor *desc); +int ibwrt(struct gpib_board *board, u8 *buf, size_t cnt, int send_eoi, size_t *bytes_written); +int ibstatus(struct gpib_board *board); +int general_ibstatus(struct gpib_board *board, const struct gpib_status_queue *device, + int clear_mask, int set_mask, struct gpib_descriptor *desc); +int io_timed_out(struct gpib_board *board); +int ibppc(struct gpib_board *board, u8 configuration); + +#endif /* GPIB_PROTO_INCLUDED */ diff --git a/drivers/gpib/include/gpib_state_machines.h b/drivers/gpib/include/gpib_state_machines.h new file mode 100644 index 000000000000..7488c00f191e --- /dev/null +++ b/drivers/gpib/include/gpib_state_machines.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2006 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_STATE_MACHINES_H +#define _GPIB_STATE_MACHINES_H + +enum talker_function_state { + talker_idle, + talker_addressed, + talker_active, + serial_poll_active +}; + +enum listener_function_state { + listener_idle, + listener_addressed, + listener_active +}; + +#endif // _GPIB_STATE_MACHINES_H diff --git a/drivers/gpib/include/gpib_types.h b/drivers/gpib/include/gpib_types.h new file mode 100644 index 000000000000..5a0978ae27e7 --- /dev/null +++ b/drivers/gpib/include/gpib_types.h @@ -0,0 +1,381 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_TYPES_H +#define _GPIB_TYPES_H + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpib_board; + +/* config parameters that are only used by driver attach functions */ +struct gpib_board_config { + /* firmware blob */ + void *init_data; + int init_data_length; + /* IO base address to use for non-pnp cards (set by core, driver should make local copy) */ + u32 ibbase; + void __iomem *mmibbase; + /* IRQ to use for non-pnp cards (set by core, driver should make local copy) */ + unsigned int ibirq; + /* dma channel to use for non-pnp cards (set by core, driver should make local copy) */ + unsigned int ibdma; + /* + * pci bus of card, useful for distinguishing multiple identical pci cards + * (negative means don't care) + */ + int pci_bus; + /* + * pci slot of card, useful for distinguishing multiple identical pci cards + * (negative means don't care) + */ + int pci_slot; + /* sysfs device path of hardware to attach */ + char *device_path; + /* serial number of hardware to attach */ + char *serial_number; +}; + +/* + * struct gpib_interface defines the interface + * between the board-specific details dealt with in the drivers + * and generic interface provided by gpib-common. + * This really should be in a different header file. + */ +struct gpib_interface { + /* name of board */ + char *name; + /* attach() initializes board and allocates resources */ + int (*attach)(struct gpib_board *board, const struct gpib_board_config *config); + /* detach() shuts down board and frees resources */ + void (*detach)(struct gpib_board *board); + /* + * read() should read at most 'length' bytes from the bus into + * 'buffer'. It should return when it fills the buffer or + * encounters an END (EOI and or EOS if appropriate). It should set 'end' + * to be nonzero if the read was terminated by an END, otherwise 'end' + * should be zero. + * Ultimately, this will be changed into or replaced by an asynchronous + * read. Zero return value for success, negative + * return indicates error. + * nbytes returns number of bytes read + */ + int (*read)(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read); + /* + * write() should write 'length' bytes from buffer to the bus. + * If the boolean value send_eoi is nonzero, then EOI should + * be sent along with the last byte. Returns number of bytes + * written or negative value on error. + */ + int (*write)(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written); + /* + * command() writes the command bytes in 'buffer' to the bus + * Returns zero on success or negative value on error. + */ + int (*command)(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written); + /* + * Take control (assert ATN). If 'asyncronous' is nonzero, take + * control asyncronously (assert ATN immediately without waiting + * for other processes to complete first). Should not return + * until board becomes controller in charge. Returns zero no success, + * nonzero on error. + */ + int (*take_control)(struct gpib_board *board, int asyncronous); + /* + * De-assert ATN. Returns zero on success, nonzer on error. + */ + int (*go_to_standby)(struct gpib_board *board); + /* request/release control of the IFC and REN lines (system controller) */ + int (*request_system_control)(struct gpib_board *board, int request_control); + /* + * Asserts or de-asserts 'interface clear' (IFC) depending on + * boolean value of 'assert' + */ + void (*interface_clear)(struct gpib_board *board, int assert); + /* + * Sends remote enable command if 'enable' is nonzero, disables remote mode + * if 'enable' is zero + */ + void (*remote_enable)(struct gpib_board *board, int enable); + /* + * enable END for reads, when byte 'eos' is received. If + * 'compare_8_bits' is nonzero, then all 8 bits are compared + * with the eos bytes. Otherwise only the 7 least significant + * bits are compared. + */ + int (*enable_eos)(struct gpib_board *board, u8 eos, int compare_8_bits); + /* disable END on eos byte (END on EOI only)*/ + void (*disable_eos)(struct gpib_board *board); + /* configure parallel poll */ + void (*parallel_poll_configure)(struct gpib_board *board, u8 configuration); + /* conduct parallel poll */ + int (*parallel_poll)(struct gpib_board *board, u8 *result); + /* set/clear ist (individual status bit) */ + void (*parallel_poll_response)(struct gpib_board *board, int ist); + /* select local parallel poll configuration mode PP2 versus remote PP1 */ + void (*local_parallel_poll_mode)(struct gpib_board *board, int local); + /* + * Returns current status of the bus lines. Should be set to + * NULL if your board does not have the ability to query the + * state of the bus lines. + */ + int (*line_status)(const struct gpib_board *board); + /* + * updates and returns the board's current status. + * The meaning of the bits are specified in gpib_user.h + * in the IBSTA section. The driver does not need to + * worry about setting the CMPL, END, TIMO, or ERR bits. + */ + unsigned int (*update_status)(struct gpib_board *board, unsigned int clear_mask); + /* + * Sets primary address 0-30 for gpib interface card. + */ + int (*primary_address)(struct gpib_board *board, unsigned int address); + /* + * Sets and enables, or disables secondary address 0-30 + * for gpib interface card. + */ + int (*secondary_address)(struct gpib_board *board, unsigned int address, + int enable); + /* + * Sets the byte the board should send in response to a serial poll. + * This function should also start or stop requests for service via + * IEEE 488.2 reqt/reqf, based on MSS (bit 6 of the status_byte). + * If the more flexible serial_poll_response2 is implemented by the + * driver, then this method should be left NULL since it will not + * be used. This method can generate spurious service requests + * which are allowed by IEEE 488.2, but not ideal. + * + * This method should implement the serial poll response method described + * by IEEE 488.2 section 11.3.3.4.3 "Allowed Coupled Control of + * STB, reqt, and reqf". + */ + void (*serial_poll_response)(struct gpib_board *board, u8 status_byte); + /* + * Sets the byte the board should send in response to a serial poll. + * This function should also request service via IEEE 488.2 reqt/reqf + * based on MSS (bit 6 of the status_byte) and new_reason_for_service. + * reqt should be set true if new_reason_for_service is true, + * and reqf should be set true if MSS is false. This function + * will never be called with MSS false and new_reason_for_service + * true simultaneously, so don't worry about that case. + * + * This method implements the serial poll response method described + * by IEEE 488.2 section 11.3.3.4.1 "Preferred Implementation". + * + * If this method is left NULL by the driver, then the user library + * function ibrsv2 will not work. + */ + void (*serial_poll_response2)(struct gpib_board *board, u8 status_byte, + int new_reason_for_service); + /* + * returns the byte the board will send in response to a serial poll. + */ + u8 (*serial_poll_status)(struct gpib_board *board); + /* adjust T1 delay */ + int (*t1_delay)(struct gpib_board *board, unsigned int nano_sec); + /* go to local mode */ + void (*return_to_local)(struct gpib_board *board); + /* board does not support 7 bit eos comparisons */ + unsigned no_7_bit_eos : 1; + /* skip check for listeners before trying to send command bytes */ + unsigned skip_check_for_command_acceptors : 1; +}; + +struct gpib_event_queue { + struct list_head event_head; + spinlock_t lock; // for access to event list + unsigned int num_events; + unsigned dropped_event : 1; +}; + +static inline void init_event_queue(struct gpib_event_queue *queue) +{ + INIT_LIST_HEAD(&queue->event_head); + queue->num_events = 0; + queue->dropped_event = 0; + spin_lock_init(&queue->lock); +} + +/* struct for supporting polling operation when irq is not available */ +struct gpib_pseudo_irq { + struct timer_list timer; + irqreturn_t (*handler)(int irq, void *arg); + struct gpib_board *board; + atomic_t active; +}; + +static inline void init_gpib_pseudo_irq(struct gpib_pseudo_irq *pseudo_irq) +{ + pseudo_irq->handler = NULL; + timer_setup(&pseudo_irq->timer, NULL, 0); + atomic_set(&pseudo_irq->active, 0); +} + +/* list so we can make a linked list of drivers */ +struct gpib_interface_list { + struct list_head list; + struct gpib_interface *interface; + struct module *module; +}; + +/* + * One struct gpib_board is allocated for each physical board in the computer. + * It provides storage for variables local to each board, and interface + * functions for performing operations on the board + */ +struct gpib_board { + /* functions used by this board */ + struct gpib_interface *interface; + /* + * Pointer to module whose use count we should increment when + * interface is in use + */ + struct module *provider_module; + /* buffer used to store read/write data for this board */ + u8 *buffer; + /* length of buffer */ + unsigned int buffer_length; + /* + * Used to hold the board's current status (see update_status() above) + */ + unsigned long status; + /* + * Driver should only sleep on this wait queue. It is special in that the + * core will wake this queue and set the TIMO bit in 'status' when the + * watchdog timer times out. + */ + wait_queue_head_t wait; + /* + * Lock that only allows one process to access this board at a time. + * Has to be first in any locking order, since it can be locked over + * multiple ioctls. + */ + struct mutex user_mutex; + /* + * Mutex which compensates for removal of "big kernel lock" from kernel. + * Should not be held for extended waits. + */ + struct mutex big_gpib_mutex; + /* pid of last process to lock the board mutex */ + pid_t locking_pid; + /* lock for setting locking pid */ + spinlock_t locking_pid_spinlock; + /* Spin lock for dealing with races with the interrupt handler */ + spinlock_t spinlock; + /* Watchdog timer to enable timeouts */ + struct timer_list timer; + /* device of attached driver if any */ + struct device *dev; + /* gpib_common device gpibN */ + struct device *gpib_dev; + /* + * 'private_data' can be used as seen fit by the driver to + * store additional variables for this board + */ + void *private_data; + /* Number of open file descriptors using this board */ + unsigned int use_count; + /* list of open devices connected to this board */ + struct list_head device_list; + /* primary address */ + unsigned int pad; + /* secondary address */ + int sad; + /* timeout for io operations, in microseconds */ + unsigned int usec_timeout; + /* board's parallel poll configuration byte */ + u8 parallel_poll_configuration; + /* t1 delay we are using */ + unsigned int t1_nano_sec; + /* Count that keeps track of whether board is up and running or not */ + unsigned int online; + /* number of processes trying to autopoll */ + int autospollers; + /* autospoll kernel thread */ + struct task_struct *autospoll_task; + /* queue for recording received trigger/clear/ifc events */ + struct gpib_event_queue event_queue; + /* minor number for this board's device file */ + int minor; + /* struct to deal with polling mode*/ + struct gpib_pseudo_irq pseudo_irq; + /* error dong autopoll */ + atomic_t stuck_srq; + struct gpib_board_config config; + /* Flag that indicates whether board is system controller of the bus */ + unsigned master : 1; + /* individual status bit */ + unsigned ist : 1; + /* + * one means local parallel poll mode ieee 488.1 PP2 (or no parallel poll PP0), + * zero means remote parallel poll configuration mode ieee 488.1 PP1 + */ + unsigned local_ppoll_mode : 1; +}; + +/* element of event queue */ +struct gpib_event { + struct list_head list; + short event_type; +}; + +/* + * Each board has a list of gpib_status_queue to keep track of all open devices + * on the bus, so we know what address to poll when we get a service request + */ +struct gpib_status_queue { + /* list_head so we can make a linked list of devices */ + struct list_head list; + unsigned int pad; /* primary gpib address */ + int sad; /* secondary gpib address (negative means disabled) */ + /* stores serial poll bytes for this device */ + struct list_head status_bytes; + unsigned int num_status_bytes; + /* number of times this address is opened */ + unsigned int reference_count; + /* flags loss of status byte error due to limit on size of queue */ + unsigned dropped_byte : 1; +}; + +struct gpib_status_byte { + struct list_head list; + u8 poll_byte; +}; + +void init_gpib_status_queue(struct gpib_status_queue *device); + +/* Used to store device-descriptor-specific information */ +struct gpib_descriptor { + unsigned int pad; /* primary gpib address */ + int sad; /* secondary gpib address (negative means disabled) */ + atomic_t io_in_progress; + unsigned is_board : 1; + unsigned autopoll_enabled : 1; +}; + +struct gpib_file_private { + atomic_t holding_mutex; + struct gpib_descriptor *descriptors[GPIB_MAX_NUM_DESCRIPTORS]; + /* locked while descriptors are being allocated/deallocated */ + struct mutex descriptors_mutex; + unsigned got_module : 1; +}; + +#endif /* __KERNEL__ */ + +#endif /* _GPIB_TYPES_H */ diff --git a/drivers/gpib/include/nec7210.h b/drivers/gpib/include/nec7210.h new file mode 100644 index 000000000000..9835aa5ef4ff --- /dev/null +++ b/drivers/gpib/include/nec7210.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _NEC7210_H +#define _NEC7210_H + +#include "gpib_state_machines.h" +#include +#include +#include +#include + +#include "gpib_types.h" +#include "nec7210_registers.h" + +/* struct used to provide variables local to a nec7210 chip */ +struct nec7210_priv { +#ifdef CONFIG_HAS_IOPORT + u32 iobase; +#endif + void __iomem *mmiobase; + unsigned int offset; // offset between successive nec7210 io addresses + unsigned int dma_channel; + u8 *dma_buffer; + unsigned int dma_buffer_length; // length of dma buffer + dma_addr_t dma_buffer_addr; // bus address of board->buffer for use with dma + // software copy of bits written to registers + u8 reg_bits[8]; + u8 auxa_bits; // bits written to auxiliary register A + u8 auxb_bits; // bits written to auxiliary register B + // used to keep track of board's state, bit definitions given below + unsigned long state; + // lock for chips that extend the nec7210 registers by paging in alternate regs + spinlock_t register_page_lock; + // wrappers for outb, inb, readb, or writeb + u8 (*read_byte)(struct nec7210_priv *priv, unsigned int register_number); + void (*write_byte)(struct nec7210_priv *priv, u8 byte, unsigned int register_number); + enum nec7210_chipset type; + enum talker_function_state talker_state; + enum listener_function_state listener_state; + void *private; + unsigned srq_pending : 1; +}; + +static inline void init_nec7210_private(struct nec7210_priv *priv) +{ + memset(priv, 0, sizeof(struct nec7210_priv)); + spin_lock_init(&priv->register_page_lock); +} + +// slightly shorter way to access read_byte and write_byte +static inline u8 read_byte(struct nec7210_priv *priv, unsigned int register_number) +{ + return priv->read_byte(priv, register_number); +} + +static inline void write_byte(struct nec7210_priv *priv, u8 byte, unsigned int register_number) +{ + priv->write_byte(priv, byte, register_number); +} + +// struct nec7210_priv.state bit numbers +enum { + PIO_IN_PROGRESS_BN, // pio transfer in progress + DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress + DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress + READ_READY_BN, // board has data byte available to read + WRITE_READY_BN, // board is ready to send a data byte + COMMAND_READY_BN, // board is ready to send a command byte + RECEIVED_END_BN, // received END + BUS_ERROR_BN, // output error has occurred + RFD_HOLDOFF_BN, // rfd holdoff in effect + DEV_CLEAR_BN, // device clear received + ADR_CHANGE_BN, // address state change occurred +}; + +// interface functions +int nec7210_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length, int *end, size_t *bytes_read); +int nec7210_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written); +int nec7210_command(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length, size_t *bytes_written); +int nec7210_take_control(struct gpib_board *board, struct nec7210_priv *priv, int syncronous); +int nec7210_go_to_standby(struct gpib_board *board, struct nec7210_priv *priv); +int nec7210_request_system_control(struct gpib_board *board, + struct nec7210_priv *priv, int request_control); +void nec7210_interface_clear(struct gpib_board *board, struct nec7210_priv *priv, int assert); +void nec7210_remote_enable(struct gpib_board *board, struct nec7210_priv *priv, int enable); +int nec7210_enable_eos(struct gpib_board *board, struct nec7210_priv *priv, u8 eos_bytes, + int compare_8_bits); +void nec7210_disable_eos(struct gpib_board *board, struct nec7210_priv *priv); +unsigned int nec7210_update_status(struct gpib_board *board, struct nec7210_priv *priv, + unsigned int clear_mask); +unsigned int nec7210_update_status_nolock(struct gpib_board *board, struct nec7210_priv *priv); +int nec7210_primary_address(const struct gpib_board *board, + struct nec7210_priv *priv, unsigned int address); +int nec7210_secondary_address(const struct gpib_board *board, struct nec7210_priv *priv, + unsigned int address, int enable); +int nec7210_parallel_poll(struct gpib_board *board, struct nec7210_priv *priv, u8 *result); +void nec7210_serial_poll_response(struct gpib_board *board, + struct nec7210_priv *priv, u8 status); +void nec7210_parallel_poll_configure(struct gpib_board *board, + struct nec7210_priv *priv, unsigned int configuration); +void nec7210_parallel_poll_response(struct gpib_board *board, + struct nec7210_priv *priv, int ist); +u8 nec7210_serial_poll_status(struct gpib_board *board, struct nec7210_priv *priv); +int nec7210_t1_delay(struct gpib_board *board, + struct nec7210_priv *priv, unsigned int nano_sec); +void nec7210_return_to_local(const struct gpib_board *board, struct nec7210_priv *priv); + +// utility functions +void nec7210_board_reset(struct nec7210_priv *priv, const struct gpib_board *board); +void nec7210_board_online(struct nec7210_priv *priv, const struct gpib_board *board); +unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg, + unsigned int mask, unsigned int bits); +void nec7210_set_handshake_mode(struct gpib_board *board, struct nec7210_priv *priv, int mode); +void nec7210_release_rfd_holdoff(struct gpib_board *board, struct nec7210_priv *priv); +u8 nec7210_read_data_in(struct gpib_board *board, struct nec7210_priv *priv, int *end); + +// wrappers for io functions +u8 nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_ioport_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num); +u8 nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_iomem_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num); +u8 nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, u8 data, + unsigned int register_num); +u8 nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num); +void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data, + unsigned int register_num); + +// interrupt service routine +irqreturn_t nec7210_interrupt(struct gpib_board *board, struct nec7210_priv *priv); +irqreturn_t nec7210_interrupt_have_status(struct gpib_board *board, + struct nec7210_priv *priv, int status1, int status2); + +#endif //_NEC7210_H diff --git a/drivers/gpib/include/nec7210_registers.h b/drivers/gpib/include/nec7210_registers.h new file mode 100644 index 000000000000..067983d7a07f --- /dev/null +++ b/drivers/gpib/include/nec7210_registers.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _NEC7210_REGISTERS_H +#define _NEC7210_REGISTERS_H + +enum nec7210_chipset { + NEC7210, // The original + TNT4882, // NI + NAT4882, // NI + CB7210, // measurement computing + IOT7210, // iotech + IGPIB7210, // Ines + TNT5004, // NI (minor differences to TNT4882) +}; + +/* + * nec7210 register numbers (might need to be multiplied by + * a board-dependent offset to get actually io address offset) + */ +// write registers +enum nec7210_write_regs { + CDOR, // command/data out + IMR1, // interrupt mask 1 + IMR2, // interrupt mask 2 + SPMR, // serial poll mode + ADMR, // address mode + AUXMR, // auxiliary mode + ADR, // address + EOSR, // end-of-string + + // nec7210 has 8 registers + nec7210_num_registers = 8, +}; + +// read registers +enum nec7210_read_regs { + DIR, // data in + ISR1, // interrupt status 1 + ISR2, // interrupt status 2 + SPSR, // serial poll status + ADSR, // address status + CPTR, // command pass though + ADR0, // address 1 + ADR1, // address 2 +}; + +// bit definitions common to nec-7210 compatible registers + +// ISR1: interrupt status register 1 +enum isr1_bits { + HR_DI = (1 << 0), + HR_DO = (1 << 1), + HR_ERR = (1 << 2), + HR_DEC = (1 << 3), + HR_END = (1 << 4), + HR_DET = (1 << 5), + HR_APT = (1 << 6), + HR_CPT = (1 << 7), +}; + +// IMR1: interrupt mask register 1 +enum imr1_bits { + HR_DIIE = (1 << 0), + HR_DOIE = (1 << 1), + HR_ERRIE = (1 << 2), + HR_DECIE = (1 << 3), + HR_ENDIE = (1 << 4), + HR_DETIE = (1 << 5), + HR_APTIE = (1 << 6), + HR_CPTIE = (1 << 7), +}; + +// ISR2, interrupt status register 2 +enum isr2_bits { + HR_ADSC = (1 << 0), + HR_REMC = (1 << 1), + HR_LOKC = (1 << 2), + HR_CO = (1 << 3), + HR_REM = (1 << 4), + HR_LOK = (1 << 5), + HR_SRQI = (1 << 6), + HR_INT = (1 << 7), +}; + +// IMR2, interrupt mask register 2 +enum imr2_bits { + // all the bits in this register that enable interrupts + IMR2_ENABLE_INTR_MASK = 0x4f, + HR_ACIE = (1 << 0), + HR_REMIE = (1 << 1), + HR_LOKIE = (1 << 2), + HR_COIE = (1 << 3), + HR_DMAI = (1 << 4), + HR_DMAO = (1 << 5), + HR_SRQIE = (1 << 6), +}; + +// SPSR, serial poll status register +enum spsr_bits { + HR_PEND = (1 << 6), +}; + +// SPMR, serial poll mode register +enum spmr_bits { + HR_RSV = (1 << 6), +}; + +// ADSR, address status register +enum adsr_bits { + HR_MJMN = (1 << 0), + HR_TA = (1 << 1), + HR_LA = (1 << 2), + HR_TPAS = (1 << 3), + HR_LPAS = (1 << 4), + HR_SPMS = (1 << 5), + HR_NATN = (1 << 6), + HR_CIC = (1 << 7), +}; + +// ADMR, address mode register +enum admr_bits { + HR_ADM0 = (1 << 0), + HR_ADM1 = (1 << 1), + HR_TRM0 = (1 << 4), + HR_TRM1 = (1 << 5), + HR_TRM_EOIOE_TRIG = 0, + HR_TRM_CIC_TRIG = HR_TRM0, + HR_TRM_CIC_EOIOE = HR_TRM1, + HR_TRM_CIC_PE = HR_TRM0 | HR_TRM1, + HR_LON = (1 << 6), + HR_TON = (1 << 7), +}; + +// ADR, bits used in address0, address1 and address0/1 registers +enum adr_bits { + ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits */ + HR_DL = (1 << 5), + HR_DT = (1 << 6), + HR_ARS = (1 << 7), +}; + +// ADR1, address1 register +enum adr1_bits { + HR_EOI = (1 << 7), +}; + +// AUXMR, auxiliary mode register +enum auxmr_bits { + ICR = 0x20, + PPR = 0x60, + AUXRA = 0x80, + AUXRB = 0xa0, + AUXRE = 0xc0, +}; + +// auxra, auxiliary register A +enum auxra_bits { + HR_HANDSHAKE_MASK = 0x3, + HR_HLDA = 0x1, + HR_HLDE = 0x2, + HR_LCM = 0x3, /* auxra listen continuous */ + HR_REOS = 0x4, + HR_XEOS = 0x8, + HR_BIN = 0x10, +}; + +// auxrb, auxiliary register B +enum auxrb_bits { + HR_CPTE = (1 << 0), + HR_SPEOI = (1 << 1), + HR_TRI = (1 << 2), + HR_INV = (1 << 3), + HR_ISS = (1 << 4), +}; + +enum auxre_bits { + HR_DAC_HLD_DCAS = 0x1, /* perform DAC holdoff on receiving clear */ + HR_DAC_HLD_DTAS = 0x2, /* perform DAC holdoff on receiving trigger */ +}; + +// parallel poll register +enum ppr_bits { + HR_PPS = (1 << 3), + HR_PPU = (1 << 4), +}; + +/* 7210 Auxiliary Commands */ +enum aux_cmds { + AUX_PON = 0x0, /* Immediate Execute pon */ + AUX_CPPF = 0x1, /* Clear Parallel Poll Flag */ + AUX_CR = 0x2, /* Chip Reset */ + AUX_FH = 0x3, /* Finish Handshake */ + AUX_TRIG = 0x4, /* Trigger */ + AUX_RTL = 0x5, /* Return to local */ + AUX_SEOI = 0x6, /* Send EOI */ + AUX_NVAL = 0x7, /* Non-Valid Secondary Command or Address */ + AUX_SPPF = 0x9, /* Set Parallel Poll Flag */ + AUX_VAL = 0xf, /* Valid Secondary Command or Address */ + AUX_GTS = 0x10, /* Go To Standby */ + AUX_TCA = 0x11, /* Take Control Asynchronously */ + AUX_TCS = 0x12, /* Take Control Synchronously */ + AUX_LTN = 0x13, /* Listen */ + AUX_DSC = 0x14, /* Disable System Control */ + AUX_CIFC = 0x16, /* Clear IFC */ + AUX_CREN = 0x17, /* Clear REN */ + AUX_TCSE = 0x1a, /* Take Control Synchronously on End */ + AUX_LTNC = 0x1b, /* Listen in Continuous Mode */ + AUX_LUN = 0x1c, /* Local Unlisten */ + AUX_EPP = 0x1d, /* Execute Parallel Poll */ + AUX_SIFC = 0x1e, /* Set IFC */ + AUX_SREN = 0x1f, /* Set REN */ +}; + +#endif //_NEC7210_REGISTERS_H diff --git a/drivers/gpib/include/plx9050.h b/drivers/gpib/include/plx9050.h new file mode 100644 index 000000000000..c911b285a0ca --- /dev/null +++ b/drivers/gpib/include/plx9050.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Header for plx9050 pci chip + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _PLX9050_GPIB_H +#define _PLX9050_GPIB_H + +// plx pci chip registers and bits +enum { + PLX9050_INTCSR_REG = 0x4c, + PLX9050_CNTRL_REG = 0x50 +}; + +enum plx9050_intcsr_bits { + PLX9050_LINTR1_EN_BIT = 0x1, + PLX9050_LINTR1_POLARITY_BIT = 0x2, + PLX9050_LINTR1_STATUS_BIT = 0x4, + PLX9050_LINTR2_EN_BIT = 0x8, + PLX9050_LINTR2_POLARITY_BIT = 0x10, + PLX9050_LINTR2_STATUS_BIT = 0x20, + PLX9050_PCI_INTR_EN_BIT = 0x40, + PLX9050_SOFT_INTR_BIT = 0x80, + PLX9050_LINTR1_SELECT_ENABLE_BIT = 0x100, // 9052 extension + PLX9050_LINTR2_SELECT_ENABLE_BIT = 0x200, // 9052 extension + PLX9050_LINTR1_EDGE_CLEAR_BIT = 0x400, // 9052 extension + PLX9050_LINTR2_EDGE_CLEAR_BIT = 0x800, // 9052 extension +}; + +enum plx9050_cntrl_bits { + PLX9050_WAITO_NOT_USER0_SELECT_BIT = 0x1, + PLX9050_USER0_OUTPUT_BIT = 0x2, + PLX9050_USER0_DATA_BIT = 0x4, + PLX9050_LLOCK_NOT_USER1_SELECT_BIT = 0x8, + PLX9050_USER1_OUTPUT_BIT = 0x10, + PLX9050_USER1_DATA_BIT = 0x20, + PLX9050_CS2_NOT_USER2_SELECT_BIT = 0x40, + PLX9050_USER2_OUTPUT_BIT = 0x80, + PLX9050_USER2_DATA_BIT = 0x100, + PLX9050_CS3_NOT_USER3_SELECT_BIT = 0x200, + PLX9050_USER3_OUTPUT_BIT = 0x400, + PLX9050_USER3_DATA_BIT = 0x800, + PLX9050_PCIBAR_ENABLE_MASK = 0x3000, + PLX9050_PCIBAR_MEMORY_AND_IO_ENABLE_BITS = 0x0, + PLX9050_PCIBAR_MEMORY_NO_IO_ENABLE_BITS = 0x1000, + PLX9050_PCIBAR_IO_NO_MEMORY_ENABLE_BITS = 0x2000, + PLX9050_PCIBAR_MEMORY_AND_IO_TOO_ENABLE_BITS = 0x3000, + PLX9050_PCI_READ_MODE_BIT = 0x4000, + PLX9050_PCI_READ_WITH_WRITE_FLUSH_MODE_BIT = 0x8000, + PLX9050_PCI_READ_NO_FLUSH_MODE_BIT = 0x10000, + PLX9050_PCI_READ_NO_WRITE_MODE_BIT = 0x20000, + PLX9050_PCI_WRITE_MODE_BIT = 0x40000, + PLX9050_PCI_RETRY_DELAY_MASK = 0x780000, + PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT = 0x800000, + PLX9050_EEPROM_CLOCK_BIT = 0x1000000, + PLX9050_EEPROM_CHIP_SELECT_BIT = 0x2000000, + PLX9050_WRITE_TO_EEPROM_BIT = 0x4000000, + PLX9050_READ_EEPROM_DATA_BIT = 0x8000000, + PLX9050_EEPROM_VALID_BIT = 0x10000000, + PLX9050_RELOAD_CONFIG_REGISTERS_BIT = 0x20000000, + PLX9050_PCI_SOFTWARE_RESET_BIT = 0x40000000, + PLX9050_MASK_REVISION_BIT = 0x80000000 +}; + +static inline unsigned int PLX9050_PCI_RETRY_DELAY_BITS(unsigned int clocks) +{ + return ((clocks / 8) << 19) & PLX9050_PCI_RETRY_DELAY_MASK; +} + +#endif // _PLX9050_GPIB_H diff --git a/drivers/gpib/include/quancom_pci.h b/drivers/gpib/include/quancom_pci.h new file mode 100644 index 000000000000..cdaf0d056be9 --- /dev/null +++ b/drivers/gpib/include/quancom_pci.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Quancom pci stuff + * copyright (C) 2005 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _QUANCOM_PCI_H +#define _QUANCOM_PCI_H + +/* quancom registers */ +enum quancom_regs { + QUANCOM_IRQ_CONTROL_STATUS_REG = 0xfc, +}; + +enum quancom_irq_control_status_bits { + QUANCOM_IRQ_ASSERTED_BIT = 0x1, /* readable */ + /* (any write to the register clears the interrupt)*/ + QUANCOM_IRQ_ENABLE_BIT = 0x4, /* writeable */ +}; + +#endif // _QUANCOM_PCI_H diff --git a/drivers/gpib/include/tms9914.h b/drivers/gpib/include/tms9914.h new file mode 100644 index 000000000000..e66b75e0fda8 --- /dev/null +++ b/drivers/gpib/include/tms9914.h @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _TMS9914_H +#define _TMS9914_H + +#include +#include +#include "gpib_state_machines.h" +#include "gpib_types.h" + +enum tms9914_holdoff_mode { + TMS9914_HOLDOFF_NONE, + TMS9914_HOLDOFF_EOI, + TMS9914_HOLDOFF_ALL, +}; + +/* struct used to provide variables local to a tms9914 chip */ +struct tms9914_priv { +#ifdef CONFIG_HAS_IOPORT + u32 iobase; +#endif + void __iomem *mmiobase; + unsigned int offset; // offset between successive tms9914 io addresses + unsigned int dma_channel; + // software copy of bits written to interrupt mask registers + u8 imr0_bits, imr1_bits; + // bits written to address mode register + u8 admr_bits; + u8 auxa_bits; // bits written to auxiliary register A + // used to keep track of board's state, bit definitions given below + unsigned long state; + u8 eos; // eos character + short eos_flags; + u8 spoll_status; + enum tms9914_holdoff_mode holdoff_mode; + unsigned int ppoll_line; + enum talker_function_state talker_state; + enum listener_function_state listener_state; + unsigned ppoll_sense : 1; + unsigned ppoll_enable : 1; + unsigned ppoll_configure_state : 1; + unsigned primary_listen_addressed : 1; + unsigned primary_talk_addressed : 1; + unsigned holdoff_on_end : 1; + unsigned holdoff_on_all : 1; + unsigned holdoff_active : 1; + // wrappers for outb, inb, readb, or writeb + u8 (*read_byte)(struct tms9914_priv *priv, unsigned int register_number); + void (*write_byte)(struct tms9914_priv *priv, u8 byte, unsigned int + register_number); +}; + +// slightly shorter way to access read_byte and write_byte +static inline u8 read_byte(struct tms9914_priv *priv, unsigned int register_number) +{ + return priv->read_byte(priv, register_number); +} + +static inline void write_byte(struct tms9914_priv *priv, u8 byte, unsigned int register_number) +{ + priv->write_byte(priv, byte, register_number); +} + +// struct tms9914_priv.state bit numbers +enum { + PIO_IN_PROGRESS_BN, // pio transfer in progress + DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress + DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress + READ_READY_BN, // board has data byte available to read + WRITE_READY_BN, // board is ready to send a data byte + COMMAND_READY_BN, // board is ready to send a command byte + RECEIVED_END_BN, // received END + BUS_ERROR_BN, // bus error + DEV_CLEAR_BN, // device clear received +}; + +// interface functions +int tms9914_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, + size_t length, int *end, size_t *bytes_read); +int tms9914_write(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written); +int tms9914_command(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, + size_t length, size_t *bytes_written); +int tms9914_take_control(struct gpib_board *board, struct tms9914_priv *priv, int syncronous); +/* + * alternate version of tms9914_take_control which works around buggy tcs + * implementation. + */ +int tms9914_take_control_workaround(struct gpib_board *board, struct tms9914_priv *priv, + int syncronous); +int tms9914_go_to_standby(struct gpib_board *board, struct tms9914_priv *priv); +int tms9914_request_system_control(struct gpib_board *board, struct tms9914_priv *priv, + int request_control); +void tms9914_interface_clear(struct gpib_board *board, struct tms9914_priv *priv, int assert); +void tms9914_remote_enable(struct gpib_board *board, struct tms9914_priv *priv, int enable); +int tms9914_enable_eos(struct gpib_board *board, struct tms9914_priv *priv, u8 eos_bytes, + int compare_8_bits); +void tms9914_disable_eos(struct gpib_board *board, struct tms9914_priv *priv); +unsigned int tms9914_update_status(struct gpib_board *board, struct tms9914_priv *priv, + unsigned int clear_mask); +int tms9914_primary_address(struct gpib_board *board, + struct tms9914_priv *priv, unsigned int address); +int tms9914_secondary_address(struct gpib_board *board, struct tms9914_priv *priv, + unsigned int address, int enable); +int tms9914_parallel_poll(struct gpib_board *board, struct tms9914_priv *priv, u8 *result); +void tms9914_parallel_poll_configure(struct gpib_board *board, + struct tms9914_priv *priv, u8 config); +void tms9914_parallel_poll_response(struct gpib_board *board, + struct tms9914_priv *priv, int ist); +void tms9914_serial_poll_response(struct gpib_board *board, + struct tms9914_priv *priv, u8 status); +u8 tms9914_serial_poll_status(struct gpib_board *board, struct tms9914_priv *priv); +int tms9914_line_status(const struct gpib_board *board, struct tms9914_priv *priv); +unsigned int tms9914_t1_delay(struct gpib_board *board, struct tms9914_priv *priv, + unsigned int nano_sec); +void tms9914_return_to_local(const struct gpib_board *board, struct tms9914_priv *priv); + +// utility functions +void tms9914_board_reset(struct tms9914_priv *priv); +void tms9914_online(struct gpib_board *board, struct tms9914_priv *priv); +void tms9914_release_holdoff(struct tms9914_priv *priv); +void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode); + +// wrappers for io functions +u8 tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num); +void tms9914_ioport_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num); +u8 tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num); +void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num); + +// interrupt service routine +irqreturn_t tms9914_interrupt(struct gpib_board *board, struct tms9914_priv *priv); +irqreturn_t tms9914_interrupt_have_status(struct gpib_board *board, struct tms9914_priv *priv, + int status1, int status2); + +// tms9914 has 8 registers +enum { + ms9914_num_registers = 8, +}; + +/* + * tms9914 register numbers (might need to be multiplied by + * a board-dependent offset to get actually io address offset) + */ +// write registers +enum { + IMR0 = 0, /* interrupt mask 0 */ + IMR1 = 1, /* interrupt mask 1 */ + AUXCR = 3, /* auxiliary command */ + ADR = 4, /* address register */ + SPMR = 5, /* serial poll mode register */ + PPR = 6, /* parallel poll */ + CDOR = 7, /* data out register */ +}; + +// read registers +enum { + ISR0 = 0, /* interrupt status 0 */ + ISR1 = 1, /* interrupt status 1 */ + ADSR = 2, /* address status */ + BSR = 3, /* bus status */ + CPTR = 6, /* command pass thru */ + DIR = 7, /* data in register */ +}; + +// bit definitions common to tms9914 compatible registers + +/* ISR0 - Register bits */ +enum isr0_bits { + HR_MAC = (1 << 0), /* My Address Change */ + HR_RLC = (1 << 1), /* Remote/Local change */ + HR_SPAS = (1 << 2), /* Serial Poll active State */ + HR_END = (1 << 3), /* END (EOI or EOS) */ + HR_BO = (1 << 4), /* Byte Out */ + HR_BI = (1 << 5), /* Byte In */ +}; + +/* IMR0 - Register bits */ +enum imr0_bits { + HR_MACIE = (1 << 0), /* */ + HR_RLCIE = (1 << 1), /* */ + HR_SPASIE = (1 << 2), /* */ + HR_ENDIE = (1 << 3), /* */ + HR_BOIE = (1 << 4), /* */ + HR_BIIE = (1 << 5), /* */ +}; + +/* ISR1 - Register bits */ +enum isr1_bits { + HR_IFC = (1 << 0), /* IFC asserted */ + HR_SRQ = (1 << 1), /* SRQ asserted */ + HR_MA = (1 << 2), /* My Address */ + HR_DCAS = (1 << 3), /* Device Clear active State */ + HR_APT = (1 << 4), /* Address pass Through */ + HR_UNC = (1 << 5), /* Unrecognized Command */ + HR_ERR = (1 << 6), /* Data Transmission Error */ + HR_GET = (1 << 7), /* Group execute Trigger */ +}; + +/* IMR1 - Register bits */ +enum imr1_bits { + HR_IFCIE = (1 << 0), /* */ + HR_SRQIE = (1 << 1), /* */ + HR_MAIE = (1 << 2), /* */ + HR_DCASIE = (1 << 3), /* */ + HR_APTIE = (1 << 4), /* */ + HR_UNCIE = (1 << 5), /* */ + HR_ERRIE = (1 << 6), /* */ + HR_GETIE = (1 << 7), /* */ +}; + +/* ADSR - Register bits */ +enum adsr_bits { + HR_ULPA = (1 << 0), /* Store last address LSB */ + HR_TA = (1 << 1), /* Talker Adressed */ + HR_LA = (1 << 2), /* Listener adressed */ + HR_TPAS = (1 << 3), /* talker primary address state */ + HR_LPAS = (1 << 4), /* listener " */ + HR_ATN = (1 << 5), /* ATN active */ + HR_LLO = (1 << 6), /* LLO active */ + HR_REM = (1 << 7), /* REM active */ +}; + +/* ADR - Register bits */ +enum adr_bits { + ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits for ADR */ + HR_DAT = (1 << 5), /* disable talker */ + HR_DAL = (1 << 6), /* disable listener */ + HR_EDPA = (1 << 7), /* enable dual primary addressing */ +}; + +enum bus_status_bits { + BSR_REN_BIT = 0x1, + BSR_IFC_BIT = 0x2, + BSR_SRQ_BIT = 0x4, + BSR_EOI_BIT = 0x8, + BSR_NRFD_BIT = 0x10, + BSR_NDAC_BIT = 0x20, + BSR_DAV_BIT = 0x40, + BSR_ATN_BIT = 0x80, +}; + +/*---------------------------------------------------------*/ +/* TMS 9914 Auxiliary Commands */ +/*---------------------------------------------------------*/ + +enum aux_cmd_bits { + AUX_CS = 0x80, /* set bit instead of clearing it, used with commands marked 'd' below */ + AUX_CHIP_RESET = 0x0, /* d Chip reset */ + AUX_INVAL = 0x1, /* release dac holdoff, invalid command byte */ + AUX_VAL = (AUX_INVAL | AUX_CS), /* release dac holdoff, valid command byte */ + AUX_RHDF = 0x2, /* X Release RFD holdoff */ + AUX_HLDA = 0x3, /* d holdoff on all data */ + AUX_HLDE = 0x4, /* d holdoff on EOI only */ + AUX_NBAF = 0x5, /* X Set new byte available false */ + AUX_FGET = 0x6, /* d force GET */ + AUX_RTL = 0x7, /* d return to local */ + AUX_SEOI = 0x8, /* X send EOI with next byte */ + AUX_LON = 0x9, /* d Listen only */ + AUX_TON = 0xa, /* d Talk only */ + AUX_GTS = 0xb, /* X goto standby */ + AUX_TCA = 0xc, /* X take control asynchronously */ + AUX_TCS = 0xd, /* X take " synchronously */ + AUX_RPP = 0xe, /* d Request parallel poll */ + AUX_SIC = 0xf, /* d send interface clear */ + AUX_SRE = 0x10, /* d send remote enable */ + AUX_RQC = 0x11, /* X request control */ + AUX_RLC = 0x12, /* X release control */ + AUX_DAI = 0x13, /* d disable all interrupts */ + AUX_PTS = 0x14, /* X pass through next secondary */ + AUX_STDL = 0x15, /* d short T1 delay */ + AUX_SHDW = 0x16, /* d shadow handshake */ + AUX_VSTDL = 0x17, /* d very short T1 delay (smj9914 extension) */ + AUX_RSV2 = 0x18, /* d request service bit 2 (smj9914 extension) */ +}; + +#endif //_TMS9914_H diff --git a/drivers/gpib/include/tnt4882_registers.h b/drivers/gpib/include/tnt4882_registers.h new file mode 100644 index 000000000000..d54c4cc61168 --- /dev/null +++ b/drivers/gpib/include/tnt4882_registers.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2002, 2004 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _TNT4882_REGISTERS_H +#define _TNT4882_REGISTERS_H + +// tnt4882 register offsets +enum { + ACCWR = 0x5, + // offset of auxiliary command register in 9914 mode + AUXCR = 0x6, + INTRT = 0x7, + // register number for auxiliary command register when swap bit is set (9914 mode) + SWAPPED_AUXCR = 0xa, + HSSEL = 0xd, // handshake select register + CNT2 = 0x9, + CNT3 = 0xb, + CFG = 0x10, + SASR = 0x1b, + IMR0 = 0x1d, + IMR3 = 0x12, + CNT0 = 0x14, + CNT1 = 0x16, + KEYREG = 0x17, // key control register (7210 mode only) + CSR = KEYREG, + FIFOB = 0x18, + FIFOA = 0x19, + CCR = 0x1a, // carry cycle register + CMDR = 0x1c, // command register + TIMER = 0x1e, // timer register + + STS1 = 0x10, // T488 Status Register 1 + STS2 = 0x1c, // T488 Status Register 2 + ISR0 = IMR0, + ISR3 = 0x1a, // T488 Interrupt Status Register 3 + BCR = 0x1f, // bus control/status register + BSR = BCR, +}; + +enum { + tnt_pagein_offset = 0x11, +}; + +/*============================================================*/ + +/* TURBO-488 registers bit definitions */ + +enum bus_control_status_bits { + BCSR_REN_BIT = 0x1, + BCSR_IFC_BIT = 0x2, + BCSR_SRQ_BIT = 0x4, + BCSR_EOI_BIT = 0x8, + BCSR_NRFD_BIT = 0x10, + BCSR_NDAC_BIT = 0x20, + BCSR_DAV_BIT = 0x40, + BCSR_ATN_BIT = 0x80, +}; + +/* CFG -- Configuration Register (write only) */ +enum cfg_bits { + TNT_COMMAND = 0x80, /* bytes are command bytes instead of data bytes + * (tnt4882 one-chip and newer only?) + */ + TNT_TLCHE = (1 << 6), /* halt transfer on imr0, imr1, or imr2 interrupt */ + TNT_IN = (1 << 5), /* transfer is GPIB read */ + TNT_A_B = (1 << 4), /* order to use fifos 1=fifo A first(big endian), + * 0=fifo b first(little endian) + */ + TNT_CCEN = (1 << 3), /* enable carry cycle */ + TNT_TMOE = (1 << 2), /* enable CPU bus time limit */ + TNT_TIM_BYTN = (1 << 1), /* tmot reg is: 1=125ns clocks, 0=num bytes */ + TNT_B_16BIT = (1 << 0), /* 1=FIFO is 16-bit register, 0=8-bit */ +}; + +/* CMDR -- Command Register */ +enum cmdr_bits { + CLRSC = 0x2, /* clear the system controller bit */ + SETSC = 0x3, /* set the system controller bit */ + GO = 0x4, /* start fifos */ + STOP = 0x8, /* stop fifos */ + RESET_FIFO = 0x10, /* reset the FIFOs */ + SOFT_RESET = 0x22, /* issue a software reset */ + HARD_RESET = 0x40 /* 500x only? */ +}; + +/* HSSEL -- handshake select register (write only) */ +enum hssel_bits { + TNT_ONE_CHIP_BIT = 0x1, + NODMA = 0x10, + TNT_GO2SIDS_BIT = 0x20, +}; + +/* IMR0 -- Interrupt Mode Register 0 */ +enum imr0_bits { + TNT_SYNCIE_BIT = 0x1, /* handshake sync */ + TNT_TOIE_BIT = 0x2, /* timeout */ + TNT_ATNIE_BIT = 0x4, /* ATN interrupt */ + TNT_IFCIE_BIT = 0x8, /* interface clear interrupt */ + TNT_BTO_BIT = 0x10, /* byte timeout */ + TNT_NLEN_BIT = 0x20, /* treat new line as EOS char */ + TNT_STBOIE_BIT = 0x40, /* status byte out */ + TNT_IMR0_ALWAYS_BITS = 0x80, /* always set this bit on write */ +}; + +/* ISR0 -- Interrupt Status Register 0 */ +enum isr0_bits { + TNT_SYNC_BIT = 0x1, /* handshake sync */ + TNT_TO_BIT = 0x2, /* timeout */ + TNT_ATNI_BIT = 0x4, /* ATN interrupt */ + TNT_IFCI_BIT = 0x8, /* interface clear interrupt */ + TNT_EOS_BIT = 0x10, /* end of string */ + TNT_NL_BIT = 0x20, /* new line receive */ + TNT_STBO_BIT = 0x40, /* status byte out */ + TNT_NBA_BIT = 0x80, /* new byte available */ +}; + +/* ISR3 -- Interrupt Status Register 3 (read only) */ +enum isr3_bits { + HR_DONE = (1 << 0), /* transfer done */ + HR_TLCI = (1 << 1), /* isr0, isr1, or isr2 interrupt asserted */ + HR_NEF = (1 << 2), /* NOT empty fifo */ + HR_NFF = (1 << 3), /* NOT full fifo */ + HR_STOP = (1 << 4), /* fifo empty or STOP command issued */ + HR_SRQI_CIC = (1 << 5), /* SRQ asserted and we are CIC (500x only?)*/ + HR_INTR = (1 << 7), /* isr3 interrupt active */ +}; + +enum keyreg_bits { + MSTD = 0x20, /* enable 350ns T1 delay */ +}; + +/* STS1 -- Status Register 1 (read only) */ +enum sts1_bits { + S_DONE = 0x80, /* DMA done */ + S_SC = 0x40, /* is system controller */ + S_IN = 0x20, /* DMA in (to memory) */ + S_DRQ = 0x10, /* DRQ line (for diagnostics) */ + S_STOP = 0x08, /* DMA stopped */ + S_NDAV = 0x04, /* inverse of DAV */ + S_HALT = 0x02, /* status of transfer machine */ + S_GSYNC = 0x01, /* indicates if GPIB is in sync w I/O */ +}; + +/* STS2 -- Status Register 2 */ +enum sts2_bits { + AFFN = (1 << 3), /* "A full FIFO NOT" (0=FIFO full) */ + AEFN = (1 << 2), /* "A empty FIFO NOT" (0=FIFO empty) */ + BFFN = (1 << 1), /* "B full FIFO NOT" (0=FIFO full) */ + BEFN = (1 << 0), /* "B empty FIFO NOT" (0=FIFO empty) */ +}; + +// Auxiliary commands +enum tnt4882_aux_cmds { + AUX_9914 = 0x15, // switch to 9914 mode + AUX_REQT = 0x18, + AUX_REQF = 0x19, + AUX_PAGEIN = 0x50, // page in alternate registers + AUX_HLDI = 0x51, // rfd holdoff immediately + AUX_CLEAR_END = 0x55, + AUX_7210 = 0x99, // switch to 7210 mode +}; + +enum tnt4882_aux_regs { + AUXRG = 0x40, + AUXRI = 0xe0, +}; + +enum auxg_bits { + /* no talking when no listeners bit (prevents bus errors when data written at wrong time) */ + NTNL_BIT = 0x8, + RPP2_BIT = 0x4, /* set/clear local rpp message */ + CHES_BIT = 0x1, /*clear holdoff on end select bit*/ +}; + +enum auxi_bits { + SISB = 0x1, // static interrupt bits (don't clear isr1, isr2 on read) + PP2 = 0x4, // ignore remote parallel poll configuration + USTD = 0x8, // ultra short (1100 nanosec) T1 delay +}; + +enum sasr_bits { + ACRDY_BIT = 0x4, /* acceptor ready state */ + ADHS_BIT = 0x8, /* acceptor data holdoff state */ + ANHS2_BIT = 0x10, /* acceptor not ready holdoff immediately state */ + ANHS1_BIT = 0x20, /* acceptor not ready holdoff state */ + AEHS_BIT = 0x40, /* acceptor end holdoff state */ +}; + +#endif // _TNT4882_REGISTERS_H diff --git a/drivers/gpib/ines/Makefile b/drivers/gpib/ines/Makefile new file mode 100644 index 000000000000..88241f15ecea --- /dev/null +++ b/drivers/gpib/ines/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_GPIB_INES) += ines_gpib.o + + diff --git a/drivers/gpib/ines/ines.h b/drivers/gpib/ines/ines.h new file mode 100644 index 000000000000..6ad57e9a1216 --- /dev/null +++ b/drivers/gpib/ines/ines.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * Header for ines GPIB boards + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _INES_GPIB_H +#define _INES_GPIB_H + +#include "nec7210.h" +#include "gpibP.h" +#include "plx9050.h" +#include "amcc5920.h" +#include "quancom_pci.h" +#include + +enum ines_pci_chip { + PCI_CHIP_NONE, + PCI_CHIP_PLX9050, + PCI_CHIP_AMCC5920, + PCI_CHIP_QUANCOM, + PCI_CHIP_QUICKLOGIC5030, +}; + +struct ines_priv { + struct nec7210_priv nec7210_priv; + struct pci_dev *pci_device; + // base address for plx9052 pci chip + unsigned long plx_iobase; + // base address for amcc5920 pci chip + unsigned long amcc_iobase; + unsigned int irq; + enum ines_pci_chip pci_chip_type; + u8 extend_mode_bits; +}; + +/* inb/outb wrappers */ +static inline unsigned int ines_inb(struct ines_priv *priv, unsigned int register_number) +{ + return inb(priv->nec7210_priv.iobase + + register_number * priv->nec7210_priv.offset); +} + +static inline void ines_outb(struct ines_priv *priv, unsigned int value, + unsigned int register_number) +{ + outb(value, priv->nec7210_priv.iobase + + register_number * priv->nec7210_priv.offset); +} + +enum ines_regs { + // read + FIFO_STATUS = 0x8, + ISR3 = 0x9, + ISR4 = 0xa, + IN_FIFO_COUNT = 0x10, + OUT_FIFO_COUNT = 0x11, + EXTEND_STATUS = 0xf, + + // write + XDMA_CONTROL = 0x8, + IMR3 = ISR3, + IMR4 = ISR4, + IN_FIFO_WATERMARK = IN_FIFO_COUNT, + OUT_FIFO_WATERMARK = OUT_FIFO_COUNT, + EXTEND_MODE = 0xf, + + // read-write + XFER_COUNT_LOWER = 0xb, + XFER_COUNT_UPPER = 0xc, + BUS_CONTROL_MONITOR = 0x13, +}; + +enum isr3_imr3_bits { + HW_TIMEOUT_BIT = 0x1, + XFER_COUNT_BIT = 0x2, + CMD_RECEIVED_BIT = 0x4, + TCT_RECEIVED_BIT = 0x8, + IFC_ACTIVE_BIT = 0x10, + ATN_ACTIVE_BIT = 0x20, + FIFO_ERROR_BIT = 0x40, +}; + +enum isr4_imr4_bits { + IN_FIFO_WATERMARK_BIT = 0x1, + OUT_FIFO_WATERMARK_BIT = 0x2, + IN_FIFO_FULL_BIT = 0x4, + OUT_FIFO_EMPTY_BIT = 0x8, + IN_FIFO_READY_BIT = 0x10, + OUT_FIFO_READY_BIT = 0x20, + IN_FIFO_EXIT_WATERMARK_BIT = 0x40, + OUT_FIFO_EXIT_WATERMARK_BIT = 0x80, +}; + +enum extend_mode_bits { + TR3_TRIG_ENABLE_BIT = 0x1, // enable generation of trigger pulse T/R3 pin + // clear message available status bit when chip writes byte with EOI true + MAV_ENABLE_BIT = 0x2, + EOS1_ENABLE_BIT = 0x4, // enable eos register 1 + EOS2_ENABLE_BIT = 0x8, // enable eos register 2 + EOIDIS_BIT = 0x10, // disable EOI interrupt when doing rfd holdoff on end? + XFER_COUNTER_ENABLE_BIT = 0x20, + XFER_COUNTER_OUTPUT_BIT = 0x40, // use counter for output, clear for input + // when xfer counter hits 0, assert EOI on write or RFD holdoff on read + LAST_BYTE_HANDLING_BIT = 0x80, +}; + +enum extend_status_bits { + OUTPUT_MESSAGE_IN_PROGRESS_BIT = 0x1, + SCSEL_BIT = 0x2, // statue of SCSEL pin + LISTEN_DISABLED = 0x4, + IN_FIFO_EMPTY_BIT = 0x8, + OUT_FIFO_FULL_BIT = 0x10, +}; + +// ines adds fifo enable bits to address mode register +enum ines_admr_bits { + IN_FIFO_ENABLE_BIT = 0x8, + OUT_FIFO_ENABLE_BIT = 0x4, +}; + +enum xdma_control_bits { + DMA_OUTPUT_BIT = 0x1, // use dma for output, clear for input + ENABLE_SYNC_DMA_BIT = 0x2, + DMA_ACCESS_EVERY_CYCLE = 0x4, // dma accesses fifo every cycle, clear for every other cycle + DMA_16BIT = 0x8, // clear for 8 bit transfers +}; + +enum bus_control_monitor_bits { + BCM_DAV_BIT = 0x1, + BCM_NRFD_BIT = 0x2, + BCM_NDAC_BIT = 0x4, + BCM_IFC_BIT = 0x8, + BCM_ATN_BIT = 0x10, + BCM_SRQ_BIT = 0x20, + BCM_REN_BIT = 0x40, + BCM_EOI_BIT = 0x80, +}; + +enum ines_aux_reg_bits { + INES_AUXD = 0x40, +}; + +enum ines_aux_cmds { + INES_RFD_HLD_IMMEDIATE = 0x4, + INES_AUX_CLR_OUT_FIFO = 0x5, + INES_AUX_CLR_IN_FIFO = 0x6, + INES_AUX_XMODE = 0xa, +}; + +enum ines_auxd_bits { + INES_FOLLOWING_T1_MASK = 0x3, + INES_FOLLOWING_T1_500ns = 0x0, + INES_FOLLOWING_T1_350ns = 0x1, + INES_FOLLOWING_T1_250ns = 0x2, + INES_INITIAL_TI_MASK = 0xc, + INES_INITIAL_T1_2000ns = 0x0, + INES_INITIAL_T1_1100ns = 0x4, + INES_INITIAL_T1_700ns = 0x8, + INES_T6_2us = 0x0, + INES_T6_50us = 0x10, +}; + +#endif // _INES_GPIB_H diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c new file mode 100644 index 000000000000..a3cf846fd0f9 --- /dev/null +++ b/drivers/gpib/ines/ines_gpib.c @@ -0,0 +1,1500 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 1999 Axel Dziemba (axel.dziemba@ines.de) + * (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include "ines.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gpib_pci_ids.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for Ines iGPIB 72010"); + +static irqreturn_t ines_interrupt(struct gpib_board *board); + +static int ines_line_status(const struct gpib_board *board) +{ + int status = VALID_ALL; + int bcm_bits; + struct ines_priv *ines_priv; + + ines_priv = board->private_data; + + bcm_bits = ines_inb(ines_priv, BUS_CONTROL_MONITOR); + + if (bcm_bits & BCM_REN_BIT) + status |= BUS_REN; + if (bcm_bits & BCM_IFC_BIT) + status |= BUS_IFC; + if (bcm_bits & BCM_SRQ_BIT) + status |= BUS_SRQ; + if (bcm_bits & BCM_EOI_BIT) + status |= BUS_EOI; + if (bcm_bits & BCM_NRFD_BIT) + status |= BUS_NRFD; + if (bcm_bits & BCM_NDAC_BIT) + status |= BUS_NDAC; + if (bcm_bits & BCM_DAV_BIT) + status |= BUS_DAV; + if (bcm_bits & BCM_ATN_BIT) + status |= BUS_ATN; + + return status; +} + +static void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count) +{ + if (count > 0xffff) { + pr_err("bug! tried to set xfer counter > 0xffff\n"); + return; + } + ines_outb(priv, (count >> 8) & 0xff, XFER_COUNT_UPPER); + ines_outb(priv, count & 0xff, XFER_COUNT_LOWER); +} + +static int ines_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + + if (nano_sec <= 250) { + write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_250ns | + INES_INITIAL_T1_2000ns, AUXMR); + retval = 250; + } else if (nano_sec <= 350) { + write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_350ns | + INES_INITIAL_T1_2000ns, AUXMR); + retval = 350; + } else { + write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_500ns | + INES_INITIAL_T1_2000ns, AUXMR); + retval = 500; + } + + return retval; +} + +static inline unsigned short num_in_fifo_bytes(struct ines_priv *ines_priv) +{ + return ines_inb(ines_priv, IN_FIFO_COUNT); +} + +static ssize_t pio_read(struct gpib_board *board, struct ines_priv *ines_priv, u8 *buffer, + size_t length, size_t *nbytes) +{ + ssize_t retval = 0; + unsigned int num_fifo_bytes, i; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + + *nbytes = 0; + while (*nbytes < length) { + if (wait_event_interruptible(board->wait, + num_in_fifo_bytes(ines_priv) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + return -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + return -EINTR; + + num_fifo_bytes = num_in_fifo_bytes(ines_priv); + if (num_fifo_bytes + *nbytes > length) + num_fifo_bytes = length - *nbytes; + + for (i = 0; i < num_fifo_bytes; i++) + buffer[(*nbytes)++] = read_byte(nec_priv, DIR); + if (test_bit(RECEIVED_END_BN, &nec_priv->state) && + num_in_fifo_bytes(ines_priv) == 0) + break; + if (need_resched()) + schedule(); + } + /* make sure RECEIVED_END is in sync */ + ines_interrupt(board); + return retval; +} + +static int ines_accel_read(struct gpib_board *board, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + int counter_setting; + + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + + // clear in fifo + nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, 0); + nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, IN_FIFO_ENABLE_BIT); + + ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT; + ines_priv->extend_mode_bits &= ~XFER_COUNTER_OUTPUT_BIT & ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + counter_setting = length - num_in_fifo_bytes(ines_priv); + if (counter_setting > 0) { + ines_set_xfer_counter(ines_priv, length); + ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + // holdoff on END + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + /* release rfd holdoff */ + write_byte(nec_priv, AUX_FH, AUXMR); + } + + retval = pio_read(board, ines_priv, buffer, length, bytes_read); + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + if (retval < 0) { + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + return retval; + } + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + + return retval; +} + +static const int out_fifo_size = 0xff; + +static inline unsigned short num_out_fifo_bytes(struct ines_priv *ines_priv) +{ + return ines_inb(ines_priv, OUT_FIFO_COUNT); +} + +static int ines_write_wait(struct gpib_board *board, struct ines_priv *ines_priv, + unsigned int fifo_threshold) +{ + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + + // wait until byte is ready to be sent + if (wait_event_interruptible(board->wait, + num_out_fifo_bytes(ines_priv) < fifo_threshold || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + return -ERESTARTSYS; + + if (test_bit(BUS_ERROR_BN, &nec_priv->state)) + return -EIO; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + return -EINTR; + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + + return 0; +} + +static int ines_accel_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + size_t count = 0; + ssize_t retval = 0; + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + unsigned int num_bytes, i; + + *bytes_written = 0; + // clear out fifo + nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, 0); + nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, OUT_FIFO_ENABLE_BIT); + + ines_priv->extend_mode_bits |= XFER_COUNTER_OUTPUT_BIT; + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_priv->extend_mode_bits &= ~LAST_BYTE_HANDLING_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + ines_set_xfer_counter(ines_priv, length); + if (send_eoi) + ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT; + ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + + while (count < length) { + retval = ines_write_wait(board, ines_priv, out_fifo_size); + if (retval < 0) + break; + + num_bytes = out_fifo_size - num_out_fifo_bytes(ines_priv); + if (num_bytes + count > length) + num_bytes = length - count; + for (i = 0; i < num_bytes; i++) + write_byte(nec_priv, buffer[count++], CDOR); + } + if (retval < 0) { + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + *bytes_written = length - num_out_fifo_bytes(ines_priv); + return retval; + } + // wait last byte has been sent + retval = ines_write_wait(board, ines_priv, 1); + ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + *bytes_written = length - num_out_fifo_bytes(ines_priv); + + return retval; +} + +static irqreturn_t ines_pci_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + struct ines_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + + if (priv->pci_chip_type == PCI_CHIP_QUANCOM) { + if ((inb(nec_priv->iobase + + QUANCOM_IRQ_CONTROL_STATUS_REG) & + QUANCOM_IRQ_ASSERTED_BIT)) + outb(QUANCOM_IRQ_ENABLE_BIT, nec_priv->iobase + + QUANCOM_IRQ_CONTROL_STATUS_REG); + } + + return ines_interrupt(board); +} + +static irqreturn_t ines_interrupt(struct gpib_board *board) +{ + struct ines_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + unsigned int isr3_bits, isr4_bits; + unsigned long flags; + int wake = 0; + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_interrupt(board, nec_priv); + isr3_bits = ines_inb(priv, ISR3); + isr4_bits = ines_inb(priv, ISR4); + if (isr3_bits & IFC_ACTIVE_BIT) { + push_gpib_event(board, EVENT_IFC); + wake++; + } + if (isr3_bits & FIFO_ERROR_BIT) + dev_err(board->gpib_dev, "fifo error\n"); + if (isr3_bits & XFER_COUNT_BIT) + wake++; + + if (isr4_bits & (IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT | OUT_FIFO_WATERMARK_BIT | + OUT_FIFO_EMPTY_BIT)) + wake++; + + if (wake) + wake_up_interruptible(&board->wait); + spin_unlock_irqrestore(&board->spinlock, flags); + return IRQ_HANDLED; +} + +static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_config *config); +static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config); +static int ines_isa_attach(struct gpib_board *board, const struct gpib_board_config *config); + +static void ines_pci_detach(struct gpib_board *board); +static void ines_isa_detach(struct gpib_board *board); + +enum ines_pci_vendor_ids { + PCI_VENDOR_ID_INES_QUICKLOGIC = 0x16da +}; + +enum ines_pci_device_ids { + PCI_DEVICE_ID_INES_GPIB_AMCC = 0x8507, + PCI_DEVICE_ID_INES_GPIB_QL5030 = 0x11, +}; + +enum ines_pci_subdevice_ids { + PCI_SUBDEVICE_ID_INES_GPIB = 0x1072 +}; + +static struct pci_device_id ines_pci_table[] = { + {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, + {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC, + PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, + {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, + PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0}, + {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)}, + {0} +}; +MODULE_DEVICE_TABLE(pci, ines_pci_table); + +struct ines_pci_id { + unsigned int vendor_id; + unsigned int device_id; + int subsystem_vendor_id; + int subsystem_device_id; + unsigned int gpib_region; + unsigned int io_offset; + enum ines_pci_chip pci_chip_type; +}; + +static struct ines_pci_id pci_ids[] = { + {.vendor_id = PCI_VENDOR_ID_PLX, + .device_id = PCI_DEVICE_ID_PLX_9050, + .subsystem_vendor_id = PCI_VENDOR_ID_PLX, + .subsystem_device_id = PCI_SUBDEVICE_ID_INES_GPIB, + .gpib_region = 2, + .io_offset = 1, + .pci_chip_type = PCI_CHIP_PLX9050, + }, + {.vendor_id = PCI_VENDOR_ID_AMCC, + .device_id = PCI_DEVICE_ID_INES_GPIB_AMCC, + .subsystem_vendor_id = PCI_VENDOR_ID_AMCC, + .subsystem_device_id = PCI_SUBDEVICE_ID_INES_GPIB, + .gpib_region = 1, + .io_offset = 1, + .pci_chip_type = PCI_CHIP_AMCC5920, + }, + {.vendor_id = PCI_VENDOR_ID_INES_QUICKLOGIC, + .device_id = PCI_DEVICE_ID_INES_GPIB_QL5030, + .subsystem_vendor_id = PCI_VENDOR_ID_INES_QUICKLOGIC, + .subsystem_device_id = PCI_DEVICE_ID_INES_GPIB_QL5030, + .gpib_region = 1, + .io_offset = 1, + .pci_chip_type = PCI_CHIP_QUICKLOGIC5030, + }, + {.vendor_id = PCI_VENDOR_ID_QUANCOM, + .device_id = PCI_DEVICE_ID_QUANCOM_GPIB, + .subsystem_vendor_id = -1, + .subsystem_device_id = -1, + .gpib_region = 0, + .io_offset = 4, + .pci_chip_type = PCI_CHIP_QUANCOM, + }, +}; + +static const int num_pci_chips = ARRAY_SIZE(pci_ids); + +// wrappers for interface functions +static int ines_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + struct ines_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + ssize_t retval; + int dummy; + + retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); + if (retval < 0) { + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + nec7210_read_data_in(board, nec_priv, &dummy); + } + return retval; +} + +static int ines_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int ines_command(struct gpib_board *board, u8 *buffer, size_t length, size_t *bytes_written) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int ines_take_control(struct gpib_board *board, int synchronous) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int ines_go_to_standby(struct gpib_board *board) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static int ines_request_system_control(struct gpib_board *board, int request_control) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_request_system_control(board, &priv->nec7210_priv, request_control); +} + +static void ines_interface_clear(struct gpib_board *board, int assert) +{ + struct ines_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void ines_remote_enable(struct gpib_board *board, int enable) +{ + struct ines_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int ines_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void ines_disable_eos(struct gpib_board *board) +{ + struct ines_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int ines_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int ines_primary_address(struct gpib_board *board, unsigned int address) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int ines_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int ines_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void ines_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + struct ines_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); +} + +static void ines_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct ines_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void ines_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct ines_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static u8 ines_serial_poll_status(struct gpib_board *board) +{ + struct ines_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static void ines_return_to_local(struct gpib_board *board) +{ + struct ines_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +static struct gpib_interface ines_pci_unaccel_interface = { + .name = "ines_pci_unaccel", + .attach = ines_pci_attach, + .detach = ines_pci_detach, + .read = ines_read, + .write = ines_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + +static struct gpib_interface ines_pci_interface = { + .name = "ines_pci", + .attach = ines_pci_accel_attach, + .detach = ines_pci_detach, + .read = ines_accel_read, + .write = ines_accel_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + +static struct gpib_interface ines_pci_accel_interface = { + .name = "ines_pci_accel", + .attach = ines_pci_accel_attach, + .detach = ines_pci_detach, + .read = ines_accel_read, + .write = ines_accel_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + +static struct gpib_interface ines_isa_interface = { + .name = "ines_isa", + .attach = ines_isa_attach, + .detach = ines_isa_detach, + .read = ines_accel_read, + .write = ines_accel_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + +static int ines_allocate_private(struct gpib_board *board) +{ + struct ines_priv *priv; + + board->private_data = kmalloc(sizeof(struct ines_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + priv = board->private_data; + memset(priv, 0, sizeof(struct ines_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +static void ines_free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int ines_generic_attach(struct gpib_board *board) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + + if (ines_allocate_private(board)) + return -ENOMEM; + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + nec_priv->read_byte = nec7210_ioport_read_byte; + nec_priv->write_byte = nec7210_ioport_write_byte; + nec_priv->offset = 1; + nec_priv->type = IGPIB7210; + ines_priv->pci_chip_type = PCI_CHIP_NONE; + + return 0; +} + +static void ines_online(struct ines_priv *ines_priv, const struct gpib_board *board, int use_accel) +{ + struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; + + /* ines doesn't seem to use internal count register */ + write_byte(nec_priv, ICR | 0, AUXMR); + + write_byte(nec_priv, INES_AUX_XMODE, AUXMR); + write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + write_byte(nec_priv, INES_AUXD | 0, AUXMR); + ines_outb(ines_priv, 0, XDMA_CONTROL); + ines_priv->extend_mode_bits = 0; + ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); + if (use_accel) { + ines_outb(ines_priv, 0x80, OUT_FIFO_WATERMARK); + ines_outb(ines_priv, 0x80, IN_FIFO_WATERMARK); + ines_outb(ines_priv, IFC_ACTIVE_BIT | ATN_ACTIVE_BIT | + FIFO_ERROR_BIT | XFER_COUNT_BIT, IMR3); + ines_outb(ines_priv, IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT | + OUT_FIFO_WATERMARK_BIT | OUT_FIFO_EMPTY_BIT, IMR4); + } else { + nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT | OUT_FIFO_ENABLE_BIT, 0); + ines_outb(ines_priv, IFC_ACTIVE_BIT | FIFO_ERROR_BIT, IMR3); + ines_outb(ines_priv, 0, IMR4); + } + + nec7210_board_online(nec_priv, board); + if (use_accel) + nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, 0); +} + +static int ines_common_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + struct ines_pci_id found_id; + unsigned int i; + struct pci_dev *pdev; + + memset(&found_id, 0, sizeof(found_id)); + + retval = ines_generic_attach(board); + if (retval) + return retval; + + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + + // find board + ines_priv->pci_device = NULL; + for (i = 0; i < num_pci_chips && !ines_priv->pci_device; i++) { + pdev = NULL; + do { + if (pci_ids[i].subsystem_vendor_id >= 0 && + pci_ids[i].subsystem_device_id >= 0) + pdev = pci_get_subsys(pci_ids[i].vendor_id, pci_ids[i].device_id, + pci_ids[i].subsystem_vendor_id, + pci_ids[i].subsystem_device_id, pdev); + else + pdev = pci_get_device(pci_ids[i].vendor_id, pci_ids[i].device_id, + pdev); + if (!pdev) + break; + if (config->pci_bus >= 0 && config->pci_bus != pdev->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(pdev->devfn)) + continue; + found_id = pci_ids[i]; + ines_priv->pci_device = pdev; + break; + } while (1); + } + if (!ines_priv->pci_device) { + dev_err(board->gpib_dev, "could not find ines PCI board\n"); + return -1; + } + + if (pci_enable_device(ines_priv->pci_device)) { + dev_err(board->gpib_dev, "error enabling pci device\n"); + return -1; + } + + if (pci_request_regions(ines_priv->pci_device, DRV_NAME)) + return -1; + nec_priv->iobase = pci_resource_start(ines_priv->pci_device, + found_id.gpib_region); + + ines_priv->pci_chip_type = found_id.pci_chip_type; + nec_priv->offset = found_id.io_offset; + switch (ines_priv->pci_chip_type) { + case PCI_CHIP_PLX9050: + ines_priv->plx_iobase = pci_resource_start(ines_priv->pci_device, 1); + break; + case PCI_CHIP_AMCC5920: + ines_priv->amcc_iobase = pci_resource_start(ines_priv->pci_device, 0); + break; + case PCI_CHIP_QUANCOM: + break; + case PCI_CHIP_QUICKLOGIC5030: + break; + default: + dev_err(board->gpib_dev, "unspecified chip type? (bug)\n"); + nec_priv->iobase = 0; + pci_release_regions(ines_priv->pci_device); + return -1; + } + + nec7210_board_reset(nec_priv, board); +#ifdef QUANCOM_PCI + if (ines_priv->pci_chip_type == PCI_CHIP_QUANCOM) { + /* change interrupt polarity */ + nec_priv->auxb_bits |= HR_INV; + ines_outb(ines_priv, nec_priv->auxb_bits, AUXMR); + } +#endif + isr_flags |= IRQF_SHARED; + if (request_irq(ines_priv->pci_device->irq, ines_pci_interrupt, isr_flags, + DRV_NAME, board)) { + dev_err(board->gpib_dev, "can't request IRQ %d\n", ines_priv->pci_device->irq); + return -1; + } + ines_priv->irq = ines_priv->pci_device->irq; + + // enable interrupts on pci chip + switch (ines_priv->pci_chip_type) { + case PCI_CHIP_PLX9050: + outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT, + ines_priv->plx_iobase + PLX9050_INTCSR_REG); + break; + case PCI_CHIP_AMCC5920: + { + static const int region = 1; + static const int num_wait_states = 7; + u32 bits; + + bits = amcc_prefetch_bits(region, PREFETCH_DISABLED); + bits |= amcc_PTADR_mode_bit(region); + bits |= amcc_disable_write_fifo_bit(region); + bits |= amcc_wait_state_bits(region, num_wait_states); + outl(bits, ines_priv->amcc_iobase + AMCC_PASS_THRU_REG); + outl(AMCC_ADDON_INTR_ENABLE_BIT, ines_priv->amcc_iobase + AMCC_INTCS_REG); + } + break; + case PCI_CHIP_QUANCOM: + outb(QUANCOM_IRQ_ENABLE_BIT, nec_priv->iobase + + QUANCOM_IRQ_CONTROL_STATUS_REG); + break; + case PCI_CHIP_QUICKLOGIC5030: + break; + default: + dev_err(board->gpib_dev, "unspecified chip type? (bug)\n"); + return -1; + } + + return 0; +} + +static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pci_attach(board, config); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 0); + + return 0; +} + +static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pci_attach(board, config); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 1); + + return 0; +} + +static const int ines_isa_iosize = 0x20; + +static int ines_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + int retval; + + retval = ines_generic_attach(board); + if (retval) + return retval; + + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + + if (!request_region(config->ibbase, ines_isa_iosize, DRV_NAME)) { + dev_err(board->gpib_dev, "ioports at 0x%x already in use\n", + config->ibbase); + return -EBUSY; + } + nec_priv->iobase = config->ibbase; + nec_priv->offset = 1; + nec7210_board_reset(nec_priv, board); + if (request_irq(config->ibirq, ines_pci_interrupt, isr_flags, DRV_NAME, board)) { + dev_err(board->gpib_dev, "failed to allocate IRQ %d\n", config->ibirq); + return -1; + } + ines_priv->irq = config->ibirq; + ines_online(ines_priv, board, 1); + return 0; +} + +static void ines_pci_detach(struct gpib_board *board) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (ines_priv) { + nec_priv = &ines_priv->nec7210_priv; + if (ines_priv->irq) { + // disable interrupts + switch (ines_priv->pci_chip_type) { + case PCI_CHIP_AMCC5920: + if (ines_priv->plx_iobase) + outl(0, ines_priv->plx_iobase + PLX9050_INTCSR_REG); + break; + case PCI_CHIP_QUANCOM: + if (nec_priv->iobase) + outb(0, nec_priv->iobase + + QUANCOM_IRQ_CONTROL_STATUS_REG); + break; + default: + break; + } + free_irq(ines_priv->irq, board); + } + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + pci_release_regions(ines_priv->pci_device); + } + if (ines_priv->pci_device) + pci_dev_put(ines_priv->pci_device); + } + ines_free_private(board); +} + +static void ines_isa_detach(struct gpib_board *board) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (ines_priv) { + nec_priv = &ines_priv->nec7210_priv; + if (ines_priv->irq) + free_irq(ines_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region(nec_priv->iobase, ines_isa_iosize); + } + } + ines_free_private(board); +} + +static int ines_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static struct pci_driver ines_pci_driver = { + .name = "ines_gpib", + .id_table = ines_pci_table, + .probe = &ines_pci_probe +}; + +#ifdef CONFIG_GPIB_PCMCIA + +#include +#include +#include +#include + +#include +#include +#include + +static const int ines_pcmcia_iosize = 0x20; + +/* + * The event() function is this driver's Card Services event handler. + * It will be called by Card Services when an appropriate card status + * event is received. The config() and release() entry points are + * used to configure or release a socket, in response to card insertion + * and ejection events. They are invoked from the gpib event + * handler. + */ + +static int ines_gpib_config(struct pcmcia_device *link); +static void ines_gpib_release(struct pcmcia_device *link); +static int ines_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config); +static int ines_pcmcia_accel_attach(struct gpib_board *board, + const struct gpib_board_config *config); +static void ines_pcmcia_detach(struct gpib_board *board); +static int ines_common_pcmcia_attach(struct gpib_board *board); +/* + * A linked list of "instances" of the gpib device. Each actual + * PCMCIA card corresponds to one device instance, and is described + * by one dev_link_t structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the + * memory card driver uses an array of dev_link_t pointers, where minor + * device numbers are used to derive the corresponding array index. + */ + +static struct pcmcia_device *curr_dev; + +/* + * A dev_link_t structure has fields for most things that are needed + * to keep track of a socket, but there will usually be some device + * specific information that also needs to be kept track of. The + * 'priv' pointer in a dev_link_t structure can be used to point to + * a device-specific private data structure, like this. + * + * A driver needs to provide a dev_node_t structure for each device + * on a card. In some cases, there is only one device per card (for + * example, ethernet cards, modems). In other cases, there may be + * many actual or logical devices (SCSI adapters, memory cards with + * multiple partitions). The dev_node_t structures need to be kept + * in a linked list starting at the 'dev' field of a dev_link_t + * structure. We allocate them in the card's private data structure, + * because they generally can't be allocated dynamically. + */ + +struct local_info { + struct pcmcia_device *p_dev; + struct gpib_board *dev; + u_short manfid; + u_short cardid; +}; + +/* + * gpib_attach() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ +static int ines_gpib_probe(struct pcmcia_device *link) +{ + struct local_info *info; + +// int ret, i; + + /* Allocate space for private device-specific data */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + + /* The io structure describes IO port mapping */ + link->resource[0]->end = 32; + link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + link->io_lines = 5; + + /* General socket configuration */ + link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + /* Register with Card Services */ + curr_dev = link; + return ines_gpib_config(link); +} + +/* + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ +static void ines_gpib_remove(struct pcmcia_device *link) +{ + struct local_info *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + if (info->dev) + ines_pcmcia_detach(info->dev); + ines_gpib_release(link); + + //free_netdev(dev); + kfree(info); +} + +static int ines_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) +{ + return pcmcia_request_io(link); +} + +/* + * gpib_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * device available to the system. + */ +static int ines_gpib_config(struct pcmcia_device *link) +{ + int retval; + void __iomem *virt; + + retval = pcmcia_loop_config(link, &ines_gpib_config_iteration, NULL); + if (retval) { + dev_warn(&link->dev, "no configuration found\n"); + ines_gpib_release(link); + return -ENODEV; + } + + dev_dbg(&link->dev, "ines_cs: manufacturer: 0x%x card: 0x%x\n", + link->manf_id, link->card_id); + + /* + * for the ines card we have to setup the configuration registers in + * attribute memory here + */ + link->resource[2]->flags |= WIN_MEMORY_TYPE_AM | WIN_DATA_WIDTH_8 | WIN_ENABLE; + link->resource[2]->end = 0x1000; + retval = pcmcia_request_window(link, link->resource[2], 250); + if (retval) { + dev_warn(&link->dev, "pcmcia_request_window failed\n"); + ines_gpib_release(link); + return -ENODEV; + } + retval = pcmcia_map_mem_page(link, link->resource[2], 0); + if (retval) { + dev_warn(&link->dev, "pcmcia_map_mem_page failed\n"); + ines_gpib_release(link); + return -ENODEV; + } + virt = ioremap(link->resource[2]->start, resource_size(link->resource[2])); + writeb((link->resource[2]->start >> 2) & 0xff, virt + 0xf0); // IOWindow base + iounmap(virt); + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + retval = pcmcia_enable_device(link); + if (retval) { + ines_gpib_release(link); + return -ENODEV; + } + return 0; +} /* gpib_config */ + +/* + * After a card is removed, gpib_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ + +static void ines_gpib_release(struct pcmcia_device *link) +{ + pcmcia_disable_device(link); +} /* gpib_release */ + +static int ines_gpib_suspend(struct pcmcia_device *link) +{ + //struct local_info *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + if (link->open) + dev_err(&link->dev, "Device still open\n"); + //netif_device_detach(dev); + + return 0; +} + +static int ines_gpib_resume(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + /*if (link->open) { + * ni_gpib_probe(dev); / really? + * //netif_device_attach(dev); + *} + */ + return ines_gpib_config(link); +} + +static struct pcmcia_device_id ines_pcmcia_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x01b4, 0x4730), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, ines_pcmcia_ids); + +static struct pcmcia_driver ines_gpib_cs_driver = { + .owner = THIS_MODULE, + .name = "ines_gpib_cs", + .id_table = ines_pcmcia_ids, + .probe = ines_gpib_probe, + .remove = ines_gpib_remove, + .suspend = ines_gpib_suspend, + .resume = ines_gpib_resume, +}; + +static void ines_pcmcia_cleanup_module(void) +{ + pcmcia_unregister_driver(&ines_gpib_cs_driver); +} + +static struct gpib_interface ines_pcmcia_unaccel_interface = { + .name = "ines_pcmcia_unaccel", + .attach = ines_pcmcia_attach, + .detach = ines_pcmcia_detach, + .read = ines_read, + .write = ines_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + +static struct gpib_interface ines_pcmcia_accel_interface = { + .name = "ines_pcmcia_accel", + .attach = ines_pcmcia_accel_attach, + .detach = ines_pcmcia_detach, + .read = ines_accel_read, + .write = ines_accel_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + +static struct gpib_interface ines_pcmcia_interface = { + .name = "ines_pcmcia", + .attach = ines_pcmcia_accel_attach, + .detach = ines_pcmcia_detach, + .read = ines_accel_read, + .write = ines_accel_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + +static irqreturn_t ines_pcmcia_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + + return ines_interrupt(board); +} + +static int ines_common_pcmcia_attach(struct gpib_board *board) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + int retval; + + if (!curr_dev) { + dev_err(board->gpib_dev, "no ines pcmcia cards found\n"); + return -1; + } + + retval = ines_generic_attach(board); + if (retval) + return retval; + + ines_priv = board->private_data; + nec_priv = &ines_priv->nec7210_priv; + + if (!request_region(curr_dev->resource[0]->start, + resource_size(curr_dev->resource[0]), DRV_NAME)) { + dev_err(board->gpib_dev, "ioports at 0x%lx already in use\n", + (unsigned long)(curr_dev->resource[0]->start)); + return -1; + } + + nec_priv->iobase = curr_dev->resource[0]->start; + + nec7210_board_reset(nec_priv, board); + + if (request_irq(curr_dev->irq, ines_pcmcia_interrupt, IRQF_SHARED, + "pcmcia-gpib", board)) { + dev_err(board->gpib_dev, "can't request IRQ %d\n", curr_dev->irq); + return -1; + } + ines_priv->irq = curr_dev->irq; + + return 0; +} + +static int ines_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pcmcia_attach(board); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 0); + + return 0; +} + +static int ines_pcmcia_accel_attach(struct gpib_board *board, + const struct gpib_board_config *config) +{ + struct ines_priv *ines_priv; + int retval; + + retval = ines_common_pcmcia_attach(board); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_online(ines_priv, board, 1); + + return 0; +} + +static void ines_pcmcia_detach(struct gpib_board *board) +{ + struct ines_priv *ines_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (ines_priv) { + nec_priv = &ines_priv->nec7210_priv; + if (ines_priv->irq) + free_irq(ines_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region(nec_priv->iobase, ines_pcmcia_iosize); + } + } + ines_free_private(board); +} + +#endif /* CONFIG_GPIB_PCMCIA */ + +static int __init ines_init_module(void) +{ + int ret; + + ret = pci_register_driver(&ines_pci_driver); + if (ret) { + pr_err("pci_register_driver failed: error = %d\n", ret); + return ret; + } + + ret = gpib_register_driver(&ines_pci_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pci; + } + + ret = gpib_register_driver(&ines_pci_unaccel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pci_unaccel; + } + + ret = gpib_register_driver(&ines_pci_accel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pci_accel; + } + + ret = gpib_register_driver(&ines_isa_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_isa; + } + +#ifdef CONFIG_GPIB_PCMCIA + ret = gpib_register_driver(&ines_pcmcia_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pcmcia; + } + + ret = gpib_register_driver(&ines_pcmcia_unaccel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pcmcia_unaccel; + } + + ret = gpib_register_driver(&ines_pcmcia_accel_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pcmcia_accel; + } + + ret = pcmcia_register_driver(&ines_gpib_cs_driver); + if (ret) { + pr_err("pcmcia_register_driver failed: error = %d\n", ret); + goto err_pcmcia_driver; + } +#endif + + return 0; + +#ifdef CONFIG_GPIB_PCMCIA +err_pcmcia_driver: + gpib_unregister_driver(&ines_pcmcia_accel_interface); +err_pcmcia_accel: + gpib_unregister_driver(&ines_pcmcia_unaccel_interface); +err_pcmcia_unaccel: + gpib_unregister_driver(&ines_pcmcia_interface); +err_pcmcia: +#endif + gpib_unregister_driver(&ines_isa_interface); +err_isa: + gpib_unregister_driver(&ines_pci_accel_interface); +err_pci_accel: + gpib_unregister_driver(&ines_pci_unaccel_interface); +err_pci_unaccel: + gpib_unregister_driver(&ines_pci_interface); +err_pci: + pci_unregister_driver(&ines_pci_driver); + + return ret; +} + +static void __exit ines_exit_module(void) +{ + gpib_unregister_driver(&ines_pci_interface); + gpib_unregister_driver(&ines_pci_unaccel_interface); + gpib_unregister_driver(&ines_pci_accel_interface); + gpib_unregister_driver(&ines_isa_interface); +#ifdef CONFIG_GPIB_PCMCIA + gpib_unregister_driver(&ines_pcmcia_interface); + gpib_unregister_driver(&ines_pcmcia_unaccel_interface); + gpib_unregister_driver(&ines_pcmcia_accel_interface); + ines_pcmcia_cleanup_module(); +#endif + + pci_unregister_driver(&ines_pci_driver); +} + +module_init(ines_init_module); +module_exit(ines_exit_module); diff --git a/drivers/gpib/lpvo_usb_gpib/Makefile b/drivers/gpib/lpvo_usb_gpib/Makefile new file mode 100644 index 000000000000..360553488e6d --- /dev/null +++ b/drivers/gpib/lpvo_usb_gpib/Makefile @@ -0,0 +1,3 @@ + +obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib.o + diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c new file mode 100644 index 000000000000..dd68c4843490 --- /dev/null +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -0,0 +1,2025 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * This code has been developed at the Department of Physics (University * + * of Florence, Italy) to support in linux-gpib the open usb-gpib adapter * + * implemented at the University of Ljubljana (lpvo.fe.uni-lj.si/gpib) * + * * + * copyright : (C) 2011 Marcello Carla' * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define NAME KBUILD_MODNAME + +/* base module includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpibP.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for LPVO usb devices"); + +/* + * Table of devices that work with this driver. + * + * Currently, only one device is known to be used in the + * lpvo_usb_gpib adapter (FTDI 0403:6001). + * If your adapter uses a different chip, insert a line + * in the following table with proper , . + * + * To have your chip automatically handled by the driver, + * update files "/usr/local/etc/modprobe.d/lpvo_usb_gpib.conf" + * and /usr/local/etc/udev/rules.d/99-lpvo_usb_gpib.rules. + * + */ + +static const struct usb_device_id skel_table[] = { + { USB_DEVICE(0x0403, 0x6001) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, skel_table); + +/* + * *** Diagnostics and Debug *** + * To enable the diagnostic and debug messages either compile with DEBUG set + * or control via the dynamic debug mechanisms. + * The module parameter "debug" controls the sending of debug messages to + * syslog. By default it is set to 0 + * debug = 0: only attach/detach messages are sent + * 1: every action is logged + * 2: extended logging; each single exchanged byte is documented + * (about twice the log volume of [1]) + * To switch debug level: + * At module loading: modprobe lpvo_usb_gpib debug={0,1,2} + * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug + */ + +static int debug; +module_param(debug, int, 0644); + +#define DIA_LOG(level, format, ...) \ + do { if (debug >= (level)) \ + dev_dbg(board->gpib_dev, format, ## __VA_ARGS__); } \ + while (0) + +#define WQT wait_queue_entry_t +#define WQH head +#define WQE entry + +/* standard and extended command sets of the usb-gpib adapter */ + +#define USB_GPIB_ON "\nIB\n" +#define USB_GPIB_OFF "\nIBO\n" +#define USB_GPIB_IBm0 "\nIBm0\n" /* do not assert REN with IFC */ +#define USB_GPIB_IBm1 "\nIBm1\n" /* assert REN with IFC */ +#define USB_GPIB_IBCL "\nIBZ\n" +#define USB_GPIB_STATUS "\nIBS\n" +#define USB_GPIB_READ "\nIB?\n" +#define USB_GPIB_READ_1 "\nIBB\n" +#define USB_GPIB_EOI "\nIBe0\n" +#define USB_GPIB_FTMO "\nIBf0\n" /* disable first byte timeout */ +#define USB_GPIB_TTMOZ "\nIBt0\n" /* disable byte timeout */ + +/* incomplete commands */ + +#define USB_GPIB_BTMO "\nIBt" /* set byte timeout */ +#define USB_GPIB_TTMO "\nIBT" /* set total timeout */ + +#define USB_GPIB_DEBUG_ON "\nIBDE\xAA\n" +#define USB_GPIB_SET_LISTEN "\nIBDT0\n" +#define USB_GPIB_SET_TALK "\nIBDT1\n" +#define USB_GPIB_SET_LINES "\nIBDC.\n" +#define USB_GPIB_SET_DATA "\nIBDM.\n" +#define USB_GPIB_READ_LINES "\nIBD?C\n" +#define USB_GPIB_READ_DATA "\nIBD?M\n" +#define USB_GPIB_READ_BUS "\nIBD??\n" + +/* command sequences */ + +#define USB_GPIB_UNTALK "\nIBC_\n" +#define USB_GPIB_UNLISTEN "\nIBC?\n" + +/* special characters used by the adapter */ + +#define DLE ('\020') +#define STX ('\02') +#define ETX ('\03') +#define ACK ('\06') +#define NODATA ('\03') +#define NODAV ('\011') + +#define IB_BUS_REN 0x01 +#define IB_BUS_IFC 0x02 +#define IB_BUS_NDAC 0x04 +#define IB_BUS_NRFD 0x08 +#define IB_BUS_DAV 0x10 +#define IB_BUS_EOI 0x20 +#define IB_BUS_ATN 0x40 +#define IB_BUS_SRQ 0x80 + +#define INBUF_SIZE 128 + +struct char_buf { /* used by one_char() routine */ + char *inbuf; + int last; + int nchar; +}; + +struct usb_gpib_priv { /* private data to the device */ + u8 eos; /* eos character */ + short eos_flags; /* eos mode */ + int timeout; /* current value for timeout */ + void *dev; /* the usb device private data structure */ +}; + +#define GPIB_DEV (((struct usb_gpib_priv *)board->private_data)->dev) + +static void show_status(struct gpib_board *board) +{ + DIA_LOG(2, "# - buffer_length %d\n", board->buffer_length); + DIA_LOG(2, "# - status %lx\n", board->status); + DIA_LOG(2, "# - use_count %d\n", board->use_count); + DIA_LOG(2, "# - pad %x\n", board->pad); + DIA_LOG(2, "# - sad %x\n", board->sad); + DIA_LOG(2, "# - timeout %d\n", board->usec_timeout); + DIA_LOG(2, "# - ppc %d\n", board->parallel_poll_configuration); + DIA_LOG(2, "# - t1delay %d\n", board->t1_nano_sec); + DIA_LOG(2, "# - online %d\n", board->online); + DIA_LOG(2, "# - autopoll %d\n", board->autospollers); + DIA_LOG(2, "# - autopoll task %p\n", board->autospoll_task); + DIA_LOG(2, "# - minor %d\n", board->minor); + DIA_LOG(2, "# - master %d\n", board->master); + DIA_LOG(2, "# - list %d\n", board->ist); +} + +/* + * GLOBAL VARIABLES: required for + * pairing among gpib minor and usb minor. + * MAX_DEV is the max number of usb-gpib adapters; free + * to change as you like, but no more than 32 + */ + +#define MAX_DEV 8 +static struct usb_interface *lpvo_usb_interfaces[MAX_DEV]; /* registered interfaces */ +static int usb_minors[MAX_DEV]; /* usb minors */ +static int assigned_usb_minors; /* mask of filled slots */ +static struct mutex minors_lock; /* operations on usb_minors are to be protected */ + +/* + * usb-skeleton prototypes + */ + +struct usb_skel; +static ssize_t skel_do_write(struct usb_skel *, const char *, size_t); +static ssize_t skel_do_read(struct usb_skel *, char *, size_t); +static int skel_do_open(struct gpib_board *, int); +static int skel_do_release(struct gpib_board *); + +/* + * usec_diff : take difference in MICROsec between two 'timespec' + * (unix time in sec and NANOsec) + */ + +static inline int usec_diff(struct timespec64 *a, struct timespec64 *b) +{ + return ((a->tv_sec - b->tv_sec) * 1000000 + + (a->tv_nsec - b->tv_nsec) / 1000); +} + +/* + * *** these routines are specific to the usb-gpib adapter *** + */ + +/** + * write_loop() - Send a byte sequence to the adapter + * + * @dev: the private device structure + * @msg: the byte sequence. + * @leng: the byte sequence length. + * + */ + +static int write_loop(void *dev, char *msg, int leng) +{ + return skel_do_write(dev, msg, leng); +} + +/** + * send_command() - Send a byte sequence and return a single byte reply. + * + * @board: the gpib_board_struct data area for this gpib interface + * @msg: the byte sequence. + * @leng: the byte sequence length; can be given as zero and is + * computed automatically, but if 'msg' contains a zero byte, + * it has to be given explicitly. + */ + +static int send_command(struct gpib_board *board, char *msg, int leng) +{ + char buffer[64]; + int nchar; + int retval; + struct timespec64 before, after; + + ktime_get_real_ts64 (&before); + + if (!leng) + leng = strlen(msg); + retval = write_loop(GPIB_DEV, msg, leng); + if (retval < 0) + return retval; + + nchar = skel_do_read(GPIB_DEV, buffer, 64); + + if (nchar < 0) { + dev_err(board->gpib_dev, " return from read: %d\n", nchar); + return nchar; + } else if (nchar != 1) { + dev_err(board->gpib_dev, " Irregular reply to command: %s\n", msg); + return -EIO; + } + ktime_get_real_ts64 (&after); + + DIA_LOG(1, "Sent %d - done %d us.\n", leng, usec_diff(&after, &before)); + + return buffer[0] & 0xff; +} + +/* + * set_control_line() - Set the value of a single gpib control line + * + * @board: the gpib_board_struct data area for this gpib interface + * @line: line mask + * @value: line new value (0/1) + */ + +static int set_control_line(struct gpib_board *board, int line, int value) +{ + char msg[] = USB_GPIB_SET_LINES; + int retval; + int leng = strlen(msg); + + DIA_LOG(1, "setting line %x to %x\n", line, value); + + retval = send_command(board, USB_GPIB_READ_LINES, 0); + + DIA_LOG(1, "old line values: %x\n", retval); + + if (retval == -EIO) + return retval; + + msg[leng - 2] = value ? (retval & ~line) : retval | line; + + retval = send_command(board, msg, 0); + + DIA_LOG(1, "operation result: %x\n", retval); + + return retval; +} + +/* + * one_char() - read one single byte from input buffer + * + * @board: the gpib_board_struct data area for this gpib interface + * @char_buf: the routine private data structure + */ + +static int one_char(struct gpib_board *board, struct char_buf *b) +{ + struct timespec64 before, after; + + if (b->nchar) { + DIA_LOG(2, "-> %x\n", b->inbuf[b->last - b->nchar]); + return b->inbuf[b->last - b->nchar--]; + } + ktime_get_real_ts64 (&before); + b->nchar = skel_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE); + b->last = b->nchar; + ktime_get_real_ts64 (&after); + + DIA_LOG(2, "read %d bytes in %d usec\n", + b->nchar, usec_diff(&after, &before)); + + if (b->nchar > 0) { + DIA_LOG(2, "--> %x\n", b->inbuf[b->last - b->nchar]); + return b->inbuf[b->last - b->nchar--]; + } + return -EIO; +} + +/** + * set_timeout() - set single byte / total timeouts on the adapter + * + * @board: the gpib_board_struct data area for this gpib interface + * + * For sake of speed, the operation is performed only if it + * modifies the current (saved) value. Minimum allowed timeout + * is 30 ms (T30ms -> 8); timeout disable (TNONE -> 0) currently + * not supported. + */ + +static void set_timeout(struct gpib_board *board) +{ + int n, val; + char command[sizeof(USB_GPIB_TTMO) + 6]; + struct usb_gpib_priv *data = board->private_data; + + if (data->timeout == board->usec_timeout) + return; + + n = (board->usec_timeout + 32767) / 32768; + if (n < 2) + n = 2; + + DIA_LOG(1, "Set timeout to %d us -> %d\n", board->usec_timeout, n); + + sprintf(command, "%s%d\n", USB_GPIB_BTMO, n > 255 ? 255 : n); + val = send_command(board, command, 0); + + if (val == ACK) { + if (n > 65535) + n = 65535; + sprintf(command, "%s%d\n", USB_GPIB_TTMO, n); + val = send_command(board, command, 0); + } + + if (val != ACK) + dev_err(board->gpib_dev, "error in timeout set: <%s>\n", command); + else + data->timeout = board->usec_timeout; +} + +/* + * now the standard interface functions - attach and detach + */ + +/** + * usb_gpib_attach() - activate the usb-gpib converter board + * + * @board: the gpib_board_struct data area for this gpib interface + * @config: firmware data, if any (from gpib_config -I ) + * + * The channel name is ttyUSBn, with n=0 by default. Other values for n + * passed with gpib_config -b . + * + * In this routine I trust that when an error code is returned + * detach() will be called. Always. + */ + +static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + int retval, j; + u32 base = config->ibbase; + char *device_path; + int match; + struct usb_device *udev; + + DIA_LOG(0, "Board %p -t %s -m %d -a %p -u %d -l %d -b %d\n", + board, board->interface->name, board->minor, config->device_path, + config->pci_bus, config->pci_slot, base); + + board->private_data = NULL; /* to be sure - we can detach before setting */ + + /* identify device to be attached */ + + mutex_lock(&minors_lock); + + if (config->device_path) { + /* if config->device_path given, try that first */ + for (j = 0 ; j < MAX_DEV ; j++) { + if ((assigned_usb_minors & 1 << j) == 0) + continue; + udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL); + match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev, + config->device_path); + DIA_LOG(1, "dev. %d: minor %d path: %s --> %d\n", j, + lpvo_usb_interfaces[j]->minor, device_path, match); + kfree(device_path); + if (match) + break; + } + } else if (config->pci_bus != -1 && config->pci_slot != -1) { + /* second: look for bus and slot */ + for (j = 0 ; j < MAX_DEV ; j++) { + if ((assigned_usb_minors & 1 << j) == 0) + continue; + udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j, + udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot); + if (config->pci_bus == udev->bus->busnum && + config->pci_slot == udev->devnum) + break; + } + } else { /* last chance: usb_minor, given as ibbase */ + for (j = 0 ; j < MAX_DEV ; j++) { + if (usb_minors[j] == base && assigned_usb_minors & 1 << j) + break; + } + } + mutex_unlock(&minors_lock); + + if (j == MAX_DEV) { + dev_err(board->gpib_dev, "Requested device is not registered.\n"); + return -EIO; + } + + board->private_data = kzalloc(sizeof(struct usb_gpib_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + + retval = skel_do_open(board, usb_minors[j]); + + DIA_LOG(1, "Skel open: %d\n", retval); + + if (retval) { + dev_err(board->gpib_dev, "skel open failed.\n"); + kfree(board->private_data); + board->private_data = NULL; + return -ENODEV; + } + + show_status(board); + + retval = send_command(board, USB_GPIB_ON, 0); + DIA_LOG(1, "USB_GPIB_ON returns %x\n", retval); + if (retval != ACK) + return -EIO; + + /* + * We must setup debug mode because we need the extended instruction + * set to cope with the Core (gpib_common) point of view + */ + + retval = send_command(board, USB_GPIB_DEBUG_ON, 0); + DIA_LOG(1, "USB_GPIB_DEBUG_ON returns %x\n", retval); + if (retval != ACK) + return -EIO; + + /* + * We must keep REN off after an IFC because so it is + * assumed by the Core + */ + + retval = send_command(board, USB_GPIB_IBm0, 0); + DIA_LOG(1, "USB_GPIB_IBm0 returns %x\n", retval); + if (retval != ACK) + return -EIO; + + retval = set_control_line(board, IB_BUS_REN, 0); + if (retval != ACK) + return -EIO; + + retval = send_command(board, USB_GPIB_FTMO, 0); + DIA_LOG(1, "USB_GPIB_FTMO returns %x\n", retval); + if (retval != ACK) + return -EIO; + + show_status(board); + DIA_LOG(0, "attached\n"); + return 0; +} + +/** + * usb_gpib_detach() - deactivate the usb-gpib converter board + * + * @board: the gpib_board data area for this gpib interface + * + */ + +static void usb_gpib_detach(struct gpib_board *board) +{ + int retval; + + show_status(board); + + DIA_LOG(0, "detaching\n"); + + if (board->private_data) { + if (GPIB_DEV) { + write_loop(GPIB_DEV, USB_GPIB_OFF, strlen(USB_GPIB_OFF)); + msleep(100); + DIA_LOG(1, "%s", "GPIB off\n"); + retval = skel_do_release(board); + DIA_LOG(1, "skel release -> %d\n", retval); + } + kfree(board->private_data); + board->private_data = NULL; + } + + DIA_LOG(0, "detached\n"); +} + +/* + * Other functions follow in alphabetical order + */ +/* command */ +static int usb_gpib_command(struct gpib_board *board, + u8 *buffer, + size_t length, + size_t *bytes_written) +{ + int i, retval; + char command[6] = "IBc.\n"; + + DIA_LOG(1, "enter %p\n", board); + + set_timeout(board); + + *bytes_written = 0; + for (i = 0 ; i < length ; i++) { + command[3] = buffer[i]; + retval = send_command(board, command, 5); + DIA_LOG(2, "%d ==> %x %x\n", i, buffer[i], retval); + if (retval != 0x06) + return retval; + ++(*bytes_written); + } + return 0; +} + +/** + * usb_gpib_disable_eos() - Disable END on eos byte (END on EOI only) + * + * @board: the gpib_board data area for this gpib interface + * + * With the lpvo adapter eos can only be handled via software. + * Cannot do nothing here, but remember for future use. + */ + +static void usb_gpib_disable_eos(struct gpib_board *board) +{ + ((struct usb_gpib_priv *)board->private_data)->eos_flags &= ~REOS; + DIA_LOG(1, "done: %x\n", + ((struct usb_gpib_priv *)board->private_data)->eos_flags); +} + +/** + * usb_gpib_enable_eos() - Enable END for reads when eos byte is received. + * + * @board: the gpib_board data area for this gpib interface + * @eos_byte: the 'eos' byte + * @compare_8_bits: if zero ignore eigthth bit when comparing + * + */ + +static int usb_gpib_enable_eos(struct gpib_board *board, + u8 eos_byte, + int compare_8_bits) +{ + struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data; + + DIA_LOG(1, "enter with %x\n", eos_byte); + pd->eos = eos_byte; + pd->eos_flags = REOS; + if (compare_8_bits) + pd->eos_flags |= BIN; + return 0; +} + +/** + * usb_gpib_go_to_standby() - De-assert ATN + * + * @board: the gpib_board data area for this gpib interface + */ + +static int usb_gpib_go_to_standby(struct gpib_board *board) +{ + int retval = set_control_line(board, IB_BUS_ATN, 0); + + DIA_LOG(1, "done with %x\n", retval); + + if (retval == ACK) + return 0; + return -EIO; +} + +/** + * usb_gpib_interface_clear() - Assert or de-assert IFC + * + * @board: the gpib_board data area for this gpib interface + * @assert: 1: assert IFC; 0: de-assert IFC + * + * Currently on the assert request we issue the lpvo IBZ + * command that cycles IFC low for 100 usec, then we ignore + * the de-assert request. + */ + +static void usb_gpib_interface_clear(struct gpib_board *board, int assert) +{ + int retval = 0; + + DIA_LOG(1, "enter with %d\n", assert); + + if (assert) { + retval = send_command(board, USB_GPIB_IBCL, 0); + + set_bit(CIC_NUM, &board->status); + } + + DIA_LOG(1, "done with %d %d\n", assert, retval); +} + +/** + * usb_gpib_line_status() - Read the status of the bus lines. + * + * @board: the gpib_board data area for this gpib interface + * + * We can read all lines. + */ +static int usb_gpib_line_status(const struct gpib_board *board) +{ + int buffer; + int line_status = VALID_ALL; /* all lines will be read */ + struct list_head *p, *q; + WQT *item; + unsigned long flags; + int sleep = 0; + + DIA_LOG(1, "%s\n", "request"); + + /* + * if we are on the wait queue (board->wait), do not hurry + * reading status line; instead, pause a little + */ + + spin_lock_irqsave((spinlock_t *)&board->wait.lock, flags); + q = (struct list_head *)&board->wait.WQH; + list_for_each(p, q) { + item = container_of(p, WQT, WQE); + if (item->private == current) { + sleep = 20; + break; + } + /* pid is: ((struct task_struct *) item->private)->pid); */ + } + spin_unlock_irqrestore((spinlock_t *)&board->wait.lock, flags); + if (sleep) { + DIA_LOG(1, "we are on the wait queue - sleep %d ms\n", sleep); + msleep(sleep); + } + + buffer = send_command((struct gpib_board *)board, USB_GPIB_STATUS, 0); + + if (buffer < 0) { + dev_err(board->gpib_dev, "line status read failed with %d\n", buffer); + return -1; + } + + if ((buffer & 0x01) == 0) + line_status |= BUS_REN; + if ((buffer & 0x02) == 0) + line_status |= BUS_IFC; + if ((buffer & 0x04) == 0) + line_status |= BUS_NDAC; + if ((buffer & 0x08) == 0) + line_status |= BUS_NRFD; + if ((buffer & 0x10) == 0) + line_status |= BUS_DAV; + if ((buffer & 0x20) == 0) + line_status |= BUS_EOI; + if ((buffer & 0x40) == 0) + line_status |= BUS_ATN; + if ((buffer & 0x80) == 0) + line_status |= BUS_SRQ; + + DIA_LOG(1, "done with %x %x\n", buffer, line_status); + + return line_status; +} + +/* parallel_poll */ + +static int usb_gpib_parallel_poll(struct gpib_board *board, u8 *result) +{ + /* + * request parallel poll asserting ATN | EOI; + * we suppose ATN already asserted + */ + + int retval; + + DIA_LOG(1, "enter %p\n", board); + + retval = set_control_line(board, IB_BUS_EOI, 1); + if (retval != ACK) + return -EIO; + + *result = send_command(board, USB_GPIB_READ_DATA, 0); + + DIA_LOG(1, "done with %x\n", *result); + + retval = set_control_line(board, IB_BUS_EOI, 0); + if (retval != 0x06) + return -EIO; + + return 0; +} + +/* read */ + +static int usb_gpib_read(struct gpib_board *board, + u8 *buffer, + size_t length, + int *end, + size_t *bytes_read) +{ +#define MAX_READ_EXCESS 16384 + + struct char_buf b = {NULL, 0}; + + int retval; + char c, nc; + int ic; + struct timespec64 before, after; + int read_count = MAX_READ_EXCESS; + struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data; + + DIA_LOG(1, "enter %p -> %zu\n", board, length); + + *bytes_read = 0; /* by default, things go wrong */ + *end = 0; + + set_timeout(board); + + /* single byte read has a special handling */ + + if (length == 1) { + char inbuf[2] = {0, 0}; + + /* read a single character */ + + ktime_get_real_ts64 (&before); + + retval = write_loop(GPIB_DEV, USB_GPIB_READ_1, strlen(USB_GPIB_READ_1)); + if (retval < 0) + return retval; + + retval = skel_do_read(GPIB_DEV, inbuf, 1); + retval += skel_do_read(GPIB_DEV, inbuf + 1, 1); + + ktime_get_real_ts64 (&after); + + DIA_LOG(1, "single read: %x %x %x in %d\n", retval, + inbuf[0], inbuf[1], + usec_diff(&after, &before)); + + /* good char / last char? */ + + if (retval == 2 && inbuf[1] == ACK) { + buffer[0] = inbuf[0]; + *bytes_read = 1; + return 0; + } + if (retval < 2) + return -EIO; + else + return -ETIME; + } + + /* allocate buffer for multibyte read */ + + b.inbuf = kmalloc(INBUF_SIZE, GFP_KERNEL); + if (!b.inbuf) + return -ENOMEM; + + /* send read command and check sequence */ + + retval = write_loop(GPIB_DEV, USB_GPIB_READ, strlen(USB_GPIB_READ)); + if (retval < 0) + goto read_return; + + if (one_char(board, &b) != DLE || one_char(board, &b) != STX) { + dev_err(board->gpib_dev, "wrong sequence\n"); + retval = -EIO; + goto read_return; + } + + /* get data flow */ + + while (1) { + ic = one_char(board, &b); + if (ic == -EIO) { + retval = -EIO; + goto read_return; + } + c = ic; + + if (c == DLE) + nc = one_char(board, &b); + if (c != DLE || nc == DLE) { + /* data byte - store into buffer */ + + if (*bytes_read == length) + break; /* data overflow */ + if (c == DLE) + c = nc; + buffer[(*bytes_read)++] = c; + if (c == pd->eos) { + *end = 1; + break; + } + + } else { + /* we are in the closing sequence */ + c = nc; + if (c == ETX) { + c = one_char(board, &b); + if (c == ACK) { + *end = 1; + retval = 0; + goto read_return; + } else { + dev_err(board->gpib_dev, "wrong end of message %x", c); + retval = -ETIME; + goto read_return; + } + } else { + dev_err(board->gpib_dev, "lone in stream"); + retval = -EIO; + goto read_return; + } + } + } + + /* we had a data overflow - flush excess data */ + + while (read_count--) { + if (one_char(board, &b) != DLE) + continue; + c = one_char(board, &b); + if (c == DLE) + continue; + if (c == ETX) { + c = one_char(board, &b); + if (c == ACK) { + if (MAX_READ_EXCESS - read_count > 1) + dev_dbg(board->gpib_dev, "small buffer - maybe some data lost"); + retval = 0; + goto read_return; + } + break; + } + } + + dev_err(board->gpib_dev, "no input end - board in odd state\n"); + retval = -EIO; + +read_return: + kfree(b.inbuf); + + DIA_LOG(1, "done with byte/status: %d %x %d\n", (int)*bytes_read, retval, *end); + + if (retval == 0 || retval == -ETIME) { + if (send_command(board, USB_GPIB_UNTALK, sizeof(USB_GPIB_UNTALK)) == 0x06) + return retval; + return -EIO; + } + + return retval; +} + +/* remote_enable */ + +static void usb_gpib_remote_enable(struct gpib_board *board, int enable) +{ + int retval; + + retval = set_control_line(board, IB_BUS_REN, enable ? 1 : 0); + if (retval != ACK) + dev_err(board->gpib_dev, "could not set REN line: %x\n", retval); + + DIA_LOG(1, "done with %x\n", retval); +} + +/* request_system_control */ + +static int usb_gpib_request_system_control(struct gpib_board *board, int request_control) +{ + if (!request_control) + return -EINVAL; + + DIA_LOG(1, "done with %d -> %lx\n", request_control, board->status); + return 0; +} + +/* take_control */ +/* beware: the sync flag is ignored; what is its real meaning? */ + +static int usb_gpib_take_control(struct gpib_board *board, int sync) +{ + int retval; + + retval = set_control_line(board, IB_BUS_ATN, 1); + + DIA_LOG(1, "done with %d %x\n", sync, retval); + + if (retval == ACK) + return 0; + return -EIO; +} + +/* update_status */ + +static unsigned int usb_gpib_update_status(struct gpib_board *board, + unsigned int clear_mask) +{ + /* There is nothing we can do here, I guess */ + + board->status &= ~clear_mask; + + DIA_LOG(1, "done with %x %lx\n", clear_mask, board->status); + + return board->status; +} + +/* write */ +/* beware: DLE characters are not escaped - can only send ASCII data */ + +static int usb_gpib_write(struct gpib_board *board, + u8 *buffer, + size_t length, + int send_eoi, + size_t *bytes_written) +{ + int retval; + char *msg; + + DIA_LOG(1, "enter %p -> %zu\n", board, length); + + set_timeout(board); + + msg = kmalloc(length + 8, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + memcpy(msg, "\nIB\020\002", 5); + memcpy(msg + 5, buffer, length); + memcpy(msg + 5 + length, "\020\003\n", 3); + + retval = send_command(board, msg, length + 8); + kfree(msg); + + DIA_LOG(1, "<%.*s> -> %x\n", (int)length, buffer, retval); + + if (retval != ACK) + return -EPIPE; + + *bytes_written = length; + + if (send_command(board, USB_GPIB_UNLISTEN, sizeof(USB_GPIB_UNLISTEN)) != 0x06) + return -EPIPE; + + return length; +} + +/* + * *** following functions not implemented yet *** + */ + +/* parallel_poll configure */ + +static void usb_gpib_parallel_poll_configure(struct gpib_board *board, + u8 configuration) +{ +} + +/* parallel_poll_response */ + +static void usb_gpib_parallel_poll_response(struct gpib_board *board, int ist) +{ +} + +/* primary_address */ + +static int usb_gpib_primary_address(struct gpib_board *board, unsigned int address) +{ + return 0; +} + +/* return_to_local */ + +static void usb_gpib_return_to_local(struct gpib_board *board) +{ +} + +/* secondary_address */ + +static int usb_gpib_secondary_address(struct gpib_board *board, + unsigned int address, + int enable) +{ + return 0; +} + +/* serial_poll_response */ + +static void usb_gpib_serial_poll_response(struct gpib_board *board, u8 status) +{ +} + +/* serial_poll_status */ + +static u8 usb_gpib_serial_poll_status(struct gpib_board *board) +{ + return 0; +} + +/* t1_delay */ + +static int usb_gpib_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + return 0; +} + +/* + * *** module dispatch table and init/exit functions *** + */ + +static struct gpib_interface usb_gpib_interface = { + .name = NAME, + .attach = usb_gpib_attach, + .detach = usb_gpib_detach, + .read = usb_gpib_read, + .write = usb_gpib_write, + .command = usb_gpib_command, + .take_control = usb_gpib_take_control, + .go_to_standby = usb_gpib_go_to_standby, + .request_system_control = usb_gpib_request_system_control, + .interface_clear = usb_gpib_interface_clear, + .remote_enable = usb_gpib_remote_enable, + .enable_eos = usb_gpib_enable_eos, + .disable_eos = usb_gpib_disable_eos, + .parallel_poll = usb_gpib_parallel_poll, + .parallel_poll_configure = usb_gpib_parallel_poll_configure, + .parallel_poll_response = usb_gpib_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = usb_gpib_line_status, + .update_status = usb_gpib_update_status, + .primary_address = usb_gpib_primary_address, + .secondary_address = usb_gpib_secondary_address, + .serial_poll_response = usb_gpib_serial_poll_response, + .serial_poll_status = usb_gpib_serial_poll_status, + .t1_delay = usb_gpib_t1_delay, + .return_to_local = usb_gpib_return_to_local, + .skip_check_for_command_acceptors = 1 +}; + +/* + * usb_gpib_init_module(), usb_gpib_exit_module() + * + * This functions are called every time a new device is detected + * and registered or is removed and unregistered. + * We must take note of created and destroyed usb minors to be used + * when usb_gpib_attach() and usb_gpib_detach() will be called on + * request by gpib_config. + */ + +static int usb_gpib_init_module(struct usb_interface *interface) +{ + int j, mask, rv; + + rv = mutex_lock_interruptible(&minors_lock); + if (rv < 0) + return rv; + + if (!assigned_usb_minors) { + rv = gpib_register_driver(&usb_gpib_interface, THIS_MODULE); + if (rv) { + pr_err("gpib_register_driver failed: error = %d\n", rv); + goto exit; + } + } else { + /* + * check if minor is already registered - maybe useless, but if + * it happens the code is inconsistent somewhere + */ + + for (j = 0 ; j < MAX_DEV ; j++) { + if (usb_minors[j] == interface->minor && assigned_usb_minors & 1 << j) { + pr_err("CODE BUG: USB minor %d registered at %d.\n", + interface->minor, j); + rv = -1; + goto exit; + } + } + } + + /* find a free slot */ + + for (j = 0 ; j < MAX_DEV ; j++) { + mask = 1 << j; + if ((assigned_usb_minors & mask) == 0) { + usb_minors[j] = interface->minor; + lpvo_usb_interfaces[j] = interface; + assigned_usb_minors |= mask; + rv = 0; + goto exit; + } + } + pr_err("No slot available for interface %p minor %d\n", interface, interface->minor); + rv = -1; + +exit: + mutex_unlock(&minors_lock); + return rv; +} + +static void usb_gpib_exit_module(int minor) +{ + int j; + + mutex_lock(&minors_lock); + for (j = 0 ; j < MAX_DEV ; j++) { + if (usb_minors[j] == minor && assigned_usb_minors & 1 << j) { + assigned_usb_minors &= ~(1 << j); + usb_minors[j] = -1; + if (assigned_usb_minors == 0) + gpib_unregister_driver(&usb_gpib_interface); + goto exit; + } + } + pr_err("CODE BUG: USB minor %d not found.\n", minor); + +exit: + mutex_unlock(&minors_lock); +} + +/* + * Default latency time (16 msec) is too long. + * We must use 1 msec (best); anyhow, no more than 5 msec. + * + * Defines and function taken and modified from the kernel tree + * (see ftdi_sio.h and ftdi_sio.c). + */ + +#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 +#define WDR_TIMEOUT 5000 /* default urb timeout */ +#define WDR_SHORT_TIMEOUT 1000 /* shorter urb timeout */ + +#define LATENCY_TIMER 1 /* use a small latency timer: 1 ... 5 msec */ +#define LATENCY_CHANNEL 0 /* channel selection in multichannel devices */ +static int write_latency_timer(struct usb_device *udev) +{ + int rv = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + FTDI_SIO_SET_LATENCY_TIMER_REQUEST, + FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, + LATENCY_TIMER, LATENCY_CHANNEL, + NULL, 0, WDR_TIMEOUT); + if (rv < 0) + dev_err(&udev->dev, "Unable to write latency timer: %i\n", rv); + return rv; +} + +/***************************************************************************** + * * + * The following code is a modified version of the USB Skeleton driver * + * written by Greg Kroah-Hartman and available in the kernel tree. * + * * + * Functions skel_open() and skel_release() have been rewritten and named * + * skel_do_open() and skel_do_release() to process the attach and detach * + * requests coming from gpib_config. * + * * + * Functions skel_read() and skel_write() have been split into a * + * skel_do_read() and skel_do_write(), that cover the kernel stuff of read * + * and write operations, and the original skel_read() and skel_write(), * + * that handle communication with user space and call their _do_ companion. * + * * + * Only the _do_ versions are used by the lpvo_usb_gpib driver; other ones * + * can be (optionally) maintained in the compilation to have direct access * + * to a gpib controller for debug and diagnostics. * + * * + * To avoid collisions in names, devices in user space have been renamed * + * lpvo_raw1, lpvo_raw2 .... and the usb driver has been renamed with the * + * gpib module name. * + * * + *****************************************************************************/ + +/* + * USB Skeleton driver - 2.2 + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * + * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c + * but has been rewritten to be easier to read and use. + */ + +#include +#include +#include +#include + +/* Get a minor range for your devices from the usb maintainer */ +#define USB_SKEL_MINOR_BASE 192 + +/* private defines */ + +#define MAX_TRANSFER (PAGE_SIZE - 512) +/* + * MAX_TRANSFER is chosen so that the VM is not stressed by + * allocations > PAGE_SIZE and the number of packets in a page + * is an integer 512 is the largest possible packet on EHCI + */ + +#define WRITES_IN_FLIGHT 1 /* we do not want more than one pending write */ +#define USER_DEVICE 1 /* compile for device(s) in user space */ + +/* Structure to hold all of our device specific stuff */ +struct usb_skel { + struct usb_device *udev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ + struct semaphore limit_sem; /* limiting the number of writes in progress */ + struct usb_anchor submitted; /* in case need to retract our submissions */ + struct urb *bulk_in_urb; /* the urb to read data with */ + unsigned char *bulk_in_buffer; /* the buffer to receive data */ + size_t bulk_in_size; /* the size of the receive buffer */ + size_t bulk_in_filled; /* number of bytes in the buffer */ + size_t bulk_in_copied; /* already copied to user space */ + __u8 bulk_in_endpoint_addr; /* the address of the bulk in endpoint */ + __u8 bulk_out_endpoint_addr; /* the address of the bulk out endpoint */ + int errors; /* the last request tanked */ + bool ongoing_read; /* a read is going on */ + spinlock_t err_lock; /* lock for errors */ + struct kref kref; + struct mutex io_mutex; /* synchronize I/O with disconnect */ + wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ +}; + +#define to_skel_dev(d) container_of(d, struct usb_skel, kref) + +static struct usb_driver skel_driver; +static void skel_draw_down(struct usb_skel *dev); + +static void skel_delete(struct kref *kref) +{ + struct usb_skel *dev = to_skel_dev(kref); + + usb_free_urb(dev->bulk_in_urb); + usb_put_dev(dev->udev); + kfree(dev->bulk_in_buffer); + kfree(dev); +} + +/* + * skel_do_open() - to be called by usb_gpib_attach + */ + +static int skel_do_open(struct gpib_board *board, int subminor) +{ + struct usb_skel *dev; + struct usb_interface *interface; + int retval = 0; + + interface = usb_find_interface(&skel_driver, subminor); + if (!interface) { + dev_err(board->gpib_dev, "can't find device for minor %d\n", subminor); + retval = -ENODEV; + goto exit; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto exit; + } + + retval = usb_autopm_get_interface(interface); + if (retval) + goto exit; + + /* increment our usage count for the device */ + kref_get(&dev->kref); + + /* save our object in the file's private structure */ + GPIB_DEV = dev; + +exit: + return retval; +} + +/* + * skel_do_release() - to be called by usb_gpib_detach + */ + +static int skel_do_release(struct gpib_board *board) +{ + struct usb_skel *dev; + + dev = GPIB_DEV; + if (!dev) + return -ENODEV; + + /* allow the device to be autosuspended */ + mutex_lock(&dev->io_mutex); + if (dev->interface) + usb_autopm_put_interface(dev->interface); + mutex_unlock(&dev->io_mutex); + + /* decrement the count on our device */ + kref_put(&dev->kref, skel_delete); + return 0; +} + +/* + * read functions + */ + +static void skel_read_bulk_callback(struct urb *urb) +{ + struct usb_skel *dev; + unsigned long flags; + + dev = urb->context; + + spin_lock_irqsave(&dev->err_lock, flags); + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + dev_err(&dev->interface->dev, "nonzero read bulk status received: %d\n", + urb->status); + + dev->errors = urb->status; + } else { + dev->bulk_in_filled = urb->actual_length; + } + dev->ongoing_read = 0; + spin_unlock_irqrestore(&dev->err_lock, flags); + + wake_up_interruptible(&dev->bulk_in_wait); +} + +static int skel_do_read_io(struct usb_skel *dev, size_t count) +{ + int rv; + + /* prepare a read */ + usb_fill_bulk_urb(dev->bulk_in_urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpoint_addr), + dev->bulk_in_buffer, + min(dev->bulk_in_size, count), + skel_read_bulk_callback, + dev); + /* tell everybody to leave the URB alone */ + spin_lock_irq(&dev->err_lock); + dev->ongoing_read = 1; + spin_unlock_irq(&dev->err_lock); + + /* submit bulk in urb, which means no data to deliver */ + dev->bulk_in_filled = 0; + dev->bulk_in_copied = 0; + + /* do it */ + rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); + if (rv < 0) { + dev_err(&dev->interface->dev, "failed submitting read urb, error %d\n", rv); + rv = (rv == -ENOMEM) ? rv : -EIO; + spin_lock_irq(&dev->err_lock); + dev->ongoing_read = 0; + spin_unlock_irq(&dev->err_lock); + } + + return rv; +} + +/* + * skel_do_read() - read operations from lpvo_usb_gpib + */ + +static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) +{ + int rv; + bool ongoing_io; + + /* if we cannot read at all, return EOF */ + + if (!dev->bulk_in_urb || !count) + return 0; + +restart: /* added to comply with ftdi timeout technique */ + + /* no concurrent readers */ + + rv = mutex_lock_interruptible(&dev->io_mutex); + if (rv < 0) + return rv; + + if (!dev->interface) { /* disconnect() was called */ + rv = -ENODEV; + goto exit; + } + +retry: + /* if IO is under way, we must not touch things */ + spin_lock_irq(&dev->err_lock); + ongoing_io = dev->ongoing_read; + spin_unlock_irq(&dev->err_lock); + + if (ongoing_io) { +// /* nonblocking IO shall not wait */ +// /* no file, no O_NONBLOCK; maybe provide when from user space */ +// if (file->f_flags & O_NONBLOCK) { +// rv = -EAGAIN; +// goto exit; +// } + + /* + * IO may take forever + * hence wait in an interruptible state + */ + rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read)); + if (rv < 0) + goto exit; + } + + /* errors must be reported */ + rv = dev->errors; + if (rv < 0) { + /* any error is reported once */ + dev->errors = 0; + /* to preserve notifications about reset */ + rv = (rv == -EPIPE) ? rv : -EIO; + /* report it */ + goto exit; + } + + /* + * if the buffer is filled we may satisfy the read + * else we need to start IO + */ + + if (dev->bulk_in_filled) { + /* we had read data */ + + size_t available = dev->bulk_in_filled - dev->bulk_in_copied; +// size_t chunk = min(available, count); /* compute chunk later */ + size_t chunk; + + if (!available) { + /* + * all data has been used + * actual IO needs to be done + */ + /* + * it seems that requests for less than dev->bulk_in_size + * are not accepted + */ + rv = skel_do_read_io(dev, dev->bulk_in_size); + if (rv < 0) + goto exit; + else + goto retry; + } + + /* + * data is available - chunk tells us how much shall be copied + */ + + /* + * Condition dev->bulk_in_copied > 0 maybe will never happen. In case, + * signal the event and copy using the original procedure, i.e., copy + * first two bytes also + */ + + if (dev->bulk_in_copied) { + chunk = min(available, count); + memcpy(buffer, dev->bulk_in_buffer + dev->bulk_in_copied, chunk); + rv = chunk; + dev->bulk_in_copied += chunk; + + /* copy discarding first two bytes that contain ftdi chip status */ + + } else { + /* account for two bytes to be discarded */ + chunk = min(available, count + 2); + if (chunk < 2) { + dev_err(&dev->udev->dev, "BAD READ - chunk: %zu\n", chunk); + rv = -EIO; + goto exit; + } + + memcpy(buffer, dev->bulk_in_buffer + 2, chunk - 2); + rv = chunk; + dev->bulk_in_copied += chunk; + } + + /* + * if we are asked for more than we have, + * we start IO but don't wait + * + * No, no read ahead allowed; if the case, more data will be + * asked for by the lpvo_usb_gpib layer. + */ +// if (available < count) +// skel_do_read_io(dev, dev->bulk_in_size); + } else { + /* no data in the buffer */ + rv = skel_do_read_io(dev, dev->bulk_in_size); + if (rv < 0) + goto exit; + else + goto retry; + } +exit: + mutex_unlock(&dev->io_mutex); + if (rv == 2) + goto restart; /* ftdi chip returns two status bytes after a latency anyhow */ + + if (rv > 0) + return rv - 2; /* account for 2 discarded bytes in a valid buffer */ + return rv; +} + +/* + * write functions + */ + +static void skel_write_bulk_callback(struct urb *urb) +{ + struct usb_skel *dev; + unsigned long flags; + + dev = urb->context; + + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + dev_err(&dev->interface->dev, + "nonzero write bulk status received: %d\n", urb->status); + + spin_lock_irqsave(&dev->err_lock, flags); + dev->errors = urb->status; + spin_unlock_irqrestore(&dev->err_lock, flags); + } + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + up(&dev->limit_sem); +} + +/* + * skel_do_write() - write operations from lpvo_usb_gpib + */ + +static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t count) +{ + int retval = 0; + struct urb *urb = NULL; + char *buf = NULL; + size_t writesize = min_t(size_t, count, (size_t)MAX_TRANSFER); + + /* verify that we actually have some data to write */ + if (count == 0) + goto exit; + + /* + * limit the number of URBs in flight to stop a user from using up all + * RAM + */ + /* Only one URB is used, because we can't have a pending write() and go on */ + +// if (!(file->f_flags & O_NONBLOCK)) { /* no NONBLOCK provided */ + if (down_interruptible(&dev->limit_sem)) { + retval = -ERESTARTSYS; + goto exit; + } +// } else { +// if (down_trylock(&dev->limit_sem)) { +// retval = -EAGAIN; +// goto exit; +// } +// } + + spin_lock_irq(&dev->err_lock); + retval = dev->errors; + if (retval < 0) { + /* any error is reported once */ + dev->errors = 0; + /* to preserve notifications about reset */ + retval = (retval == -EPIPE) ? retval : -EIO; + } + spin_unlock_irq(&dev->err_lock); + if (retval < 0) + goto error; + + /* create a urb, and a buffer for it, and copy the data to the urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error; + } + + buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + retval = -ENOMEM; + goto error; + } + + memcpy(buf, buffer, count); + + /* this lock makes sure we don't submit URBs to gone devices */ + mutex_lock(&dev->io_mutex); + if (!dev->interface) { /* disconnect() was called */ + mutex_unlock(&dev->io_mutex); + retval = -ENODEV; + goto error; + } + + /* initialize the urb properly */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpoint_addr), + buf, writesize, skel_write_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->submitted); + + /* send the data out the bulk port */ + retval = usb_submit_urb(urb, GFP_KERNEL); + mutex_unlock(&dev->io_mutex); + if (retval) { + dev_err(&dev->interface->dev, "failed submitting write urb, error %d\n", retval); + goto error_unanchor; + } + + /* + * release our reference to this urb, the USB core will eventually free + * it entirely + */ + usb_free_urb(urb); + + return writesize; + +error_unanchor: + usb_unanchor_urb(urb); +error: + if (urb) { + usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma); + usb_free_urb(urb); + } + up(&dev->limit_sem); + +exit: + return retval; +} + +/* + * services for the user space devices + */ + +#if USER_DEVICE /* conditional compilation of user space device */ + +static int skel_flush(struct file *file, fl_owner_t id) +{ + struct usb_skel *dev; + int res; + + dev = file->private_data; + if (!dev) + return -ENODEV; + + /* wait for io to stop */ + mutex_lock(&dev->io_mutex); + skel_draw_down(dev); + + /* read out errors, leave subsequent opens a clean slate */ + spin_lock_irq(&dev->err_lock); + res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0; + dev->errors = 0; + spin_unlock_irq(&dev->err_lock); + + mutex_unlock(&dev->io_mutex); + + return res; +} + +static int skel_open(struct inode *inode, struct file *file) +{ + struct usb_skel *dev; + struct usb_interface *interface; + int subminor; + int retval = 0; + + subminor = iminor(inode); + + interface = usb_find_interface(&skel_driver, subminor); + if (!interface) { + pr_err("can't find device for minor %d\n", subminor); + retval = -ENODEV; + goto exit; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto exit; + } + + retval = usb_autopm_get_interface(interface); + if (retval) + goto exit; + + /* increment our usage count for the device */ + kref_get(&dev->kref); + + /* save our object in the file's private structure */ + file->private_data = dev; + +exit: + return retval; +} + +static int skel_release(struct inode *inode, struct file *file) +{ + struct usb_skel *dev; + + dev = file->private_data; + if (!dev) + return -ENODEV; + + /* allow the device to be autosuspended */ + mutex_lock(&dev->io_mutex); + if (dev->interface) + usb_autopm_put_interface(dev->interface); + mutex_unlock(&dev->io_mutex); + + /* decrement the count on our device */ + kref_put(&dev->kref, skel_delete); + return 0; +} + +/* + * user space access to read function + */ + +static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct usb_skel *dev; + char *buf; + ssize_t rv; + + dev = file->private_data; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rv = skel_do_read(dev, buf, count); + + if (rv > 0) { + if (copy_to_user(buffer, buf, rv)) { + kfree(buf); + return -EFAULT; + } + } + kfree(buf); + return rv; +} + +/* + * user space access to write function + */ + +static ssize_t skel_write(struct file *file, const char __user *user_buffer, + size_t count, loff_t *ppos) +{ + struct usb_skel *dev; + char *buf; + ssize_t rv; + + dev = file->private_data; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user_buffer, count)) { + kfree(buf); + return -EFAULT; + } + + rv = skel_do_write(dev, buf, count); + kfree(buf); + return rv; +} +#endif + +static const struct file_operations skel_fops = { + .owner = THIS_MODULE, +#if USER_DEVICE + .read = skel_read, + .write = skel_write, + .open = skel_open, + .release = skel_release, + .flush = skel_flush, + .llseek = noop_llseek, +#endif +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with the driver core + */ +#if USER_DEVICE +static struct usb_class_driver skel_class = { + .name = "lpvo_raw%d", + .fops = &skel_fops, + .minor_base = USB_SKEL_MINOR_BASE, +}; +#endif + +static int skel_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_skel *dev; + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + int retval; + char *device_path; + + mutex_init(&minors_lock); /* required for handling minor numbers table */ + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + kref_init(&dev->kref); + sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); + mutex_init(&dev->io_mutex); + spin_lock_init(&dev->err_lock); + init_usb_anchor(&dev->submitted); + init_waitqueue_head(&dev->bulk_in_wait); + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + retval = usb_find_common_endpoints(interface->cur_altsetting, + &bulk_in, &bulk_out, NULL, NULL); + if (retval) { + dev_err(&interface->dev, + "Could not find both bulk-in and bulk-out endpoints\n"); + goto error; + } + + dev->bulk_in_size = usb_endpoint_maxp(bulk_in); + dev->bulk_in_endpoint_addr = bulk_in->bEndpointAddress; + dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL); + if (!dev->bulk_in_buffer) { + retval = -ENOMEM; + goto error; + } + dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->bulk_in_urb) { + retval = -ENOMEM; + goto error; + } + + dev->bulk_out_endpoint_addr = bulk_out->bEndpointAddress; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* let the world know */ + + device_path = kobject_get_path(&dev->udev->dev.kobj, GFP_KERNEL); + dev_dbg(&interface->dev, "New lpvo_usb_device -> bus: %d dev: %d path: %s\n", + dev->udev->bus->busnum, dev->udev->devnum, device_path); + kfree(device_path); + +#if USER_DEVICE + /* we can register the device now, as it is ready */ + retval = usb_register_dev(interface, &skel_class); + if (retval) { + /* something prevented us from registering this driver */ + dev_err(&interface->dev, + "Not able to get a minor for this device.\n"); + usb_set_intfdata(interface, NULL); + goto error; + } +#endif + + write_latency_timer(dev->udev); /* adjust the latency timer */ + + usb_gpib_init_module(interface); /* last, init the lpvo for this minor */ + + return 0; + +error: + /* this frees allocated memory */ + kref_put(&dev->kref, skel_delete); + + return retval; +} + +static void skel_disconnect(struct usb_interface *interface) +{ + struct usb_skel *dev; + int minor = interface->minor; + + usb_gpib_exit_module(minor); /* first, disactivate the lpvo */ + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + +#if USER_DEVICE + /* give back our minor */ + usb_deregister_dev(interface, &skel_class); +#endif + + /* prevent more I/O from starting */ + mutex_lock(&dev->io_mutex); + dev->interface = NULL; + mutex_unlock(&dev->io_mutex); + + usb_kill_anchored_urbs(&dev->submitted); + + /* decrement our usage count */ + kref_put(&dev->kref, skel_delete); +} + +static void skel_draw_down(struct usb_skel *dev) +{ + int time; + + time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); + if (!time) + usb_kill_anchored_urbs(&dev->submitted); + usb_kill_urb(dev->bulk_in_urb); +} + +static int skel_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + if (!dev) + return 0; + skel_draw_down(dev); + return 0; +} + +static int skel_resume(struct usb_interface *intf) +{ + return 0; +} + +static int skel_pre_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + mutex_lock(&dev->io_mutex); + skel_draw_down(dev); + + return 0; +} + +static int skel_post_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + /* we are sure no URBs are active - no locking needed */ + dev->errors = -EPIPE; + mutex_unlock(&dev->io_mutex); + + return 0; +} + +static struct usb_driver skel_driver = { + .name = NAME, + .probe = skel_probe, + .disconnect = skel_disconnect, + .suspend = skel_suspend, + .resume = skel_resume, + .pre_reset = skel_pre_reset, + .post_reset = skel_post_reset, + .id_table = skel_table, + .supports_autosuspend = 1, +}; + +module_usb_driver(skel_driver); diff --git a/drivers/gpib/nec7210/Makefile b/drivers/gpib/nec7210/Makefile new file mode 100644 index 000000000000..64330f2e89d1 --- /dev/null +++ b/drivers/gpib/nec7210/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_GPIB_NEC7210) += nec7210.o + + diff --git a/drivers/gpib/nec7210/board.h b/drivers/gpib/nec7210/board.h new file mode 100644 index 000000000000..ac3fe38ade57 --- /dev/null +++ b/drivers/gpib/nec7210/board.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_PCIIA_BOARD_H +#define _GPIB_PCIIA_BOARD_H + +#include "gpibP.h" +#include +#include +#include +#include + +#include "nec7210.h" + +#endif //_GPIB_PCIIA_BOARD_H + diff --git a/drivers/gpib/nec7210/nec7210.c b/drivers/gpib/nec7210/nec7210.c new file mode 100644 index 000000000000..bbf39367f5e4 --- /dev/null +++ b/drivers/gpib/nec7210/nec7210.c @@ -0,0 +1,1121 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "board.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB library code for NEC uPD7210"); + +int nec7210_enable_eos(struct gpib_board *board, struct nec7210_priv *priv, u8 eos_byte, + int compare_8_bits) +{ + write_byte(priv, eos_byte, EOSR); + priv->auxa_bits |= HR_REOS; + if (compare_8_bits) + priv->auxa_bits |= HR_BIN; + else + priv->auxa_bits &= ~HR_BIN; + write_byte(priv, priv->auxa_bits, AUXMR); + return 0; +} +EXPORT_SYMBOL(nec7210_enable_eos); + +void nec7210_disable_eos(struct gpib_board *board, struct nec7210_priv *priv) +{ + priv->auxa_bits &= ~HR_REOS; + write_byte(priv, priv->auxa_bits, AUXMR); +} +EXPORT_SYMBOL(nec7210_disable_eos); + +int nec7210_parallel_poll(struct gpib_board *board, struct nec7210_priv *priv, u8 *result) +{ + int ret; + + clear_bit(COMMAND_READY_BN, &priv->state); + + // execute parallel poll + write_byte(priv, AUX_EPP, AUXMR); + // wait for result FIXME: support timeouts + ret = wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state)); + if (ret) { + dev_dbg(board->gpib_dev, "gpib: parallel poll interrupted\n"); + return -ERESTARTSYS; + } + *result = read_byte(priv, CPTR); + + return 0; +} +EXPORT_SYMBOL(nec7210_parallel_poll); + +void nec7210_parallel_poll_configure(struct gpib_board *board, + struct nec7210_priv *priv, unsigned int configuration) +{ + write_byte(priv, PPR | configuration, AUXMR); +} +EXPORT_SYMBOL(nec7210_parallel_poll_configure); + +void nec7210_parallel_poll_response(struct gpib_board *board, struct nec7210_priv *priv, int ist) +{ + if (ist) + write_byte(priv, AUX_SPPF, AUXMR); + else + write_byte(priv, AUX_CPPF, AUXMR); +} +EXPORT_SYMBOL(nec7210_parallel_poll_response); +/* + * This is really only adequate for chips that do a 488.2 style reqt/reqf + * based on bit 6 of the SPMR (see chapter 11.3.3 of 488.2). For simpler chips that simply + * set rsv directly based on bit 6, we either need to do more hardware setup to expose + * the 488.2 capability (for example with NI chips), or we need to implement the + * 488.2 set srv state machine in the driver (if that is even viable). + */ +void nec7210_serial_poll_response(struct gpib_board *board, + struct nec7210_priv *priv, u8 status) +{ + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + if (status & request_service_bit) { + priv->srq_pending = 1; + clear_bit(SPOLL_NUM, &board->status); + + } else { + priv->srq_pending = 0; + } + write_byte(priv, status, SPMR); + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(nec7210_serial_poll_response); + +u8 nec7210_serial_poll_status(struct gpib_board *board, struct nec7210_priv *priv) +{ + return read_byte(priv, SPSR); +} +EXPORT_SYMBOL(nec7210_serial_poll_status); + +int nec7210_primary_address(const struct gpib_board *board, struct nec7210_priv *priv, + unsigned int address) +{ + // put primary address in address0 + write_byte(priv, address & ADDRESS_MASK, ADR); + return 0; +} +EXPORT_SYMBOL(nec7210_primary_address); + +int nec7210_secondary_address(const struct gpib_board *board, struct nec7210_priv *priv, + unsigned int address, int enable) +{ + if (enable) { + // put secondary address in address1 + write_byte(priv, HR_ARS | (address & ADDRESS_MASK), ADR); + // go to address mode 2 + priv->reg_bits[ADMR] &= ~HR_ADM0; + priv->reg_bits[ADMR] |= HR_ADM1; + } else { + // disable address1 register + write_byte(priv, HR_ARS | HR_DT | HR_DL, ADR); + // go to address mode 1 + priv->reg_bits[ADMR] |= HR_ADM0; + priv->reg_bits[ADMR] &= ~HR_ADM1; + } + write_byte(priv, priv->reg_bits[ADMR], ADMR); + return 0; +} +EXPORT_SYMBOL(nec7210_secondary_address); + +static void update_talker_state(struct nec7210_priv *priv, unsigned int address_status_bits) +{ + if ((address_status_bits & HR_TA)) { + if ((address_status_bits & HR_NATN)) { + if (address_status_bits & HR_SPMS) + priv->talker_state = serial_poll_active; + else + priv->talker_state = talker_active; + } else { + priv->talker_state = talker_addressed; + } + } else { + priv->talker_state = talker_idle; + } +} + +static void update_listener_state(struct nec7210_priv *priv, unsigned int address_status_bits) +{ + if (address_status_bits & HR_LA) { + if ((address_status_bits & HR_NATN)) + priv->listener_state = listener_active; + else + priv->listener_state = listener_addressed; + } else { + priv->listener_state = listener_idle; + } +} + +unsigned int nec7210_update_status_nolock(struct gpib_board *board, struct nec7210_priv *priv) +{ + int address_status_bits; + u8 spoll_status; + + if (!priv) + return 0; + + address_status_bits = read_byte(priv, ADSR); + if (address_status_bits & HR_CIC) + set_bit(CIC_NUM, &board->status); + else + clear_bit(CIC_NUM, &board->status); + // check for talker/listener addressed + update_talker_state(priv, address_status_bits); + if (priv->talker_state == talker_active || priv->talker_state == talker_addressed) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + update_listener_state(priv, address_status_bits); + if (priv->listener_state == listener_active || + priv->listener_state == listener_addressed) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + if (address_status_bits & HR_NATN) + clear_bit(ATN_NUM, &board->status); + else + set_bit(ATN_NUM, &board->status); + spoll_status = nec7210_serial_poll_status(board, priv); + if (priv->srq_pending && (spoll_status & request_service_bit) == 0) { + priv->srq_pending = 0; + set_bit(SPOLL_NUM, &board->status); + } + + /* + * we rely on the interrupt handler to set the + * rest of the status bits + */ + + return board->status; +} +EXPORT_SYMBOL(nec7210_update_status_nolock); + +unsigned int nec7210_update_status(struct gpib_board *board, struct nec7210_priv *priv, + unsigned int clear_mask) +{ + unsigned long flags; + unsigned int retval; + + spin_lock_irqsave(&board->spinlock, flags); + board->status &= ~clear_mask; + retval = nec7210_update_status_nolock(board, priv); + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} +EXPORT_SYMBOL(nec7210_update_status); + +unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg, + unsigned int mask, unsigned int bits) +{ + priv->reg_bits[reg] &= ~mask; + priv->reg_bits[reg] |= mask & bits; + write_byte(priv, priv->reg_bits[reg], reg); + return priv->reg_bits[reg]; +} +EXPORT_SYMBOL(nec7210_set_reg_bits); + +void nec7210_set_handshake_mode(struct gpib_board *board, struct nec7210_priv *priv, int mode) +{ + unsigned long flags; + + mode &= HR_HANDSHAKE_MASK; + + spin_lock_irqsave(&board->spinlock, flags); + if ((priv->auxa_bits & HR_HANDSHAKE_MASK) != mode) { + priv->auxa_bits &= ~HR_HANDSHAKE_MASK; + priv->auxa_bits |= mode; + write_byte(priv, priv->auxa_bits, AUXMR); + } + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(nec7210_set_handshake_mode); + +u8 nec7210_read_data_in(struct gpib_board *board, struct nec7210_priv *priv, int *end) +{ + unsigned long flags; + u8 data; + + spin_lock_irqsave(&board->spinlock, flags); + data = read_byte(priv, DIR); + clear_bit(READ_READY_BN, &priv->state); + if (test_and_clear_bit(RECEIVED_END_BN, &priv->state)) + *end = 1; + else + *end = 0; + spin_unlock_irqrestore(&board->spinlock, flags); + + return data; +} +EXPORT_SYMBOL(nec7210_read_data_in); + +int nec7210_take_control(struct gpib_board *board, struct nec7210_priv *priv, int syncronous) +{ + int i; + const int timeout = 100; + int retval = 0; + unsigned int adsr_bits = 0; + + if (syncronous) + write_byte(priv, AUX_TCS, AUXMR); + else + write_byte(priv, AUX_TCA, AUXMR); + // busy wait until ATN is asserted + for (i = 0; i < timeout; i++) { + adsr_bits = read_byte(priv, ADSR); + if ((adsr_bits & HR_NATN) == 0) + break; + udelay(1); + } + if (i == timeout) + return -ETIMEDOUT; + + clear_bit(WRITE_READY_BN, &priv->state); + + return retval; +} +EXPORT_SYMBOL(nec7210_take_control); + +int nec7210_go_to_standby(struct gpib_board *board, struct nec7210_priv *priv) +{ + int i; + const int timeout = 1000; + unsigned int adsr_bits = 0; + int retval = 0; + + write_byte(priv, AUX_GTS, AUXMR); + // busy wait until ATN is released + for (i = 0; i < timeout; i++) { + adsr_bits = read_byte(priv, ADSR); + if (adsr_bits & HR_NATN) + break; + udelay(1); + } + // if busy wait has failed, try sleeping + if (i == timeout) { + for (i = 0; i < HZ; i++) { + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(1)) + return -ERESTARTSYS; + adsr_bits = read_byte(priv, ADSR); + if (adsr_bits & HR_NATN) + break; + } + if (i == HZ) + return -ETIMEDOUT; + } + + clear_bit(COMMAND_READY_BN, &priv->state); + return retval; +} +EXPORT_SYMBOL(nec7210_go_to_standby); + +int nec7210_request_system_control(struct gpib_board *board, struct nec7210_priv *priv, + int request_control) +{ + if (request_control == 0) { + write_byte(priv, AUX_CREN, AUXMR); + write_byte(priv, AUX_CIFC, AUXMR); + write_byte(priv, AUX_DSC, AUXMR); + } + return 0; +} +EXPORT_SYMBOL(nec7210_request_system_control); + +void nec7210_interface_clear(struct gpib_board *board, struct nec7210_priv *priv, int assert) +{ + if (assert) + write_byte(priv, AUX_SIFC, AUXMR); + else + write_byte(priv, AUX_CIFC, AUXMR); +} +EXPORT_SYMBOL(nec7210_interface_clear); + +void nec7210_remote_enable(struct gpib_board *board, struct nec7210_priv *priv, int enable) +{ + if (enable) + write_byte(priv, AUX_SREN, AUXMR); + else + write_byte(priv, AUX_CREN, AUXMR); +} +EXPORT_SYMBOL(nec7210_remote_enable); + +void nec7210_release_rfd_holdoff(struct gpib_board *board, struct nec7210_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + if (test_bit(RFD_HOLDOFF_BN, &priv->state) && + test_bit(READ_READY_BN, &priv->state) == 0) { + write_byte(priv, AUX_FH, AUXMR); + clear_bit(RFD_HOLDOFF_BN, &priv->state); + } + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(nec7210_release_rfd_holdoff); + +int nec7210_t1_delay(struct gpib_board *board, struct nec7210_priv *priv, + unsigned int nano_sec) +{ + unsigned int retval; + + if (nano_sec <= 500) { + priv->auxb_bits |= HR_TRI; + retval = 500; + } else { + priv->auxb_bits &= ~HR_TRI; + retval = 2000; + } + write_byte(priv, priv->auxb_bits, AUXMR); + + return retval; +} +EXPORT_SYMBOL(nec7210_t1_delay); + +void nec7210_return_to_local(const struct gpib_board *board, struct nec7210_priv *priv) +{ + write_byte(priv, AUX_RTL, AUXMR); +} +EXPORT_SYMBOL(nec7210_return_to_local); + +static inline short nec7210_atn_has_changed(struct gpib_board *board, struct nec7210_priv *priv) +{ + short address_status_bits = read_byte(priv, ADSR); + + if (address_status_bits & HR_NATN) { + if (test_bit(ATN_NUM, &board->status)) + return 1; + else + return 0; + } else { + if (test_bit(ATN_NUM, &board->status)) + return 0; + else + return 1; + } + return -1; +} + +int nec7210_command(struct gpib_board *board, struct nec7210_priv *priv, u8 + *buffer, size_t length, size_t *bytes_written) +{ + int retval = 0; + unsigned long flags; + + *bytes_written = 0; + + clear_bit(BUS_ERROR_BN, &priv->state); + + while (*bytes_written < length) { + if (wait_event_interruptible(board->wait, + test_bit(COMMAND_READY_BN, &priv->state) || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + dev_dbg(board->gpib_dev, "command wait interrupted\n"); + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status)) + break; + if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) + break; + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(COMMAND_READY_BN, &priv->state); + write_byte(priv, buffer[*bytes_written], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + + ++(*bytes_written); + + if (need_resched()) + schedule(); + } + // wait for last byte to get sent + if (wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state) || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + + if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) + retval = -EIO; + + return retval; +} +EXPORT_SYMBOL(nec7210_command); + +static int pio_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + + *bytes_read = 0; + *end = 0; + + while (*bytes_read < length) { + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + break; + } + if (test_bit(READ_READY_BN, &priv->state)) { + if (*bytes_read == 0) { + /* + * We set the handshake mode here because we know + * no new bytes will arrive (it has already arrived + * and is awaiting being read out of the chip) while we are changing + * modes. This ensures we can reliably keep track + * of the holdoff state. + */ + nec7210_set_handshake_mode(board, priv, HR_HLDA); + } + buffer[(*bytes_read)++] = nec7210_read_data_in(board, priv, end); + if (*end) + break; + } + if (test_bit(TIMO_NUM, &board->status)) { + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &priv->state)) { + retval = -EINTR; + break; + } + + if (*bytes_read < length) + nec7210_release_rfd_holdoff(board, priv); + + if (need_resched()) + schedule(); + } + return retval; +} + +#ifdef NEC_DMA +static ssize_t __dma_read(struct gpib_board *board, struct nec7210_priv *priv, size_t length) +{ + ssize_t retval = 0; + size_t count = 0; + unsigned long flags, dma_irq_flags; + + if (length == 0) + return 0; + + spin_lock_irqsave(&board->spinlock, flags); + + dma_irq_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + /* program dma controller */ + clear_dma_ff(priv->dma_channel); + set_dma_count(priv->dma_channel, length); + set_dma_addr(priv->dma_channel, priv->dma_buffer_addr); + set_dma_mode(priv->dma_channel, DMA_MODE_READ); + release_dma_lock(dma_irq_flags); + + enable_dma(priv->dma_channel); + + set_bit(DMA_READ_IN_PROGRESS_BN, &priv->state); + clear_bit(READ_READY_BN, &priv->state); + + // enable nec7210 dma + nec7210_set_reg_bits(priv, IMR2, HR_DMAI, HR_DMAI); + + spin_unlock_irqrestore(&board->spinlock, flags); + + // wait for data to transfer + if (wait_event_interruptible(board->wait, + test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state) == 0 || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &priv->state)) + retval = -EINTR; + + // disable nec7210 dma + nec7210_set_reg_bits(priv, IMR2, HR_DMAI, 0); + + // record how many bytes we transferred + flags = claim_dma_lock(); + clear_dma_ff(priv->dma_channel); + disable_dma(priv->dma_channel); + count += length - get_dma_residue(priv->dma_channel); + release_dma_lock(flags); + + return retval ? retval : count; +} + +static ssize_t dma_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length) +{ + size_t remain = length; + size_t transfer_size; + ssize_t retval = 0; + + while (remain > 0) { + transfer_size = (priv->dma_buffer_length < remain) ? + priv->dma_buffer_length : remain; + retval = __dma_read(board, priv, transfer_size); + if (retval < 0) + break; + memcpy(buffer, priv->dma_buffer, transfer_size); + remain -= retval; + buffer += retval; + if (test_bit(RECEIVED_END_BN, &priv->state)) + break; + } + + if (retval < 0) + return retval; + + return length - remain; +} +#endif + +int nec7210_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + + *end = 0; + *bytes_read = 0; + + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &priv->state); // XXX wrong + + nec7210_release_rfd_holdoff(board, priv); + + retval = pio_read(board, priv, buffer, length, end, bytes_read); + + return retval; +} +EXPORT_SYMBOL(nec7210_read); + +static int pio_write_wait(struct gpib_board *board, struct nec7210_priv *priv, + short wake_on_lacs, short wake_on_atn, short wake_on_bus_error) +{ + // wait until byte is ready to be sent + if (wait_event_interruptible(board->wait, + (test_bit(TACS_NUM, &board->status) && + test_bit(WRITE_READY_BN, &priv->state)) || + test_bit(DEV_CLEAR_BN, &priv->state) || + (wake_on_bus_error && test_bit(BUS_ERROR_BN, &priv->state)) || + (wake_on_lacs && test_bit(LACS_NUM, &board->status)) || + (wake_on_atn && test_bit(ATN_NUM, &board->status)) || + test_bit(TIMO_NUM, &board->status))) + return -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + + if (test_bit(DEV_CLEAR_BN, &priv->state)) + return -EINTR; + + if (wake_on_bus_error && test_and_clear_bit(BUS_ERROR_BN, &priv->state)) + return -EIO; + + return 0; +} + +static int pio_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length, size_t *bytes_written) +{ + size_t last_count = 0; + ssize_t retval = 0; + unsigned long flags; + const int max_bus_errors = (length > 1000) ? length : 1000; + int bus_error_count = 0; + *bytes_written = 0; + + clear_bit(BUS_ERROR_BN, &priv->state); + + while (*bytes_written < length) { + if (need_resched()) + schedule(); + + retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210); + if (retval == -EIO) { + /* resend last byte on bus error */ + *bytes_written = last_count; + /* + * we can get unrecoverable bus errors, + * so give up after a while + */ + bus_error_count++; + if (bus_error_count > max_bus_errors) + return retval; + continue; + } else { + if (retval < 0) + return retval; + } + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(BUS_ERROR_BN, &priv->state); + clear_bit(WRITE_READY_BN, &priv->state); + last_count = *bytes_written; + write_byte(priv, buffer[(*bytes_written)++], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + } + retval = pio_write_wait(board, priv, 1, 1, priv->type == NEC7210); + return retval; +} + +#ifdef NEC_DMA +static ssize_t __dma_write(struct gpib_board *board, struct nec7210_priv *priv, dma_addr_t address, + size_t length) +{ + unsigned long flags, dma_irq_flags; + int residue = 0; + int retval = 0; + + spin_lock_irqsave(&board->spinlock, flags); + + /* program dma controller */ + dma_irq_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + clear_dma_ff(priv->dma_channel); + set_dma_count(priv->dma_channel, length); + set_dma_addr(priv->dma_channel, address); + set_dma_mode(priv->dma_channel, DMA_MODE_WRITE); + enable_dma(priv->dma_channel); + release_dma_lock(dma_irq_flags); + + // enable board's dma for output + nec7210_set_reg_bits(priv, IMR2, HR_DMAO, HR_DMAO); + + clear_bit(WRITE_READY_BN, &priv->state); + set_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state); + + spin_unlock_irqrestore(&board->spinlock, flags); + + // suspend until message is sent + if (wait_event_interruptible(board->wait, + test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0 || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_and_clear_bit(DEV_CLEAR_BN, &priv->state)) + retval = -EINTR; + if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) + retval = -EIO; + + // disable board's dma + nec7210_set_reg_bits(priv, IMR2, HR_DMAO, 0); + + dma_irq_flags = claim_dma_lock(); + clear_dma_ff(priv->dma_channel); + disable_dma(priv->dma_channel); + residue = get_dma_residue(priv->dma_channel); + release_dma_lock(dma_irq_flags); + + if (residue) + retval = -EPIPE; + + return retval ? retval : length; +} + +static ssize_t dma_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, + size_t length) +{ + size_t remain = length; + size_t transfer_size; + ssize_t retval = 0; + + while (remain > 0) { + transfer_size = (priv->dma_buffer_length < remain) ? + priv->dma_buffer_length : remain; + memcpy(priv->dma_buffer, buffer, transfer_size); + retval = __dma_write(board, priv, priv->dma_buffer_addr, transfer_size); + if (retval < 0) + break; + remain -= retval; + buffer += retval; + } + + if (retval < 0) + return retval; + + return length - remain; +} +#endif +int nec7210_write(struct gpib_board *board, struct nec7210_priv *priv, + u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + int retval = 0; + + *bytes_written = 0; + + clear_bit(DEV_CLEAR_BN, &priv->state); // XXX + + if (send_eoi) + length-- ; // save the last byte for sending EOI + + if (length > 0) { + // isa dma transfer + if (0 /*priv->dma_channel*/) { +/* + * dma writes are unreliable since they can't recover from bus errors + * (which happen when ATN is asserted in the middle of a write) + */ +#ifdef NEC_DMA + retval = dma_write(board, priv, buffer, length); + if (retval < 0) + return retval; + count += retval; +#endif + } else { // PIO transfer + size_t num_bytes; + + retval = pio_write(board, priv, buffer, length, &num_bytes); + + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + } + if (send_eoi) { + size_t num_bytes; + + /* + * We need to wait to make sure we will immediately be able to write the data byte + * into the chip before sending the associated AUX_SEOI command. This is really + * only needed for length==1 since otherwise the earlier calls to pio_write + * will have dont the wait already. + */ + retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210); + if (retval < 0) + return retval; + /*send EOI */ + write_byte(priv, AUX_SEOI, AUXMR); + + retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + + return retval; +} +EXPORT_SYMBOL(nec7210_write); + +/* + * interrupt service routine + */ +irqreturn_t nec7210_interrupt(struct gpib_board *board, struct nec7210_priv *priv) +{ + int status1, status2; + + // read interrupt status (also clears status) + status1 = read_byte(priv, ISR1); + status2 = read_byte(priv, ISR2); + + return nec7210_interrupt_have_status(board, priv, status1, status2); +} +EXPORT_SYMBOL(nec7210_interrupt); + +irqreturn_t nec7210_interrupt_have_status(struct gpib_board *board, + struct nec7210_priv *priv, int status1, int status2) +{ +#ifdef NEC_DMA + unsigned long dma_flags; +#endif + int retval = IRQ_NONE; + + // record service request in status + if (status2 & HR_SRQI) + set_bit(SRQI_NUM, &board->status); + + // change in lockout status + if (status2 & HR_LOKC) { + if (status2 & HR_LOK) + set_bit(LOK_NUM, &board->status); + else + clear_bit(LOK_NUM, &board->status); + } + + // change in remote status + if (status2 & HR_REMC) { + if (status2 & HR_REM) + set_bit(REM_NUM, &board->status); + else + clear_bit(REM_NUM, &board->status); + } + + // record reception of END + if (status1 & HR_END) { + set_bit(RECEIVED_END_BN, &priv->state); + if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE) + set_bit(RFD_HOLDOFF_BN, &priv->state); + } + + // get incoming data in PIO mode + if ((status1 & HR_DI)) { + set_bit(READ_READY_BN, &priv->state); + if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDA) + set_bit(RFD_HOLDOFF_BN, &priv->state); + } +#ifdef NEC_DMA + // check for dma read transfer complete + if (test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state)) { + dma_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + clear_dma_ff(priv->dma_channel); + if ((status1 & HR_END) || get_dma_residue(priv->dma_channel) == 0) + clear_bit(DMA_READ_IN_PROGRESS_BN, &priv->state); + else + enable_dma(priv->dma_channel); + release_dma_lock(dma_flags); + } +#endif + if ((status1 & HR_DO)) { + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0) + set_bit(WRITE_READY_BN, &priv->state); +#ifdef NEC_DMA + if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state)) { // write data, isa dma mode + // check if dma transfer is complete + dma_flags = claim_dma_lock(); + disable_dma(priv->dma_channel); + clear_dma_ff(priv->dma_channel); + if (get_dma_residue(priv->dma_channel) == 0) { + clear_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state); + // XXX race? byte may still be in CDOR reg + } else { + clear_bit(WRITE_READY_BN, &priv->state); + enable_dma(priv->dma_channel); + } + release_dma_lock(dma_flags); + } +#endif + } + + // outgoing command can be sent + if (status2 & HR_CO) + set_bit(COMMAND_READY_BN, &priv->state); + + // command pass through received + if (status1 & HR_CPT) + write_byte(priv, AUX_NVAL, AUXMR); + + if (status1 & HR_ERR) + set_bit(BUS_ERROR_BN, &priv->state); + + if (status1 & HR_DEC) { + unsigned short address_status_bits = read_byte(priv, ADSR); + + // ignore device clear events if we are controller in charge + if ((address_status_bits & HR_CIC) == 0) { + push_gpib_event(board, EVENT_DEV_CLR); + set_bit(DEV_CLEAR_BN, &priv->state); + } + } + + if (status1 & HR_DET) + push_gpib_event(board, EVENT_DEV_TRG); + + // Addressing status has changed + if (status2 & HR_ADSC) + set_bit(ADR_CHANGE_BN, &priv->state); + + if ((status1 & priv->reg_bits[IMR1]) || + (status2 & (priv->reg_bits[IMR2] & IMR2_ENABLE_INTR_MASK)) || + nec7210_atn_has_changed(board, priv)) { + nec7210_update_status_nolock(board, priv); + dev_dbg(board->gpib_dev, "minor %i, stat %lx, isr1 0x%x, imr1 0x%x, isr2 0x%x, imr2 0x%x\n", + board->minor, board->status, status1, priv->reg_bits[IMR1], status2, + priv->reg_bits[IMR2]); + wake_up_interruptible(&board->wait); /* wake up sleeping process */ + retval = IRQ_HANDLED; + } + + return retval; +} +EXPORT_SYMBOL(nec7210_interrupt_have_status); + +void nec7210_board_reset(struct nec7210_priv *priv, const struct gpib_board *board) +{ + /* 7210 chip reset */ + write_byte(priv, AUX_CR, AUXMR); + + /* disable all interrupts */ + priv->reg_bits[IMR1] = 0; + write_byte(priv, priv->reg_bits[IMR1], IMR1); + priv->reg_bits[IMR2] = 0; + write_byte(priv, priv->reg_bits[IMR2], IMR2); + write_byte(priv, 0, SPMR); + + /* clear registers by reading */ + read_byte(priv, CPTR); + read_byte(priv, ISR1); + read_byte(priv, ISR2); + + /* parallel poll unconfigure */ + write_byte(priv, PPR | HR_PPU, AUXMR); + + priv->reg_bits[ADMR] = HR_TRM0 | HR_TRM1; + + priv->auxa_bits = AUXRA | HR_HLDA; + write_byte(priv, priv->auxa_bits, AUXMR); + + write_byte(priv, AUXRE | 0, AUXMR); + + /* set INT pin to active high, enable command pass through of unknown commands */ + priv->auxb_bits = AUXRB | HR_CPTE; + write_byte(priv, priv->auxb_bits, AUXMR); + write_byte(priv, AUXRE, AUXMR); +} +EXPORT_SYMBOL(nec7210_board_reset); + +void nec7210_board_online(struct nec7210_priv *priv, const struct gpib_board *board) +{ + /* set GPIB address */ + nec7210_primary_address(board, priv, board->pad); + nec7210_secondary_address(board, priv, board->sad, board->sad >= 0); + + /* enable interrupts */ + priv->reg_bits[IMR1] = HR_ERRIE | HR_DECIE | HR_ENDIE | + HR_DETIE | HR_CPTIE | HR_DOIE | HR_DIIE; + priv->reg_bits[IMR2] = IMR2_ENABLE_INTR_MASK; + write_byte(priv, priv->reg_bits[IMR1], IMR1); + write_byte(priv, priv->reg_bits[IMR2], IMR2); + + write_byte(priv, AUX_PON, AUXMR); +} +EXPORT_SYMBOL(nec7210_board_online); + +#ifdef CONFIG_HAS_IOPORT +/* wrappers for io */ +u8 nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + return inb(priv->iobase + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_ioport_read_byte); + +void nec7210_ioport_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num) +{ + if (register_num == AUXMR) + /* + * locking makes absolutely sure noone accesses the + * AUXMR register faster than once per microsecond + */ + nec7210_locking_ioport_write_byte(priv, data, register_num); + else + outb(data, priv->iobase + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_ioport_write_byte); + +/* locking variants of io wrappers, for chips that page-in registers */ +u8 nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + retval = inb(priv->iobase + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); + return retval; +} +EXPORT_SYMBOL(nec7210_locking_ioport_read_byte); + +void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, u8 data, + unsigned int register_num) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + if (register_num == AUXMR) + udelay(1); + outb(data, priv->iobase + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); +} +EXPORT_SYMBOL(nec7210_locking_ioport_write_byte); +#endif + +u8 nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + return readb(priv->mmiobase + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_iomem_read_byte); + +void nec7210_iomem_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num) +{ + if (register_num == AUXMR) + /* + * locking makes absolutely sure noone accesses the + * AUXMR register faster than once per microsecond + */ + nec7210_locking_iomem_write_byte(priv, data, register_num); + else + writeb(data, priv->mmiobase + register_num * priv->offset); +} +EXPORT_SYMBOL(nec7210_iomem_write_byte); + +u8 nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num) +{ + u8 retval; + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + retval = readb(priv->mmiobase + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); + return retval; +} +EXPORT_SYMBOL(nec7210_locking_iomem_read_byte); + +void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data, + unsigned int register_num) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->register_page_lock, flags); + if (register_num == AUXMR) + udelay(1); + writeb(data, priv->mmiobase + register_num * priv->offset); + spin_unlock_irqrestore(&priv->register_page_lock, flags); +} +EXPORT_SYMBOL(nec7210_locking_iomem_write_byte); + +static int __init nec7210_init_module(void) +{ + return 0; +} + +static void __exit nec7210_exit_module(void) +{ +} + +module_init(nec7210_init_module); +module_exit(nec7210_exit_module); diff --git a/drivers/gpib/ni_usb/Makefile b/drivers/gpib/ni_usb/Makefile new file mode 100644 index 000000000000..469c5d16add3 --- /dev/null +++ b/drivers/gpib/ni_usb/Makefile @@ -0,0 +1,4 @@ + +obj-$(CONFIG_GPIB_NI_USB) += ni_usb_gpib.o + + diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.c b/drivers/gpib/ni_usb/ni_usb_gpib.c new file mode 100644 index 000000000000..1f8412de9fa3 --- /dev/null +++ b/drivers/gpib/ni_usb/ni_usb_gpib.c @@ -0,0 +1,2678 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * driver for National Instruments usb to gpib adapters + * copyright : (C) 2004 by Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include +#include +#include +#include "ni_usb_gpib.h" +#include "gpibP.h" +#include "nec7210.h" +#include "tnt4882_registers.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for National Instruments USB devices"); + +#define MAX_NUM_NI_USB_INTERFACES 128 +static struct usb_interface *ni_usb_driver_interfaces[MAX_NUM_NI_USB_INTERFACES]; + +static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status); +static int ni_usb_set_interrupt_monitor(struct gpib_board *board, unsigned int monitored_bits); +static void ni_usb_stop(struct ni_usb_priv *ni_priv); + +static DEFINE_MUTEX(ni_usb_hotplug_lock); + +// calculates a reasonable timeout in that can be passed to usb functions +static inline unsigned long ni_usb_timeout_msecs(unsigned int usec) +{ + if (usec == 0) + return 0; + return 2000 + usec / 500; +}; + +// returns timeout code byte for use in ni-usb-b instructions +static unsigned short ni_usb_timeout_code(unsigned int usec) +{ + if (usec == 0) + return 0xf0; + else if (usec <= 10) + return 0xf1; + else if (usec <= 30) + return 0xf2; + else if (usec <= 100) + return 0xf3; + else if (usec <= 300) + return 0xf4; + else if (usec <= 1000) + return 0xf5; + else if (usec <= 3000) + return 0xf6; + else if (usec <= 10000) + return 0xf7; + else if (usec <= 30000) + return 0xf8; + else if (usec <= 100000) + return 0xf9; + else if (usec <= 300000) + return 0xfa; + else if (usec <= 1000000) + return 0xfb; + else if (usec <= 3000000) + return 0xfc; + else if (usec <= 10000000) + return 0xfd; + else if (usec <= 30000000) + return 0xfe; + else if (usec <= 100000000) + return 0xff; + else if (usec <= 300000000) + return 0x01; + /* + * NI driver actually uses 0xff for timeout T1000s, which is a bug in their code. + * I've verified on a usb-b that a code of 0x2 is correct for a 1000 sec timeout + */ + else if (usec <= 1000000000) + return 0x02; + pr_err("bug? usec is greater than 1e9\n"); + return 0xf0; +} + +static void ni_usb_bulk_complete(struct urb *urb) +{ + struct ni_usb_urb_ctx *context = urb->context; + + complete(&context->complete); +} + +static void ni_usb_timeout_handler(struct timer_list *t) +{ + struct ni_usb_priv *ni_priv = timer_container_of(ni_priv, t, + bulk_timer); + struct ni_usb_urb_ctx *context = &ni_priv->context; + + context->timed_out = 1; + complete(&context->complete); +}; + +// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases +static int ni_usb_nonblocking_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data, + int data_length, int *actual_data_length, + int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int out_pipe; + struct ni_usb_urb_ctx *context = &ni_priv->context; + + *actual_data_length = 0; + mutex_lock(&ni_priv->bulk_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENODEV; + } + if (ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -EAGAIN; + } + ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_pipe = usb_sndbulkpipe(usb_dev, ni_priv->bulk_out_endpoint); + init_completion(&context->complete); + context->timed_out = 0; + usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, out_pipe, data, data_length, + &ni_usb_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL); + if (retval) { + timer_delete_sync(&ni_priv->bulk_timer); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + dev_err(&usb_dev->dev, "failed to submit bulk out urb, retval=%i\n", + retval); + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; + } + mutex_unlock(&ni_priv->bulk_transfer_lock); + wait_for_completion(&context->complete); // wait for ni_usb_bulk_complete + if (context->timed_out) { + usb_kill_urb(ni_priv->bulk_urb); + dev_err(&usb_dev->dev, "killed urb due to timeout\n"); + retval = -ETIMEDOUT; + } else { + retval = ni_priv->bulk_urb->status; + } + + timer_delete_sync(&ni_priv->bulk_timer); + *actual_data_length = ni_priv->bulk_urb->actual_length; + mutex_lock(&ni_priv->bulk_transfer_lock); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; +} + +static int ni_usb_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data, int data_length, + int *actual_data_length, int timeout_msecs) +{ + int retval; + int timeout_msecs_remaining = timeout_msecs; + + retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length, actual_data_length, + timeout_msecs_remaining); + while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) { + usleep_range(1000, 1500); + retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length, + actual_data_length, + timeout_msecs_remaining); + if (timeout_msecs != 0) + --timeout_msecs_remaining; + } + if (timeout_msecs != 0 && timeout_msecs_remaining <= 0) + return -ETIMEDOUT; + return retval; +} + +// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases +static int ni_usb_nonblocking_receive_bulk_msg(struct ni_usb_priv *ni_priv, + void *data, int data_length, + int *actual_data_length, int timeout_msecs, + int interruptible) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + struct ni_usb_urb_ctx *context = &ni_priv->context; + + *actual_data_length = 0; + mutex_lock(&ni_priv->bulk_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENODEV; + } + if (ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -EAGAIN; + } + ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ni_priv->bulk_urb) { + mutex_unlock(&ni_priv->bulk_transfer_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + in_pipe = usb_rcvbulkpipe(usb_dev, ni_priv->bulk_in_endpoint); + init_completion(&context->complete); + context->timed_out = 0; + usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, in_pipe, data, data_length, + &ni_usb_bulk_complete, context); + + if (timeout_msecs) + mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); + + retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL); + if (retval) { + timer_delete_sync(&ni_priv->bulk_timer); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + dev_err(&usb_dev->dev, "failed to submit bulk in urb, retval=%i\n", retval); + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; + } + mutex_unlock(&ni_priv->bulk_transfer_lock); + if (interruptible) { + if (wait_for_completion_interruptible(&context->complete)) { + /* + * If we got interrupted by a signal while + * waiting for the usb gpib to respond, we + * should send a stop command so it will + * finish up with whatever it was doing and + * send its response now. + */ + ni_usb_stop(ni_priv); + retval = -ERESTARTSYS; + /* + * now do an uninterruptible wait, it shouldn't take long + * for the board to respond now. + */ + wait_for_completion(&context->complete); + } + } else { + wait_for_completion(&context->complete); + } + if (context->timed_out) { + usb_kill_urb(ni_priv->bulk_urb); + dev_err(&usb_dev->dev, "killed urb due to timeout\n"); + retval = -ETIMEDOUT; + } else { + if (ni_priv->bulk_urb->status) + retval = ni_priv->bulk_urb->status; + } + timer_delete_sync(&ni_priv->bulk_timer); + *actual_data_length = ni_priv->bulk_urb->actual_length; + mutex_lock(&ni_priv->bulk_transfer_lock); + usb_free_urb(ni_priv->bulk_urb); + ni_priv->bulk_urb = NULL; + mutex_unlock(&ni_priv->bulk_transfer_lock); + return retval; +} + +static int ni_usb_receive_bulk_msg(struct ni_usb_priv *ni_priv, void *data, + int data_length, int *actual_data_length, int timeout_msecs, + int interruptible) +{ + int retval; + int timeout_msecs_remaining = timeout_msecs; + + retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length, + actual_data_length, timeout_msecs_remaining, + interruptible); + while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) { + usleep_range(1000, 1500); + retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length, + actual_data_length, + timeout_msecs_remaining, + interruptible); + if (timeout_msecs != 0) + --timeout_msecs_remaining; + } + if (timeout_msecs && timeout_msecs_remaining <= 0) + return -ETIMEDOUT; + return retval; +} + +static int ni_usb_receive_control_msg(struct ni_usb_priv *ni_priv, __u8 request, + __u8 requesttype, __u16 value, __u16 index, + void *data, __u16 size, int timeout_msecs) +{ + struct usb_device *usb_dev; + int retval; + unsigned int in_pipe; + + mutex_lock(&ni_priv->control_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->control_transfer_lock); + return -ENODEV; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + in_pipe = usb_rcvctrlpipe(usb_dev, 0); + retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data, + size, timeout_msecs); + mutex_unlock(&ni_priv->control_transfer_lock); + return retval; +} + +static void ni_usb_soft_update_status(struct gpib_board *board, unsigned int ni_usb_ibsta, + unsigned int clear_mask) +{ + static const unsigned int ni_usb_ibsta_mask = SRQI | ATN | CIC | REM | LACS | TACS | LOK; + + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + unsigned int need_monitoring_bits = ni_usb_ibsta_monitor_mask; + unsigned long flags; + + board->status &= ~clear_mask; + board->status &= ~ni_usb_ibsta_mask; + board->status |= ni_usb_ibsta & ni_usb_ibsta_mask; + if (ni_usb_ibsta & DCAS) + push_gpib_event(board, EVENT_DEV_CLR); + if (ni_usb_ibsta & DTAS) + push_gpib_event(board, EVENT_DEV_TRG); + + spin_lock_irqsave(&board->spinlock, flags); +/* remove set status bits from monitored set why ?***/ + ni_priv->monitored_ibsta_bits &= ~ni_usb_ibsta; + need_monitoring_bits &= ~ni_priv->monitored_ibsta_bits; /* mm - monitored set */ + spin_unlock_irqrestore(&board->spinlock, flags); + dev_dbg(&usb_dev->dev, "need_monitoring_bits=0x%x\n", need_monitoring_bits); + + if (need_monitoring_bits & ~ni_usb_ibsta) + ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); + else if (need_monitoring_bits & ni_usb_ibsta) + wake_up_interruptible(&board->wait); + + dev_dbg(&usb_dev->dev, "ibsta=0x%x\n", ni_usb_ibsta); +} + +static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status) +{ + u16 count; + + status->id = buffer[0]; + status->ibsta = (buffer[1] << 8) | buffer[2]; + status->error_code = buffer[3]; + count = buffer[4] | (buffer[5] << 8); + count = ~count; + count++; + status->count = count; + return 8; +}; + +static void ni_usb_dump_raw_block(const u8 *raw_data, int length) +{ + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true); +} + +static int ni_usb_parse_register_read_block(const u8 *raw_data, unsigned int *results, + int num_results) +{ + int i = 0; + int j; + int unexpected = 0; + static const int results_per_chunk = 3; + + for (j = 0; j < num_results;) { + int k; + + if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_START_ID) { + pr_err("parse error: wrong start id\n"); + unexpected = 1; + } + for (k = 0; k < results_per_chunk && j < num_results; ++k) + results[j++] = raw_data[i++]; + } + while (i % 4) + i++; + if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_END_ID) { + pr_err("parse error: wrong end id\n"); + unexpected = 1; + } + if (raw_data[i++] % results_per_chunk != num_results % results_per_chunk) { + pr_err("parse error: wrong count=%i for NIUSB_REGISTER_READ_DATA_END\n", + (int)raw_data[i - 1]); + unexpected = 1; + } + while (i % 4) { + if (raw_data[i++] != 0) { + pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", + i - 1, (int)raw_data[i - 1]); + unexpected = 1; + } + } + if (unexpected) + ni_usb_dump_raw_block(raw_data, i); + return i; +} + +static int ni_usb_parse_termination_block(const u8 *buffer) +{ + int i = 0; + + if (buffer[i++] != NIUSB_TERM_ID || + buffer[i++] != 0x0 || + buffer[i++] != 0x0 || + buffer[i++] != 0x0) { + pr_err("received unexpected termination block\n"); + pr_err(" expected: 0x%x 0x%x 0x%x 0x%x\n", NIUSB_TERM_ID, 0x0, 0x0, 0x0); + pr_err(" received: 0x%x 0x%x 0x%x 0x%x\n", + buffer[i - 4], buffer[i - 3], buffer[i - 2], buffer[i - 1]); + } + return i; +}; + +static int parse_board_ibrd_readback(const u8 *raw_data, struct ni_usb_status_block *status, + u8 *parsed_data, int parsed_data_length, + int *actual_bytes_read) +{ + static const int ibrd_data_block_length = 0xf; + static const int ibrd_extended_data_block_length = 0x1e; + int data_block_length = 0; + int i = 0; + int j = 0; + int k; + int num_data_blocks = 0; + struct ni_usb_status_block register_write_status; + int unexpected = 0; + + while (raw_data[i] == NIUSB_IBRD_DATA_ID || raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) { + if (raw_data[i] == NIUSB_IBRD_DATA_ID) { + data_block_length = ibrd_data_block_length; + } else if (raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) { + data_block_length = ibrd_extended_data_block_length; + if (raw_data[++i] != 0) { + pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", + i, (int)raw_data[i]); + unexpected = 1; + } + } else { + pr_err("Unexpected NIUSB_IBRD ID\n"); + return -EINVAL; + } + ++i; + for (k = 0; k < data_block_length; k++) { + if (j < parsed_data_length) + parsed_data[j++] = raw_data[i++]; + else + ++i; + } + ++num_data_blocks; + } + i += ni_usb_parse_status_block(&raw_data[i], status); + if (status->id != NIUSB_IBRD_STATUS_ID) { + pr_err("bug: status->id=%i, != ibrd_status_id\n", status->id); + return -EIO; + } + i++; + if (num_data_blocks) { + *actual_bytes_read = (num_data_blocks - 1) * data_block_length + raw_data[i++]; + } else { + ++i; + *actual_bytes_read = 0; + } + if (*actual_bytes_read > j) + pr_err("bug: discarded data. actual_bytes_read=%i, j=%i\n", *actual_bytes_read, j); + for (k = 0; k < 2; k++) + if (raw_data[i++] != 0) { + pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", + i - 1, (int)raw_data[i - 1]); + unexpected = 1; + } + i += ni_usb_parse_status_block(&raw_data[i], ®ister_write_status); + if (register_write_status.id != NIUSB_REG_WRITE_ID) { + pr_err("unexpected data: register write status id=0x%x, expected 0x%x\n", + register_write_status.id, NIUSB_REG_WRITE_ID); + unexpected = 1; + } + if (raw_data[i++] != 2) { + pr_err("unexpected data: register write count=%i, expected 2\n", + (int)raw_data[i - 1]); + unexpected = 1; + } + for (k = 0; k < 3; k++) + if (raw_data[i++] != 0) { + pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", + i - 1, (int)raw_data[i - 1]); + unexpected = 1; + } + i += ni_usb_parse_termination_block(&raw_data[i]); + if (unexpected) + ni_usb_dump_raw_block(raw_data, i); + return i; +} + +static int ni_usb_parse_reg_write_status_block(const u8 *raw_data, + struct ni_usb_status_block *status, + int *writes_completed) +{ + int i = 0; + + i += ni_usb_parse_status_block(raw_data, status); + *writes_completed = raw_data[i++]; + while (i % 4) + i++; + return i; +} + +static int ni_usb_write_registers(struct ni_usb_priv *ni_priv, + const struct ni_usb_register *writes, int num_writes, + unsigned int *ibsta) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + u8 *out_data, *in_data; + int out_data_length; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + int j; + struct ni_usb_status_block status; + static const int bytes_per_write = 3; + int reg_writes_completed; + + out_data_length = num_writes * bytes_per_write + 0x10; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + i += ni_usb_bulk_register_write_header(&out_data[i], num_writes); + for (j = 0; j < num_writes; j++) + i += ni_usb_bulk_register_write(&out_data[i], writes[j]); + while (i % 4) + out_data[i++] = 0x00; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + if (retval || bytes_read != 16) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + ni_usb_dump_raw_block(in_data, bytes_read); + kfree(in_data); + return retval; + } + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + ni_usb_parse_reg_write_status_block(in_data, &status, ®_writes_completed); + // FIXME parse extra 09 status bits and termination + kfree(in_data); + if (status.id != NIUSB_REG_WRITE_ID) { + dev_err(&usb_dev->dev, "parse error, id=0x%x != NIUSB_REG_WRITE_ID\n", status.id); + return -EIO; + } + if (status.error_code) { + dev_err(&usb_dev->dev, "nonzero error code 0x%x\n", status.error_code); + return -EIO; + } + if (reg_writes_completed != num_writes) { + dev_err(&usb_dev->dev, "reg_writes_completed=%i, num_writes=%i\n", + reg_writes_completed, num_writes); + return -EIO; + } + if (ibsta) + *ibsta = status.ibsta; + return 0; +} + +// interface functions +static int ni_usb_read(struct gpib_board *board, u8 *buffer, size_t length, + int *end, size_t *bytes_read) +{ + int retval, parse_retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + static const int out_data_length = 0x20; + int in_data_length; + int usb_bytes_written = 0, usb_bytes_read = 0; + int i = 0; + int complement_count; + int actual_length; + struct ni_usb_status_block status; + static const int max_read_length = 0xffff; + struct ni_usb_register reg; + + *bytes_read = 0; + if (!ni_priv->bus_interface) + return -ENODEV; + if (length > max_read_length) + return -EINVAL; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = 0x0a; + out_data[i++] = ni_priv->eos_mode >> 8; + out_data[i++] = ni_priv->eos_char; + out_data[i++] = ni_usb_timeout_code(board->usec_timeout); + complement_count = length - 1; + complement_count = ~complement_count; + out_data[i++] = complement_count & 0xff; + out_data[i++] = (complement_count >> 8) & 0xff; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_register_write_header(&out_data[i], 2); + reg.device = NIUSB_SUBDEV_TNT4882; + reg.address = nec7210_to_tnt4882_offset(AUXMR); + reg.value = AUX_HLDI; + i += ni_usb_bulk_register_write(&out_data[i], reg); + reg.value = AUX_CLEAR_END; + i += ni_usb_bulk_register_write(&out_data[i], reg); + while (i % 4) // pad with zeros to 4-byte boundary + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written, 1000); + kfree(out_data); + if (retval || usb_bytes_written != i) { + if (retval == 0) + retval = -EIO; + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n", + retval, usb_bytes_written, i); + mutex_unlock(&ni_priv->addressed_transfer_lock); + return retval; + } + + in_data_length = (length / 30 + 1) * 0x20 + 0x20; + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read, + ni_usb_timeout_msecs(board->usec_timeout), 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if (retval == -ERESTARTSYS) { + } else if (retval) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, usb_bytes_read=%i\n", + retval, usb_bytes_read); + kfree(in_data); + return retval; + } + parse_retval = parse_board_ibrd_readback(in_data, &status, buffer, length, &actual_length); + if (parse_retval != usb_bytes_read) { + if (parse_retval >= 0) + parse_retval = -EIO; + dev_err(&usb_dev->dev, "retval=%i usb_bytes_read=%i\n", + parse_retval, usb_bytes_read); + kfree(in_data); + return parse_retval; + } + if (actual_length != length - status.count) { + dev_err(&usb_dev->dev, "actual_length=%i expected=%li\n", + actual_length, (long)(length - status.count)); + ni_usb_dump_raw_block(in_data, usb_bytes_read); + } + kfree(in_data); + switch (status.error_code) { + case NIUSB_NO_ERROR: + retval = 0; + break; + case NIUSB_ABORTED_ERROR: + /* + * this is expected if ni_usb_receive_bulk_msg got + * interrupted by a signal and returned -ERESTARTSYS + */ + break; + case NIUSB_ATN_STATE_ERROR: + if (status.ibsta & DCAS) { + retval = -EINTR; + } else { + retval = -EIO; + dev_dbg(&usb_dev->dev, "read when ATN set stat: 0x%06x\n", status.ibsta); + } + break; + case NIUSB_ADDRESSING_ERROR: + retval = -EIO; + break; + case NIUSB_TIMEOUT_ERROR: + retval = -ETIMEDOUT; + break; + case NIUSB_EOSMODE_ERROR: + dev_err(&usb_dev->dev, "driver bug, we should have been able to avoid NIUSB_EOSMODE_ERROR.\n"); + retval = -EINVAL; + break; + default: + dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code); + retval = -EIO; + break; + } + ni_usb_soft_update_status(board, status.ibsta, 0); + if (status.ibsta & END) + *end = 1; + else + *end = 0; + *bytes_read = actual_length; + return retval; +} + +static int ni_usb_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, size_t *bytes_written) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + int out_data_length; + static const int in_data_length = 0x10; + int usb_bytes_written = 0, usb_bytes_read = 0; + int i = 0, j; + int complement_count; + struct ni_usb_status_block status; + static const int max_write_length = 0xffff; + + if (!ni_priv->bus_interface) + return -ENODEV; + if (length > max_write_length) + return -EINVAL; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_data_length = length + 0x10; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = 0x0d; + complement_count = length - 1; + complement_count = ~complement_count; + out_data[i++] = complement_count & 0xff; + out_data[i++] = (complement_count >> 8) & 0xff; + out_data[i++] = ni_usb_timeout_code(board->usec_timeout); + out_data[i++] = 0x0; + out_data[i++] = 0x0; + if (send_eoi) + out_data[i++] = 0x8; + else + out_data[i++] = 0x0; + out_data[i++] = 0x0; + for (j = 0; j < length; j++) + out_data[i++] = buffer[j]; + while (i % 4) // pad with zeros to 4-byte boundary + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written, + ni_usb_timeout_msecs(board->usec_timeout)); + kfree(out_data); + if (retval || usb_bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n", + retval, usb_bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read, + ni_usb_timeout_msecs(board->usec_timeout), 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if ((retval && retval != -ERESTARTSYS) || usb_bytes_read != 12) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, usb_bytes_read=%i\n", + retval, usb_bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + switch (status.error_code) { + case NIUSB_NO_ERROR: + retval = 0; + break; + case NIUSB_ABORTED_ERROR: + /* + * this is expected if ni_usb_receive_bulk_msg got + * interrupted by a signal and returned -ERESTARTSYS + */ + break; + case NIUSB_ADDRESSING_ERROR: + dev_err(&usb_dev->dev, "Addressing error retval %d error code=%i\n", + retval, status.error_code); + retval = -ENXIO; + break; + case NIUSB_NO_LISTENER_ERROR: + retval = -ECOMM; + break; + case NIUSB_TIMEOUT_ERROR: + retval = -ETIMEDOUT; + break; + default: + dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code); + retval = -EPIPE; + break; + } + ni_usb_soft_update_status(board, status.ibsta, 0); + *bytes_written = length - status.count; + return retval; +} + +static int ni_usb_command_chunk(struct gpib_board *board, u8 *buffer, size_t length, + size_t *command_bytes_written) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + int out_data_length; + static const int in_data_length = 0x10; + int bytes_written = 0, bytes_read = 0; + int i = 0, j; + unsigned int complement_count; + struct ni_usb_status_block status; + // usb-b gives error 4 if you try to send more than 16 command bytes at once + static const int max_command_length = 0x10; + + *command_bytes_written = 0; + if (!ni_priv->bus_interface) + return -ENODEV; + if (length > max_command_length) + length = max_command_length; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_data_length = length + 0x10; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = 0x0c; + complement_count = length - 1; + complement_count = ~complement_count; + out_data[i++] = complement_count; + out_data[i++] = 0x0; + out_data[i++] = ni_usb_timeout_code(board->usec_timeout); + for (j = 0; j < length; j++) + out_data[i++] = buffer[j]; + while (i % 4) // pad with zeros to 4-byte boundary + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, + ni_usb_timeout_msecs(board->usec_timeout)); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, + ni_usb_timeout_msecs(board->usec_timeout), 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + *command_bytes_written = length - status.count; + switch (status.error_code) { + case NIUSB_NO_ERROR: + break; + case NIUSB_ABORTED_ERROR: + /* + * this is expected if ni_usb_receive_bulk_msg got + * interrupted by a signal and returned -ERESTARTSYS + */ + break; + case NIUSB_NO_BUS_ERROR: + return -ENOTCONN; + case NIUSB_EOSMODE_ERROR: + dev_err(&usb_dev->dev, "got eosmode error. Driver bug?\n"); + return -EIO; + case NIUSB_TIMEOUT_ERROR: + return -ETIMEDOUT; + default: + dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code); + return -EIO; + } + ni_usb_soft_update_status(board, status.ibsta, 0); + return 0; +} + +static int ni_usb_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + size_t count; + int retval; + + *bytes_written = 0; + while (*bytes_written < length) { + retval = ni_usb_command_chunk(board, buffer + *bytes_written, + length - *bytes_written, &count); + *bytes_written += count; + if (retval < 0) + return retval; + } + return 0; +} + +static int ni_usb_take_control(struct gpib_board *board, int synchronous) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x10; + int bytes_written = 0, bytes_read = 0; + int i = 0; + struct ni_usb_status_block status; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + out_data[i++] = NIUSB_IBCAC_ID; + if (synchronous) + out_data[i++] = 0x1; + else + out_data[i++] = 0x0; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 1); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) { + if (retval == 0) + retval = -EIO; + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + ni_usb_soft_update_status(board, status.ibsta, 0); + return retval; +} + +static int ni_usb_go_to_standby(struct gpib_board *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + struct ni_usb_status_block status; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = NIUSB_IBGTS_ID; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + + mutex_lock(&ni_priv->addressed_transfer_lock); + + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if (retval || bytes_read != 12) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + kfree(in_data); + return retval; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + if (status.id != NIUSB_IBGTS_ID) + dev_err(&usb_dev->dev, "bug: status.id 0x%x != INUSB_IBGTS_ID\n", status.id); + ni_usb_soft_update_status(board, status.ibsta, 0); + return 0; +} + +static int ni_usb_request_system_control(struct gpib_board *board, int request_control) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int i = 0; + struct ni_usb_register writes[4]; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + if (request_control) { + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + writes[i].value = SETSC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CIFC; + i++; + } else { + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CREN; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CIFC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_DSC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + writes[i].value = CLRSC; + i++; + } + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return retval; + } + if (!request_control) + ni_priv->ren_state = 0; + ni_usb_soft_update_status(board, ibsta, 0); + return 0; +} + +// FIXME maybe the interface should have a "pulse interface clear" function that can return an error? +static void ni_usb_interface_clear(struct gpib_board *board, int assert) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x10; + int bytes_written = 0, bytes_read = 0; + int i = 0; + struct ni_usb_status_block status; + + if (!ni_priv->bus_interface) + return; // -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); +// FIXME: we are going to pulse when assert is true, and ignore otherwise + if (assert == 0) + return; + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return; + out_data[i++] = NIUSB_IBSIC_ID; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + return; + } + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) + return; + + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + if (retval || bytes_read != 12) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + kfree(in_data); + return; + } + ni_usb_parse_status_block(in_data, &status); + kfree(in_data); + ni_usb_soft_update_status(board, status.ibsta, 0); +} + +static void ni_usb_remote_enable(struct gpib_board *board, int enable) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + struct ni_usb_register reg; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return; // -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + reg.device = NIUSB_SUBDEV_TNT4882; + reg.address = nec7210_to_tnt4882_offset(AUXMR); + if (enable) + reg.value = AUX_SREN; + else + reg.value = AUX_CREN; + retval = ni_usb_write_registers(ni_priv, ®, 1, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return; //retval; + } + ni_priv->ren_state = enable; + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static int ni_usb_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct ni_usb_priv *ni_priv = board->private_data; + + ni_priv->eos_char = eos_byte; + ni_priv->eos_mode |= REOS; + if (compare_8_bits) + ni_priv->eos_mode |= BIN; + else + ni_priv->eos_mode &= ~BIN; + return 0; +} + +static void ni_usb_disable_eos(struct gpib_board *board) +{ + struct ni_usb_priv *ni_priv = board->private_data; + /* + * adapter gets unhappy if you don't zero all the bits + * for the eos mode and eos char (returns error 4 on reads). + */ + ni_priv->eos_mode = 0; + ni_priv->eos_char = 0; +} + +static unsigned int ni_usb_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + static const int buffer_length = 8; + u8 *buffer; + struct ni_usb_status_block status; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return board->status; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x200, 0x0, buffer, buffer_length, 1000); + if (retval != buffer_length) { + dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval); + kfree(buffer); + return board->status; + } + ni_usb_parse_status_block(buffer, &status); + kfree(buffer); + ni_usb_soft_update_status(board, status.ibsta, clear_mask); + return board->status; +} + +// tells ni-usb to immediately stop an ongoing i/o operation +static void ni_usb_stop(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + static const int buffer_length = 8; + u8 *buffer; + struct ni_usb_status_block status; + + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_STOP_REQUEST, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, buffer_length, 1000); + if (retval != buffer_length) { + dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval); + kfree(buffer); + return; + } + ni_usb_parse_status_block(buffer, &status); + kfree(buffer); +} + +static int ni_usb_primary_address(struct gpib_board *board, unsigned int address) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int i = 0; + struct ni_usb_register writes[2]; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADR); + writes[i].value = address; + i++; + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x0; + writes[i].value = address; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return 0; +} + +static int ni_usb_write_sad(struct ni_usb_register *writes, int address, int enable) +{ + unsigned int adr_bits, admr_bits; + int i = 0; + + adr_bits = HR_ARS; + admr_bits = HR_TRM0 | HR_TRM1; + if (enable) { + adr_bits |= address; + admr_bits |= HR_ADM1; + } else { + adr_bits |= HR_DT | HR_DL; + admr_bits |= HR_ADM0; + } + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADR); + writes[i].value = adr_bits; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADMR); + writes[i].value = admr_bits; + i++; + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x1; + writes[i].value = enable ? MSA(address) : 0x0; + i++; + return i; +} + +static int ni_usb_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int i = 0; + struct ni_usb_register writes[3]; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + i += ni_usb_write_sad(writes, address, enable); + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return 0; +} + +static int ni_usb_parallel_poll(struct gpib_board *board, u8 *result) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + static const int out_data_length = 0x10; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + int j = 0; + struct ni_usb_status_block status; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + out_data[i++] = NIUSB_IBRPP_ID; + out_data[i++] = 0xf0; // FIXME: this should be the parallel poll timeout code + out_data[i++] = 0x0; + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + /*FIXME: 1000 should use parallel poll timeout (not supported yet)*/ + retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + + kfree(out_data); + if (retval || bytes_written != i) { + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + return retval; + } + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) + return -ENOMEM; + + /*FIXME: should use parallel poll timeout (not supported yet)*/ + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, + &bytes_read, 1000, 1); + + if (retval && retval != -ERESTARTSYS) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + kfree(in_data); + return retval; + } + j += ni_usb_parse_status_block(in_data, &status); + *result = in_data[j++]; + kfree(in_data); + ni_usb_soft_update_status(board, status.ibsta, 0); + return retval; +} + +static void ni_usb_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return; // -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = PPR | config; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static void ni_usb_parallel_poll_response(struct gpib_board *board, int ist) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return; // -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + if (ist) + writes[i].value = AUX_SPPF; + else + writes[i].value = AUX_CPPF; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static void ni_usb_serial_poll_response(struct gpib_board *board, u8 status) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return; // -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(SPMR); + writes[i].value = status; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static u8 ni_usb_serial_poll_status(struct gpib_board *board) +{ + return 0; +} + +static void ni_usb_return_to_local(struct gpib_board *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int i = 0; + struct ni_usb_register writes[1]; + unsigned int ibsta; + + if (!ni_priv->bus_interface) + return; // -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_RTL; + i++; + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return;// retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return;// 0; +} + +static int ni_usb_line_status(const struct gpib_board *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + u8 *out_data, *in_data; + static const int out_data_length = 0x20; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + unsigned int bsr_bits; + int line_status = VALID_ALL; + // NI windows driver reads 0xd(HSSEL), 0xc (ARD0), 0x1f (BSR) + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) + return -ENOMEM; + + /* line status gets called during ibwait */ + retval = mutex_trylock(&ni_priv->addressed_transfer_lock); + + if (retval == 0) { + kfree(out_data); + return -EBUSY; + } + i += ni_usb_bulk_register_read_header(&out_data[i], 1); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_TNT4882, BSR); + while (i % 4) + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); + kfree(out_data); + if (retval || bytes_written != i) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", + retval, bytes_written, i); + return retval; + } + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) { + mutex_unlock(&ni_priv->addressed_transfer_lock); + return -ENOMEM; + } + retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, in_data, in_data_length, + &bytes_read, 1000, 0); + + mutex_unlock(&ni_priv->addressed_transfer_lock); + + if (retval) { + if (retval != -EAGAIN) + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + kfree(in_data); + return retval; + } + + ni_usb_parse_register_read_block(in_data, &bsr_bits, 1); + kfree(in_data); + if (bsr_bits & BCSR_REN_BIT) + line_status |= BUS_REN; + if (bsr_bits & BCSR_IFC_BIT) + line_status |= BUS_IFC; + if (bsr_bits & BCSR_SRQ_BIT) + line_status |= BUS_SRQ; + if (bsr_bits & BCSR_EOI_BIT) + line_status |= BUS_EOI; + if (bsr_bits & BCSR_NRFD_BIT) + line_status |= BUS_NRFD; + if (bsr_bits & BCSR_NDAC_BIT) + line_status |= BUS_NDAC; + if (bsr_bits & BCSR_DAV_BIT) + line_status |= BUS_DAV; + if (bsr_bits & BCSR_ATN_BIT) + line_status |= BUS_ATN; + return line_status; +} + +static int ni_usb_setup_t1_delay(struct ni_usb_register *reg, unsigned int nano_sec, + unsigned int *actual_ns) +{ + int i = 0; + + *actual_ns = 2000; + + reg[i].device = NIUSB_SUBDEV_TNT4882; + reg[i].address = nec7210_to_tnt4882_offset(AUXMR); + if (nano_sec <= 1100) { + reg[i].value = AUXRI | USTD | SISB; + *actual_ns = 1100; + } else { + reg[i].value = AUXRI | SISB; + } + i++; + reg[i].device = NIUSB_SUBDEV_TNT4882; + reg[i].address = nec7210_to_tnt4882_offset(AUXMR); + if (nano_sec <= 500) { + reg[i].value = AUXRB | HR_TRI; + *actual_ns = 500; + } else { + reg[i].value = AUXRB; + } + i++; + reg[i].device = NIUSB_SUBDEV_TNT4882; + reg[i].address = KEYREG; + if (nano_sec <= 350) { + reg[i].value = MSTD; + *actual_ns = 350; + } else { + reg[i].value = 0x0; + } + i++; + return i; +} + +static int ni_usb_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + struct ni_usb_register writes[3]; + unsigned int ibsta; + unsigned int actual_ns; + int i; + + if (!ni_priv->bus_interface) + return -ENODEV; + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + i = ni_usb_setup_t1_delay(writes, nano_sec, &actual_ns); + retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); + if (retval < 0) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return retval; + } + board->t1_nano_sec = actual_ns; + ni_usb_soft_update_status(board, ibsta, 0); + return actual_ns; +} + +static int ni_usb_allocate_private(struct gpib_board *board) +{ + struct ni_usb_priv *ni_priv; + + board->private_data = kmalloc(sizeof(struct ni_usb_priv), GFP_KERNEL); + if (!board->private_data) + return -ENOMEM; + ni_priv = board->private_data; + memset(ni_priv, 0, sizeof(struct ni_usb_priv)); + mutex_init(&ni_priv->bulk_transfer_lock); + mutex_init(&ni_priv->control_transfer_lock); + mutex_init(&ni_priv->interrupt_transfer_lock); + mutex_init(&ni_priv->addressed_transfer_lock); + return 0; +} + +static void ni_usb_free_private(struct ni_usb_priv *ni_priv) +{ + usb_free_urb(ni_priv->interrupt_urb); + kfree(ni_priv); +} + +#define NUM_INIT_WRITES 26 +static int ni_usb_setup_init(struct gpib_board *board, struct ni_usb_register *writes) +{ + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + unsigned int mask, actual_ns; + int i = 0; + + writes[i].device = NIUSB_SUBDEV_UNKNOWN3; + writes[i].address = 0x10; + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + writes[i].value = SOFT_RESET; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + mask = AUXRA | HR_HLDA; + if (ni_priv->eos_mode & BIN) + mask |= HR_BIN; + writes[i].value = mask; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = AUXCR; + writes[i].value = mask; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = HSSEL; + writes[i].value = TNT_ONE_CHIP_BIT; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CR; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = IMR0; + writes[i].value = TNT_IMR0_ALWAYS_BITS; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(IMR1); + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(IMR2); + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = IMR3; + writes[i].value = 0x0; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_HLDI; + i++; + + i += ni_usb_setup_t1_delay(&writes[i], board->t1_nano_sec, &actual_ns); + + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUXRG | NTNL_BIT; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = CMDR; + if (board->master) + mask = SETSC; // set system controller + else + mask = CLRSC; // clear system controller + writes[i].value = mask; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CIFC; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(ADR); + writes[i].value = board->pad; + i++; + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x0; + writes[i].value = board->pad; + i++; + + i += ni_usb_write_sad(&writes[i], board->sad, board->sad >= 0); + + writes[i].device = NIUSB_SUBDEV_UNKNOWN2; + writes[i].address = 0x2; // could this be a timeout ? + writes[i].value = 0xfd; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = 0xf; // undocumented address + writes[i].value = 0x11; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_PON; + i++; + writes[i].device = NIUSB_SUBDEV_TNT4882; + writes[i].address = nec7210_to_tnt4882_offset(AUXMR); + writes[i].value = AUX_CPPF; + i++; + if (i > NUM_INIT_WRITES) { + dev_err(&usb_dev->dev, "bug!, buffer overrun, i=%i\n", i); + return 0; + } + return i; +} + +static int ni_usb_init(struct gpib_board *board) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + struct ni_usb_register *writes; + unsigned int ibsta; + int writes_len; + + writes = kmalloc_array(NUM_INIT_WRITES, sizeof(*writes), GFP_KERNEL); + if (!writes) + return -ENOMEM; + + writes_len = ni_usb_setup_init(board, writes); + if (writes_len) + retval = ni_usb_write_registers(ni_priv, writes, writes_len, &ibsta); + else + return -EFAULT; + kfree(writes); + if (retval) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return retval; + } + ni_usb_soft_update_status(board, ibsta, 0); + return 0; +} + +static void ni_usb_interrupt_complete(struct urb *urb) +{ + struct gpib_board *board = urb->context; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + struct ni_usb_status_block status; + unsigned long flags; + + switch (urb->status) { + /* success */ + case 0: + break; + /* unlinked, don't resubmit */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* other error, resubmit */ + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); + return; + } + + ni_usb_parse_status_block(urb->transfer_buffer, &status); + + spin_lock_irqsave(&board->spinlock, flags); + ni_priv->monitored_ibsta_bits &= ~status.ibsta; + spin_unlock_irqrestore(&board->spinlock, flags); + + wake_up_interruptible(&board->wait); + + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC); + if (retval) + dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); +} + +static int ni_usb_set_interrupt_monitor(struct gpib_board *board, unsigned int monitored_bits) +{ + int retval; + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + static const int buffer_length = 8; + u8 *buffer; + struct ni_usb_status_block status; + unsigned long flags; + + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + spin_lock_irqsave(&board->spinlock, flags); + ni_priv->monitored_ibsta_bits = ni_usb_ibsta_monitor_mask & monitored_bits; + spin_unlock_irqrestore(&board->spinlock, flags); + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x300, ni_usb_ibsta_monitor_mask & monitored_bits, + buffer, buffer_length, 1000); + if (retval != buffer_length) { + dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval); + kfree(buffer); + return -1; + } + ni_usb_parse_status_block(buffer, &status); + kfree(buffer); + return 0; +} + +static int ni_usb_setup_urbs(struct gpib_board *board) +{ + struct ni_usb_priv *ni_priv = board->private_data; + struct usb_device *usb_dev; + int int_pipe; + int retval; + + if (ni_priv->interrupt_in_endpoint < 0) + return 0; + + mutex_lock(&ni_priv->interrupt_transfer_lock); + if (!ni_priv->bus_interface) { + mutex_unlock(&ni_priv->interrupt_transfer_lock); + return -ENODEV; + } + ni_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ni_priv->interrupt_urb) { + mutex_unlock(&ni_priv->interrupt_transfer_lock); + return -ENOMEM; + } + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int_pipe = usb_rcvintpipe(usb_dev, ni_priv->interrupt_in_endpoint); + usb_fill_int_urb(ni_priv->interrupt_urb, usb_dev, int_pipe, ni_priv->interrupt_buffer, + sizeof(ni_priv->interrupt_buffer), &ni_usb_interrupt_complete, board, 1); + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL); + mutex_unlock(&ni_priv->interrupt_transfer_lock); + if (retval) { + dev_err(&usb_dev->dev, "failed to submit first interrupt urb, retval=%i\n", retval); + return retval; + } + return 0; +} + +static void ni_usb_cleanup_urbs(struct ni_usb_priv *ni_priv) +{ + if (ni_priv && ni_priv->bus_interface) { + if (ni_priv->interrupt_urb) + usb_kill_urb(ni_priv->interrupt_urb); + if (ni_priv->bulk_urb) + usb_kill_urb(ni_priv->bulk_urb); + } +} + +static int ni_usb_b_read_serial_number(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + u8 *out_data; + u8 *in_data; + static const int out_data_length = 0x20; + static const int in_data_length = 0x20; + int bytes_written = 0, bytes_read = 0; + int i = 0; + static const int num_reads = 4; + unsigned int results[4]; + int j; + unsigned int serial_number; + + in_data = kmalloc(in_data_length, GFP_KERNEL); + if (!in_data) + return -ENOMEM; + + out_data = kmalloc(out_data_length, GFP_KERNEL); + if (!out_data) { + kfree(in_data); + return -ENOMEM; + } + i += ni_usb_bulk_register_read_header(&out_data[i], num_reads); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_1_REG); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_2_REG); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_3_REG); + i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_4_REG); + while (i % 4) + out_data[i++] = 0x0; + i += ni_usb_bulk_termination(&out_data[i]); + retval = ni_usb_send_bulk_msg(ni_priv, out_data, out_data_length, &bytes_written, 1000); + if (retval) { + dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%li\n", + retval, bytes_written, (long)out_data_length); + goto serial_out; + } + retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); + if (retval) { + dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", + retval, bytes_read); + ni_usb_dump_raw_block(in_data, bytes_read); + goto serial_out; + } + if (ARRAY_SIZE(results) < num_reads) { + dev_err(&usb_dev->dev, "serial number eetup bug\n"); + retval = -EINVAL; + goto serial_out; + } + ni_usb_parse_register_read_block(in_data, results, num_reads); + serial_number = 0; + for (j = 0; j < num_reads; ++j) + serial_number |= (results[j] & 0xff) << (8 * j); + dev_dbg(&usb_dev->dev, "board serial number is 0x%x\n", serial_number); + retval = 0; +serial_out: + kfree(in_data); + kfree(out_data); + return retval; +} + +static int ni_usb_hs_wait_for_ready(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + static const int buffer_size = 0x10; + static const int timeout = 50; + static const int msec_sleep_duration = 100; + int i; int retval; + int j; + int unexpected = 0; + unsigned int serial_number; + u8 *buffer; + + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_SERIAL_NUMBER_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, buffer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", + NI_USB_SERIAL_NUMBER_REQUEST, retval); + goto ready_out; + } + j = 0; + if (buffer[j] != NI_USB_SERIAL_NUMBER_REQUEST) { + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n", + j, (int)buffer[j], NI_USB_SERIAL_NUMBER_REQUEST); + unexpected = 1; + } + if (unexpected) + ni_usb_dump_raw_block(buffer, retval); + // NI-USB-HS+ pads the serial with 0x0 to make 16 bytes + if (retval != 5 && retval != 16) { + dev_err(&usb_dev->dev, "received unexpected number of bytes = %i, expected 5 or 16\n", + retval); + ni_usb_dump_raw_block(buffer, retval); + } + serial_number = 0; + serial_number |= buffer[++j]; + serial_number |= (buffer[++j] << 8); + serial_number |= (buffer[++j] << 16); + serial_number |= (buffer[++j] << 24); + dev_dbg(&usb_dev->dev, "board serial number is 0x%x\n", serial_number); + for (i = 0; i < timeout; ++i) { + int ready = 0; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_POLL_READY_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, buffer_size, 100); + if (retval < 0) { + dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", + NI_USB_POLL_READY_REQUEST, retval); + goto ready_out; + } + j = 0; + unexpected = 0; + if (buffer[j] != NI_USB_POLL_READY_REQUEST) { // [0] + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n", + j, (int)buffer[j], NI_USB_POLL_READY_REQUEST); + unexpected = 1; + } + ++j; + if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [1] HS+ sends 0x0 + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n", + j, (int)buffer[j]); + unexpected = 1; + } + if (buffer[++j] != 0x0) { // [2] + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n", + j, (int)buffer[j], 0x0); + unexpected = 1; + } + ++j; + /* + * MC usb-488 (and sometimes NI-USB-HS?) sends 0x8 here; MC usb-488A sends 0x7 here + * NI-USB-HS+ sends 0x0 + */ + if (buffer[j] != 0x1 && buffer[j] != 0x8 && buffer[j] != 0x7 && buffer[j] != 0x0) { + // [3] + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0, 0x1, 0x7 or 0x8\n", + j, (int)buffer[j]); + unexpected = 1; + } + ++j; + // NI-USB-HS+ sends 0 here + if (buffer[j] != 0x30 && buffer[j] != 0x0) { // [4] + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x30\n", + j, (int)buffer[j]); + unexpected = 1; + } + ++j; + // MC usb-488 (and sometimes NI-USB-HS?) and NI-USB-HS+ sends 0x0 here + if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [5] + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n", + j, (int)buffer[j]); + unexpected = 1; + } + if (buffer[++j] != 0x0) { // [6] + ready = 1; + // NI-USB-HS+ sends 0xf or 0x19 here + if (buffer[j] != 0x2 && buffer[j] != 0xe && buffer[j] != 0xf && + buffer[j] != 0x16 && buffer[j] != 0x19) { + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x2, 0xe, 0xf, 0x16 or 0x19\n", + j, (int)buffer[j]); + unexpected = 1; + } + } + if (buffer[++j] != 0x0) { // [7] + ready = 1; + // MC usb-488 sends 0x5 here; MC usb-488A sends 0x6 here + if (buffer[j] != 0x3 && buffer[j] != 0x5 && buffer[j] != 0x6 && + buffer[j] != 0x8) { + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x3 or 0x5, 0x6 or 0x08\n", + j, (int)buffer[j]); + unexpected = 1; + } + } + ++j; + if (buffer[j] != 0x0 && buffer[j] != 0x2) { // [8] MC usb-488 sends 0x2 here + dev_err(&usb_dev->dev, " unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x2\n", + j, (int)buffer[j]); + unexpected = 1; + } + ++j; + // MC usb-488A and NI-USB-HS sends 0x3 here; NI-USB-HS+ sends 0x30 here + if (buffer[j] != 0x0 && buffer[j] != 0x3 && buffer[j] != 0x30) { // [9] + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0, 0x3 or 0x30\n", + j, (int)buffer[j]); + unexpected = 1; + } + if (buffer[++j] != 0x0) { // [10] MC usb-488 sends 0x7 here, new HS+ sends 0x59 + ready = 1; + if (buffer[j] != 0x96 && buffer[j] != 0x7 && buffer[j] != 0x6e && + buffer[j] != 0x59) { + dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x96, 0x07, 0x6e or 0x59\n", + j, (int)buffer[j]); + unexpected = 1; + } + } + if (unexpected) + ni_usb_dump_raw_block(buffer, retval); + if (ready) + break; + retval = msleep_interruptible(msec_sleep_duration); + if (retval) { + retval = -ERESTARTSYS; + goto ready_out; + } + } + retval = 0; + +ready_out: + kfree(buffer); + dev_dbg(&usb_dev->dev, "exit retval=%d\n", retval); + return retval; +} + +/* + * This does some extra init for HS+ models, as observed on Windows. One of the + * control requests causes the LED to stop blinking. + * I'm not sure what the other 2 requests do. None of these requests are actually required + * for the adapter to work, maybe they do some init for the analyzer interface + * (which we don't use). + */ +static int ni_usb_hs_plus_extra_init(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + u8 *buffer; + static const int buffer_size = 16; + int transfer_size; + + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + do { + transfer_size = 16; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0x48_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0x0, buffer, transfer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", + NI_USB_HS_PLUS_0x48_REQUEST, retval); + break; + } + // expected response data: 48 f3 30 00 00 00 00 00 00 00 00 00 00 00 00 00 + if (buffer[0] != NI_USB_HS_PLUS_0x48_REQUEST) + dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n", + (int)buffer[0], NI_USB_HS_PLUS_0x48_REQUEST); + + transfer_size = 2; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_LED_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x1, 0x0, buffer, transfer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", + NI_USB_HS_PLUS_LED_REQUEST, retval); + break; + } + // expected response data: 4b 00 + if (buffer[0] != NI_USB_HS_PLUS_LED_REQUEST) + dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n", + (int)buffer[0], NI_USB_HS_PLUS_LED_REQUEST); + + transfer_size = 9; + + retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0xf8_REQUEST, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + 0x0, 0x1, buffer, transfer_size, 1000); + if (retval < 0) { + dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", + NI_USB_HS_PLUS_0xf8_REQUEST, retval); + break; + } + // expected response data: f8 01 00 00 00 01 00 00 00 + if (buffer[0] != NI_USB_HS_PLUS_0xf8_REQUEST) + dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n", + (int)buffer[0], NI_USB_HS_PLUS_0xf8_REQUEST); + } while (0); + + // cleanup + kfree(buffer); + return retval; +} + +static inline int ni_usb_device_match(struct usb_interface *interface, + const struct gpib_board_config *config) +{ + if (gpib_match_device_path(&interface->dev, config->device_path) == 0) + return 0; + return 1; +} + +static int ni_usb_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + int retval; + int i, index; + struct ni_usb_priv *ni_priv; + int product_id; + struct usb_device *usb_dev; + + mutex_lock(&ni_usb_hotplug_lock); + retval = ni_usb_allocate_private(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + ni_priv = board->private_data; + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] && + !usb_get_intfdata(ni_usb_driver_interfaces[i]) && + ni_usb_device_match(ni_usb_driver_interfaces[i], config)) { + ni_priv->bus_interface = ni_usb_driver_interfaces[i]; + usb_set_intfdata(ni_usb_driver_interfaces[i], board); + usb_dev = interface_to_usbdev(ni_priv->bus_interface); + index = i; + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + mutex_unlock(&ni_usb_hotplug_lock); + dev_err(board->gpib_dev, "No supported adapters found, have you loaded its firmware?\n"); + return -ENODEV; + } + if (usb_reset_configuration(interface_to_usbdev(ni_priv->bus_interface))) + dev_err(&usb_dev->dev, "usb_reset_configuration() failed.\n"); + + product_id = le16_to_cpu(usb_dev->descriptor.idProduct); + ni_priv->product_id = product_id; + + timer_setup(&ni_priv->bulk_timer, ni_usb_timeout_handler, 0); + + switch (product_id) { + case USB_DEVICE_ID_NI_USB_B: + ni_priv->bulk_out_endpoint = NIUSB_B_BULK_OUT_ENDPOINT; + ni_priv->bulk_in_endpoint = NIUSB_B_BULK_IN_ENDPOINT; + ni_priv->interrupt_in_endpoint = NIUSB_B_INTERRUPT_IN_ENDPOINT; + ni_usb_b_read_serial_number(ni_priv); + break; + case USB_DEVICE_ID_NI_USB_HS: + case USB_DEVICE_ID_MC_USB_488: + case USB_DEVICE_ID_KUSB_488A: + ni_priv->bulk_out_endpoint = NIUSB_HS_BULK_OUT_ENDPOINT; + ni_priv->bulk_in_endpoint = NIUSB_HS_BULK_IN_ENDPOINT; + ni_priv->interrupt_in_endpoint = NIUSB_HS_INTERRUPT_IN_ENDPOINT; + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + case USB_DEVICE_ID_NI_USB_HS_PLUS: + ni_priv->bulk_out_endpoint = NIUSB_HS_PLUS_BULK_OUT_ENDPOINT; + ni_priv->bulk_in_endpoint = NIUSB_HS_PLUS_BULK_IN_ENDPOINT; + ni_priv->interrupt_in_endpoint = NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT; + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_hs_plus_extra_init(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + default: + mutex_unlock(&ni_usb_hotplug_lock); + dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id %x\n", + product_id); + return -EINVAL; + } + + retval = ni_usb_setup_urbs(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_set_interrupt_monitor(board, 0); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + + board->t1_nano_sec = 500; + + retval = ni_usb_init(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + + mutex_unlock(&ni_usb_hotplug_lock); + dev_info(&usb_dev->dev, + "bus %d dev num %d attached to gpib%d, intf %i\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, index); + return retval; +} + +static int ni_usb_shutdown_hardware(struct ni_usb_priv *ni_priv) +{ + struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); + int retval; + struct ni_usb_register writes[2]; + static const int writes_length = ARRAY_SIZE(writes); + unsigned int ibsta; + + writes[0].device = NIUSB_SUBDEV_TNT4882; + writes[0].address = nec7210_to_tnt4882_offset(AUXMR); + writes[0].value = AUX_CR; + writes[1].device = NIUSB_SUBDEV_UNKNOWN3; + writes[1].address = 0x10; + writes[1].value = 0x0; + retval = ni_usb_write_registers(ni_priv, writes, writes_length, &ibsta); + if (retval) { + dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); + return retval; + } + return 0; +} + +static void ni_usb_detach(struct gpib_board *board) +{ + struct ni_usb_priv *ni_priv; + + mutex_lock(&ni_usb_hotplug_lock); + /* + * under windows, software unplug does chip_reset nec7210 aux command, + * then writes 0x0 to address 0x10 of device 3 + */ + ni_priv = board->private_data; + if (ni_priv) { + if (ni_priv->bus_interface) { + ni_usb_set_interrupt_monitor(board, 0); + ni_usb_shutdown_hardware(ni_priv); + usb_set_intfdata(ni_priv->bus_interface, NULL); + } + mutex_lock(&ni_priv->bulk_transfer_lock); + mutex_lock(&ni_priv->control_transfer_lock); + mutex_lock(&ni_priv->interrupt_transfer_lock); + ni_usb_cleanup_urbs(ni_priv); + ni_usb_free_private(ni_priv); + } + mutex_unlock(&ni_usb_hotplug_lock); +} + +static struct gpib_interface ni_usb_gpib_interface = { + .name = "ni_usb_b", + .attach = ni_usb_attach, + .detach = ni_usb_detach, + .read = ni_usb_read, + .write = ni_usb_write, + .command = ni_usb_command, + .take_control = ni_usb_take_control, + .go_to_standby = ni_usb_go_to_standby, + .request_system_control = ni_usb_request_system_control, + .interface_clear = ni_usb_interface_clear, + .remote_enable = ni_usb_remote_enable, + .enable_eos = ni_usb_enable_eos, + .disable_eos = ni_usb_disable_eos, + .parallel_poll = ni_usb_parallel_poll, + .parallel_poll_configure = ni_usb_parallel_poll_configure, + .parallel_poll_response = ni_usb_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ni_usb_line_status, + .update_status = ni_usb_update_status, + .primary_address = ni_usb_primary_address, + .secondary_address = ni_usb_secondary_address, + .serial_poll_response = ni_usb_serial_poll_response, + .serial_poll_status = ni_usb_serial_poll_status, + .t1_delay = ni_usb_t1_delay, + .return_to_local = ni_usb_return_to_local, + .skip_check_for_command_acceptors = 1 +}; + +// Table with the USB-devices: just now only testing IDs +static struct usb_device_id ni_usb_driver_device_table[] = { + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_B)}, + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS)}, + // gpib-usb-hs+ has a second interface for the analyzer, which we ignore + {USB_DEVICE_INTERFACE_NUMBER(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS_PLUS, 0)}, + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_KUSB_488A)}, + {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_MC_USB_488)}, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, ni_usb_driver_device_table); + +static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + int i; + char *path; + static const int path_length = 1024; + + mutex_lock(&ni_usb_hotplug_lock); + usb_get_dev(usb_dev); + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (!ni_usb_driver_interfaces[i]) { + ni_usb_driver_interfaces[i] = interface; + usb_set_intfdata(interface, NULL); + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + usb_put_dev(usb_dev); + mutex_unlock(&ni_usb_hotplug_lock); + dev_err(&usb_dev->dev, "ni_usb_driver_interfaces[] full\n"); + return -1; + } + path = kmalloc(path_length, GFP_KERNEL); + if (!path) { + usb_put_dev(usb_dev); + mutex_unlock(&ni_usb_hotplug_lock); + return -ENOMEM; + } + usb_make_path(usb_dev, path, path_length); + dev_info(&usb_dev->dev, "probe succeeded for path: %s\n", path); + kfree(path); + mutex_unlock(&ni_usb_hotplug_lock); + return 0; +} + +static void ni_usb_driver_disconnect(struct usb_interface *interface) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + int i; + + mutex_lock(&ni_usb_hotplug_lock); + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] == interface) { + struct gpib_board *board = usb_get_intfdata(interface); + + if (board) { + struct ni_usb_priv *ni_priv = board->private_data; + + if (ni_priv) { + mutex_lock(&ni_priv->bulk_transfer_lock); + mutex_lock(&ni_priv->control_transfer_lock); + mutex_lock(&ni_priv->interrupt_transfer_lock); + ni_usb_cleanup_urbs(ni_priv); + ni_priv->bus_interface = NULL; + mutex_unlock(&ni_priv->interrupt_transfer_lock); + mutex_unlock(&ni_priv->control_transfer_lock); + mutex_unlock(&ni_priv->bulk_transfer_lock); + } + } + ni_usb_driver_interfaces[i] = NULL; + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) + dev_err(&usb_dev->dev, "unable to find interface bug?\n"); + usb_put_dev(usb_dev); + mutex_unlock(&ni_usb_hotplug_lock); +} + +static int ni_usb_driver_suspend(struct usb_interface *interface, pm_message_t message) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + struct gpib_board *board; + int i, retval; + + mutex_lock(&ni_usb_hotplug_lock); + + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] == interface) { + board = usb_get_intfdata(interface); + if (board) + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + mutex_unlock(&ni_usb_hotplug_lock); + return 0; + } + + struct ni_usb_priv *ni_priv = board->private_data; + + if (ni_priv) { + ni_usb_set_interrupt_monitor(board, 0); + retval = ni_usb_shutdown_hardware(ni_priv); + if (retval) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + if (ni_priv->interrupt_urb) { + mutex_lock(&ni_priv->interrupt_transfer_lock); + ni_usb_cleanup_urbs(ni_priv); + mutex_unlock(&ni_priv->interrupt_transfer_lock); + } + dev_dbg(&usb_dev->dev, + "bus %d dev num %d gpib%d, interface %i suspended\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + } + + mutex_unlock(&ni_usb_hotplug_lock); + return 0; +} + +static int ni_usb_driver_resume(struct usb_interface *interface) +{ + struct usb_device *usb_dev = interface_to_usbdev(interface); + + struct gpib_board *board; + int i, retval; + + mutex_lock(&ni_usb_hotplug_lock); + + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { + if (ni_usb_driver_interfaces[i] == interface) { + board = usb_get_intfdata(interface); + if (board) + break; + } + } + if (i == MAX_NUM_NI_USB_INTERFACES) { + mutex_unlock(&ni_usb_hotplug_lock); + return 0; + } + + struct ni_usb_priv *ni_priv = board->private_data; + + if (ni_priv) { + if (ni_priv->interrupt_urb) { + mutex_lock(&ni_priv->interrupt_transfer_lock); + retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL); + if (retval) { + dev_err(&usb_dev->dev, "resume failed to resubmit interrupt urb, retval=%i\n", + retval); + mutex_unlock(&ni_priv->interrupt_transfer_lock); + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + mutex_unlock(&ni_priv->interrupt_transfer_lock); + } else { + dev_err(&usb_dev->dev, "bug! resume int urb not set up\n"); + mutex_unlock(&ni_usb_hotplug_lock); + return -EINVAL; + } + + switch (ni_priv->product_id) { + case USB_DEVICE_ID_NI_USB_B: + ni_usb_b_read_serial_number(ni_priv); + break; + case USB_DEVICE_ID_NI_USB_HS: + case USB_DEVICE_ID_MC_USB_488: + case USB_DEVICE_ID_KUSB_488A: + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + case USB_DEVICE_ID_NI_USB_HS_PLUS: + retval = ni_usb_hs_wait_for_ready(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_hs_plus_extra_init(ni_priv); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + break; + default: + mutex_unlock(&ni_usb_hotplug_lock); + dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id\n"); + return -EINVAL; + } + + retval = ni_usb_set_interrupt_monitor(board, 0); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + + retval = ni_usb_init(board); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); + if (retval < 0) { + mutex_unlock(&ni_usb_hotplug_lock); + return retval; + } + if (board->master) + ni_usb_interface_clear(board, 1); // this is a pulsed action + if (ni_priv->ren_state) + ni_usb_remote_enable(board, 1); + + dev_dbg(&usb_dev->dev, + "bus %d dev num %d gpib%d, interface %i resumed\n", + usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); + } + + mutex_unlock(&ni_usb_hotplug_lock); + return 0; +} + +static struct usb_driver ni_usb_bus_driver = { + .name = DRV_NAME, + .probe = ni_usb_driver_probe, + .disconnect = ni_usb_driver_disconnect, + .suspend = ni_usb_driver_suspend, + .resume = ni_usb_driver_resume, + .id_table = ni_usb_driver_device_table, +}; + +static int __init ni_usb_init_module(void) +{ + int i; + int ret; + + for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) + ni_usb_driver_interfaces[i] = NULL; + + ret = usb_register(&ni_usb_bus_driver); + if (ret) { + pr_err("usb_register failed: error = %d\n", ret); + return ret; + } + + ret = gpib_register_driver(&ni_usb_gpib_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + return ret; + } + + return 0; +} + +static void __exit ni_usb_exit_module(void) +{ + gpib_unregister_driver(&ni_usb_gpib_interface); + usb_deregister(&ni_usb_bus_driver); +} + +module_init(ni_usb_init_module); +module_exit(ni_usb_exit_module); diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.h b/drivers/gpib/ni_usb/ni_usb_gpib.h new file mode 100644 index 000000000000..688f5e08792f --- /dev/null +++ b/drivers/gpib/ni_usb/ni_usb_gpib.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/*************************************************************************** + * copyright : (C) 2004 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _NI_USB_GPIB_H +#define _NI_USB_GPIB_H + +#include +#include +#include +#include +#include "gpibP.h" + +enum { + USB_VENDOR_ID_NI = 0x3923 +}; + +enum { + USB_DEVICE_ID_NI_USB_B = 0x702a, + USB_DEVICE_ID_NI_USB_B_PREINIT = 0x702b, // device id before firmware is loaded + USB_DEVICE_ID_NI_USB_HS = 0x709b, + USB_DEVICE_ID_NI_USB_HS_PLUS = 0x7618, + USB_DEVICE_ID_KUSB_488A = 0x725c, + USB_DEVICE_ID_MC_USB_488 = 0x725d +}; + +enum ni_usb_device { + NIUSB_SUBDEV_TNT4882 = 1, + NIUSB_SUBDEV_UNKNOWN2 = 2, + NIUSB_SUBDEV_UNKNOWN3 = 3, +}; + +enum endpoint_addresses { + NIUSB_B_BULK_OUT_ENDPOINT = 0x2, + NIUSB_B_BULK_IN_ENDPOINT = 0x2, + NIUSB_B_BULK_IN_ALT_ENDPOINT = 0x6, + NIUSB_B_INTERRUPT_IN_ENDPOINT = 0x4, +}; + +enum hs_enpoint_addresses { + NIUSB_HS_BULK_OUT_ENDPOINT = 0x2, + NIUSB_HS_BULK_OUT_ALT_ENDPOINT = 0x6, + NIUSB_HS_BULK_IN_ENDPOINT = 0x4, + NIUSB_HS_BULK_IN_ALT_ENDPOINT = 0x8, + NIUSB_HS_INTERRUPT_IN_ENDPOINT = 0x1, +}; + +enum hs_plus_endpoint_addresses { + NIUSB_HS_PLUS_BULK_OUT_ENDPOINT = 0x1, + NIUSB_HS_PLUS_BULK_OUT_ALT_ENDPOINT = 0x4, + NIUSB_HS_PLUS_BULK_IN_ENDPOINT = 0x2, + NIUSB_HS_PLUS_BULK_IN_ALT_ENDPOINT = 0x5, + NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT = 0x3, +}; + +struct ni_usb_urb_ctx { + struct completion complete; + unsigned timed_out : 1; +}; + +// struct which defines private_data for ni_usb devices +struct ni_usb_priv { + struct usb_interface *bus_interface; + int bulk_out_endpoint; + int bulk_in_endpoint; + int interrupt_in_endpoint; + u8 eos_char; + unsigned short eos_mode; + unsigned int monitored_ibsta_bits; + struct urb *bulk_urb; + struct urb *interrupt_urb; + u8 interrupt_buffer[0x11]; + struct mutex addressed_transfer_lock; // protect transfer lock + struct mutex bulk_transfer_lock; // protect bulk message sends + struct mutex control_transfer_lock; // protect control messages + struct mutex interrupt_transfer_lock; // protect interrupt messages + struct timer_list bulk_timer; + struct ni_usb_urb_ctx context; + int product_id; + unsigned short ren_state; +}; + +struct ni_usb_status_block { + short id; + unsigned short ibsta; + short error_code; + unsigned short count; +}; + +struct ni_usb_register { + enum ni_usb_device device; + short address; + unsigned short value; +}; + +enum ni_usb_bulk_ids { + NIUSB_IBCAC_ID = 0x1, + NIUSB_UNKNOWN3_ID = 0x3, // device level function id? + NIUSB_TERM_ID = 0x4, + NIUSB_IBGTS_ID = 0x6, + NIUSB_IBRPP_ID = 0x7, + NIUSB_REG_READ_ID = 0x8, + NIUSB_REG_WRITE_ID = 0x9, + NIUSB_IBSIC_ID = 0xf, + NIUSB_REGISTER_READ_DATA_START_ID = 0x34, + NIUSB_REGISTER_READ_DATA_END_ID = 0x35, + NIUSB_IBRD_DATA_ID = 0x36, + NIUSB_IBRD_EXTENDED_DATA_ID = 0x37, + NIUSB_IBRD_STATUS_ID = 0x38 +}; + +enum ni_usb_error_codes { + NIUSB_NO_ERROR = 0, + /* + * NIUSB_ABORTED_ERROR occurs when I/O is interrupted early by + * doing a NI_USB_STOP_REQUEST on the control endpoint. + */ + NIUSB_ABORTED_ERROR = 1, + /* + * NIUSB_READ_ATN_ERROR occurs when you do a board read while + * ATN is set + */ + NIUSB_ATN_STATE_ERROR = 2, + /* + * NIUSB_ADDRESSING_ERROR occurs when you do a board + * read/write as CIC but are not in LACS/TACS + */ + NIUSB_ADDRESSING_ERROR = 3, + /* + * NIUSB_EOSMODE_ERROR occurs on reads if any eos mode or char + * bits are set when REOS is not set. + * Have also seen error 4 if you try to send more than 16 + * command bytes at once on a usb-b. + */ + NIUSB_EOSMODE_ERROR = 4, + /* + * NIUSB_NO_BUS_ERROR occurs when you try to write a command + * byte but there are no devices connected to the gpib bus + */ + NIUSB_NO_BUS_ERROR = 5, + /* + * NIUSB_NO_LISTENER_ERROR occurs when you do a board write as + * CIC with no listener + */ + NIUSB_NO_LISTENER_ERROR = 8, + /* get NIUSB_TIMEOUT_ERROR on board read/write timeout */ + NIUSB_TIMEOUT_ERROR = 10, +}; + +enum ni_usb_control_requests { + NI_USB_STOP_REQUEST = 0x20, + NI_USB_WAIT_REQUEST = 0x21, + NI_USB_POLL_READY_REQUEST = 0x40, + NI_USB_SERIAL_NUMBER_REQUEST = 0x41, + NI_USB_HS_PLUS_0x48_REQUEST = 0x48, + NI_USB_HS_PLUS_LED_REQUEST = 0x4b, + NI_USB_HS_PLUS_0xf8_REQUEST = 0xf8 +}; + +static const unsigned int ni_usb_ibsta_monitor_mask = + SRQI | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS; + +static inline int nec7210_to_tnt4882_offset(int offset) +{ + return 2 * offset; +}; + +static inline int ni_usb_bulk_termination(u8 *buffer) +{ + int i = 0; + + buffer[i++] = NIUSB_TERM_ID; + buffer[i++] = 0x0; + buffer[i++] = 0x0; + buffer[i++] = 0x0; + return i; +} + +enum ni_usb_unknown3_register { + SERIAL_NUMBER_4_REG = 0x8, + SERIAL_NUMBER_3_REG = 0x9, + SERIAL_NUMBER_2_REG = 0xa, + SERIAL_NUMBER_1_REG = 0xb, +}; + +static inline int ni_usb_bulk_register_write_header(u8 *buffer, int num_writes) +{ + int i = 0; + + buffer[i++] = NIUSB_REG_WRITE_ID; + buffer[i++] = num_writes; + buffer[i++] = 0x0; + return i; +} + +static inline int ni_usb_bulk_register_write(u8 *buffer, struct ni_usb_register reg) +{ + int i = 0; + + buffer[i++] = reg.device; + buffer[i++] = reg.address; + buffer[i++] = reg.value; + return i; +} + +static inline int ni_usb_bulk_register_read_header(u8 *buffer, int num_reads) +{ + int i = 0; + + buffer[i++] = NIUSB_REG_READ_ID; + buffer[i++] = num_reads; + return i; +} + +static inline int ni_usb_bulk_register_read(u8 *buffer, int device, int address) +{ + int i = 0; + + buffer[i++] = device; + buffer[i++] = address; + return i; +} + +#endif // _NI_USB_GPIB_H diff --git a/drivers/gpib/pc2/Makefile b/drivers/gpib/pc2/Makefile new file mode 100644 index 000000000000..481ee4296e1b --- /dev/null +++ b/drivers/gpib/pc2/Makefile @@ -0,0 +1,5 @@ + +obj-$(CONFIG_GPIB_PC2) += pc2_gpib.o + + + diff --git a/drivers/gpib/pc2/pc2_gpib.c b/drivers/gpib/pc2/pc2_gpib.c new file mode 100644 index 000000000000..9f3943d1df66 --- /dev/null +++ b/drivers/gpib/pc2/pc2_gpib.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nec7210.h" +#include "gpibP.h" + +// struct which defines private_data for pc2 driver +struct pc2_priv { + struct nec7210_priv nec7210_priv; + unsigned int irq; + // io address that clears interrupt for pc2a (0x2f0 + irq) + unsigned int clear_intr_addr; +}; + +// pc2 uses 8 consecutive io addresses +static const int pc2_iosize = 8; +static const int pc2a_iosize = 8; +static const int pc2_2a_iosize = 16; + +// offset between io addresses of successive nec7210 registers +static const int pc2a_reg_offset = 0x400; +static const int pc2_reg_offset = 1; + +// interrupt service routine +static irqreturn_t pc2_interrupt(int irq, void *arg); +static irqreturn_t pc2a_interrupt(int irq, void *arg); + +// pc2 specific registers and bits + +// interrupt clear register address +static const int pc2a_clear_intr_iobase = 0x2f0; +static inline unsigned int CLEAR_INTR_REG(unsigned int irq) +{ + return pc2a_clear_intr_iobase + irq; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for PC2/PC2a and compatible devices"); + +/* + * GPIB interrupt service routines + */ + +irqreturn_t pc2_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + struct pc2_priv *priv = board->private_data; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = nec7210_interrupt(board, &priv->nec7210_priv); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +irqreturn_t pc2a_interrupt(int irq, void *arg) +{ + struct gpib_board *board = arg; + struct pc2_priv *priv = board->private_data; + int status1, status2; + unsigned long flags; + irqreturn_t retval; + + spin_lock_irqsave(&board->spinlock, flags); + // read interrupt status (also clears status) + status1 = read_byte(&priv->nec7210_priv, ISR1); + status2 = read_byte(&priv->nec7210_priv, ISR2); + /* clear interrupt circuit */ + if (priv->irq) + outb(0xff, CLEAR_INTR_REG(priv->irq)); + retval = nec7210_interrupt_have_status(board, &priv->nec7210_priv, status1, status2); + spin_unlock_irqrestore(&board->spinlock, flags); + return retval; +} + +// wrappers for interface functions +static int pc2_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); +} + +static int pc2_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int pc2_command(struct gpib_board *board, u8 *buffer, + size_t length, size_t *bytes_written) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int pc2_take_control(struct gpib_board *board, int synchronous) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int pc2_go_to_standby(struct gpib_board *board) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static int pc2_request_system_control(struct gpib_board *board, int request_control) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_request_system_control(board, &priv->nec7210_priv, request_control); +} + +static void pc2_interface_clear(struct gpib_board *board, int assert) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void pc2_remote_enable(struct gpib_board *board, int enable) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int pc2_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void pc2_disable_eos(struct gpib_board *board) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int pc2_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); +} + +static int pc2_primary_address(struct gpib_board *board, unsigned int address) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int pc2_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int pc2_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_parallel_poll(board, &priv->nec7210_priv, result); +} + +static void pc2_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); +} + +static void pc2_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +static void pc2_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static u8 pc2_serial_poll_status(struct gpib_board *board) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static int pc2_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct pc2_priv *priv = board->private_data; + + return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec); +} + +static void pc2_return_to_local(struct gpib_board *board) +{ + struct pc2_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +static int allocate_private(struct gpib_board *board) +{ + struct pc2_priv *priv; + + board->private_data = kmalloc(sizeof(struct pc2_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + priv = board->private_data; + memset(priv, 0, sizeof(struct pc2_priv)); + init_nec7210_private(&priv->nec7210_priv); + return 0; +} + +static void free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static int pc2_generic_attach(struct gpib_board *board, const struct gpib_board_config *config, + enum nec7210_chipset chipset) +{ + struct pc2_priv *pc2_priv; + struct nec7210_priv *nec_priv; + + board->status = 0; + if (allocate_private(board)) + return -ENOMEM; + pc2_priv = board->private_data; + nec_priv = &pc2_priv->nec7210_priv; + nec_priv->read_byte = nec7210_ioport_read_byte; + nec_priv->write_byte = nec7210_ioport_write_byte; + nec_priv->type = chipset; + +#ifndef PC2_DMA + /* + * board->dev hasn't been initialized, so forget about DMA until this driver + * is adapted to use isa_register_driver. + */ + if (config->ibdma) + // driver needs to be adapted to use isa_register_driver to get a struct device* + dev_err(board->gpib_dev, "DMA disabled for pc2 gpib"); +#else + if (config->ibdma) { + nec_priv->dma_buffer_length = 0x1000; + nec_priv->dma_buffer = dma_alloc_coherent(board->dev, + nec_priv->dma_buffer_length, & + nec_priv->dma_buffer_addr, GFP_ATOMIC); + if (!nec_priv->dma_buffer) + return -ENOMEM; + + // request isa dma channel + if (request_dma(config->ibdma, "pc2")) { + dev_err(board->gpib_dev, "can't request DMA %d\n", config->ibdma); + return -1; + } + nec_priv->dma_channel = config->ibdma; + } +#endif + + return 0; +} + +static int pc2_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + int isr_flags = 0; + struct pc2_priv *pc2_priv; + struct nec7210_priv *nec_priv; + int retval; + + retval = pc2_generic_attach(board, config, NEC7210); + if (retval) + return retval; + + pc2_priv = board->private_data; + nec_priv = &pc2_priv->nec7210_priv; + nec_priv->offset = pc2_reg_offset; + + if (!request_region(config->ibbase, pc2_iosize, "pc2")) { + dev_err(board->gpib_dev, "ioports are already in use\n"); + return -EBUSY; + } + nec_priv->iobase = config->ibbase; + + nec7210_board_reset(nec_priv, board); + + // install interrupt handler + if (config->ibirq) { + if (request_irq(config->ibirq, pc2_interrupt, isr_flags, "pc2", board)) { + dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq); + return -EBUSY; + } + } + pc2_priv->irq = config->ibirq; + /* poll so we can detect assertion of ATN */ + if (gpib_request_pseudo_irq(board, pc2_interrupt)) { + dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n"); + return -1; + } + /* set internal counter register for 8 MHz input clock */ + write_byte(nec_priv, ICR | 8, AUXMR); + + nec7210_board_online(nec_priv, board); + + return 0; +} + +static void pc2_detach(struct gpib_board *board) +{ + struct pc2_priv *pc2_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (pc2_priv) { + nec_priv = &pc2_priv->nec7210_priv; +#ifdef PC2_DMA + if (nec_priv->dma_channel) + free_dma(nec_priv->dma_channel); +#endif + gpib_free_pseudo_irq(board); + if (pc2_priv->irq) + free_irq(pc2_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + release_region(nec_priv->iobase, pc2_iosize); + } + if (nec_priv->dma_buffer) { + dma_free_coherent(board->dev, nec_priv->dma_buffer_length, + nec_priv->dma_buffer, nec_priv->dma_buffer_addr); + nec_priv->dma_buffer = NULL; + } + } + free_private(board); +} + +static int pc2a_common_attach(struct gpib_board *board, const struct gpib_board_config *config, + unsigned int num_registers, enum nec7210_chipset chipset) +{ + unsigned int i, j; + struct pc2_priv *pc2_priv; + struct nec7210_priv *nec_priv; + int retval; + + retval = pc2_generic_attach(board, config, chipset); + if (retval) + return retval; + + pc2_priv = board->private_data; + nec_priv = &pc2_priv->nec7210_priv; + nec_priv->offset = pc2a_reg_offset; + + switch (config->ibbase) { + case 0x02e1: + case 0x22e1: + case 0x42e1: + case 0x62e1: + break; + default: + dev_err(board->gpib_dev, "PCIIa base range invalid, must be one of 0x[0246]2e1, but is 0x%x\n", + config->ibbase); + return -1; + } + + if (config->ibirq) { + if (config->ibirq < 2 || config->ibirq > 7) { + dev_err(board->gpib_dev, "illegal interrupt level %i\n", + config->ibirq); + return -1; + } + } else { + dev_err(board->gpib_dev, "interrupt disabled, using polling mode (slow)\n"); + } +#ifdef CHECK_IOPORTS + unsigned int err = 0; + + for (i = 0; i < num_registers; i++) { + if (check_region(config->ibbase + i * pc2a_reg_offset, 1)) + err++; + } + if (config->ibirq && check_region(pc2a_clear_intr_iobase + config->ibirq, 1)) + err++; + if (err) { + dev_err(board->gpib_dev, "ioports are already in use"); + return -EBUSY; + } +#endif + for (i = 0; i < num_registers; i++) { + if (!request_region(config->ibbase + + i * pc2a_reg_offset, 1, "pc2a")) { + dev_err(board->gpib_dev, "ioports are already in use"); + for (j = 0; j < i; j++) + release_region(config->ibbase + + j * pc2a_reg_offset, 1); + return -EBUSY; + } + } + nec_priv->iobase = config->ibbase; + if (config->ibirq) { + if (!request_region(pc2a_clear_intr_iobase + config->ibirq, 1, "pc2a")) { + dev_err(board->gpib_dev, "ioports are already in use"); + return -1; + } + pc2_priv->clear_intr_addr = pc2a_clear_intr_iobase + config->ibirq; + if (request_irq(config->ibirq, pc2a_interrupt, 0, "pc2a", board)) { + dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq); + return -EBUSY; + } + } + pc2_priv->irq = config->ibirq; + /* poll so we can detect assertion of ATN */ + if (gpib_request_pseudo_irq(board, pc2_interrupt)) { + dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n"); + return -1; + } + + // make sure interrupt is clear + if (pc2_priv->irq) + outb(0xff, CLEAR_INTR_REG(pc2_priv->irq)); + + nec7210_board_reset(nec_priv, board); + + /* set internal counter register for 8 MHz input clock */ + write_byte(nec_priv, ICR | 8, AUXMR); + + nec7210_board_online(nec_priv, board); + + return 0; +} + +static int pc2a_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + return pc2a_common_attach(board, config, pc2a_iosize, NEC7210); +} + +static int pc2a_cb7210_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + return pc2a_common_attach(board, config, pc2a_iosize, CB7210); +} + +static int pc2_2a_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + return pc2a_common_attach(board, config, pc2_2a_iosize, NAT4882); +} + +static void pc2a_common_detach(struct gpib_board *board, unsigned int num_registers) +{ + int i; + struct pc2_priv *pc2_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (pc2_priv) { + nec_priv = &pc2_priv->nec7210_priv; +#ifdef PC2_DMA + if (nec_priv->dma_channel) + free_dma(nec_priv->dma_channel); +#endif + gpib_free_pseudo_irq(board); + if (pc2_priv->irq) + free_irq(pc2_priv->irq, board); + if (nec_priv->iobase) { + nec7210_board_reset(nec_priv, board); + for (i = 0; i < num_registers; i++) + release_region(nec_priv->iobase + + i * pc2a_reg_offset, 1); + } + if (pc2_priv->clear_intr_addr) + release_region(pc2_priv->clear_intr_addr, 1); + if (nec_priv->dma_buffer) { + dma_free_coherent(board->dev, nec_priv->dma_buffer_length, + nec_priv->dma_buffer, + nec_priv->dma_buffer_addr); + nec_priv->dma_buffer = NULL; + } + } + free_private(board); +} + +static void pc2a_detach(struct gpib_board *board) +{ + pc2a_common_detach(board, pc2a_iosize); +} + +static void pc2_2a_detach(struct gpib_board *board) +{ + pc2a_common_detach(board, pc2_2a_iosize); +} + +static struct gpib_interface pc2_interface = { + .name = "pcII", + .attach = pc2_attach, + .detach = pc2_detach, + .read = pc2_read, + .write = pc2_write, + .command = pc2_command, + .take_control = pc2_take_control, + .go_to_standby = pc2_go_to_standby, + .request_system_control = pc2_request_system_control, + .interface_clear = pc2_interface_clear, + .remote_enable = pc2_remote_enable, + .enable_eos = pc2_enable_eos, + .disable_eos = pc2_disable_eos, + .parallel_poll = pc2_parallel_poll, + .parallel_poll_configure = pc2_parallel_poll_configure, + .parallel_poll_response = pc2_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = NULL, + .update_status = pc2_update_status, + .primary_address = pc2_primary_address, + .secondary_address = pc2_secondary_address, + .serial_poll_response = pc2_serial_poll_response, + .serial_poll_status = pc2_serial_poll_status, + .t1_delay = pc2_t1_delay, + .return_to_local = pc2_return_to_local, +}; + +static struct gpib_interface pc2a_interface = { + .name = "pcIIa", + .attach = pc2a_attach, + .detach = pc2a_detach, + .read = pc2_read, + .write = pc2_write, + .command = pc2_command, + .take_control = pc2_take_control, + .go_to_standby = pc2_go_to_standby, + .request_system_control = pc2_request_system_control, + .interface_clear = pc2_interface_clear, + .remote_enable = pc2_remote_enable, + .enable_eos = pc2_enable_eos, + .disable_eos = pc2_disable_eos, + .parallel_poll = pc2_parallel_poll, + .parallel_poll_configure = pc2_parallel_poll_configure, + .parallel_poll_response = pc2_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = NULL, + .update_status = pc2_update_status, + .primary_address = pc2_primary_address, + .secondary_address = pc2_secondary_address, + .serial_poll_response = pc2_serial_poll_response, + .serial_poll_status = pc2_serial_poll_status, + .t1_delay = pc2_t1_delay, + .return_to_local = pc2_return_to_local, +}; + +static struct gpib_interface pc2a_cb7210_interface = { + .name = "pcIIa_cb7210", + .attach = pc2a_cb7210_attach, + .detach = pc2a_detach, + .read = pc2_read, + .write = pc2_write, + .command = pc2_command, + .take_control = pc2_take_control, + .go_to_standby = pc2_go_to_standby, + .request_system_control = pc2_request_system_control, + .interface_clear = pc2_interface_clear, + .remote_enable = pc2_remote_enable, + .enable_eos = pc2_enable_eos, + .disable_eos = pc2_disable_eos, + .parallel_poll = pc2_parallel_poll, + .parallel_poll_configure = pc2_parallel_poll_configure, + .parallel_poll_response = pc2_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = NULL, // XXX + .update_status = pc2_update_status, + .primary_address = pc2_primary_address, + .secondary_address = pc2_secondary_address, + .serial_poll_response = pc2_serial_poll_response, + .serial_poll_status = pc2_serial_poll_status, + .t1_delay = pc2_t1_delay, + .return_to_local = pc2_return_to_local, +}; + +static struct gpib_interface pc2_2a_interface = { + .name = "pcII_IIa", + .attach = pc2_2a_attach, + .detach = pc2_2a_detach, + .read = pc2_read, + .write = pc2_write, + .command = pc2_command, + .take_control = pc2_take_control, + .go_to_standby = pc2_go_to_standby, + .request_system_control = pc2_request_system_control, + .interface_clear = pc2_interface_clear, + .remote_enable = pc2_remote_enable, + .enable_eos = pc2_enable_eos, + .disable_eos = pc2_disable_eos, + .parallel_poll = pc2_parallel_poll, + .parallel_poll_configure = pc2_parallel_poll_configure, + .parallel_poll_response = pc2_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = NULL, + .update_status = pc2_update_status, + .primary_address = pc2_primary_address, + .secondary_address = pc2_secondary_address, + .serial_poll_response = pc2_serial_poll_response, + .serial_poll_status = pc2_serial_poll_status, + .t1_delay = pc2_t1_delay, + .return_to_local = pc2_return_to_local, +}; + +static int __init pc2_init_module(void) +{ + int ret; + + ret = gpib_register_driver(&pc2_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + return ret; + } + + ret = gpib_register_driver(&pc2a_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pc2a; + } + + ret = gpib_register_driver(&pc2a_cb7210_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_cb7210; + } + + ret = gpib_register_driver(&pc2_2a_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pc2_2a; + } + + return 0; + +err_pc2_2a: + gpib_unregister_driver(&pc2a_cb7210_interface); +err_cb7210: + gpib_unregister_driver(&pc2a_interface); +err_pc2a: + gpib_unregister_driver(&pc2_interface); + + return ret; +} + +static void __exit pc2_exit_module(void) +{ + gpib_unregister_driver(&pc2_interface); + gpib_unregister_driver(&pc2a_interface); + gpib_unregister_driver(&pc2a_cb7210_interface); + gpib_unregister_driver(&pc2_2a_interface); +} + +module_init(pc2_init_module); +module_exit(pc2_exit_module); + diff --git a/drivers/gpib/tms9914/Makefile b/drivers/gpib/tms9914/Makefile new file mode 100644 index 000000000000..4705ab07f413 --- /dev/null +++ b/drivers/gpib/tms9914/Makefile @@ -0,0 +1,6 @@ + +obj-$(CONFIG_GPIB_TMS9914) += tms9914.o + + + + diff --git a/drivers/gpib/tms9914/tms9914.c b/drivers/gpib/tms9914/tms9914.c new file mode 100644 index 000000000000..72a11596a35e --- /dev/null +++ b/drivers/gpib/tms9914/tms9914.c @@ -0,0 +1,914 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpibP.h" +#include "tms9914.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB library for tms9914"); + +static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv); + +int tms9914_take_control(struct gpib_board *board, struct tms9914_priv *priv, int synchronous) +{ + int i; + const int timeout = 100; + + if (synchronous) + write_byte(priv, AUX_TCS, AUXCR); + else + write_byte(priv, AUX_TCA, AUXCR); + // busy wait until ATN is asserted + for (i = 0; i < timeout; i++) { + if ((read_byte(priv, ADSR) & HR_ATN)) + break; + udelay(1); + } + if (i == timeout) + return -ETIMEDOUT; + + clear_bit(WRITE_READY_BN, &priv->state); + + return 0; +} +EXPORT_SYMBOL_GPL(tms9914_take_control); + +/* + * The agilent 82350B has a buggy implementation of tcs which interferes with the + * operation of tca. It appears to be based on the controller state machine + * described in the TI 9900 TMS9914A data manual published in 1982. This + * manual describes tcs as putting the controller into a CWAS + * state where it waits indefinitely for ANRS and ignores tca. Since a + * functioning tca is far more important than tcs, we work around the + * problem by never issuing tcs. + * + * I don't know if this problem exists in the real tms9914a or just in the fpga + * of the 82350B. For now, only the agilent_82350b uses this workaround. + * The rest of the tms9914 based drivers still use tms9914_take_control + * directly (which does issue tcs). + */ +int tms9914_take_control_workaround(struct gpib_board *board, + struct tms9914_priv *priv, int synchronous) +{ + if (synchronous) + return -ETIMEDOUT; + return tms9914_take_control(board, priv, synchronous); +} +EXPORT_SYMBOL_GPL(tms9914_take_control_workaround); + +int tms9914_go_to_standby(struct gpib_board *board, struct tms9914_priv *priv) +{ + int i; + const int timeout = 1000; + + write_byte(priv, AUX_GTS, AUXCR); + // busy wait until ATN is released + for (i = 0; i < timeout; i++) { + if ((read_byte(priv, ADSR) & HR_ATN) == 0) + break; + udelay(1); + } + if (i == timeout) + return -ETIMEDOUT; + + clear_bit(COMMAND_READY_BN, &priv->state); + + return 0; +} +EXPORT_SYMBOL_GPL(tms9914_go_to_standby); + +void tms9914_interface_clear(struct gpib_board *board, struct tms9914_priv *priv, int assert) +{ + if (assert) { + write_byte(priv, AUX_SIC | AUX_CS, AUXCR); + + set_bit(CIC_NUM, &board->status); + } else { + write_byte(priv, AUX_SIC, AUXCR); + } +} +EXPORT_SYMBOL_GPL(tms9914_interface_clear); + +void tms9914_remote_enable(struct gpib_board *board, struct tms9914_priv *priv, int enable) +{ + if (enable) + write_byte(priv, AUX_SRE | AUX_CS, AUXCR); + else + write_byte(priv, AUX_SRE, AUXCR); +} +EXPORT_SYMBOL_GPL(tms9914_remote_enable); + +int tms9914_request_system_control(struct gpib_board *board, struct tms9914_priv *priv, + int request_control) +{ + if (request_control) { + write_byte(priv, AUX_RQC, AUXCR); + } else { + clear_bit(CIC_NUM, &board->status); + write_byte(priv, AUX_RLC, AUXCR); + } + return 0; +} +EXPORT_SYMBOL_GPL(tms9914_request_system_control); + +unsigned int tms9914_t1_delay(struct gpib_board *board, struct tms9914_priv *priv, + unsigned int nano_sec) +{ + static const int clock_period = 200; // assuming 5Mhz input clock + int num_cycles; + + num_cycles = 12; + + if (nano_sec <= 8 * clock_period) { + write_byte(priv, AUX_STDL | AUX_CS, AUXCR); + num_cycles = 8; + } else { + write_byte(priv, AUX_STDL, AUXCR); + } + + if (nano_sec <= 4 * clock_period) { + write_byte(priv, AUX_VSTDL | AUX_CS, AUXCR); + num_cycles = 4; + } else { + write_byte(priv, AUX_VSTDL, AUXCR); + } + + return num_cycles * clock_period; +} +EXPORT_SYMBOL_GPL(tms9914_t1_delay); + +void tms9914_return_to_local(const struct gpib_board *board, struct tms9914_priv *priv) +{ + write_byte(priv, AUX_RTL, AUXCR); +} +EXPORT_SYMBOL_GPL(tms9914_return_to_local); + +void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode) +{ + switch (mode) { + case TMS9914_HOLDOFF_NONE: + write_byte(priv, AUX_HLDE, AUXCR); + write_byte(priv, AUX_HLDA, AUXCR); + break; + case TMS9914_HOLDOFF_EOI: + write_byte(priv, AUX_HLDE | AUX_CS, AUXCR); + write_byte(priv, AUX_HLDA, AUXCR); + break; + case TMS9914_HOLDOFF_ALL: + write_byte(priv, AUX_HLDE, AUXCR); + write_byte(priv, AUX_HLDA | AUX_CS, AUXCR); + break; + default: + pr_err("bug! bad holdoff mode %i\n", mode); + break; + } + priv->holdoff_mode = mode; +} +EXPORT_SYMBOL_GPL(tms9914_set_holdoff_mode); + +void tms9914_release_holdoff(struct tms9914_priv *priv) +{ + if (priv->holdoff_active) { + write_byte(priv, AUX_RHDF, AUXCR); + priv->holdoff_active = 0; + } +} +EXPORT_SYMBOL_GPL(tms9914_release_holdoff); + +int tms9914_enable_eos(struct gpib_board *board, struct tms9914_priv *priv, u8 eos_byte, + int compare_8_bits) +{ + priv->eos = eos_byte; + priv->eos_flags = REOS; + if (compare_8_bits) + priv->eos_flags |= BIN; + return 0; +} +EXPORT_SYMBOL(tms9914_enable_eos); + +void tms9914_disable_eos(struct gpib_board *board, struct tms9914_priv *priv) +{ + priv->eos_flags &= ~REOS; +} +EXPORT_SYMBOL(tms9914_disable_eos); + +int tms9914_parallel_poll(struct gpib_board *board, struct tms9914_priv *priv, u8 *result) +{ + // execute parallel poll + write_byte(priv, AUX_CS | AUX_RPP, AUXCR); + udelay(2); + *result = read_byte(priv, CPTR); + // clear parallel poll state + write_byte(priv, AUX_RPP, AUXCR); + return 0; +} +EXPORT_SYMBOL(tms9914_parallel_poll); + +static void set_ppoll_reg(struct tms9914_priv *priv, int enable, + unsigned int dio_line, int sense, int ist) +{ + u8 dio_byte; + + if (enable && ((sense && ist) || (!sense && !ist))) { + dio_byte = 1 << (dio_line - 1); + write_byte(priv, dio_byte, PPR); + } else { + write_byte(priv, 0, PPR); + } +} + +void tms9914_parallel_poll_configure(struct gpib_board *board, + struct tms9914_priv *priv, u8 config) +{ + priv->ppoll_enable = (config & PPC_DISABLE) == 0; + priv->ppoll_line = (config & PPC_DIO_MASK) + 1; + priv->ppoll_sense = (config & PPC_SENSE) != 0; + set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, board->ist); +} +EXPORT_SYMBOL(tms9914_parallel_poll_configure); + +void tms9914_parallel_poll_response(struct gpib_board *board, + struct tms9914_priv *priv, int ist) +{ + set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, ist); +} +EXPORT_SYMBOL(tms9914_parallel_poll_response); + +void tms9914_serial_poll_response(struct gpib_board *board, + struct tms9914_priv *priv, u8 status) +{ + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + write_byte(priv, status, SPMR); + priv->spoll_status = status; + if (status & request_service_bit) + write_byte(priv, AUX_RSV2 | AUX_CS, AUXCR); + else + write_byte(priv, AUX_RSV2, AUXCR); + spin_unlock_irqrestore(&board->spinlock, flags); +} +EXPORT_SYMBOL(tms9914_serial_poll_response); + +u8 tms9914_serial_poll_status(struct gpib_board *board, struct tms9914_priv *priv) +{ + u8 status; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + status = priv->spoll_status; + spin_unlock_irqrestore(&board->spinlock, flags); + + return status; +} +EXPORT_SYMBOL(tms9914_serial_poll_status); + +int tms9914_primary_address(struct gpib_board *board, + struct tms9914_priv *priv, unsigned int address) +{ + // put primary address in address0 + write_byte(priv, address & ADDRESS_MASK, ADR); + return 0; +} +EXPORT_SYMBOL(tms9914_primary_address); + +int tms9914_secondary_address(struct gpib_board *board, struct tms9914_priv *priv, + unsigned int address, int enable) +{ + if (enable) + priv->imr1_bits |= HR_APTIE; + else + priv->imr1_bits &= ~HR_APTIE; + + write_byte(priv, priv->imr1_bits, IMR1); + return 0; +} +EXPORT_SYMBOL(tms9914_secondary_address); + +unsigned int tms9914_update_status(struct gpib_board *board, struct tms9914_priv *priv, + unsigned int clear_mask) +{ + unsigned long flags; + unsigned int retval; + + spin_lock_irqsave(&board->spinlock, flags); + retval = update_status_nolock(board, priv); + board->status &= ~clear_mask; + spin_unlock_irqrestore(&board->spinlock, flags); + + return retval; +} +EXPORT_SYMBOL(tms9914_update_status); + +static void update_talker_state(struct tms9914_priv *priv, unsigned int address_status_bits) +{ + if (address_status_bits & HR_TA) { + if (address_status_bits & HR_ATN) + priv->talker_state = talker_addressed; + else + /* + * this could also be serial_poll_active, but the tms9914 provides no + * way to distinguish, so we'll assume talker_active + */ + priv->talker_state = talker_active; + } else { + priv->talker_state = talker_idle; + } +} + +static void update_listener_state(struct tms9914_priv *priv, unsigned int address_status_bits) +{ + if (address_status_bits & HR_LA) { + if (address_status_bits & HR_ATN) + priv->listener_state = listener_addressed; + else + priv->listener_state = listener_active; + } else { + priv->listener_state = listener_idle; + } +} + +static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv) +{ + int address_status; + int bsr_bits; + + address_status = read_byte(priv, ADSR); + + // check for remote/local + if (address_status & HR_REM) + set_bit(REM_NUM, &board->status); + else + clear_bit(REM_NUM, &board->status); + // check for lockout + if (address_status & HR_LLO) + set_bit(LOK_NUM, &board->status); + else + clear_bit(LOK_NUM, &board->status); + // check for ATN + if (address_status & HR_ATN) + set_bit(ATN_NUM, &board->status); + else + clear_bit(ATN_NUM, &board->status); + // check for talker/listener addressed + update_talker_state(priv, address_status); + if (priv->talker_state == talker_active || priv->talker_state == talker_addressed) + set_bit(TACS_NUM, &board->status); + else + clear_bit(TACS_NUM, &board->status); + + update_listener_state(priv, address_status); + if (priv->listener_state == listener_active || priv->listener_state == listener_addressed) + set_bit(LACS_NUM, &board->status); + else + clear_bit(LACS_NUM, &board->status); + // Check for SRQI - not reset elsewhere except in autospoll + if (board->status & SRQI) { + bsr_bits = read_byte(priv, BSR); + if (!(bsr_bits & BSR_SRQ_BIT)) + clear_bit(SRQI_NUM, &board->status); + } + + dev_dbg(board->gpib_dev, "status 0x%lx, state 0x%lx\n", board->status, priv->state); + + return board->status; +} + +int tms9914_line_status(const struct gpib_board *board, struct tms9914_priv *priv) +{ + int bsr_bits; + int status = VALID_ALL; + + bsr_bits = read_byte(priv, BSR); + + if (bsr_bits & BSR_REN_BIT) + status |= BUS_REN; + if (bsr_bits & BSR_IFC_BIT) + status |= BUS_IFC; + if (bsr_bits & BSR_SRQ_BIT) + status |= BUS_SRQ; + if (bsr_bits & BSR_EOI_BIT) + status |= BUS_EOI; + if (bsr_bits & BSR_NRFD_BIT) + status |= BUS_NRFD; + if (bsr_bits & BSR_NDAC_BIT) + status |= BUS_NDAC; + if (bsr_bits & BSR_DAV_BIT) + status |= BUS_DAV; + if (bsr_bits & BSR_ATN_BIT) + status |= BUS_ATN; + + return status; +} +EXPORT_SYMBOL(tms9914_line_status); + +static int check_for_eos(struct tms9914_priv *priv, u8 byte) +{ + static const u8 seven_bit_compare_mask = 0x7f; + + if ((priv->eos_flags & REOS) == 0) + return 0; + + if (priv->eos_flags & BIN) { + if (priv->eos == byte) + return 1; + } else { + if ((priv->eos & seven_bit_compare_mask) == (byte & seven_bit_compare_mask)) + return 1; + } + return 0; +} + +static int wait_for_read_byte(struct gpib_board *board, struct tms9914_priv *priv) +{ + if (wait_event_interruptible(board->wait, + test_bit(READ_READY_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) + return -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + + if (test_bit(DEV_CLEAR_BN, &priv->state)) + return -EINTR; + return 0; +} + +static inline u8 tms9914_read_data_in(struct gpib_board *board, + struct tms9914_priv *priv, int *end) +{ + unsigned long flags; + u8 data; + + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(READ_READY_BN, &priv->state); + data = read_byte(priv, DIR); + if (test_and_clear_bit(RECEIVED_END_BN, &priv->state)) + *end = 1; + else + *end = 0; + switch (priv->holdoff_mode) { + case TMS9914_HOLDOFF_EOI: + if (*end) + priv->holdoff_active = 1; + break; + case TMS9914_HOLDOFF_ALL: + priv->holdoff_active = 1; + break; + case TMS9914_HOLDOFF_NONE: + break; + default: + dev_err(board->gpib_dev, "bug! bad holdoff mode %i\n", priv->holdoff_mode); + break; + } + spin_unlock_irqrestore(&board->spinlock, flags); + + return data; +} + +static int pio_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + + *bytes_read = 0; + *end = 0; + while (*bytes_read < length && *end == 0) { + tms9914_release_holdoff(priv); + retval = wait_for_read_byte(board, priv); + if (retval < 0) + return retval; + buffer[(*bytes_read)++] = tms9914_read_data_in(board, priv, end); + + if (check_for_eos(priv, buffer[*bytes_read - 1])) + *end = 1; + } + + return retval; +} + +int tms9914_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, + size_t length, int *end, size_t *bytes_read) +{ + ssize_t retval = 0; + size_t num_bytes; + + *end = 0; + *bytes_read = 0; + if (length == 0) + return 0; + + clear_bit(DEV_CLEAR_BN, &priv->state); + + // transfer data (except for last byte) + if (length > 1) { + if (priv->eos_flags & REOS) + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); + else + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_EOI); + // PIO transfer + retval = pio_read(board, priv, buffer, length - 1, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + buffer += num_bytes; + length -= num_bytes; + } + // read last bytes if we haven't received an END yet + if (*end == 0) { + // make sure we holdoff after last byte read + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); + retval = pio_read(board, priv, buffer, length, end, &num_bytes); + *bytes_read += num_bytes; + if (retval < 0) + return retval; + } + return 0; +} +EXPORT_SYMBOL(tms9914_read); + +static int pio_write_wait(struct gpib_board *board, struct tms9914_priv *priv) +{ + // wait until next byte is ready to be sent + if (wait_event_interruptible(board->wait, + test_bit(WRITE_READY_BN, &priv->state) || + test_bit(BUS_ERROR_BN, &priv->state) || + test_bit(DEV_CLEAR_BN, &priv->state) || + test_bit(TIMO_NUM, &board->status))) + return -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_bit(BUS_ERROR_BN, &priv->state)) + return -EIO; + if (test_bit(DEV_CLEAR_BN, &priv->state)) + return -EINTR; + + return 0; +} + +static int pio_write(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, + size_t length, size_t *bytes_written) +{ + ssize_t retval = 0; + unsigned long flags; + + *bytes_written = 0; + while (*bytes_written < length) { + retval = pio_write_wait(board, priv); + if (retval < 0) + break; + + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(WRITE_READY_BN, &priv->state); + write_byte(priv, buffer[(*bytes_written)++], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + } + retval = pio_write_wait(board, priv); + if (retval < 0) + return retval; + + return length; +} + +int tms9914_write(struct gpib_board *board, struct tms9914_priv *priv, + u8 *buffer, size_t length, int send_eoi, size_t *bytes_written) +{ + ssize_t retval = 0; + + *bytes_written = 0; + if (length == 0) + return 0; + + clear_bit(BUS_ERROR_BN, &priv->state); + clear_bit(DEV_CLEAR_BN, &priv->state); + + if (send_eoi) + length-- ; /* save the last byte for sending EOI */ + + if (length > 0) { + size_t num_bytes; + // PIO transfer + retval = pio_write(board, priv, buffer, length, &num_bytes); + *bytes_written += num_bytes; + if (retval < 0) + return retval; + } + if (send_eoi) { + size_t num_bytes; + /*send EOI */ + write_byte(priv, AUX_SEOI, AUXCR); + + retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes); + *bytes_written += num_bytes; + } + return retval; +} +EXPORT_SYMBOL(tms9914_write); + +static void check_my_address_state(struct gpib_board *board, + struct tms9914_priv *priv, int cmd_byte) +{ + if (cmd_byte == MLA(board->pad)) { + priv->primary_listen_addressed = 1; + // become active listener + if (board->sad < 0) + write_byte(priv, AUX_LON | AUX_CS, AUXCR); + } else if (board->sad >= 0 && priv->primary_listen_addressed && + cmd_byte == MSA(board->sad)) { + // become active listener + write_byte(priv, AUX_LON | AUX_CS, AUXCR); + } else if (cmd_byte != MLA(board->pad) && (cmd_byte & 0xe0) == LAD) { + priv->primary_listen_addressed = 0; + } else if (cmd_byte == UNL) { + priv->primary_listen_addressed = 0; + write_byte(priv, AUX_LON, AUXCR); + } else if (cmd_byte == MTA(board->pad)) { + priv->primary_talk_addressed = 1; + if (board->sad < 0) + // make active talker + write_byte(priv, AUX_TON | AUX_CS, AUXCR); + } else if (board->sad >= 0 && priv->primary_talk_addressed && + cmd_byte == MSA(board->sad)) { + // become active talker + write_byte(priv, AUX_TON | AUX_CS, AUXCR); + } else if (cmd_byte != MTA(board->pad) && (cmd_byte & 0xe0) == TAD) { + // Other Talk Address + priv->primary_talk_addressed = 0; + write_byte(priv, AUX_TON, AUXCR); + } else if (cmd_byte == UNT) { + priv->primary_talk_addressed = 0; + write_byte(priv, AUX_TON, AUXCR); + } +} + +int tms9914_command(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, + size_t length, size_t *bytes_written) +{ + int retval = 0; + unsigned long flags; + + *bytes_written = 0; + while (*bytes_written < length) { + if (wait_event_interruptible(board->wait, + test_bit(COMMAND_READY_BN, + &priv->state) || + test_bit(TIMO_NUM, &board->status))) + break; + if (test_bit(TIMO_NUM, &board->status)) + break; + + spin_lock_irqsave(&board->spinlock, flags); + clear_bit(COMMAND_READY_BN, &priv->state); + write_byte(priv, buffer[*bytes_written], CDOR); + spin_unlock_irqrestore(&board->spinlock, flags); + + check_my_address_state(board, priv, buffer[*bytes_written]); + + ++(*bytes_written); + } + // wait until last command byte is written + if (wait_event_interruptible(board->wait, + test_bit(COMMAND_READY_BN, + &priv->state) || test_bit(TIMO_NUM, &board->status))) + retval = -ERESTARTSYS; + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + + return retval; +} +EXPORT_SYMBOL(tms9914_command); + +irqreturn_t tms9914_interrupt(struct gpib_board *board, struct tms9914_priv *priv) +{ + int status0, status1; + + // read interrupt status (also clears status) + status0 = read_byte(priv, ISR0); + status1 = read_byte(priv, ISR1); + return tms9914_interrupt_have_status(board, priv, status0, status1); +} +EXPORT_SYMBOL(tms9914_interrupt); + +irqreturn_t tms9914_interrupt_have_status(struct gpib_board *board, struct tms9914_priv *priv, + int status0, int status1) +{ + // record reception of END + if (status0 & HR_END) + set_bit(RECEIVED_END_BN, &priv->state); + // get incoming data in PIO mode + if ((status0 & HR_BI)) + set_bit(READ_READY_BN, &priv->state); + if ((status0 & HR_BO)) { + if (read_byte(priv, ADSR) & HR_ATN) + set_bit(COMMAND_READY_BN, &priv->state); + else + set_bit(WRITE_READY_BN, &priv->state); + } + + if (status0 & HR_SPAS) { + priv->spoll_status &= ~request_service_bit; + write_byte(priv, priv->spoll_status, SPMR); + // FIXME: set SPOLL status bit + } + // record service request in status + if (status1 & HR_SRQ) + set_bit(SRQI_NUM, &board->status); + // have been addressed (with secondary addressing disabled) + if (status1 & HR_MA) + // clear dac holdoff + write_byte(priv, AUX_VAL, AUXCR); + // unrecognized command received + if (status1 & HR_UNC) { + unsigned short command_byte = read_byte(priv, CPTR) & gpib_command_mask; + + switch (command_byte) { + case PP_CONFIG: + priv->ppoll_configure_state = 1; + /* + * AUX_PTS generates another UNC interrupt on the next command byte + * if it is in the secondary address group (such as PPE and PPD). + */ + write_byte(priv, AUX_PTS, AUXCR); + write_byte(priv, AUX_VAL, AUXCR); + break; + case PPU: + tms9914_parallel_poll_configure(board, priv, command_byte); + write_byte(priv, AUX_VAL, AUXCR); + break; + default: + if (is_PPE(command_byte) || is_PPD(command_byte)) { + if (priv->ppoll_configure_state) { + tms9914_parallel_poll_configure(board, priv, command_byte); + write_byte(priv, AUX_VAL, AUXCR); + } else {// bad parallel poll configure byte + // clear dac holdoff + write_byte(priv, AUX_INVAL, AUXCR); + } + } else { + // clear dac holdoff + write_byte(priv, AUX_INVAL, AUXCR); + } + break; + } + + if (in_primary_command_group(command_byte) && command_byte != PP_CONFIG) + priv->ppoll_configure_state = 0; + } + + if (status1 & HR_ERR) { + dev_dbg(board->gpib_dev, "gpib bus error\n"); + set_bit(BUS_ERROR_BN, &priv->state); + } + + if (status1 & HR_IFC) { + push_gpib_event(board, EVENT_IFC); + clear_bit(CIC_NUM, &board->status); + } + + if (status1 & HR_GET) { + push_gpib_event(board, EVENT_DEV_TRG); + // clear dac holdoff + write_byte(priv, AUX_VAL, AUXCR); + } + + if (status1 & HR_DCAS) { + push_gpib_event(board, EVENT_DEV_CLR); + // clear dac holdoff + write_byte(priv, AUX_VAL, AUXCR); + set_bit(DEV_CLEAR_BN, &priv->state); + } + + // check for being addressed with secondary addressing + if (status1 & HR_APT) { + if (board->sad < 0) + dev_err(board->gpib_dev, "bug, APT interrupt without secondary addressing?\n"); + if ((read_byte(priv, CPTR) & gpib_command_mask) == MSA(board->sad)) + write_byte(priv, AUX_VAL, AUXCR); + else + write_byte(priv, AUX_INVAL, AUXCR); + } + + if ((status0 & priv->imr0_bits) || (status1 & priv->imr1_bits)) { + dev_dbg(board->gpib_dev, "isr0 0x%x, imr0 0x%x, isr1 0x%x, imr1 0x%x\n", + status0, priv->imr0_bits, status1, priv->imr1_bits); + update_status_nolock(board, priv); + wake_up_interruptible(&board->wait); + } + return IRQ_HANDLED; +} +EXPORT_SYMBOL(tms9914_interrupt_have_status); + +void tms9914_board_reset(struct tms9914_priv *priv) +{ + /* chip reset */ + write_byte(priv, AUX_CHIP_RESET | AUX_CS, AUXCR); + + /* disable all interrupts */ + priv->imr0_bits = 0; + write_byte(priv, priv->imr0_bits, IMR0); + priv->imr1_bits = 0; + write_byte(priv, priv->imr1_bits, IMR1); + write_byte(priv, AUX_DAI | AUX_CS, AUXCR); + + /* clear registers by reading */ + read_byte(priv, CPTR); + read_byte(priv, ISR0); + read_byte(priv, ISR1); + + write_byte(priv, 0, SPMR); + + /* parallel poll unconfigure */ + write_byte(priv, 0, PPR); + /* request for data holdoff */ + tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); +} +EXPORT_SYMBOL_GPL(tms9914_board_reset); + +void tms9914_online(struct gpib_board *board, struct tms9914_priv *priv) +{ + /* set GPIB address */ + tms9914_primary_address(board, priv, board->pad); + tms9914_secondary_address(board, priv, board->sad, board->sad >= 0); + + /* enable tms9914 interrupts */ + priv->imr0_bits |= HR_MACIE | HR_RLCIE | HR_ENDIE | HR_BOIE | HR_BIIE | + HR_SPASIE; + priv->imr1_bits |= HR_MAIE | HR_SRQIE | HR_UNCIE | HR_ERRIE | HR_IFCIE | + HR_GETIE | HR_DCASIE; + write_byte(priv, priv->imr0_bits, IMR0); + write_byte(priv, priv->imr1_bits, IMR1); + write_byte(priv, AUX_DAI, AUXCR); + + /* turn off reset state */ + write_byte(priv, AUX_CHIP_RESET, AUXCR); +} +EXPORT_SYMBOL_GPL(tms9914_online); + +#ifdef CONFIG_HAS_IOPORT +// wrapper for inb +u8 tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return inb(priv->iobase + register_num * priv->offset); +} +EXPORT_SYMBOL_GPL(tms9914_ioport_read_byte); + +// wrapper for outb +void tms9914_ioport_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) +{ + outb(data, priv->iobase + register_num * priv->offset); + if (register_num == AUXCR) + udelay(1); +} +EXPORT_SYMBOL_GPL(tms9914_ioport_write_byte); +#endif + +// wrapper for readb +u8 tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num) +{ + return readb(priv->mmiobase + register_num * priv->offset); +} +EXPORT_SYMBOL_GPL(tms9914_iomem_read_byte); + +// wrapper for writeb +void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) +{ + writeb(data, priv->mmiobase + register_num * priv->offset); + if (register_num == AUXCR) + udelay(1); +} +EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte); + +static int __init tms9914_init_module(void) +{ + return 0; +} + +static void __exit tms9914_exit_module(void) +{ +} + +module_init(tms9914_init_module); +module_exit(tms9914_exit_module); + diff --git a/drivers/gpib/tnt4882/Makefile b/drivers/gpib/tnt4882/Makefile new file mode 100644 index 000000000000..fa1687ad0d1b --- /dev/null +++ b/drivers/gpib/tnt4882/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882.o + +tnt4882-objs := tnt4882_gpib.o mite.o + + + diff --git a/drivers/gpib/tnt4882/mite.c b/drivers/gpib/tnt4882/mite.c new file mode 100644 index 000000000000..847b96f411bd --- /dev/null +++ b/drivers/gpib/tnt4882/mite.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Hardware driver for NI Mite PCI interface chip, + * adapted from COMEDI + * + * Copyright (C) 1997-8 David A. Schleef + * Copyright (C) 2002 Frank Mori Hess + * + * The PCI-MIO E series driver was originally written by + * Tomasz Motylewski <...>, and ported to comedi by ds. + * + * References for specifications: + * + * 321747b.pdf Register Level Programmer Manual (obsolete) + * 321747c.pdf Register Level Programmer Manual (new) + * DAQ-STC reference manual + * + * Other possibly relevant info: + * + * 320517c.pdf User manual (obsolete) + * 320517f.pdf User manual (new) + * 320889a.pdf delete + * 320906c.pdf maximum signal ratings + * 321066a.pdf about 16x + * 321791a.pdf discontinuation of at-mio-16e-10 rev. c + * 321808a.pdf about at-mio-16e-10 rev P + * 321837a.pdf discontinuation of at-mio-16de-10 rev d + * 321838a.pdf about at-mio-16de-10 rev N + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mite.h" + +#define PCI_MITE_SIZE 4096 +#define PCI_DAQ_SIZE 4096 + +struct mite_struct *mite_devices; + +#define TOP_OF_PAGE(x) ((x) | (~(PAGE_MASK))) + +void mite_init(void) +{ + struct pci_dev *pcidev; + struct mite_struct *mite; + + for (pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, NULL); + pcidev; + pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, pcidev)) { + mite = kzalloc(sizeof(*mite), GFP_KERNEL); + if (!mite) + return; + + mite->pcidev = pcidev; + pci_dev_get(mite->pcidev); + mite->next = mite_devices; + mite_devices = mite; + } +} + +int mite_setup(struct mite_struct *mite) +{ + u32 addr; + + if (pci_enable_device(mite->pcidev)) { + pr_err("mite: error enabling mite.\n"); + return -EIO; + } + pci_set_master(mite->pcidev); + if (pci_request_regions(mite->pcidev, "mite")) { + pr_err("mite: failed to request mite io regions.\n"); + return -EIO; + } + addr = pci_resource_start(mite->pcidev, 0); + mite->mite_phys_addr = addr; + mite->mite_io_addr = ioremap(addr, pci_resource_len(mite->pcidev, 0)); + if (!mite->mite_io_addr) { + pr_err("mite: failed to remap mite io memory address.\n"); + return -ENOMEM; + } + addr = pci_resource_start(mite->pcidev, 1); + mite->daq_phys_addr = addr; + mite->daq_io_addr = ioremap(mite->daq_phys_addr, pci_resource_len(mite->pcidev, 1)); + if (!mite->daq_io_addr) { + pr_err("mite: failed to remap daq io memory address.\n"); + return -ENOMEM; + } + writel(mite->daq_phys_addr | WENAB, mite->mite_io_addr + MITE_IODWBSR); + mite->used = 1; + return 0; +} + +void mite_cleanup(void) +{ + struct mite_struct *mite, *next; + + for (mite = mite_devices; mite; mite = next) { + next = mite->next; + if (mite->pcidev) + pci_dev_put(mite->pcidev); + kfree(mite); + } +} + +void mite_unsetup(struct mite_struct *mite) +{ + if (!mite) + return; + if (mite->mite_io_addr) { + iounmap(mite->mite_io_addr); + mite->mite_io_addr = NULL; + } + if (mite->daq_io_addr) { + iounmap(mite->daq_io_addr); + mite->daq_io_addr = NULL; + } + if (mite->mite_phys_addr) { + pci_release_regions(mite->pcidev); + pci_disable_device(mite->pcidev); + mite->mite_phys_addr = 0; + } + mite->used = 0; +} diff --git a/drivers/gpib/tnt4882/mite.h b/drivers/gpib/tnt4882/mite.h new file mode 100644 index 000000000000..a1fdba9672a0 --- /dev/null +++ b/drivers/gpib/tnt4882/mite.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Hardware driver for NI Mite PCI interface chip + * + * Copyright (C) 1999 David A. Schleef + */ + +#ifndef _MITE_H_ +#define _MITE_H_ + +#include + +#define PCI_VENDOR_ID_NATINST 0x1093 + +//#define DEBUG_MITE + +#ifdef DEBUG_MITE +#define MDPRINTK(format, args...) pr_debug(format, ## args) +#else +#define MDPRINTK(args...) +#endif + +#define MITE_RING_SIZE 3000 +struct mite_dma_chain { + u32 count; + u32 addr; + u32 next; +}; + +struct mite_struct { + struct mite_struct *next; + int used; + + struct pci_dev *pcidev; + unsigned long mite_phys_addr; + void __iomem *mite_io_addr; + unsigned long daq_phys_addr; + void __iomem *daq_io_addr; + + int DMA_CheckNearEnd; + + struct mite_dma_chain ring[MITE_RING_SIZE]; +}; + +extern struct mite_struct *mite_devices; + +extern inline unsigned int mite_irq(struct mite_struct *mite) +{ + return mite->pcidev->irq; +}; + +extern inline unsigned int mite_device_id(struct mite_struct *mite) +{ + return mite->pcidev->device; +}; + +void mite_init(void); +void mite_cleanup(void); +int mite_setup(struct mite_struct *mite); +void mite_unsetup(struct mite_struct *mite); +void mite_list_devices(void); + +#define CHAN_OFFSET(x) (0x100 * (x)) + +/* DMA base for chan 0 is 0x500, chan 1 is 0x600 */ + +#define MITE_CHOR 0x500 +#define CHOR_DMARESET BIT(31) +#define CHOR_SET_SEND_TC BIT(11) +#define CHOR_CLR_SEND_TC BIT(10) +#define CHOR_SET_LPAUSE BIT(9) +#define CHOR_CLR_LPAUSE BIT(8) +#define CHOR_CLRDONE BIT(7) +#define CHOR_CLRRB BIT(6) +#define CHOR_CLRLC BIT(5) +#define CHOR_FRESET BIT(4) +#define CHOR_ABORT BIT(3) +#define CHOR_STOP BIT(2) +#define CHOR_CONT BIT(1) +#define CHOR_START BIT(0) +#define CHOR_PON (CHOR_CLR_SEND_TC | CHOR_CLR_LPAUSE) + +#define MITE_CHCR 0x504 +#define CHCR_SET_DMA_IE BIT(31) +#define CHCR_CLR_DMA_IE BIT(30) +#define CHCR_SET_LINKP_IE BIT(29) +#define CHCR_CLR_LINKP_IE BIT(28) +#define CHCR_SET_SAR_IE BIT(27) +#define CHCR_CLR_SAR_IE BIT(26) +#define CHCR_SET_DONE_IE BIT(25) +#define CHCR_CLR_DONE_IE BIT(24) +#define CHCR_SET_MRDY_IE BIT(23) +#define CHCR_CLR_MRDY_IE BIT(22) +#define CHCR_SET_DRDY_IE BIT(21) +#define CHCR_CLR_DRDY_IE BIT(20) +#define CHCR_SET_LC_IE BIT(19) +#define CHCR_CLR_LC_IE BIT(18) +#define CHCR_SET_CONT_RB_IE BIT(17) +#define CHCR_CLR_CONT_RB_IE BIT(16) +#define CHCR_FIFODIS BIT(15) +#define CHCR_FIFO_ON 0 +#define CHCR_BURSTEN BIT(14) +#define CHCR_NO_BURSTEN 0 +#define CHCR_NFTP(x) ((x) << 11) +#define CHCR_NFTP0 CHCR_NFTP(0) +#define CHCR_NFTP1 CHCR_NFTP(1) +#define CHCR_NFTP2 CHCR_NFTP(2) +#define CHCR_NFTP4 CHCR_NFTP(3) +#define CHCR_NFTP8 CHCR_NFTP(4) +#define CHCR_NFTP16 CHCR_NFTP(5) +#define CHCR_NETP(x) ((x) << 11) +#define CHCR_NETP0 CHCR_NETP(0) +#define CHCR_NETP1 CHCR_NETP(1) +#define CHCR_NETP2 CHCR_NETP(2) +#define CHCR_NETP4 CHCR_NETP(3) +#define CHCR_NETP8 CHCR_NETP(4) +#define CHCR_CHEND1 BIT(5) +#define CHCR_CHEND0 BIT(4) +#define CHCR_DIR BIT(3) +#define CHCR_DEV_TO_MEM CHCR_DIR +#define CHCR_MEM_TO_DEV 0 +#define CHCR_NORMAL ((0) << 0) +#define CHCR_CONTINUE ((1) << 0) +#define CHCR_RINGBUFF ((2) << 0) +#define CHCR_LINKSHORT ((4) << 0) +#define CHCR_LINKLONG ((5) << 0) +#define CHCRPON (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE | \ + CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE | \ + CHCR_CLR_LC_IE | CHCR_CLR_CONT_IE) + +#define MITE_TCR 0x508 + +/* CR bits */ +#define CR_RL(x) ((x) << 21) +#define CR_RL0 CR_RL(0) +#define CR_RL1 CR_RL(1) +#define CR_RL2 CR_RL(2) +#define CR_RL4 CR_RL(3) +#define CR_RL8 CR_RL(4) +#define CR_RL16 CR_RL(5) +#define CR_RL32 CR_RL(6) +#define CR_RL64 CR_RL(7) +#define CR_RD(x) ((x) << 19) +#define CR_RD0 CR_RD(0) +#define CR_RD32 CR_RD(1) +#define CR_RD512 CR_RD(2) +#define CR_RD8192 CR_RD(3) +#define CR_REQS(x) ((x) << 16) +#define CR_REQSDRQ0 CR_REQS(4) +#define CR_REQSDRQ1 CR_REQS(5) +#define CR_REQSDRQ2 CR_REQS(6) +#define CR_REQSDRQ3 CR_REQS(7) +#define CR_ASEQX(x) ((x) << 10) +#define CR_ASEQX0 CR_ASEQX(0) +#define CR_ASEQDONT CR_ASEQX0 +#define CR_ASEQXP1 CR_ASEQX(1) +#define CR_ASEQUP CR_ASEQXP1 +#define CR_ASEQXP2 CR_ASEQX(2) +#define CR_ASEQDOWN CR_ASEQXP2 +#define CR_ASEQXP4 CR_ASEQX(3) +#define CR_ASEQXP8 CR_ASEQX(4) +#define CR_ASEQXP16 CR_ASEQX(5) +#define CR_ASEQXP32 CR_ASEQX(6) +#define CR_ASEQXP64 CR_ASEQX(7) +#define CR_ASEQXM1 CR_ASEQX(9) +#define CR_ASEQXM2 CR_ASEQX(10) +#define CR_ASEQXM4 CR_ASEQX(11) +#define CR_ASEQXM8 CR_ASEQX(12) +#define CR_ASEQXM16 CR_ASEQX(13) +#define CR_ASEQXM32 CR_ASEQX(14) +#define CR_ASEQXM64 CR_ASEQX(15) +#define CR_PSIZEBYTE BIT(8) +#define CR_PSIZEHALF (2 << 8) +#define CR_PSIZEWORD (3 << 8) +#define CR_PORTCPU (0 << 6) +#define CR_PORTIO BIT(6) +#define CR_PORTVXI (2 << 6) +#define CR_PORTMXI (3 << 6) +#define CR_AMDEVICE BIT(0) + +#define CHSR_INT 0x80000000 +#define CHSR_DONE 0x02000000 +#define CHSR_LINKC 0x00080000 + +#define MITE_MCR 0x50c +#define MCRPON 0 + +#define MITE_MAR 0x510 + +#define MITE_DCR 0x514 +#define DCR_NORMAL BIT(29) +#define DCRPON 0 + +#define MITE_DAR 0x518 + +#define MITE_LKCR 0x51c + +#define MITE_LKAR 0x520 +#define MITE_LLKAR 0x524 +#define MITE_BAR 0x528 +#define MITE_BCR 0x52c +#define MITE_SAR 0x530 +#define MITE_WSCR 0x534 +#define MITE_WSER 0x538 +#define MITE_CHSR 0x53c +#define MITE_FCR 0x540 + +#define MITE_FIFO 0x80 +#define MITE_FIFOEND 0xff + +#define MITE_AMRAM 0x00 +#define MITE_AMDEVICE 0x01 +#define MITE_AMHOST_A32_SINGLE 0x09 +#define MITE_AMHOST_A24_SINGLE 0x39 +#define MITE_AMHOST_A16_SINGLE 0x29 +#define MITE_AMHOST_A32_BLOCK 0x0b +#define MITE_AMHOST_A32D64_BLOCK 0x08 +#define MITE_AMHOST_A24_BLOCK 0x3b + +enum mite_registers { + MITE_IODWBSR = 0xc0, // IO Device Window Base Size Register + MITE_CSIGR = 0x460, // chip signature + MITE_IODWBSR_1 = 0xc4, // IO Device Window Base Size Register 1 (used by 6602 boards) + MITE_IODWCR_1 = 0xf4 +}; + +enum MITE_IODWBSR_bits { + WENAB = 0x80, // window enable + WENAB_6602 = 0x8c // window enable for 6602 boards +}; + +#endif + diff --git a/drivers/gpib/tnt4882/tnt4882_gpib.c b/drivers/gpib/tnt4882/tnt4882_gpib.c new file mode 100644 index 000000000000..c03a976b7380 --- /dev/null +++ b/drivers/gpib/tnt4882/tnt4882_gpib.c @@ -0,0 +1,1838 @@ +// SPDX-License-Identifier: GPL-2.0 + +/*************************************************************************** + * National Instruments boards using tnt4882 or compatible chips (at-gpib, etc). + * copyright : (C) 2001, 2002 by Frank Mori Hess + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt +#define DRV_NAME KBUILD_MODNAME + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nec7210.h" +#include "gpibP.h" +#include "mite.h" +#include "tnt4882_registers.h" + +static const int ISAPNP_VENDOR_ID_NI = ISAPNP_VENDOR('N', 'I', 'C'); +static const int ISAPNP_ID_NI_ATGPIB_TNT = 0xc601; +enum { + PCI_DEVICE_ID_NI_GPIB = 0xc801, + PCI_DEVICE_ID_NI_GPIB_PLUS = 0xc811, + PCI_DEVICE_ID_NI_GPIB_PLUS2 = 0x71ad, + PCI_DEVICE_ID_NI_PXIGPIB = 0xc821, + PCI_DEVICE_ID_NI_PMCGPIB = 0xc831, + PCI_DEVICE_ID_NI_PCIEGPIB = 0x70cf, + PCI_DEVICE_ID_NI_PCIE2GPIB = 0x710e, +// Measurement Computing PCI-488 same design as PCI-GPIB with TNT5004 + PCI_DEVICE_ID_MC_PCI488 = 0x7259, + PCI_DEVICE_ID_CEC_NI_GPIB = 0x7258 +}; + +// struct which defines private_data for tnt4882 devices +struct tnt4882_priv { + struct nec7210_priv nec7210_priv; + struct mite_struct *mite; + struct pnp_dev *pnp_dev; + unsigned int irq; + unsigned short imr0_bits; + unsigned short imr3_bits; + unsigned short auxg_bits; // bits written to auxiliary register G +}; + +static irqreturn_t tnt4882_internal_interrupt(struct gpib_board *board); + +// register offset for nec7210 compatible registers +static const int atgpib_reg_offset = 2; + +// number of ioports used +static const int atgpib_iosize = 32; + +/* paged io */ +static inline unsigned int tnt_paged_readb(struct tnt4882_priv *priv, unsigned long offset) +{ + iowrite8(AUX_PAGEIN, priv->nec7210_priv.mmiobase + AUXMR * priv->nec7210_priv.offset); + udelay(1); + return ioread8(priv->nec7210_priv.mmiobase + offset); +} + +static inline void tnt_paged_writeb(struct tnt4882_priv *priv, unsigned int value, + unsigned long offset) +{ + iowrite8(AUX_PAGEIN, priv->nec7210_priv.mmiobase + AUXMR * priv->nec7210_priv.offset); + udelay(1); + iowrite8(value, priv->nec7210_priv.mmiobase + offset); +} + +/* readb/writeb wrappers */ +static inline unsigned short tnt_readb(struct tnt4882_priv *priv, unsigned long offset) +{ + void __iomem *address = priv->nec7210_priv.mmiobase + offset; + unsigned long flags; + unsigned short retval; + spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock; + + spin_lock_irqsave(register_lock, flags); + switch (offset) { + case CSR: + case SASR: + case ISR0: + case BSR: + switch (priv->nec7210_priv.type) { + case TNT4882: + case TNT5004: + retval = ioread8(address); + break; + case NAT4882: + retval = tnt_paged_readb(priv, offset - tnt_pagein_offset); + break; + case NEC7210: + retval = 0; + break; + default: + retval = 0; + break; + } + break; + default: + retval = ioread8(address); + break; + } + spin_unlock_irqrestore(register_lock, flags); + return retval; +} + +static inline void tnt_writeb(struct tnt4882_priv *priv, unsigned short value, unsigned long offset) +{ + void __iomem *address = priv->nec7210_priv.mmiobase + offset; + unsigned long flags; + spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock; + + spin_lock_irqsave(register_lock, flags); + switch (offset) { + case KEYREG: + case IMR0: + case BCR: + switch (priv->nec7210_priv.type) { + case TNT4882: + case TNT5004: + iowrite8(value, address); + break; + case NAT4882: + tnt_paged_writeb(priv, value, offset - tnt_pagein_offset); + break; + case NEC7210: + break; + default: + break; + } + break; + default: + iowrite8(value, address); + break; + } + spin_unlock_irqrestore(register_lock, flags); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIB driver for National Instruments boards using tnt4882 or compatible chips"); + +static int tnt4882_line_status(const struct gpib_board *board) +{ + int status = VALID_ALL; + int bcsr_bits; + struct tnt4882_priv *tnt_priv; + + tnt_priv = board->private_data; + + bcsr_bits = tnt_readb(tnt_priv, BSR); + + if (bcsr_bits & BCSR_REN_BIT) + status |= BUS_REN; + if (bcsr_bits & BCSR_IFC_BIT) + status |= BUS_IFC; + if (bcsr_bits & BCSR_SRQ_BIT) + status |= BUS_SRQ; + if (bcsr_bits & BCSR_EOI_BIT) + status |= BUS_EOI; + if (bcsr_bits & BCSR_NRFD_BIT) + status |= BUS_NRFD; + if (bcsr_bits & BCSR_NDAC_BIT) + status |= BUS_NDAC; + if (bcsr_bits & BCSR_DAV_BIT) + status |= BUS_DAV; + if (bcsr_bits & BCSR_ATN_BIT) + status |= BUS_ATN; + + return status; +} + +static int tnt4882_t1_delay(struct gpib_board *board, unsigned int nano_sec) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned int retval; + + retval = nec7210_t1_delay(board, nec_priv, nano_sec); + if (nec_priv->type == NEC7210) + return retval; + + if (nano_sec <= 350) { + tnt_writeb(tnt_priv, MSTD, KEYREG); + retval = 350; + } else { + tnt_writeb(tnt_priv, 0, KEYREG); + } + if (nano_sec > 500 && nano_sec <= 1100) { + write_byte(nec_priv, AUXRI | USTD, AUXMR); + retval = 1100; + } else { + write_byte(nec_priv, AUXRI, AUXMR); + } + return retval; +} + +static int fifo_word_available(struct tnt4882_priv *tnt_priv) +{ + int status2; + int retval; + + status2 = tnt_readb(tnt_priv, STS2); + retval = (status2 & AEFN) && (status2 & BEFN); + + return retval; +} + +static int fifo_byte_available(struct tnt4882_priv *tnt_priv) +{ + int status2; + int retval; + + status2 = tnt_readb(tnt_priv, STS2); + retval = (status2 & AEFN) || (status2 & BEFN); + + return retval; +} + +static int fifo_xfer_done(struct tnt4882_priv *tnt_priv) +{ + int status1; + int retval; + + status1 = tnt_readb(tnt_priv, STS1); + retval = status1 & (S_DONE | S_HALT); + + return retval; +} + +static int drain_fifo_words(struct tnt4882_priv *tnt_priv, u8 *buffer, int num_bytes) +{ + int count = 0; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + while (fifo_word_available(tnt_priv) && count + 2 <= num_bytes) { + short word; + + word = ioread16(nec_priv->mmiobase + FIFOB); + buffer[count++] = word & 0xff; + buffer[count++] = (word >> 8) & 0xff; + } + return count; +} + +static void tnt4882_release_holdoff(struct gpib_board *board, struct tnt4882_priv *tnt_priv) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned short sasr_bits; + + sasr_bits = tnt_readb(tnt_priv, SASR); + + /* + * tnt4882 not in one-chip mode won't always release holdoff unless we + * are in the right mode when release handshake command is given + */ + if (sasr_bits & AEHS_BIT) /* holding off due to holdoff on end mode*/ { + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + write_byte(nec_priv, AUX_FH, AUXMR); + } else if (sasr_bits & ANHS1_BIT) { /* held off due to holdoff on all data mode*/ + nec7210_set_handshake_mode(board, nec_priv, HR_HLDA); + write_byte(nec_priv, AUX_FH, AUXMR); + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + } else { /* held off due to holdoff immediately command */ + nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); + write_byte(nec_priv, AUX_FH, AUXMR); + } +} + +static int tnt4882_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read) +{ + size_t count = 0; + ssize_t retval = 0; + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned int bits; + s32 hw_count; + unsigned long flags; + + *bytes_read = 0; + // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + clear_bit(ADR_CHANGE_BN, &nec_priv->state); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, HR_ENDIE); + if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); + else + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + tnt_writeb(tnt_priv, nec_priv->auxa_bits | HR_HLDA, CCR); + bits = TNT_B_16BIT | TNT_IN | TNT_CCEN; + tnt_writeb(tnt_priv, bits, CFG); + tnt_writeb(tnt_priv, RESET_FIFO, CMDR); + udelay(1); + // load 2's complement of count into hardware counters + hw_count = -length; + tnt_writeb(tnt_priv, hw_count & 0xff, CNT0); + tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1); + tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2); + tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3); + + tnt4882_release_holdoff(board, tnt_priv); + + tnt_writeb(tnt_priv, GO, CMDR); + udelay(1); + + spin_lock_irqsave(&board->spinlock, flags); + tnt_priv->imr3_bits |= HR_DONE | HR_NEF; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + while (count + 2 <= length && + test_bit(RECEIVED_END_BN, &nec_priv->state) == 0 && + fifo_xfer_done(tnt_priv) == 0) { + // wait until a word is ready + if (wait_event_interruptible(board->wait, + fifo_word_available(tnt_priv) || + fifo_xfer_done(tnt_priv) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(ADR_CHANGE_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status)) { + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + retval = -EINTR; + break; + } + if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) { + retval = -EINTR; + break; + } + + spin_lock_irqsave(&board->spinlock, flags); + count += drain_fifo_words(tnt_priv, &buffer[count], length - count); + tnt_priv->imr3_bits |= HR_NEF; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + if (need_resched()) + schedule(); + } + // wait for last byte + if (count < length) { + spin_lock_irqsave(&board->spinlock, flags); + tnt_priv->imr3_bits |= HR_DONE | HR_NEF; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + if (wait_event_interruptible(board->wait, + fifo_xfer_done(tnt_priv) || + test_bit(RECEIVED_END_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(ADR_CHANGE_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) { + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + retval = -EINTR; + if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) + retval = -EINTR; + count += drain_fifo_words(tnt_priv, &buffer[count], length - count); + if (fifo_byte_available(tnt_priv) && count < length) + buffer[count++] = tnt_readb(tnt_priv, FIFOB); + } + if (count < length) + tnt_writeb(tnt_priv, STOP, CMDR); + udelay(1); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, 0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); + /* + * force handling of any pending interrupts (seems to be needed + * to keep interrupts from getting hosed, plus for syncing + * with RECEIVED_END below) + */ + tnt4882_internal_interrupt(board); + /* RECEIVED_END should be in sync now */ + if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) + *end = 1; + if (retval < 0) { + // force immediate holdoff + write_byte(nec_priv, AUX_HLDI, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + } + *bytes_read = count; + + return retval; +} + +static int fifo_space_available(struct tnt4882_priv *tnt_priv) +{ + int status2; + int retval; + + status2 = tnt_readb(tnt_priv, STS2); + retval = (status2 & AFFN) && (status2 & BFFN); + + return retval; +} + +static unsigned int tnt_transfer_count(struct tnt4882_priv *tnt_priv) +{ + unsigned int count = 0; + + count |= tnt_readb(tnt_priv, CNT0) & 0xff; + count |= (tnt_readb(tnt_priv, CNT1) << 8) & 0xff00; + count |= (tnt_readb(tnt_priv, CNT2) << 16) & 0xff0000; + count |= (tnt_readb(tnt_priv, CNT3) << 24) & 0xff000000; + // return two's complement + return -count; +}; + +static int write_wait(struct gpib_board *board, struct tnt4882_priv *tnt_priv, + int wait_for_done, int send_commands) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + if (wait_event_interruptible(board->wait, + (!wait_for_done && fifo_space_available(tnt_priv)) || + fifo_xfer_done(tnt_priv) || + test_bit(BUS_ERROR_BN, &nec_priv->state) || + test_bit(DEV_CLEAR_BN, &nec_priv->state) || + test_bit(TIMO_NUM, &board->status))) + return -ERESTARTSYS; + + if (test_bit(TIMO_NUM, &board->status)) + return -ETIMEDOUT; + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) + return (send_commands) ? -ENOTCONN : -ECOMM; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) + return -EINTR; + return 0; +} + +static int generic_write(struct gpib_board *board, u8 *buffer, size_t length, + int send_eoi, int send_commands, size_t *bytes_written) +{ + size_t count = 0; + ssize_t retval = 0; + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + unsigned int bits; + s32 hw_count; + unsigned long flags; + + *bytes_written = 0; + // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race + clear_bit(DEV_CLEAR_BN, &nec_priv->state); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ERRIE, HR_ERRIE); + + if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); + else + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); + + tnt_writeb(tnt_priv, RESET_FIFO, CMDR); + udelay(1); + + bits = TNT_B_16BIT; + if (send_eoi) { + bits |= TNT_CCEN; + if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) + tnt_writeb(tnt_priv, AUX_SEOI, CCR); + } + if (send_commands) + bits |= TNT_COMMAND; + tnt_writeb(tnt_priv, bits, CFG); + + // load 2's complement of count into hardware counters + hw_count = -length; + tnt_writeb(tnt_priv, hw_count & 0xff, CNT0); + tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1); + tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2); + tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3); + + tnt_writeb(tnt_priv, GO, CMDR); + udelay(1); + + spin_lock_irqsave(&board->spinlock, flags); + tnt_priv->imr3_bits |= HR_DONE; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + while (count < length) { + // wait until byte is ready to be sent + retval = write_wait(board, tnt_priv, 0, send_commands); + if (retval < 0) + break; + if (fifo_xfer_done(tnt_priv)) + break; + spin_lock_irqsave(&board->spinlock, flags); + while (fifo_space_available(tnt_priv) && count < length) { + u16 word; + + word = buffer[count++] & 0xff; + if (count < length) + word |= (buffer[count++] << 8) & 0xff00; + iowrite16(word, nec_priv->mmiobase + FIFOB); + } +// avoid unnecessary HR_NFF interrupts +// tnt_priv->imr3_bits |= HR_NFF; +// tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + spin_unlock_irqrestore(&board->spinlock, flags); + + if (need_resched()) + schedule(); + } + // wait last byte has been sent + if (retval == 0) + retval = write_wait(board, tnt_priv, 1, send_commands); + + tnt_writeb(tnt_priv, STOP, CMDR); + udelay(1); + + nec7210_set_reg_bits(nec_priv, IMR1, HR_ERR, 0x0); + nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0x0); + /* + * force handling of any interrupts that happened + * while they were masked (this appears to be needed) + */ + tnt4882_internal_interrupt(board); + *bytes_written = length - tnt_transfer_count(tnt_priv); + return retval; +} + +static int tnt4882_accel_write(struct gpib_board *board, u8 *buffer, + size_t length, int send_eoi, size_t *bytes_written) +{ + return generic_write(board, buffer, length, send_eoi, 0, bytes_written); +} + +static int tnt4882_command(struct gpib_board *board, u8 *buffer, size_t length, + size_t *bytes_written) +{ + return generic_write(board, buffer, length, 0, 1, bytes_written); +} + +static irqreturn_t tnt4882_internal_interrupt(struct gpib_board *board) +{ + struct tnt4882_priv *priv = board->private_data; + int isr0_bits, isr3_bits, imr3_bits; + unsigned long flags; + + spin_lock_irqsave(&board->spinlock, flags); + + nec7210_interrupt(board, &priv->nec7210_priv); + + isr0_bits = tnt_readb(priv, ISR0); + isr3_bits = tnt_readb(priv, ISR3); + imr3_bits = priv->imr3_bits; + + if (isr0_bits & TNT_IFCI_BIT) + push_gpib_event(board, EVENT_IFC); + // XXX don't need this wakeup, one below should do? +// wake_up_interruptible(&board->wait); + + if (isr3_bits & HR_NFF) + priv->imr3_bits &= ~HR_NFF; + if (isr3_bits & HR_NEF) + priv->imr3_bits &= ~HR_NEF; + if (isr3_bits & HR_DONE) + priv->imr3_bits &= ~HR_DONE; + if (isr3_bits & (HR_INTR | HR_TLCI)) { + dev_dbg(board->gpib_dev, "minor %i isr0 0x%x imr0 0x%x isr3 0x%x imr3 0x%x\n", + board->minor, isr0_bits, priv->imr0_bits, isr3_bits, imr3_bits); + tnt_writeb(priv, priv->imr3_bits, IMR3); + wake_up_interruptible(&board->wait); + } + spin_unlock_irqrestore(&board->spinlock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t tnt4882_interrupt(int irq, void *arg) +{ + return tnt4882_internal_interrupt(arg); +} + +// wrappers for interface functions +static int tnt4882_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, + size_t *bytes_read) +{ + struct tnt4882_priv *priv = board->private_data; + struct nec7210_priv *nec_priv = &priv->nec7210_priv; + int retval; + int dummy; + + retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); + + if (retval < 0) { // force immediate holdoff + write_byte(nec_priv, AUX_HLDI, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + nec7210_read_data_in(board, nec_priv, &dummy); + } + return retval; +} + +static int tnt4882_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); +} + +static int tnt4882_command_unaccel(struct gpib_board *board, u8 *buffer, + size_t length, size_t *bytes_written) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); +} + +static int tnt4882_take_control(struct gpib_board *board, int synchronous) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +static int tnt4882_go_to_standby(struct gpib_board *board) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +static int tnt4882_request_system_control(struct gpib_board *board, int request_control) +{ + struct tnt4882_priv *priv = board->private_data; + int retval; + + if (request_control) { + tnt_writeb(priv, SETSC, CMDR); + udelay(1); + } + retval = nec7210_request_system_control(board, &priv->nec7210_priv, request_control); + if (!request_control) { + tnt_writeb(priv, CLRSC, CMDR); + udelay(1); + } + return retval; +} + +static void tnt4882_interface_clear(struct gpib_board *board, int assert) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +static void tnt4882_remote_enable(struct gpib_board *board, int enable) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +static int tnt4882_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); +} + +static void tnt4882_disable_eos(struct gpib_board *board) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +static unsigned int tnt4882_update_status(struct gpib_board *board, unsigned int clear_mask) +{ + unsigned long flags; + u8 line_status; + struct tnt4882_priv *priv = board->private_data; + + spin_lock_irqsave(&board->spinlock, flags); + board->status &= ~clear_mask; + nec7210_update_status_nolock(board, &priv->nec7210_priv); + /* set / clear SRQ state since it is not cleared by interrupt */ + line_status = tnt_readb(priv, BSR); + if (line_status & BCSR_SRQ_BIT) + set_bit(SRQI_NUM, &board->status); + else + clear_bit(SRQI_NUM, &board->status); + spin_unlock_irqrestore(&board->spinlock, flags); + return board->status; +} + +static int tnt4882_primary_address(struct gpib_board *board, unsigned int address) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +static int tnt4882_secondary_address(struct gpib_board *board, unsigned int address, int enable) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +static int tnt4882_parallel_poll(struct gpib_board *board, u8 *result) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + + if (tnt_priv->nec7210_priv.type != NEC7210) { + tnt_priv->auxg_bits |= RPP2_BIT; + write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); + udelay(2); // FIXME use parallel poll timeout + *result = read_byte(&tnt_priv->nec7210_priv, CPTR); + tnt_priv->auxg_bits &= ~RPP2_BIT; + write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); + return 0; + } else { + return nec7210_parallel_poll(board, &tnt_priv->nec7210_priv, result); + } +} + +static void tnt4882_parallel_poll_configure(struct gpib_board *board, u8 config) +{ + struct tnt4882_priv *priv = board->private_data; + + if (priv->nec7210_priv.type == TNT5004) { + /* configure locally */ + write_byte(&priv->nec7210_priv, AUXRI | 0x4, AUXMR); + if (config) + /* set response + clear sense */ + write_byte(&priv->nec7210_priv, PPR | config, AUXMR); + else + /* disable ppoll */ + write_byte(&priv->nec7210_priv, PPR | 0x10, AUXMR); + } else { + nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); + } +} + +static void tnt4882_parallel_poll_response(struct gpib_board *board, int ist) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); +} + +/* + * this is just used by the old nec7210 isa interfaces, the newer + * boards use tnt4882_serial_poll_response2 + */ +static void tnt4882_serial_poll_response(struct gpib_board *board, u8 status) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static void tnt4882_serial_poll_response2(struct gpib_board *board, u8 status, + int new_reason_for_service) +{ + struct tnt4882_priv *priv = board->private_data; + unsigned long flags; + const int MSS = status & request_service_bit; + const int reqt = MSS && new_reason_for_service; + const int reqf = MSS == 0; + + spin_lock_irqsave(&board->spinlock, flags); + if (reqt) { + priv->nec7210_priv.srq_pending = 1; + clear_bit(SPOLL_NUM, &board->status); + } else { + if (reqf) + priv->nec7210_priv.srq_pending = 0; + } + if (reqt) + /* + * It may seem like a race to issue reqt before updating + * the status byte, but it is not. The chip does not + * issue the reqt until the SPMR is written to at + * a later time. + */ + write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR); + else if (reqf) + write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR); + /* + * We need to always zero bit 6 of the status byte before writing it to + * the SPMR to insure we are using + * serial poll mode SP1, and not accidentally triggering mode SP3. + */ + write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR); + spin_unlock_irqrestore(&board->spinlock, flags); +} + +static u8 tnt4882_serial_poll_status(struct gpib_board *board) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +static void tnt4882_return_to_local(struct gpib_board *board) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +static void tnt4882_board_reset(struct tnt4882_priv *tnt_priv, struct gpib_board *board) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + tnt_priv->imr0_bits = 0; + tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0); + tnt_priv->imr3_bits = 0; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + tnt_readb(tnt_priv, IMR0); + tnt_readb(tnt_priv, IMR3); + nec7210_board_reset(nec_priv, board); +} + +static int tnt4882_allocate_private(struct gpib_board *board) +{ + struct tnt4882_priv *tnt_priv; + + board->private_data = kmalloc(sizeof(struct tnt4882_priv), GFP_KERNEL); + if (!board->private_data) + return -1; + tnt_priv = board->private_data; + memset(tnt_priv, 0, sizeof(struct tnt4882_priv)); + init_nec7210_private(&tnt_priv->nec7210_priv); + return 0; +} + +static void tnt4882_free_private(struct gpib_board *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +static void tnt4882_init(struct tnt4882_priv *tnt_priv, const struct gpib_board *board) +{ + struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; + + /* Turbo488 software reset */ + tnt_writeb(tnt_priv, SOFT_RESET, CMDR); + udelay(1); + + // turn off one-chip mode + tnt_writeb(tnt_priv, NODMA, HSSEL); + tnt_writeb(tnt_priv, 0, ACCWR); + // make sure we are in 7210 mode + tnt_writeb(tnt_priv, AUX_7210, AUXCR); + udelay(1); + // registers might be swapped, so write it to the swapped address too + tnt_writeb(tnt_priv, AUX_7210, SWAPPED_AUXCR); + udelay(1); + // turn on one-chip mode + if (nec_priv->type == TNT4882 || nec_priv->type == TNT5004) + tnt_writeb(tnt_priv, NODMA | TNT_ONE_CHIP_BIT, HSSEL); + else + tnt_writeb(tnt_priv, NODMA, HSSEL); + + nec7210_board_reset(nec_priv, board); + // read-clear isr0 + tnt_readb(tnt_priv, ISR0); + + // enable passing of nat4882 interrupts + tnt_priv->imr3_bits = HR_TLCI; + tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); + + // enable interrupt + tnt_writeb(tnt_priv, 0x1, INTRT); + + // force immediate holdoff + write_byte(&tnt_priv->nec7210_priv, AUX_HLDI, AUXMR); + + set_bit(RFD_HOLDOFF_BN, &nec_priv->state); + + tnt_priv->auxg_bits = AUXRG | NTNL_BIT; + write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); + + nec7210_board_online(nec_priv, board); + // enable interface clear interrupt for event queue + tnt_priv->imr0_bits = TNT_IMR0_ALWAYS_BITS | TNT_ATNI_BIT | TNT_IFCIE_BIT; + tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0); +} + +static int ni_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = IRQF_SHARED; + int retval; + struct mite_struct *mite; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + tnt_priv = board->private_data; + nec_priv = &tnt_priv->nec7210_priv; + nec_priv->type = TNT4882; + nec_priv->read_byte = nec7210_locking_iomem_read_byte; + nec_priv->write_byte = nec7210_locking_iomem_write_byte; + nec_priv->offset = atgpib_reg_offset; + + if (!mite_devices) + return -ENODEV; + + for (mite = mite_devices; mite; mite = mite->next) { + short found_board; + + if (mite->used) + continue; + if (config->pci_bus >= 0 && config->pci_bus != mite->pcidev->bus->number) + continue; + if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(mite->pcidev->devfn)) + continue; + switch (mite_device_id(mite)) { + case PCI_DEVICE_ID_NI_GPIB: + case PCI_DEVICE_ID_NI_GPIB_PLUS: + case PCI_DEVICE_ID_NI_GPIB_PLUS2: + case PCI_DEVICE_ID_NI_PXIGPIB: + case PCI_DEVICE_ID_NI_PMCGPIB: + case PCI_DEVICE_ID_NI_PCIEGPIB: + case PCI_DEVICE_ID_NI_PCIE2GPIB: +// support for Measurement Computing PCI-488 + case PCI_DEVICE_ID_MC_PCI488: + case PCI_DEVICE_ID_CEC_NI_GPIB: + found_board = 1; + break; + default: + found_board = 0; + break; + } + if (found_board) + break; + } + if (!mite) + return -ENODEV; + + tnt_priv->mite = mite; + retval = mite_setup(tnt_priv->mite); + if (retval < 0) + return retval; + + nec_priv->mmiobase = tnt_priv->mite->daq_io_addr; + + // get irq + retval = request_irq(mite_irq(tnt_priv->mite), tnt4882_interrupt, isr_flags, "ni-pci-gpib", + board); + if (retval) { + dev_err(board->gpib_dev, "failed to obtain pci irq %d\n", mite_irq(tnt_priv->mite)); + return retval; + } + tnt_priv->irq = mite_irq(tnt_priv->mite); + + // TNT5004 detection + switch (tnt_readb(tnt_priv, CSR) & 0xf0) { + case 0x30: + nec_priv->type = TNT4882; + break; + case 0x40: + nec_priv->type = TNT5004; + break; + } + tnt4882_init(tnt_priv, board); + + return 0; +} + +static void ni_pci_detach(struct gpib_board *board) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (tnt_priv) { + nec_priv = &tnt_priv->nec7210_priv; + + if (nec_priv->mmiobase) + tnt4882_board_reset(tnt_priv, board); + if (tnt_priv->irq) + free_irq(tnt_priv->irq, board); + if (tnt_priv->mite) + mite_unsetup(tnt_priv->mite); + } + tnt4882_free_private(board); +} + +static int ni_isapnp_find(struct pnp_dev **dev) +{ + *dev = pnp_find_dev(NULL, ISAPNP_VENDOR_ID_NI, + ISAPNP_FUNCTION(ISAPNP_ID_NI_ATGPIB_TNT), NULL); + if (!*dev || !(*dev)->card) + return -ENODEV; + if (pnp_device_attach(*dev) < 0) + return -EBUSY; + if (pnp_activate_dev(*dev) < 0) { + pnp_device_detach(*dev); + return -EAGAIN; + } + if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { + pnp_device_detach(*dev); + return -EINVAL; + } + return 0; +} + +static int ni_isa_attach_common(struct gpib_board *board, const struct gpib_board_config *config, + enum nec7210_chipset chipset) +{ + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + u32 iobase; + int irq; + int retval; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + tnt_priv = board->private_data; + nec_priv = &tnt_priv->nec7210_priv; + nec_priv->type = chipset; + nec_priv->read_byte = nec7210_locking_ioport_read_byte; + nec_priv->write_byte = nec7210_locking_ioport_write_byte; + nec_priv->offset = atgpib_reg_offset; + + // look for plug-n-play board + if (config->ibbase == 0) { + struct pnp_dev *dev; + + retval = ni_isapnp_find(&dev); + if (retval < 0) + return retval; + tnt_priv->pnp_dev = dev; + iobase = pnp_port_start(dev, 0); + irq = pnp_irq(dev, 0); + } else { + iobase = config->ibbase; + irq = config->ibirq; + } + // allocate ioports + if (!request_region(iobase, atgpib_iosize, "atgpib")) + return -EBUSY; + + nec_priv->mmiobase = ioport_map(iobase, atgpib_iosize); + if (!nec_priv->mmiobase) + return -EBUSY; + + // get irq + retval = request_irq(irq, tnt4882_interrupt, isr_flags, "atgpib", board); + if (retval) { + dev_err(board->gpib_dev, "failed to request ISA irq %d\n", irq); + return retval; + } + tnt_priv->irq = irq; + + tnt4882_init(tnt_priv, board); + + return 0; +} + +static int ni_tnt_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + return ni_isa_attach_common(board, config, TNT4882); +} + +static int ni_nat4882_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + return ni_isa_attach_common(board, config, NAT4882); +} + +static int ni_nec_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + return ni_isa_attach_common(board, config, NEC7210); +} + +static void ni_isa_detach(struct gpib_board *board) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (tnt_priv) { + nec_priv = &tnt_priv->nec7210_priv; + if (nec_priv->iobase) + tnt4882_board_reset(tnt_priv, board); + if (tnt_priv->irq) + free_irq(tnt_priv->irq, board); + if (nec_priv->mmiobase) + ioport_unmap(nec_priv->mmiobase); + if (nec_priv->iobase) + release_region(nec_priv->iobase, atgpib_iosize); + if (tnt_priv->pnp_dev) + pnp_device_detach(tnt_priv->pnp_dev); + } + tnt4882_free_private(board); +} + +static int tnt4882_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static struct gpib_interface ni_pci_interface = { + .name = "ni_pci", + .attach = ni_pci_attach, + .detach = ni_pci_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response2 = tnt4882_serial_poll_response2, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_pci_accel_interface = { + .name = "ni_pci_accel", + .attach = ni_pci_attach, + .detach = ni_pci_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response2 = tnt4882_serial_poll_response2, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_isa_interface = { + .name = "ni_isa", + .attach = ni_tnt_isa_attach, + .detach = ni_isa_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response2 = tnt4882_serial_poll_response2, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_nat4882_isa_interface = { + .name = "ni_nat4882_isa", + .attach = ni_nat4882_isa_attach, + .detach = ni_isa_detach, + .read = tnt4882_read, + .write = tnt4882_write, + .command = tnt4882_command_unaccel, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response2 = tnt4882_serial_poll_response2, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_nec_isa_interface = { + .name = "ni_nec_isa", + .attach = ni_nec_isa_attach, + .detach = ni_isa_detach, + .read = tnt4882_read, + .write = tnt4882_write, + .command = tnt4882_command_unaccel, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = NULL, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response = tnt4882_serial_poll_response, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_isa_accel_interface = { + .name = "ni_isa_accel", + .attach = ni_tnt_isa_attach, + .detach = ni_isa_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response2 = tnt4882_serial_poll_response2, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_nat4882_isa_accel_interface = { + .name = "ni_nat4882_isa_accel", + .attach = ni_nat4882_isa_attach, + .detach = ni_isa_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command_unaccel, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response2 = tnt4882_serial_poll_response2, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_nec_isa_accel_interface = { + .name = "ni_nec_isa_accel", + .attach = ni_nec_isa_attach, + .detach = ni_isa_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command_unaccel, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = NULL, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response = tnt4882_serial_poll_response, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static const struct pci_device_id tnt4882_pci_table[] = { + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS2)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PXIGPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PMCGPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIEGPIB)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIE2GPIB)}, + // support for Measurement Computing PCI-488 + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_MC_PCI488)}, + {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_CEC_NI_GPIB)}, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, tnt4882_pci_table); + +static struct pci_driver tnt4882_pci_driver = { + .name = DRV_NAME, + .id_table = tnt4882_pci_table, + .probe = &tnt4882_pci_probe +}; + +#if 0 +/* unused, will be needed when the driver is turned into a pnp_driver */ +static const struct pnp_device_id tnt4882_pnp_table[] = { + {.id = "NICC601"}, + {.id = ""} +}; +MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table); +#endif + +#ifdef CONFIG_GPIB_PCMCIA +static struct gpib_interface ni_pcmcia_interface; +static struct gpib_interface ni_pcmcia_accel_interface; +static int __init init_ni_gpib_cs(void); +static void __exit exit_ni_gpib_cs(void); +#endif + +static int __init tnt4882_init_module(void) +{ + int result; + + result = pci_register_driver(&tnt4882_pci_driver); + if (result) { + pr_err("pci_register_driver failed: error = %d\n", result); + return result; + } + + result = gpib_register_driver(&ni_isa_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_isa; + } + + result = gpib_register_driver(&ni_isa_accel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_isa_accel; + } + + result = gpib_register_driver(&ni_nat4882_isa_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_nat4882_isa; + } + + result = gpib_register_driver(&ni_nat4882_isa_accel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_nat4882_isa_accel; + } + + result = gpib_register_driver(&ni_nec_isa_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_nec_isa; + } + + result = gpib_register_driver(&ni_nec_isa_accel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_nec_isa_accel; + } + + result = gpib_register_driver(&ni_pci_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_pci; + } + + result = gpib_register_driver(&ni_pci_accel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_pci_accel; + } + +#ifdef CONFIG_GPIB_PCMCIA + result = gpib_register_driver(&ni_pcmcia_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_pcmcia; + } + + result = gpib_register_driver(&ni_pcmcia_accel_interface, THIS_MODULE); + if (result) { + pr_err("gpib_register_driver failed: error = %d\n", result); + goto err_pcmcia_accel; + } + + result = init_ni_gpib_cs(); + if (result) { + pr_err("pcmcia_register_driver failed: error = %d\n", result); + goto err_pcmcia_driver; + } +#endif + + mite_init(); + + return 0; + +#ifdef CONFIG_GPIB_PCMCIA +err_pcmcia_driver: + gpib_unregister_driver(&ni_pcmcia_accel_interface); +err_pcmcia_accel: + gpib_unregister_driver(&ni_pcmcia_interface); +err_pcmcia: +#endif + gpib_unregister_driver(&ni_pci_accel_interface); +err_pci_accel: + gpib_unregister_driver(&ni_pci_interface); +err_pci: + gpib_unregister_driver(&ni_nec_isa_accel_interface); +err_nec_isa_accel: + gpib_unregister_driver(&ni_nec_isa_interface); +err_nec_isa: + gpib_unregister_driver(&ni_nat4882_isa_accel_interface); +err_nat4882_isa_accel: + gpib_unregister_driver(&ni_nat4882_isa_interface); +err_nat4882_isa: + gpib_unregister_driver(&ni_isa_accel_interface); +err_isa_accel: + gpib_unregister_driver(&ni_isa_interface); +err_isa: + pci_unregister_driver(&tnt4882_pci_driver); + + return result; +} + +static void __exit tnt4882_exit_module(void) +{ + gpib_unregister_driver(&ni_isa_interface); + gpib_unregister_driver(&ni_isa_accel_interface); + gpib_unregister_driver(&ni_nat4882_isa_interface); + gpib_unregister_driver(&ni_nat4882_isa_accel_interface); + gpib_unregister_driver(&ni_nec_isa_interface); + gpib_unregister_driver(&ni_nec_isa_accel_interface); + gpib_unregister_driver(&ni_pci_interface); + gpib_unregister_driver(&ni_pci_accel_interface); +#ifdef CONFIG_GPIB_PCMCIA + gpib_unregister_driver(&ni_pcmcia_interface); + gpib_unregister_driver(&ni_pcmcia_accel_interface); + exit_ni_gpib_cs(); +#endif + + mite_cleanup(); + + pci_unregister_driver(&tnt4882_pci_driver); +} + +#ifdef CONFIG_GPIB_PCMCIA + +#include +#include +#include +#include +#include + +#include +#include +#include + +static int ni_gpib_config(struct pcmcia_device *link); +static void ni_gpib_release(struct pcmcia_device *link); +static void ni_pcmcia_detach(struct gpib_board *board); + +/* + * A linked list of "instances" of the dummy device. Each actual + * PCMCIA card corresponds to one device instance, and is described + * by one dev_link_t structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the + * memory card driver uses an array of dev_link_t pointers, where minor + * device numbers are used to derive the corresponding array index. + * + * I think this dev_list is obsolete but the pointer is needed to keep + * the module instance for the ni_pcmcia_attach function. + */ + +static struct pcmcia_device *curr_dev; + +struct local_info_t { + struct pcmcia_device *p_dev; + struct gpib_board *dev; + int stop; + struct bus_operations *bus; +}; + +/* + * ni_gpib_probe() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + */ + +static int ni_gpib_probe(struct pcmcia_device *link) +{ + struct local_info_t *info; + //struct struct gpib_board *dev; + + /* Allocate space for private device-specific data */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + + /* + * General socket configuration defaults can go here. In this + * client, we assume very little, and rely on the CIS for almost + * everything. In most clients, many details (i.e., number, sizes, + * and attributes of IO windows) are fixed by the nature of the + * device, and can be hard-wired here. + */ + link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + /* Register with Card Services */ + curr_dev = link; + return ni_gpib_config(link); +} + +/* + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ +static void ni_gpib_remove(struct pcmcia_device *link) +{ + struct local_info_t *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + if (info->dev) + ni_pcmcia_detach(info->dev); + ni_gpib_release(link); + + //free_netdev(dev); + kfree(info); +} + +static int ni_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) +{ + int retval; + + retval = pcmcia_request_io(link); + if (retval != 0) + return retval; + + return 0; +} + +/* + * ni_gpib_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * device available to the system. + */ +static int ni_gpib_config(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //struct gpib_board *dev = info->dev; + int last_ret; + + last_ret = pcmcia_loop_config(link, &ni_gpib_config_iteration, NULL); + if (last_ret) { + dev_warn(&link->dev, "no configuration found\n"); + ni_gpib_release(link); + return last_ret; + } + + last_ret = pcmcia_enable_device(link); + if (last_ret) { + ni_gpib_release(link); + return last_ret; + } + return 0; +} /* ni_gpib_config */ + +/* + * After a card is removed, ni_gpib_release() will unregister the + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ +static void ni_gpib_release(struct pcmcia_device *link) +{ + pcmcia_disable_device(link); +} /* ni_gpib_release */ + +static int ni_gpib_suspend(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + if (link->open) + dev_warn(&link->dev, "Device still open\n"); + //netif_device_detach(dev); + + return 0; +} + +static int ni_gpib_resume(struct pcmcia_device *link) +{ + //struct local_info_t *info = link->priv; + //struct struct gpib_board *dev = info->dev; + + /*if (link->open) { + * ni_gpib_probe(dev); / really? + * //netif_device_attach(dev); + *} + */ + return ni_gpib_config(link); +} + +static struct pcmcia_device_id ni_pcmcia_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4882), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0c71), // NI PCMCIA-GPIB+ + PCMCIA_DEVICE_NULL +}; + +MODULE_DEVICE_TABLE(pcmcia, ni_pcmcia_ids); + +static struct pcmcia_driver ni_gpib_cs_driver = { + .name = "ni_gpib_cs", + .owner = THIS_MODULE, + .drv = { .name = "ni_gpib_cs", }, + .id_table = ni_pcmcia_ids, + .probe = ni_gpib_probe, + .remove = ni_gpib_remove, + .suspend = ni_gpib_suspend, + .resume = ni_gpib_resume, +}; + +static int __init init_ni_gpib_cs(void) +{ + return pcmcia_register_driver(&ni_gpib_cs_driver); +} + +static void __exit exit_ni_gpib_cs(void) +{ + pcmcia_unregister_driver(&ni_gpib_cs_driver); +} + +static const int pcmcia_gpib_iosize = 32; + +static int ni_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct local_info_t *info; + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = IRQF_SHARED; + int retval; + + if (!curr_dev) + return -ENODEV; + + info = curr_dev->priv; + info->dev = board; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + + tnt_priv = board->private_data; + nec_priv = &tnt_priv->nec7210_priv; + nec_priv->type = TNT4882; + nec_priv->read_byte = nec7210_locking_ioport_read_byte; + nec_priv->write_byte = nec7210_locking_ioport_write_byte; + nec_priv->offset = atgpib_reg_offset; + + if (!request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]), + DRV_NAME)) + return -ENOMEM; + + nec_priv->mmiobase = ioport_map(curr_dev->resource[0]->start, + resource_size(curr_dev->resource[0])); + if (!nec_priv->mmiobase) + return -ENOMEM; + + // get irq + retval = request_irq(curr_dev->irq, tnt4882_interrupt, isr_flags, DRV_NAME, board); + if (retval) { + dev_err(board->gpib_dev, "failed to obtain PCMCIA irq %d\n", curr_dev->irq); + return retval; + } + tnt_priv->irq = curr_dev->irq; + + tnt4882_init(tnt_priv, board); + + return 0; +} + +static void ni_pcmcia_detach(struct gpib_board *board) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv; + + if (tnt_priv) { + nec_priv = &tnt_priv->nec7210_priv; + if (tnt_priv->irq) + free_irq(tnt_priv->irq, board); + if (nec_priv->mmiobase) + ioport_unmap(nec_priv->mmiobase); + if (nec_priv->iobase) { + tnt4882_board_reset(tnt_priv, board); + release_region(nec_priv->iobase, pcmcia_gpib_iosize); + } + } + tnt4882_free_private(board); +} + +static struct gpib_interface ni_pcmcia_interface = { + .name = "ni_pcmcia", + .attach = ni_pcmcia_attach, + .detach = ni_pcmcia_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response = tnt4882_serial_poll_response, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +static struct gpib_interface ni_pcmcia_accel_interface = { + .name = "ni_pcmcia_accel", + .attach = ni_pcmcia_attach, + .detach = ni_pcmcia_detach, + .read = tnt4882_accel_read, + .write = tnt4882_accel_write, + .command = tnt4882_command, + .take_control = tnt4882_take_control, + .go_to_standby = tnt4882_go_to_standby, + .request_system_control = tnt4882_request_system_control, + .interface_clear = tnt4882_interface_clear, + .remote_enable = tnt4882_remote_enable, + .enable_eos = tnt4882_enable_eos, + .disable_eos = tnt4882_disable_eos, + .parallel_poll = tnt4882_parallel_poll, + .parallel_poll_configure = tnt4882_parallel_poll_configure, + .parallel_poll_response = tnt4882_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = tnt4882_line_status, + .update_status = tnt4882_update_status, + .primary_address = tnt4882_primary_address, + .secondary_address = tnt4882_secondary_address, + .serial_poll_response = tnt4882_serial_poll_response, + .serial_poll_status = tnt4882_serial_poll_status, + .t1_delay = tnt4882_t1_delay, + .return_to_local = tnt4882_return_to_local, +}; + +#endif // CONFIG_GPIB_PCMCIA + +module_init(tnt4882_init_module); +module_exit(tnt4882_exit_module); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 075e775d3868..2f92cd698bef 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -48,6 +48,4 @@ source "drivers/staging/axis-fifo/Kconfig" source "drivers/staging/vme_user/Kconfig" -source "drivers/staging/gpib/Kconfig" - endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e681e403509c..f5b8876aa536 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -13,4 +13,3 @@ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_GREYBUS) += greybus/ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ -obj-$(CONFIG_GPIB) += gpib/ diff --git a/drivers/staging/gpib/Kconfig b/drivers/staging/gpib/Kconfig deleted file mode 100644 index aa01538d5beb..000000000000 --- a/drivers/staging/gpib/Kconfig +++ /dev/null @@ -1,255 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -menuconfig GPIB - tristate "Linux GPIB drivers" - help - Enable support for GPIB cards and dongles for Linux. GPIB - is the General Purpose Interface Bus which conforms to the - IEEE488 standard. - - This set of drivers can be used with the corresponding user - space library that can be found on Sourceforge under linux-gpib. - Select the drivers for your hardware from the list. - -if GPIB - -config GPIB_COMMON - tristate "GPIB core" - help - - Core common driver for all GPIB drivers. It provides the - interface for the userland library - - To compile this driver as a module, choose M here: the module will be - called gpib_common - -config GPIB_AGILENT_82350B - tristate "Agilent 8235xx PCI(e) adapters" - depends on PCI - select GPIB_COMMON - select GPIB_TMS9914 - help - Enable support for HP/Agilent/Keysight boards - 82350A - 82350B - 82351A - - To compile this driver as a module, choose M here: the module will be - called agilent_82350b. - -config GPIB_AGILENT_82357A - tristate "Agilent 82357a/b USB dongles" - select GPIB_COMMON - depends on USB - help - Enable support for Agilent/Keysight 82357x USB dongles. - - To compile this driver as a module, choose M here: the module will be - called agilent_82357a. - -config GPIB_CEC_PCI - tristate "CEC PCI board" - depends on PCI - depends on HAS_IOPORT - select GPIB_COMMON - select GPIB_NEC7210 - help - Enable support for Capital Equipment Corporation PCI-488 - and Keithly KPCI-488 boards. - - To compile this driver as a module, choose M here: the module will be - called cec_gpib. - -config GPIB_NI_PCI_ISA - tristate "NI PCI/ISA compatible boards" - depends on ISA_BUS || PCI || PCMCIA - depends on HAS_IOPORT - depends on PCMCIA || !PCMCIA - depends on HAS_IOPORT_MAP - select GPIB_COMMON - select GPIB_NEC7210 - help - Enable support for National Instruments boards based - on TNT4882 chips: - AT-GPIB (with NAT4882 chip) - AT-GPIB (with NEC7210 chip) - AT-GPIB/TNT - PCI-GPIB - PCIe-GPIB - PCI-GPIB+ - PCM-GPIB - PXI-GPIB - PCMCIA-GPIB - and Capital Equipment Corporation CEC-488 board. - - To compile this driver as a module, choose M here: the module will be - called tnt4882. - -config GPIB_CB7210 - tristate "Measurement Computing compatible boards" - depends on HAS_IOPORT - depends on ISA_BUS || PCI || PCMCIA - depends on PCMCIA || !PCMCIA - select GPIB_COMMON - select GPIB_NEC7210 - help - Enable support for Measurement Computing (Computer Boards): - CPCI_GPIB, ISA-GPIB, ISA-GPIB/LC, PCI-GPIB/1M, PCI-GPIB/300K and - PCMCIA-GPIB - Quancom PCIGPIB-1 with MC cb7210 chip - - To compile this driver as a module, choose M here: the module will be - -config GPIB_NI_USB - tristate "NI USB dongles" - select GPIB_COMMON - depends on USB - help - Enable support for National Instruments - GPIB-USB-B - GPIB-USB-HS - GPIB-USB-HS+ - Keithly - KUSB-488 - KUSB-488A - Measurement Computing (Computer Boards) - USB-488 - - To compile this driver as a module, choose M here: the module will be - called ni_usb. - -config GPIB_FLUKE - tristate "Fluke" - depends on OF - select GPIB_COMMON - select GPIB_NEC7210 - help - GPIB driver for Fluke based cda devices. - - To compile this driver as a module, choose M here: the module will be - called fluke_gpib - -config GPIB_FMH - tristate "FMH FPGA based devices" - select GPIB_COMMON - select GPIB_NEC7210 - depends on !PPC - depends on OF && PCI - help - GPIB driver for fmhess FPGA based devices - - To compile this driver as a module, choose M here: the module will be - called fmh_gpib - -config GPIB_GPIO - tristate "RPi GPIO bitbang" - depends on ARCH_BCM2835 || COMPILE_TEST - select GPIB_COMMON - help - GPIB bitbang driver Raspberry Pi GPIO adapters - - To compile this driver as a module, choose M here: the module will be - called gpib_bitbang - -config GPIB_HP82335 - tristate "HP82335/HP27209" - depends on ISA_BUS - select GPIB_COMMON - select GPIB_TMS9914 - help - GPIB driver for HP82335 and HP27209 boards - - To compile this driver as a module, choose M here: the module will be - called hp82335 - - -config GPIB_HP82341 - tristate "HP82341x" - select GPIB_COMMON - select GPIB_TMS9914 - depends on ISA_BUS || EISA - help - GPIB driver for HP82341 A/B/C/D boards - - To compile this driver as a module, choose M here: the module will be - called hp82341 - -config GPIB_INES - tristate "INES" - depends on PCI || ISA_BUS || PCMCIA - depends on PCMCIA || !PCMCIA - depends on HAS_IOPORT - select GPIB_COMMON - select GPIB_NEC7210 - help - GPIB driver for Ines compatible boards - Ines - GPIB-HS-NT - GPIB for Compact PCI - GPIB for PCI - GPIB for PCMCIA - GPIB PC/104 - Hameg - HO80-2 - Quancom - PCIGPIB-1 based on Ines iGPIB 72010 chip - - To compile this driver as a module, choose M here: the module will be - called ines_gpib - called cb7210. - -config GPIB_PCMCIA - def_bool y - depends on PCMCIA && (GPIB_NI_PCI_ISA || GPIB_CB7210 || GPIB_INES) - help - Enable PCMCIA/CArdbus support for National Instruments, - measurement computing boards and Ines boards. - -config GPIB_LPVO - tristate "LPVO DIY USB GPIB" - select GPIB_COMMON - depends on USB - help - Enable support for LPVO Self-made usb-gpib adapter - - To compile this driver as a module, choose M here: the module will be - called lpvo_usb_gpib - -config GPIB_PC2 - tristate "PC2 PC2a" - depends on ISA_BUS - depends on HAS_IOPORT - select GPIB_COMMON - select GPIB_NEC7210 - help - Enable support for pc2 and pc2a compatible adapters - Capital Equipment Corporation PC-488 - CONTEC GP-IB(PC) - Hameg HO80 - Iotech GP488B - Keithly MBC-488 - Measurement Computing ISA-GPIB-PCA2 - National Instruments PCII, PCIIa and PCII/IIa - - To compile this driver as a module, choose M here: the module will be - called pc2_gpib - - -config GPIB_TMS9914 - tristate - select GPIB_COMMON - help - Enable support for TMS 9914 chip. - - To compile this driver as a module, choose M here: the module will be - called tms9914 - -config GPIB_NEC7210 - tristate - select GPIB_COMMON - help - Enable support for NEC 7210 compatible chips. - - To compile this driver as a module, choose M here: the module will be - called nec7210 - -endif # GPIB diff --git a/drivers/staging/gpib/Makefile b/drivers/staging/gpib/Makefile deleted file mode 100644 index d0e88f5c0844..000000000000 --- a/drivers/staging/gpib/Makefile +++ /dev/null @@ -1,20 +0,0 @@ - -subdir-ccflags-y += -I$(src)/include -I$(src)/uapi - -obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b/ -obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a/ -obj-$(CONFIG_GPIB_CB7210) += cb7210/ -obj-$(CONFIG_GPIB_CEC_PCI) += cec/ -obj-$(CONFIG_GPIB_COMMON) += common/ -obj-$(CONFIG_GPIB_FLUKE) += eastwood/ -obj-$(CONFIG_GPIB_FMH) += fmh_gpib/ -obj-$(CONFIG_GPIB_GPIO) += gpio/ -obj-$(CONFIG_GPIB_HP82335) += hp_82335/ -obj-$(CONFIG_GPIB_HP82341) += hp_82341/ -obj-$(CONFIG_GPIB_INES) += ines/ -obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib/ -obj-$(CONFIG_GPIB_NEC7210) += nec7210/ -obj-$(CONFIG_GPIB_NI_USB) += ni_usb/ -obj-$(CONFIG_GPIB_PC2) += pc2/ -obj-$(CONFIG_GPIB_TMS9914) += tms9914/ -obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882/ diff --git a/drivers/staging/gpib/TODO b/drivers/staging/gpib/TODO deleted file mode 100644 index ac07dd90b4ef..000000000000 --- a/drivers/staging/gpib/TODO +++ /dev/null @@ -1,10 +0,0 @@ -TODO: -- checkpatch.pl fixes - These checks should be ignored: - CHECK:ALLOC_SIZEOF_STRUCT: Prefer kmalloc(sizeof(*board->private_data)...) over kmalloc(sizeof(struct xxx_priv)...) - ./gpio/gpib_bitbang.c:50: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parenthese - This warning will be addressed later: WARNING:UNDOCUMENTED_DT_STRING: DT compatible string -- resolve XXX notes where possible -- fix FIXME notes -- clean-up commented-out code -- fix typos diff --git a/drivers/staging/gpib/agilent_82350b/Makefile b/drivers/staging/gpib/agilent_82350b/Makefile deleted file mode 100644 index f24e1e713a63..000000000000 --- a/drivers/staging/gpib/agilent_82350b/Makefile +++ /dev/null @@ -1,2 +0,0 @@ - -obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b.o diff --git a/drivers/staging/gpib/agilent_82350b/agilent_82350b.c b/drivers/staging/gpib/agilent_82350b/agilent_82350b.c deleted file mode 100644 index 01a5bb43cd2d..000000000000 --- a/drivers/staging/gpib/agilent_82350b/agilent_82350b.c +++ /dev/null @@ -1,896 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2002, 2004 by Frank Mori Hess * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "agilent_82350b.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for Agilent 82350b"); - -static int read_transfer_counter(struct agilent_82350b_priv *a_priv); -static unsigned short read_and_clear_event_status(struct gpib_board *board); -static void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count); -static int agilent_82350b_write(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written); - -static int agilent_82350b_accel_read(struct gpib_board *board, u8 *buffer, - size_t length, int *end, size_t *bytes_read) - -{ - struct agilent_82350b_priv *a_priv = board->private_data; - struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; - int retval = 0; - unsigned short event_status; - int i, num_fifo_bytes; - /* hardware doesn't support checking for end-of-string character when using fifo */ - if (tms_priv->eos_flags & REOS) - return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); - - clear_bit(DEV_CLEAR_BN, &tms_priv->state); - - read_and_clear_event_status(board); - *end = 0; - *bytes_read = 0; - if (length == 0) - return 0; - /* disable fifo for the moment */ - writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); - /* handle corner case of board not in holdoff and one byte might slip in early */ - if (tms_priv->holdoff_active == 0 && length > 1) { - size_t num_bytes; - - retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0 || *end) - return retval; - ++buffer; - --length; - } - tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); - tms9914_release_holdoff(tms_priv); - i = 0; - num_fifo_bytes = length - 1; - /* disable BI interrupts */ - write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BIIE, IMR0); - while (i < num_fifo_bytes && *end == 0) { - int block_size; - int j; - int count; - - block_size = min(num_fifo_bytes - i, agilent_82350b_fifo_size); - set_transfer_counter(a_priv, block_size); - writeb(ENABLE_TI_TO_SRAM | DIRECTION_GPIB_TO_HOST, - a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); - if (agilent_82350b_fifo_is_halted(a_priv)) - writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG); - - clear_bit(READ_READY_BN, &tms_priv->state); - - retval = wait_event_interruptible(board->wait, - ((event_status = - read_and_clear_event_status(board)) & - (TERM_COUNT_STATUS_BIT | - BUFFER_END_STATUS_BIT)) || - test_bit(DEV_CLEAR_BN, &tms_priv->state) || - test_bit(TIMO_NUM, &board->status)); - if (retval) { - retval = -ERESTARTSYS; - break; - } - count = block_size - read_transfer_counter(a_priv); - for (j = 0; j < count && i < num_fifo_bytes; ++j) - buffer[i++] = readb(a_priv->sram_base + j); - if (event_status & BUFFER_END_STATUS_BIT) { - clear_bit(RECEIVED_END_BN, &tms_priv->state); - - tms_priv->holdoff_active = 1; - *end = 1; - } - if (test_bit(TIMO_NUM, &board->status)) { - retval = -ETIMEDOUT; - break; - } - if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { - retval = -EINTR; - break; - } - } - /* re-enable BI interrupts */ - write_byte(tms_priv, tms_priv->imr0_bits, IMR0); - *bytes_read += i; - buffer += i; - length -= i; - writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); - if (retval < 0) - return retval; - /* read last bytes if we havn't received an END yet */ - if (*end == 0) { - size_t num_bytes; - /* try to make sure we holdoff after last byte read */ - retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0) - return retval; - } - return 0; -} - -static int translate_wait_return_value(struct gpib_board *board, int retval) - -{ - struct agilent_82350b_priv *a_priv = board->private_data; - struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; - - if (retval) - return -ERESTARTSYS; - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) - return -EINTR; - return 0; -} - -static int agilent_82350b_accel_write(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, - size_t *bytes_written) -{ - struct agilent_82350b_priv *a_priv = board->private_data; - struct tms9914_priv *tms_priv = &a_priv->tms9914_priv; - int i, j; - unsigned short event_status; - int retval = 0; - int fifotransferlength = length; - int block_size = 0; - size_t num_bytes; - - *bytes_written = 0; - if (send_eoi) - --fifotransferlength; - - clear_bit(DEV_CLEAR_BN, &tms_priv->state); - - writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); - - event_status = read_and_clear_event_status(board); - -#ifdef EXPERIMENTAL - /* wait for previous BO to complete if any */ - retval = wait_event_interruptible(board->wait, - test_bit(DEV_CLEAR_BN, &tms_priv->state) || - test_bit(WRITE_READY_BN, &tms_priv->state) || - test_bit(TIMO_NUM, &board->status)); - retval = translate_wait_return_value(board, retval); - - if (retval) - return retval; -#endif - - if (fifotransferlength > 0) { - retval = agilent_82350b_write(board, buffer, 1, 0, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - } - - write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BOIE, IMR0); - for (i = 1; i < fifotransferlength;) { - clear_bit(WRITE_READY_BN, &tms_priv->state); - - block_size = min(fifotransferlength - i, agilent_82350b_fifo_size); - set_transfer_counter(a_priv, block_size); - for (j = 0; j < block_size; ++j, ++i) { - /* load data into board's sram */ - writeb(buffer[i], a_priv->sram_base + j); - } - writeb(ENABLE_TI_TO_SRAM, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); - - if (agilent_82350b_fifo_is_halted(a_priv)) - writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG); - - retval = wait_event_interruptible(board->wait, - ((event_status = - read_and_clear_event_status(board)) & - TERM_COUNT_STATUS_BIT) || - test_bit(DEV_CLEAR_BN, &tms_priv->state) || - test_bit(TIMO_NUM, &board->status)); - writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); - num_bytes = block_size - read_transfer_counter(a_priv); - - *bytes_written += num_bytes; - retval = translate_wait_return_value(board, retval); - if (retval) - break; - } - write_byte(tms_priv, tms_priv->imr0_bits, IMR0); - if (retval < 0) - return retval; - - if (send_eoi) { - retval = agilent_82350b_write(board, buffer + fifotransferlength, 1, send_eoi, - &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - } - return 0; -} - -static unsigned short read_and_clear_event_status(struct gpib_board *board) -{ - struct agilent_82350b_priv *a_priv = board->private_data; - unsigned long flags; - unsigned short status; - - spin_lock_irqsave(&board->spinlock, flags); - status = a_priv->event_status_bits; - a_priv->event_status_bits = 0; - spin_unlock_irqrestore(&board->spinlock, flags); - return status; -} - -static irqreturn_t agilent_82350b_interrupt(int irq, void *arg) - -{ - int tms9914_status1 = 0, tms9914_status2 = 0; - int event_status; - struct gpib_board *board = arg; - struct agilent_82350b_priv *a_priv = board->private_data; - unsigned long flags; - irqreturn_t retval = IRQ_NONE; - - spin_lock_irqsave(&board->spinlock, flags); - event_status = readb(a_priv->gpib_base + EVENT_STATUS_REG); - if (event_status & IRQ_STATUS_BIT) - retval = IRQ_HANDLED; - - if (event_status & TMS9914_IRQ_STATUS_BIT) { - tms9914_status1 = read_byte(&a_priv->tms9914_priv, ISR0); - tms9914_status2 = read_byte(&a_priv->tms9914_priv, ISR1); - tms9914_interrupt_have_status(board, &a_priv->tms9914_priv, tms9914_status1, - tms9914_status2); - } - /* write-clear status bits */ - if (event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT)) { - writeb(event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT), - a_priv->gpib_base + EVENT_STATUS_REG); - a_priv->event_status_bits |= event_status; - wake_up_interruptible(&board->wait); - } - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -static void agilent_82350b_detach(struct gpib_board *board); - -static int read_transfer_counter(struct agilent_82350b_priv *a_priv) -{ - int lo, mid, value; - - lo = readb(a_priv->gpib_base + XFER_COUNT_LO_REG); - mid = readb(a_priv->gpib_base + XFER_COUNT_MID_REG); - value = (lo & 0xff) | ((mid << 8) & 0x7f00); - value = ~(value - 1) & 0x7fff; - return value; -} - -static void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count) -{ - int complement = -count; - - writeb(complement & 0xff, a_priv->gpib_base + XFER_COUNT_LO_REG); - writeb((complement >> 8) & 0xff, a_priv->gpib_base + XFER_COUNT_MID_REG); - /* I don't think the hi count reg is even used, but oh well */ - writeb((complement >> 16) & 0xf, a_priv->gpib_base + XFER_COUNT_HI_REG); -} - -/* wrappers for interface functions */ -static int agilent_82350b_read(struct gpib_board *board, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); -} - -static int agilent_82350b_write(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written) - -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); -} - -static int agilent_82350b_command(struct gpib_board *board, u8 *buffer, - size_t length, size_t *bytes_written) - -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); -} - -static int agilent_82350b_take_control(struct gpib_board *board, int synchronous) - -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_take_control_workaround(board, &priv->tms9914_priv, synchronous); -} - -static int agilent_82350b_go_to_standby(struct gpib_board *board) - -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_go_to_standby(board, &priv->tms9914_priv); -} - -static int agilent_82350b_request_system_control(struct gpib_board *board, int request_control) -{ - struct agilent_82350b_priv *a_priv = board->private_data; - - if (request_control) { - a_priv->card_mode_bits |= CM_SYSTEM_CONTROLLER_BIT; - if (a_priv->model != MODEL_82350A) - writeb(IC_SYSTEM_CONTROLLER_BIT, a_priv->gpib_base + INTERNAL_CONFIG_REG); - } else { - a_priv->card_mode_bits &= ~CM_SYSTEM_CONTROLLER_BIT; - if (a_priv->model != MODEL_82350A) - writeb(0, a_priv->gpib_base + INTERNAL_CONFIG_REG); - } - writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG); - return tms9914_request_system_control(board, &a_priv->tms9914_priv, request_control); -} - -static void agilent_82350b_interface_clear(struct gpib_board *board, int assert) - -{ - struct agilent_82350b_priv *priv = board->private_data; - - tms9914_interface_clear(board, &priv->tms9914_priv, assert); -} - -static void agilent_82350b_remote_enable(struct gpib_board *board, int enable) -{ - struct agilent_82350b_priv *priv = board->private_data; - - tms9914_remote_enable(board, &priv->tms9914_priv, enable); -} - -static int agilent_82350b_enable_eos(struct gpib_board *board, u8 eos_byte, - int compare_8_bits) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); -} - -static void agilent_82350b_disable_eos(struct gpib_board *board) -{ - struct agilent_82350b_priv *priv = board->private_data; - - tms9914_disable_eos(board, &priv->tms9914_priv); -} - -static unsigned int agilent_82350b_update_status(struct gpib_board *board, - unsigned int clear_mask) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); -} - -static int agilent_82350b_primary_address(struct gpib_board *board, - unsigned int address) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_primary_address(board, &priv->tms9914_priv, address); -} - -static int agilent_82350b_secondary_address(struct gpib_board *board, - unsigned int address, int enable) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); -} - -static int agilent_82350b_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_parallel_poll(board, &priv->tms9914_priv, result); -} - -static void agilent_82350b_parallel_poll_configure(struct gpib_board *board, - u8 config) -{ - struct agilent_82350b_priv *priv = board->private_data; - - tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); -} - -static void agilent_82350b_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct agilent_82350b_priv *priv = board->private_data; - - tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); -} - -static void agilent_82350b_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct agilent_82350b_priv *priv = board->private_data; - - tms9914_serial_poll_response(board, &priv->tms9914_priv, status); -} - -static u8 agilent_82350b_serial_poll_status(struct gpib_board *board) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_serial_poll_status(board, &priv->tms9914_priv); -} - -static int agilent_82350b_line_status(const struct gpib_board *board) -{ - struct agilent_82350b_priv *priv = board->private_data; - - return tms9914_line_status(board, &priv->tms9914_priv); -} - -static int agilent_82350b_t1_delay(struct gpib_board *board, unsigned int nanosec) -{ - struct agilent_82350b_priv *a_priv = board->private_data; - static const int nanosec_per_clock = 30; - unsigned int value; - - tms9914_t1_delay(board, &a_priv->tms9914_priv, nanosec); - - value = (nanosec + nanosec_per_clock - 1) / nanosec_per_clock; - if (value > 0xff) - value = 0xff; - writeb(value, a_priv->gpib_base + T1_DELAY_REG); - return value * nanosec_per_clock; -} - -static void agilent_82350b_return_to_local(struct gpib_board *board) -{ - struct agilent_82350b_priv *priv = board->private_data; - - tms9914_return_to_local(board, &priv->tms9914_priv); -} - -static int agilent_82350b_allocate_private(struct gpib_board *board) -{ - board->private_data = kzalloc(sizeof(struct agilent_82350b_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - return 0; -} - -static void agilent_82350b_free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static int init_82350a_hardware(struct gpib_board *board, - const struct gpib_board_config *config) -{ - struct agilent_82350b_priv *a_priv = board->private_data; - static const unsigned int firmware_length = 5302; - unsigned int borg_status; - static const unsigned int timeout = 1000; - int i, j; - const char *firmware_data = config->init_data; - const unsigned int plx_cntrl_static_bits = PLX9050_WAITO_NOT_USER0_SELECT_BIT | - PLX9050_USER0_OUTPUT_BIT | - PLX9050_LLOCK_NOT_USER1_SELECT_BIT | - PLX9050_USER1_OUTPUT_BIT | - PLX9050_USER2_OUTPUT_BIT | - PLX9050_USER3_OUTPUT_BIT | - PLX9050_PCI_READ_MODE_BIT | - PLX9050_PCI_WRITE_MODE_BIT | - PLX9050_PCI_RETRY_DELAY_BITS(64) | - PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT; - - /* load borg data */ - borg_status = readb(a_priv->borg_base); - if ((borg_status & BORG_DONE_BIT)) - return 0; - /* need to programme borg */ - if (!config->init_data || config->init_data_length != firmware_length) { - dev_err(board->gpib_dev, "the 82350A board requires firmware after powering on.\n"); - return -EIO; - } - dev_dbg(board->gpib_dev, "Loading firmware...\n"); - - /* tickle the borg */ - writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT, - a_priv->plx_base + PLX9050_CNTRL_REG); - usleep_range(1000, 2000); - writel(plx_cntrl_static_bits, a_priv->plx_base + PLX9050_CNTRL_REG); - usleep_range(1000, 2000); - writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT, - a_priv->plx_base + PLX9050_CNTRL_REG); - usleep_range(1000, 2000); - - for (i = 0; i < config->init_data_length; ++i) { - for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_READY_BIT) == 0; ++j) { - if (need_resched()) - schedule(); - usleep_range(10, 20); - } - if (j == timeout) { - dev_err(board->gpib_dev, "timed out loading firmware.\n"); - return -ETIMEDOUT; - } - writeb(firmware_data[i], a_priv->gpib_base + CONFIG_DATA_REG); - } - for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_DONE_BIT) == 0; ++j) { - if (need_resched()) - schedule(); - usleep_range(10, 20); - } - if (j == timeout) { - dev_err(board->gpib_dev, "timed out waiting for firmware load to complete.\n"); - return -ETIMEDOUT; - } - dev_dbg(board->gpib_dev, " ...done.\n"); - return 0; -} - -static int test_sram(struct gpib_board *board) - -{ - struct agilent_82350b_priv *a_priv = board->private_data; - unsigned int i; - const unsigned int sram_length = pci_resource_len(a_priv->pci_device, SRAM_82350A_REGION); - /* test SRAM */ - const unsigned int byte_mask = 0xff; - - for (i = 0; i < sram_length; ++i) { - writeb(i & byte_mask, a_priv->sram_base + i); - if (need_resched()) - schedule(); - } - for (i = 0; i < sram_length; ++i) { - unsigned int read_value = readb(a_priv->sram_base + i); - - if ((i & byte_mask) != read_value) { - dev_err(board->gpib_dev, "SRAM test failed at %d wanted %d got %d\n", - i, (i & byte_mask), read_value); - return -EIO; - } - if (need_resched()) - schedule(); - } - dev_dbg(board->gpib_dev, "SRAM test passed 0x%x bytes checked\n", sram_length); - return 0; -} - -static int agilent_82350b_generic_attach(struct gpib_board *board, - const struct gpib_board_config *config, - int use_fifos) - -{ - struct agilent_82350b_priv *a_priv; - struct tms9914_priv *tms_priv; - int retval; - - board->status = 0; - - if (agilent_82350b_allocate_private(board)) - return -ENOMEM; - a_priv = board->private_data; - a_priv->using_fifos = use_fifos; - tms_priv = &a_priv->tms9914_priv; - tms_priv->read_byte = tms9914_iomem_read_byte; - tms_priv->write_byte = tms9914_iomem_write_byte; - tms_priv->offset = 1; - - /* find board */ - a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT, - PCI_DEVICE_ID_82350B, NULL); - if (a_priv->pci_device) { - a_priv->model = MODEL_82350B; - dev_dbg(board->gpib_dev, "Agilent 82350B board found\n"); - - } else { - a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT, - PCI_DEVICE_ID_82351A, NULL); - if (a_priv->pci_device) { - a_priv->model = MODEL_82351A; - dev_dbg(board->gpib_dev, "Agilent 82351B board found\n"); - - } else { - a_priv->pci_device = gpib_pci_get_subsys(config, PCI_VENDOR_ID_PLX, - PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_HP, - PCI_SUBDEVICE_ID_82350A, - a_priv->pci_device); - if (a_priv->pci_device) { - a_priv->model = MODEL_82350A; - dev_dbg(board->gpib_dev, "HP/Agilent 82350A board found\n"); - } else { - dev_err(board->gpib_dev, "no 82350/82351 board found\n"); - return -ENODEV; - } - } - } - if (pci_enable_device(a_priv->pci_device)) { - dev_err(board->gpib_dev, "error enabling pci device\n"); - return -EIO; - } - if (pci_request_regions(a_priv->pci_device, DRV_NAME)) - return -ENOMEM; - switch (a_priv->model) { - case MODEL_82350A: - a_priv->plx_base = ioremap(pci_resource_start(a_priv->pci_device, PLX_MEM_REGION), - pci_resource_len(a_priv->pci_device, PLX_MEM_REGION)); - dev_dbg(board->gpib_dev, "plx base address remapped to 0x%p\n", a_priv->plx_base); - a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, - GPIB_82350A_REGION), - pci_resource_len(a_priv->pci_device, - GPIB_82350A_REGION)); - dev_dbg(board->gpib_dev, "chip base address remapped to 0x%p\n", a_priv->gpib_base); - tms_priv->mmiobase = a_priv->gpib_base + TMS9914_BASE_REG; - a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, - SRAM_82350A_REGION), - pci_resource_len(a_priv->pci_device, - SRAM_82350A_REGION)); - dev_dbg(board->gpib_dev, "sram base address remapped to 0x%p\n", a_priv->sram_base); - a_priv->borg_base = ioremap(pci_resource_start(a_priv->pci_device, - BORG_82350A_REGION), - pci_resource_len(a_priv->pci_device, - BORG_82350A_REGION)); - dev_dbg(board->gpib_dev, "borg base address remapped to 0x%p\n", a_priv->borg_base); - - retval = init_82350a_hardware(board, config); - if (retval < 0) - return retval; - break; - case MODEL_82350B: - case MODEL_82351A: - a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, GPIB_REGION), - pci_resource_len(a_priv->pci_device, GPIB_REGION)); - dev_dbg(board->gpib_dev, "chip base address remapped to 0x%p\n", a_priv->gpib_base); - tms_priv->mmiobase = a_priv->gpib_base + TMS9914_BASE_REG; - a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, SRAM_REGION), - pci_resource_len(a_priv->pci_device, SRAM_REGION)); - dev_dbg(board->gpib_dev, "sram base address remapped to 0x%p\n", a_priv->sram_base); - a_priv->misc_base = ioremap(pci_resource_start(a_priv->pci_device, MISC_REGION), - pci_resource_len(a_priv->pci_device, MISC_REGION)); - dev_dbg(board->gpib_dev, "misc base address remapped to 0x%p\n", a_priv->misc_base); - break; - default: - dev_err(board->gpib_dev, "invalid board\n"); - return -ENODEV; - } - - retval = test_sram(board); - if (retval < 0) - return retval; - - if (request_irq(a_priv->pci_device->irq, agilent_82350b_interrupt, - IRQF_SHARED, DRV_NAME, board)) { - dev_err(board->gpib_dev, "failed to obtain irq %d\n", a_priv->pci_device->irq); - return -EIO; - } - a_priv->irq = a_priv->pci_device->irq; - dev_dbg(board->gpib_dev, " IRQ %d\n", a_priv->irq); - - writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG); - a_priv->card_mode_bits = ENABLE_PCI_IRQ_BIT; - writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG); - - if (a_priv->model == MODEL_82350A) { - /* enable PCI interrupts for 82350a */ - writel(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR2_POLARITY_BIT | - PLX9050_PCI_INTR_EN_BIT, - a_priv->plx_base + PLX9050_INTCSR_REG); - } - - if (use_fifos) { - writeb(ENABLE_BUFFER_END_EVENTS_BIT | ENABLE_TERM_COUNT_EVENTS_BIT, - a_priv->gpib_base + EVENT_ENABLE_REG); - writeb(ENABLE_TERM_COUNT_INTERRUPT_BIT | ENABLE_BUFFER_END_INTERRUPT_BIT | - ENABLE_TMS9914_INTERRUPTS_BIT, a_priv->gpib_base + INTERRUPT_ENABLE_REG); - /* write-clear event status bits */ - writeb(BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT, - a_priv->gpib_base + EVENT_STATUS_REG); - } else { - writeb(0, a_priv->gpib_base + EVENT_ENABLE_REG); - writeb(ENABLE_TMS9914_INTERRUPTS_BIT, - a_priv->gpib_base + INTERRUPT_ENABLE_REG); - } - board->t1_nano_sec = agilent_82350b_t1_delay(board, 2000); - tms9914_board_reset(tms_priv); - - tms9914_online(board, tms_priv); - - return 0; -} - -static int agilent_82350b_unaccel_attach(struct gpib_board *board, - const struct gpib_board_config *config) -{ - return agilent_82350b_generic_attach(board, config, 0); -} - -static int agilent_82350b_accel_attach(struct gpib_board *board, - const struct gpib_board_config *config) -{ - return agilent_82350b_generic_attach(board, config, 1); -} - -static void agilent_82350b_detach(struct gpib_board *board) -{ - struct agilent_82350b_priv *a_priv = board->private_data; - struct tms9914_priv *tms_priv; - - if (a_priv) { - if (a_priv->plx_base) /* disable interrupts */ - writel(0, a_priv->plx_base + PLX9050_INTCSR_REG); - - tms_priv = &a_priv->tms9914_priv; - if (a_priv->irq) - free_irq(a_priv->irq, board); - if (a_priv->gpib_base) { - tms9914_board_reset(tms_priv); - if (a_priv->misc_base) - iounmap(a_priv->misc_base); - if (a_priv->borg_base) - iounmap(a_priv->borg_base); - if (a_priv->sram_base) - iounmap(a_priv->sram_base); - if (a_priv->gpib_base) - iounmap(a_priv->gpib_base); - if (a_priv->plx_base) - iounmap(a_priv->plx_base); - pci_release_regions(a_priv->pci_device); - } - if (a_priv->pci_device) - pci_dev_put(a_priv->pci_device); - } - agilent_82350b_free_private(board); -} - -static struct gpib_interface agilent_82350b_unaccel_interface = { - .name = "agilent_82350b_unaccel", - .attach = agilent_82350b_unaccel_attach, - .detach = agilent_82350b_detach, - .read = agilent_82350b_read, - .write = agilent_82350b_write, - .command = agilent_82350b_command, - .request_system_control = agilent_82350b_request_system_control, - .take_control = agilent_82350b_take_control, - .go_to_standby = agilent_82350b_go_to_standby, - .interface_clear = agilent_82350b_interface_clear, - .remote_enable = agilent_82350b_remote_enable, - .enable_eos = agilent_82350b_enable_eos, - .disable_eos = agilent_82350b_disable_eos, - .parallel_poll = agilent_82350b_parallel_poll, - .parallel_poll_configure = agilent_82350b_parallel_poll_configure, - .parallel_poll_response = agilent_82350b_parallel_poll_response, - .local_parallel_poll_mode = NULL, /* XXX */ - .line_status = agilent_82350b_line_status, - .update_status = agilent_82350b_update_status, - .primary_address = agilent_82350b_primary_address, - .secondary_address = agilent_82350b_secondary_address, - .serial_poll_response = agilent_82350b_serial_poll_response, - .serial_poll_status = agilent_82350b_serial_poll_status, - .t1_delay = agilent_82350b_t1_delay, - .return_to_local = agilent_82350b_return_to_local, -}; - -static struct gpib_interface agilent_82350b_interface = { - .name = "agilent_82350b", - .attach = agilent_82350b_accel_attach, - .detach = agilent_82350b_detach, - .read = agilent_82350b_accel_read, - .write = agilent_82350b_accel_write, - .command = agilent_82350b_command, - .request_system_control = agilent_82350b_request_system_control, - .take_control = agilent_82350b_take_control, - .go_to_standby = agilent_82350b_go_to_standby, - .interface_clear = agilent_82350b_interface_clear, - .remote_enable = agilent_82350b_remote_enable, - .enable_eos = agilent_82350b_enable_eos, - .disable_eos = agilent_82350b_disable_eos, - .parallel_poll = agilent_82350b_parallel_poll, - .parallel_poll_configure = agilent_82350b_parallel_poll_configure, - .parallel_poll_response = agilent_82350b_parallel_poll_response, - .local_parallel_poll_mode = NULL, /* XXX */ - .line_status = agilent_82350b_line_status, - .update_status = agilent_82350b_update_status, - .primary_address = agilent_82350b_primary_address, - .secondary_address = agilent_82350b_secondary_address, - .serial_poll_response = agilent_82350b_serial_poll_response, - .serial_poll_status = agilent_82350b_serial_poll_status, - .t1_delay = agilent_82350b_t1_delay, - .return_to_local = agilent_82350b_return_to_local, -}; - -static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) - -{ - return 0; -} - -static const struct pci_device_id agilent_82350b_pci_table[] = { - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP, - PCI_SUBDEVICE_ID_82350A, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table); - -static struct pci_driver agilent_82350b_pci_driver = { - .name = DRV_NAME, - .id_table = agilent_82350b_pci_table, - .probe = &agilent_82350b_pci_probe -}; - -static int __init agilent_82350b_init_module(void) -{ - int result; - - result = pci_register_driver(&agilent_82350b_pci_driver); - if (result) { - pr_err("pci_register_driver failed: error = %d\n", result); - return result; - } - - result = gpib_register_driver(&agilent_82350b_unaccel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_unaccel; - } - - result = gpib_register_driver(&agilent_82350b_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_interface; - } - - return 0; - -err_interface: - gpib_unregister_driver(&agilent_82350b_unaccel_interface); -err_unaccel: - pci_unregister_driver(&agilent_82350b_pci_driver); - - return result; -} - -static void __exit agilent_82350b_exit_module(void) -{ - gpib_unregister_driver(&agilent_82350b_interface); - gpib_unregister_driver(&agilent_82350b_unaccel_interface); - - pci_unregister_driver(&agilent_82350b_pci_driver); -} - -module_init(agilent_82350b_init_module); -module_exit(agilent_82350b_exit_module); diff --git a/drivers/staging/gpib/agilent_82350b/agilent_82350b.h b/drivers/staging/gpib/agilent_82350b/agilent_82350b.h deleted file mode 100644 index ef841957297f..000000000000 --- a/drivers/staging/gpib/agilent_82350b/agilent_82350b.h +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002, 2004 by Frank Mori Hess * - ***************************************************************************/ - -#include "gpibP.h" -#include "plx9050.h" -#include "tms9914.h" - -enum pci_vendor_ids { - PCI_VENDOR_ID_AGILENT = 0x15bc, -}; - -enum pci_device_ids { - PCI_DEVICE_ID_82350B = 0x0b01, - PCI_DEVICE_ID_82351A = 0x1218 -}; - -enum pci_subdevice_ids { - PCI_SUBDEVICE_ID_82350A = 0x10b0, -}; - -enum pci_regions_82350a { - PLX_MEM_REGION = 0, - PLX_IO_REGION = 1, - GPIB_82350A_REGION = 2, - SRAM_82350A_REGION = 3, - BORG_82350A_REGION = 4 -}; - -enum pci_regions_82350b { - GPIB_REGION = 0, - SRAM_REGION = 1, - MISC_REGION = 2, -}; - -enum board_model { - MODEL_82350A, - MODEL_82350B, - MODEL_82351A -}; - -/* struct which defines private_data for board */ -struct agilent_82350b_priv { - struct tms9914_priv tms9914_priv; - struct pci_dev *pci_device; - void __iomem *plx_base; /* 82350a only */ - void __iomem *gpib_base; - void __iomem *sram_base; - void __iomem *misc_base; - void __iomem *borg_base; - int irq; - unsigned short card_mode_bits; - unsigned short event_status_bits; - enum board_model model; - bool using_fifos; -}; - -/* registers */ -enum agilent_82350b_gpib_registers - -{ - CARD_MODE_REG = 0x1, - CONFIG_DATA_REG = 0x2, /* 82350A specific */ - INTERRUPT_ENABLE_REG = 0x3, - EVENT_STATUS_REG = 0x4, - EVENT_ENABLE_REG = 0x5, - STREAM_STATUS_REG = 0x7, - DEBUG_RAM0_REG = 0x8, - DEBUG_RAM1_REG = 0x9, - DEBUG_RAM2_REG = 0xa, - DEBUG_RAM3_REG = 0xb, - XFER_COUNT_LO_REG = 0xc, - XFER_COUNT_MID_REG = 0xd, - XFER_COUNT_HI_REG = 0xe, - TMS9914_BASE_REG = 0x10, - INTERNAL_CONFIG_REG = 0x18, - IMR0_READ_REG = 0x19, /* read */ - T1_DELAY_REG = 0x19, /* write */ - IMR1_READ_REG = 0x1a, - ADR_READ_REG = 0x1b, - SPMR_READ_REG = 0x1c, - PPR_READ_REG = 0x1d, - CDOR_READ_REG = 0x1e, - SRAM_ACCESS_CONTROL_REG = 0x1f, -}; - -enum card_mode_bits - -{ - ACTIVE_CONTROLLER_BIT = 0x2, /* read-only */ - CM_SYSTEM_CONTROLLER_BIT = 0x8, - ENABLE_BUS_MONITOR_BIT = 0x10, - ENABLE_PCI_IRQ_BIT = 0x20, -}; - -enum interrupt_enable_bits - -{ - ENABLE_TMS9914_INTERRUPTS_BIT = 0x1, - ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10, - ENABLE_TERM_COUNT_INTERRUPT_BIT = 0x20, -}; - -enum event_enable_bits - -{ - ENABLE_BUFFER_END_EVENTS_BIT = 0x10, - ENABLE_TERM_COUNT_EVENTS_BIT = 0x20, -}; - -enum event_status_bits - -{ - TMS9914_IRQ_STATUS_BIT = 0x1, - IRQ_STATUS_BIT = 0x2, - BUFFER_END_STATUS_BIT = 0x10, /* write-clear */ - TERM_COUNT_STATUS_BIT = 0x20, /* write-clear */ -}; - -enum stream_status_bits - -{ - HALTED_STATUS_BIT = 0x1, /* read */ - RESTART_STREAM_BIT = 0x1, /* write */ -}; - -enum internal_config_bits - -{ - IC_SYSTEM_CONTROLLER_BIT = 0x80, -}; - -enum sram_access_control_bits - -{ - DIRECTION_GPIB_TO_HOST = 0x20, /* transfer direction */ - ENABLE_TI_TO_SRAM = 0x40, /* enable fifo */ - ENABLE_FAST_TALKER = 0x80 /* added for 82350A (not used) */ -}; - -enum borg_bits - -{ - BORG_READY_BIT = 0x40, - BORG_DONE_BIT = 0x80 -}; - -static const int agilent_82350b_fifo_size = 0x8000; - -static inline int agilent_82350b_fifo_is_halted(struct agilent_82350b_priv *a_priv) - -{ - return readb(a_priv->gpib_base + STREAM_STATUS_REG) & HALTED_STATUS_BIT; -} - diff --git a/drivers/staging/gpib/agilent_82357a/Makefile b/drivers/staging/gpib/agilent_82357a/Makefile deleted file mode 100644 index 81a55c257a6e..000000000000 --- a/drivers/staging/gpib/agilent_82357a/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - -obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a.o - - diff --git a/drivers/staging/gpib/agilent_82357a/agilent_82357a.c b/drivers/staging/gpib/agilent_82357a/agilent_82357a.c deleted file mode 100644 index 77c8e549b208..000000000000 --- a/drivers/staging/gpib/agilent_82357a/agilent_82357a.c +++ /dev/null @@ -1,1691 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * driver for Agilent 82357A/B usb to gpib adapters * - * copyright : (C) 2004 by Frank Mori Hess * - ***************************************************************************/ - -#define _GNU_SOURCE - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include -#include -#include -#include "agilent_82357a.h" -#include "gpibP.h" -#include "tms9914.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for Agilent 82357A/B usb adapters"); - -#define MAX_NUM_82357A_INTERFACES 128 -static struct usb_interface *agilent_82357a_driver_interfaces[MAX_NUM_82357A_INTERFACES]; -static DEFINE_MUTEX(agilent_82357a_hotplug_lock); // protect board insertion and removal - -static unsigned int agilent_82357a_update_status(struct gpib_board *board, - unsigned int clear_mask); - -static int agilent_82357a_take_control_internal(struct gpib_board *board, int synchronous); - -static void agilent_82357a_bulk_complete(struct urb *urb) -{ - struct agilent_82357a_urb_ctx *context = urb->context; - - complete(&context->complete); -} - -static void agilent_82357a_timeout_handler(struct timer_list *t) -{ - struct agilent_82357a_priv *a_priv = timer_container_of(a_priv, t, - bulk_timer); - struct agilent_82357a_urb_ctx *context = &a_priv->context; - - context->timed_out = 1; - complete(&context->complete); -} - -static int agilent_82357a_send_bulk_msg(struct agilent_82357a_priv *a_priv, void *data, - int data_length, int *actual_data_length, - int timeout_msecs) -{ - struct usb_device *usb_dev; - int retval; - unsigned int out_pipe; - struct agilent_82357a_urb_ctx *context = &a_priv->context; - - *actual_data_length = 0; - retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock); - if (retval) - return retval; - if (!a_priv->bus_interface) { - mutex_unlock(&a_priv->bulk_alloc_lock); - return -ENODEV; - } - if (a_priv->bulk_urb) { - mutex_unlock(&a_priv->bulk_alloc_lock); - return -EAGAIN; - } - a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!a_priv->bulk_urb) { - mutex_unlock(&a_priv->bulk_alloc_lock); - return -ENOMEM; - } - usb_dev = interface_to_usbdev(a_priv->bus_interface); - out_pipe = usb_sndbulkpipe(usb_dev, a_priv->bulk_out_endpoint); - init_completion(&context->complete); - context->timed_out = 0; - usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, out_pipe, data, data_length, - &agilent_82357a_bulk_complete, context); - - if (timeout_msecs) - mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); - - retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL); - if (retval) { - dev_err(&usb_dev->dev, "failed to submit bulk out urb, retval=%i\n", retval); - mutex_unlock(&a_priv->bulk_alloc_lock); - goto cleanup; - } - mutex_unlock(&a_priv->bulk_alloc_lock); - if (wait_for_completion_interruptible(&context->complete)) { - retval = -ERESTARTSYS; - goto cleanup; - } - if (context->timed_out) { - retval = -ETIMEDOUT; - } else { - retval = a_priv->bulk_urb->status; - *actual_data_length = a_priv->bulk_urb->actual_length; - } -cleanup: - if (timeout_msecs) { - if (timer_pending(&a_priv->bulk_timer)) - timer_delete_sync(&a_priv->bulk_timer); - } - mutex_lock(&a_priv->bulk_alloc_lock); - if (a_priv->bulk_urb) { - usb_kill_urb(a_priv->bulk_urb); - usb_free_urb(a_priv->bulk_urb); - a_priv->bulk_urb = NULL; - } - mutex_unlock(&a_priv->bulk_alloc_lock); - return retval; -} - -static int agilent_82357a_receive_bulk_msg(struct agilent_82357a_priv *a_priv, void *data, - int data_length, int *actual_data_length, - int timeout_msecs) -{ - struct usb_device *usb_dev; - int retval; - unsigned int in_pipe; - struct agilent_82357a_urb_ctx *context = &a_priv->context; - - *actual_data_length = 0; - retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock); - if (retval) - return retval; - if (!a_priv->bus_interface) { - mutex_unlock(&a_priv->bulk_alloc_lock); - return -ENODEV; - } - if (a_priv->bulk_urb) { - mutex_unlock(&a_priv->bulk_alloc_lock); - return -EAGAIN; - } - a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!a_priv->bulk_urb) { - mutex_unlock(&a_priv->bulk_alloc_lock); - return -ENOMEM; - } - usb_dev = interface_to_usbdev(a_priv->bus_interface); - in_pipe = usb_rcvbulkpipe(usb_dev, AGILENT_82357_BULK_IN_ENDPOINT); - init_completion(&context->complete); - context->timed_out = 0; - usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, in_pipe, data, data_length, - &agilent_82357a_bulk_complete, context); - - if (timeout_msecs) - mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); - - retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL); - if (retval) { - dev_err(&usb_dev->dev, "failed to submit bulk in urb, retval=%i\n", retval); - mutex_unlock(&a_priv->bulk_alloc_lock); - goto cleanup; - } - mutex_unlock(&a_priv->bulk_alloc_lock); - if (wait_for_completion_interruptible(&context->complete)) { - retval = -ERESTARTSYS; - goto cleanup; - } - if (context->timed_out) { - retval = -ETIMEDOUT; - goto cleanup; - } - retval = a_priv->bulk_urb->status; - *actual_data_length = a_priv->bulk_urb->actual_length; -cleanup: - if (timeout_msecs) - timer_delete_sync(&a_priv->bulk_timer); - - mutex_lock(&a_priv->bulk_alloc_lock); - if (a_priv->bulk_urb) { - usb_kill_urb(a_priv->bulk_urb); - usb_free_urb(a_priv->bulk_urb); - a_priv->bulk_urb = NULL; - } - mutex_unlock(&a_priv->bulk_alloc_lock); - return retval; -} - -static int agilent_82357a_receive_control_msg(struct agilent_82357a_priv *a_priv, __u8 request, - __u8 requesttype, __u16 value, __u16 index, - void *data, __u16 size, int timeout_msecs) -{ - struct usb_device *usb_dev; - int retval; - unsigned int in_pipe; - - retval = mutex_lock_interruptible(&a_priv->control_alloc_lock); - if (retval) - return retval; - if (!a_priv->bus_interface) { - mutex_unlock(&a_priv->control_alloc_lock); - return -ENODEV; - } - usb_dev = interface_to_usbdev(a_priv->bus_interface); - in_pipe = usb_rcvctrlpipe(usb_dev, AGILENT_82357_CONTROL_ENDPOINT); - retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data, - size, timeout_msecs); - mutex_unlock(&a_priv->control_alloc_lock); - return retval; -} - -static void agilent_82357a_dump_raw_block(const u8 *raw_data, int length) -{ - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true); -} - -static int agilent_82357a_write_registers(struct agilent_82357a_priv *a_priv, - const struct agilent_82357a_register_pairlet *writes, - int num_writes) -{ - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - int retval; - u8 *out_data, *in_data; - int out_data_length, in_data_length; - int bytes_written, bytes_read; - int i = 0; - int j; - static const int bytes_per_write = 2; - static const int header_length = 2; - static const int max_writes = 31; - - if (num_writes > max_writes) { - dev_err(&usb_dev->dev, "bug! num_writes=%i too large\n", num_writes); - return -EIO; - } - out_data_length = num_writes * bytes_per_write + header_length; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - - out_data[i++] = DATA_PIPE_CMD_WR_REGS; - out_data[i++] = num_writes; - for (j = 0; j < num_writes; j++) { - out_data[i++] = writes[j].address; - out_data[i++] = writes[j].value; - } - - retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); - if (retval) { - kfree(out_data); - return retval; - } - retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000); - kfree(out_data); - if (retval) { - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - mutex_unlock(&a_priv->bulk_transfer_lock); - return retval; - } - in_data_length = 0x20; - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&a_priv->bulk_transfer_lock); - return -ENOMEM; - } - retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, - &bytes_read, 1000); - mutex_unlock(&a_priv->bulk_transfer_lock); - - if (retval) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - agilent_82357a_dump_raw_block(in_data, bytes_read); - kfree(in_data); - return -EIO; - } - if (in_data[0] != (0xff & ~DATA_PIPE_CMD_WR_REGS)) { - dev_err(&usb_dev->dev, "bulk command=0x%x != ~DATA_PIPE_CMD_WR_REGS\n", in_data[0]); - return -EIO; - } - if (in_data[1]) { - dev_err(&usb_dev->dev, "nonzero error code 0x%x in DATA_PIPE_CMD_WR_REGS response\n", - in_data[1]); - return -EIO; - } - kfree(in_data); - return 0; -} - -static int agilent_82357a_read_registers(struct agilent_82357a_priv *a_priv, - struct agilent_82357a_register_pairlet *reads, - int num_reads, int blocking) -{ - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - int retval; - u8 *out_data, *in_data; - int out_data_length, in_data_length; - int bytes_written, bytes_read; - int i = 0; - int j; - static const int header_length = 2; - static const int max_reads = 62; - - if (num_reads > max_reads) { - dev_err(&usb_dev->dev, "bug! num_reads=%i too large\n", num_reads); - return -EIO; - } - out_data_length = num_reads + header_length; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - - out_data[i++] = DATA_PIPE_CMD_RD_REGS; - out_data[i++] = num_reads; - for (j = 0; j < num_reads; j++) - out_data[i++] = reads[j].address; - - if (blocking) { - retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); - if (retval) { - kfree(out_data); - return retval; - } - } else { - retval = mutex_trylock(&a_priv->bulk_transfer_lock); - if (retval == 0) { - kfree(out_data); - return -EAGAIN; - } - } - retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000); - kfree(out_data); - if (retval) { - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - mutex_unlock(&a_priv->bulk_transfer_lock); - return retval; - } - in_data_length = 0x20; - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&a_priv->bulk_transfer_lock); - return -ENOMEM; - } - retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, - &bytes_read, 10000); - mutex_unlock(&a_priv->bulk_transfer_lock); - - if (retval) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - agilent_82357a_dump_raw_block(in_data, bytes_read); - kfree(in_data); - return -EIO; - } - i = 0; - if (in_data[i++] != (0xff & ~DATA_PIPE_CMD_RD_REGS)) { - dev_err(&usb_dev->dev, "bulk command=0x%x != ~DATA_PIPE_CMD_RD_REGS\n", in_data[0]); - return -EIO; - } - if (in_data[i++]) { - dev_err(&usb_dev->dev, "nonzero error code 0x%x in DATA_PIPE_CMD_RD_REGS response\n", - in_data[1]); - return -EIO; - } - for (j = 0; j < num_reads; j++) - reads[j].value = in_data[i++]; - kfree(in_data); - return 0; -} - -static int agilent_82357a_abort(struct agilent_82357a_priv *a_priv, int flush) -{ - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - int retval = 0; - int receive_control_retval; - u16 wIndex = 0; - u8 *status_data; - static const unsigned int status_data_len = 2; - - status_data = kmalloc(status_data_len, GFP_KERNEL); - if (!status_data) - return -ENOMEM; - - if (flush) - wIndex |= XA_FLUSH; - receive_control_retval = agilent_82357a_receive_control_msg(a_priv, - agilent_82357a_control_request, - USB_DIR_IN | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, XFER_ABORT, - wIndex, status_data, - status_data_len, 100); - if (receive_control_retval < 0) { - dev_err(&usb_dev->dev, "82357a_receive_control_msg() returned %i\n", - receive_control_retval); - retval = -EIO; - goto cleanup; - } - if (status_data[0] != (~XFER_ABORT & 0xff)) { - dev_err(&usb_dev->dev, "major code=0x%x != ~XFER_ABORT\n", status_data[0]); - retval = -EIO; - goto cleanup; - } - switch (status_data[1]) { - case UGP_SUCCESS: - retval = 0; - break; - case UGP_ERR_FLUSHING: - if (flush) { - retval = 0; - break; - } - fallthrough; - case UGP_ERR_FLUSHING_ALREADY: - default: - dev_err(&usb_dev->dev, "abort returned error code=0x%x\n", status_data[1]); - retval = -EIO; - break; - } - -cleanup: - kfree(status_data); - return retval; -} - -// interface functions -int agilent_82357a_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written); - -static int agilent_82357a_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *nbytes) -{ - int retval; - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - int out_data_length, in_data_length; - int bytes_written, bytes_read; - int i = 0; - u8 trailing_flags; - unsigned long start_jiffies = jiffies; - int msec_timeout; - - *nbytes = 0; - *end = 0; - - if (!a_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(a_priv->bus_interface); - out_data_length = 0x9; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - out_data[i++] = DATA_PIPE_CMD_READ; - out_data[i++] = 0; // primary address when ARF_NO_ADDR is not set - out_data[i++] = 0; // secondary address when ARF_NO_ADDR is not set - out_data[i] = ARF_NO_ADDRESS | ARF_END_ON_EOI; - if (a_priv->eos_mode & REOS) - out_data[i] |= ARF_END_ON_EOS_CHAR; - ++i; - out_data[i++] = length & 0xff; - out_data[i++] = (length >> 8) & 0xff; - out_data[i++] = (length >> 16) & 0xff; - out_data[i++] = (length >> 24) & 0xff; - out_data[i++] = a_priv->eos_char; - msec_timeout = (board->usec_timeout + 999) / 1000; - retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); - if (retval) { - kfree(out_data); - return retval; - } - retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, msec_timeout); - kfree(out_data); - if (retval || bytes_written != i) { - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - mutex_unlock(&a_priv->bulk_transfer_lock); - if (retval < 0) - return retval; - return -EIO; - } - in_data_length = length + 1; - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&a_priv->bulk_transfer_lock); - return -ENOMEM; - } - if (board->usec_timeout != 0) - msec_timeout -= jiffies_to_msecs(jiffies - start_jiffies) - 1; - if (msec_timeout >= 0) { - retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length, - &bytes_read, msec_timeout); - } else { - retval = -ETIMEDOUT; - bytes_read = 0; - } - if (retval == -ETIMEDOUT) { - int extra_bytes_read; - int extra_bytes_retval; - - agilent_82357a_abort(a_priv, 1); - extra_bytes_retval = agilent_82357a_receive_bulk_msg(a_priv, in_data + bytes_read, - in_data_length - bytes_read, - &extra_bytes_read, 100); - bytes_read += extra_bytes_read; - if (extra_bytes_retval) { - dev_err(&usb_dev->dev, "extra_bytes_retval=%i, bytes_read=%i\n", - extra_bytes_retval, bytes_read); - agilent_82357a_abort(a_priv, 0); - } - } else if (retval) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - agilent_82357a_abort(a_priv, 0); - } - mutex_unlock(&a_priv->bulk_transfer_lock); - if (bytes_read > length + 1) { - bytes_read = length + 1; - dev_warn(&usb_dev->dev, "bytes_read > length? truncating"); - } - - if (bytes_read >= 1) { - memcpy(buffer, in_data, bytes_read - 1); - trailing_flags = in_data[bytes_read - 1]; - *nbytes = bytes_read - 1; - if (trailing_flags & (ATRF_EOI | ATRF_EOS)) - *end = 1; - } - kfree(in_data); - - /* - * Fix for a bug in 9914A that does not return the contents of ADSR - * when the board is in listener active state and ATN is not asserted. - * Set ATN here to obtain a valid board level ibsta - */ - agilent_82357a_take_control_internal(board, 0); - - // FIXME check trailing flags for error - return retval; -} - -static ssize_t agilent_82357a_generic_write(struct gpib_board *board, - u8 *buffer, size_t length, - int send_commands, int send_eoi, - size_t *bytes_written) -{ - int retval; - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data = NULL; - u8 *status_data = NULL; - int out_data_length; - int raw_bytes_written; - int i = 0, j; - int msec_timeout; - unsigned short bsr, adsr; - struct agilent_82357a_register_pairlet read_reg; - - *bytes_written = 0; - if (!a_priv->bus_interface) - return -ENODEV; - - usb_dev = interface_to_usbdev(a_priv->bus_interface); - out_data_length = length + 0x8; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - out_data[i++] = DATA_PIPE_CMD_WRITE; - out_data[i++] = 0; // primary address when AWF_NO_ADDRESS is not set - out_data[i++] = 0; // secondary address when AWF_NO_ADDRESS is not set - out_data[i] = AWF_NO_ADDRESS | AWF_NO_FAST_TALKER_FIRST_BYTE; - if (send_commands) - out_data[i] |= AWF_ATN | AWF_NO_FAST_TALKER; - if (send_eoi) - out_data[i] |= AWF_SEND_EOI; - ++i; - out_data[i++] = length & 0xff; - out_data[i++] = (length >> 8) & 0xff; - out_data[i++] = (length >> 16) & 0xff; - out_data[i++] = (length >> 24) & 0xff; - for (j = 0; j < length; j++) - out_data[i++] = buffer[j]; - - clear_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags); - - msec_timeout = (board->usec_timeout + 999) / 1000; - retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock); - if (retval) { - kfree(out_data); - return retval; - } - retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &raw_bytes_written, - msec_timeout); - kfree(out_data); - if (retval || raw_bytes_written != i) { - agilent_82357a_abort(a_priv, 0); - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, raw_bytes_written=%i, i=%i\n", - retval, raw_bytes_written, i); - mutex_unlock(&a_priv->bulk_transfer_lock); - if (retval < 0) - return retval; - return -EIO; - } - - retval = wait_event_interruptible(board->wait, - test_bit(AIF_WRITE_COMPLETE_BN, - &a_priv->interrupt_flags) || - test_bit(TIMO_NUM, &board->status)); - if (retval) { - dev_dbg(&usb_dev->dev, "wait write complete interrupted\n"); - agilent_82357a_abort(a_priv, 0); - mutex_unlock(&a_priv->bulk_transfer_lock); - return -ERESTARTSYS; - } - - if (test_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags) == 0) { - dev_dbg(&usb_dev->dev, "write timed out ibs %i, tmo %i\n", - test_bit(TIMO_NUM, &board->status), msec_timeout); - - agilent_82357a_abort(a_priv, 0); - - mutex_unlock(&a_priv->bulk_transfer_lock); - - read_reg.address = BSR; - retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1); - if (retval) { - dev_err(&usb_dev->dev, "read_registers() returned error\n"); - return -ETIMEDOUT; - } - - bsr = read_reg.value; - dev_dbg(&usb_dev->dev, "write aborted bsr 0x%x\n", bsr); - - if (send_commands) {/* check for no listeners */ - if ((bsr & BSR_ATN_BIT) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) { - dev_dbg(&usb_dev->dev, "No listener on command\n"); - clear_bit(TIMO_NUM, &board->status); - return -ENOTCONN; // no listener on bus - } - } else { - read_reg.address = ADSR; - retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1); - if (retval) { - dev_err(&usb_dev->dev, "read_registers() returned error\n"); - return -ETIMEDOUT; - } - adsr = read_reg.value; - if ((adsr & HR_TA) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) { - dev_dbg(&usb_dev->dev, "No listener on write\n"); - clear_bit(TIMO_NUM, &board->status); - return -ECOMM; - } - } - - return -ETIMEDOUT; - } - - status_data = kmalloc(STATUS_DATA_LEN, GFP_KERNEL); - if (!status_data) { - mutex_unlock(&a_priv->bulk_transfer_lock); - return -ENOMEM; - } - - retval = agilent_82357a_receive_control_msg(a_priv, agilent_82357a_control_request, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - XFER_STATUS, 0, status_data, STATUS_DATA_LEN, - 100); - mutex_unlock(&a_priv->bulk_transfer_lock); - if (retval < 0) { - dev_err(&usb_dev->dev, "receive_control_msg() returned %i\n", retval); - kfree(status_data); - return -EIO; - } - *bytes_written = (u32)status_data[2]; - *bytes_written |= (u32)status_data[3] << 8; - *bytes_written |= (u32)status_data[4] << 16; - *bytes_written |= (u32)status_data[5] << 24; - - kfree(status_data); - return 0; -} - -static int agilent_82357a_write(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written) -{ - return agilent_82357a_generic_write(board, buffer, length, 0, send_eoi, bytes_written); -} - -int agilent_82357a_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - return agilent_82357a_generic_write(board, buffer, length, 1, 0, bytes_written); -} - -int agilent_82357a_take_control_internal(struct gpib_board *board, int synchronous) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - struct agilent_82357a_register_pairlet write; - int retval; - - write.address = AUXCR; - if (synchronous) - write.value = AUX_TCS; - else - write.value = AUX_TCA; - retval = agilent_82357a_write_registers(a_priv, &write, 1); - if (retval) - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - - return retval; -} - -static int agilent_82357a_take_control(struct gpib_board *board, int synchronous) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - const int timeout = 10; - int i; - - if (!a_priv->bus_interface) - return -ENODEV; - -/* - * It looks like the 9914 does not handle tcs properly. - * See comment above tms9914_take_control_workaround() in - * drivers/gpib/tms9914/tms9914_aux.c - */ - if (synchronous) - return -ETIMEDOUT; - - agilent_82357a_take_control_internal(board, synchronous); - // busy wait until ATN is asserted - for (i = 0; i < timeout; ++i) { - agilent_82357a_update_status(board, 0); - if (test_bit(ATN_NUM, &board->status)) - break; - udelay(1); - } - if (i == timeout) - return -ETIMEDOUT; - return 0; -} - -static int agilent_82357a_go_to_standby(struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet write; - int retval; - - if (!a_priv->bus_interface) - return -ENODEV; - - usb_dev = interface_to_usbdev(a_priv->bus_interface); - write.address = AUXCR; - write.value = AUX_GTS; - retval = agilent_82357a_write_registers(a_priv, &write, 1); - if (retval) - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return 0; -} - -static int agilent_82357a_request_system_control(struct gpib_board *board, int request_control) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet writes[2]; - int retval; - int i = 0; - - if (!a_priv->bus_interface) - return -ENODEV; - - usb_dev = interface_to_usbdev(a_priv->bus_interface); - /* 82357B needs bit to be set in 9914 AUXCR register */ - writes[i].address = AUXCR; - if (request_control) { - writes[i].value = AUX_RQC; - a_priv->hw_control_bits |= SYSTEM_CONTROLLER; - } else { - return -EINVAL; - } - ++i; - writes[i].address = HW_CONTROL; - writes[i].value = a_priv->hw_control_bits; - ++i; - retval = agilent_82357a_write_registers(a_priv, writes, i); - if (retval) - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return retval; -} - -static void agilent_82357a_interface_clear(struct gpib_board *board, int assert) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet write; - int retval; - - if (!a_priv->bus_interface) - return; // -ENODEV; - - usb_dev = interface_to_usbdev(a_priv->bus_interface); - write.address = AUXCR; - write.value = AUX_SIC; - if (assert) { - write.value |= AUX_CS; - a_priv->is_cic = 1; - } - retval = agilent_82357a_write_registers(a_priv, &write, 1); - if (retval) - dev_err(&usb_dev->dev, "write_registers() returned error\n"); -} - -static void agilent_82357a_remote_enable(struct gpib_board *board, int enable) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet write; - int retval; - - if (!a_priv->bus_interface) - return; //-ENODEV; - - usb_dev = interface_to_usbdev(a_priv->bus_interface); - write.address = AUXCR; - write.value = AUX_SRE; - if (enable) - write.value |= AUX_CS; - retval = agilent_82357a_write_registers(a_priv, &write, 1); - if (retval) - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - a_priv->ren_state = enable; - return;// 0; -} - -static int agilent_82357a_enable_eos(struct gpib_board *board, u8 eos_byte, - int compare_8_bits) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - - if (!a_priv->bus_interface) - return -ENODEV; - if (compare_8_bits == 0) - return -EOPNOTSUPP; - - a_priv->eos_char = eos_byte; - a_priv->eos_mode = REOS | BIN; - return 0; -} - -static void agilent_82357a_disable_eos(struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - - a_priv->eos_mode &= ~REOS; -} - -static unsigned int agilent_82357a_update_status(struct gpib_board *board, - unsigned int clear_mask) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet address_status, bus_status; - int retval; - - if (!a_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(a_priv->bus_interface); - board->status &= ~clear_mask; - if (a_priv->is_cic) - set_bit(CIC_NUM, &board->status); - else - clear_bit(CIC_NUM, &board->status); - address_status.address = ADSR; - retval = agilent_82357a_read_registers(a_priv, &address_status, 1, 0); - if (retval) { - if (retval != -EAGAIN) - dev_err(&usb_dev->dev, "read_registers() returned error\n"); - return board->status; - } - // check for remote/local - if (address_status.value & HR_REM) - set_bit(REM_NUM, &board->status); - else - clear_bit(REM_NUM, &board->status); - // check for lockout - if (address_status.value & HR_LLO) - set_bit(LOK_NUM, &board->status); - else - clear_bit(LOK_NUM, &board->status); - // check for ATN - if (address_status.value & HR_ATN) - set_bit(ATN_NUM, &board->status); - else - clear_bit(ATN_NUM, &board->status); - // check for talker/listener addressed - if (address_status.value & HR_TA) - set_bit(TACS_NUM, &board->status); - else - clear_bit(TACS_NUM, &board->status); - if (address_status.value & HR_LA) - set_bit(LACS_NUM, &board->status); - else - clear_bit(LACS_NUM, &board->status); - - bus_status.address = BSR; - retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0); - if (retval) { - if (retval != -EAGAIN) - dev_err(&usb_dev->dev, "read_registers() returned error\n"); - return board->status; - } - if (bus_status.value & BSR_SRQ_BIT) - set_bit(SRQI_NUM, &board->status); - else - clear_bit(SRQI_NUM, &board->status); - - return board->status; -} - -static int agilent_82357a_primary_address(struct gpib_board *board, unsigned int address) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - struct agilent_82357a_register_pairlet write; - int retval; - - if (!a_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(a_priv->bus_interface); - // put primary address in address0 - write.address = ADR; - write.value = address & ADDRESS_MASK; - retval = agilent_82357a_write_registers(a_priv, &write, 1); - if (retval) { - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return retval; - } - return retval; -} - -static int agilent_82357a_secondary_address(struct gpib_board *board, - unsigned int address, int enable) -{ - if (enable) - return -EOPNOTSUPP; - return 0; -} - -static int agilent_82357a_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet writes[2]; - struct agilent_82357a_register_pairlet read; - int retval; - - if (!a_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(a_priv->bus_interface); - // execute parallel poll - writes[0].address = AUXCR; - writes[0].value = AUX_CS | AUX_RPP; - writes[1].address = HW_CONTROL; - writes[1].value = a_priv->hw_control_bits & ~NOT_PARALLEL_POLL; - retval = agilent_82357a_write_registers(a_priv, writes, 2); - if (retval) { - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return retval; - } - udelay(2); // silly, since usb write will take way longer - read.address = CPTR; - retval = agilent_82357a_read_registers(a_priv, &read, 1, 1); - if (retval) { - dev_err(&usb_dev->dev, "read_registers() returned error\n"); - return retval; - } - *result = read.value; - // clear parallel poll state - writes[0].address = HW_CONTROL; - writes[0].value = a_priv->hw_control_bits | NOT_PARALLEL_POLL; - writes[1].address = AUXCR; - writes[1].value = AUX_RPP; - retval = agilent_82357a_write_registers(a_priv, writes, 2); - if (retval) { - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return retval; - } - return 0; -} - -static void agilent_82357a_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - // board can only be system controller - return;// 0; -} - -static void agilent_82357a_parallel_poll_response(struct gpib_board *board, int ist) -{ - // board can only be system controller - return;// 0; -} - -static void agilent_82357a_serial_poll_response(struct gpib_board *board, u8 status) -{ - // board can only be system controller - return;// 0; -} - -static u8 agilent_82357a_serial_poll_status(struct gpib_board *board) -{ - // board can only be system controller - return 0; -} - -static void agilent_82357a_return_to_local(struct gpib_board *board) -{ - // board can only be system controller - return;// 0; -} - -static int agilent_82357a_line_status(const struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet bus_status; - int retval; - int status = VALID_ALL; - - if (!a_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(a_priv->bus_interface); - bus_status.address = BSR; - retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0); - if (retval) { - if (retval != -EAGAIN) - dev_err(&usb_dev->dev, "read_registers() returned error\n"); - return retval; - } - if (bus_status.value & BSR_REN_BIT) - status |= BUS_REN; - if (bus_status.value & BSR_IFC_BIT) - status |= BUS_IFC; - if (bus_status.value & BSR_SRQ_BIT) - status |= BUS_SRQ; - if (bus_status.value & BSR_EOI_BIT) - status |= BUS_EOI; - if (bus_status.value & BSR_NRFD_BIT) - status |= BUS_NRFD; - if (bus_status.value & BSR_NDAC_BIT) - status |= BUS_NDAC; - if (bus_status.value & BSR_DAV_BIT) - status |= BUS_DAV; - if (bus_status.value & BSR_ATN_BIT) - status |= BUS_ATN; - return status; -} - -static unsigned short nanosec_to_fast_talker_bits(unsigned int *nanosec) -{ - static const int nanosec_per_bit = 21; - static const int max_value = 0x72; - static const int min_value = 0x11; - unsigned short bits; - - bits = (*nanosec + nanosec_per_bit / 2) / nanosec_per_bit; - if (bits < min_value) - bits = min_value; - if (bits > max_value) - bits = max_value; - *nanosec = bits * nanosec_per_bit; - return bits; -} - -static int agilent_82357a_t1_delay(struct gpib_board *board, unsigned int nanosec) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - struct agilent_82357a_register_pairlet write; - int retval; - - if (!a_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(a_priv->bus_interface); - write.address = FAST_TALKER_T1; - write.value = nanosec_to_fast_talker_bits(&nanosec); - retval = agilent_82357a_write_registers(a_priv, &write, 1); - if (retval) - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return nanosec; -} - -static void agilent_82357a_interrupt_complete(struct urb *urb) -{ - struct gpib_board *board = urb->context; - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - int retval; - u8 *transfer_buffer = urb->transfer_buffer; - unsigned long interrupt_flags; - - switch (urb->status) { - /* success */ - case 0: - break; - /* unlinked, don't resubmit */ - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* other error, resubmit */ - retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC); - if (retval) - dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); - return; - } - - interrupt_flags = transfer_buffer[0]; - if (test_bit(AIF_READ_COMPLETE_BN, &interrupt_flags)) - set_bit(AIF_READ_COMPLETE_BN, &a_priv->interrupt_flags); - if (test_bit(AIF_WRITE_COMPLETE_BN, &interrupt_flags)) - set_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags); - if (test_bit(AIF_SRQ_BN, &interrupt_flags)) - set_bit(SRQI_NUM, &board->status); - - wake_up_interruptible(&board->wait); - - retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC); - if (retval) - dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); -} - -static int agilent_82357a_setup_urbs(struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev; - int int_pipe; - int retval; - - retval = mutex_lock_interruptible(&a_priv->interrupt_alloc_lock); - if (retval) - return retval; - if (!a_priv->bus_interface) { - retval = -ENODEV; - goto setup_exit; - } - - a_priv->interrupt_buffer = kmalloc(INTERRUPT_BUF_LEN, GFP_KERNEL); - if (!a_priv->interrupt_buffer) { - retval = -ENOMEM; - goto setup_exit; - } - a_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!a_priv->interrupt_urb) { - retval = -ENOMEM; - goto setup_exit; - } - usb_dev = interface_to_usbdev(a_priv->bus_interface); - int_pipe = usb_rcvintpipe(usb_dev, a_priv->interrupt_in_endpoint); - usb_fill_int_urb(a_priv->interrupt_urb, usb_dev, int_pipe, a_priv->interrupt_buffer, - INTERRUPT_BUF_LEN, &agilent_82357a_interrupt_complete, board, 1); - retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL); - if (retval) { - usb_free_urb(a_priv->interrupt_urb); - a_priv->interrupt_urb = NULL; - dev_err(&usb_dev->dev, "failed to submit first interrupt urb, retval=%i\n", retval); - goto setup_exit; - } - mutex_unlock(&a_priv->interrupt_alloc_lock); - return 0; - -setup_exit: - kfree(a_priv->interrupt_buffer); - mutex_unlock(&a_priv->interrupt_alloc_lock); - return retval; -} - -static void agilent_82357a_cleanup_urbs(struct agilent_82357a_priv *a_priv) -{ - if (a_priv && a_priv->bus_interface) { - if (a_priv->interrupt_urb) - usb_kill_urb(a_priv->interrupt_urb); - if (a_priv->bulk_urb) - usb_kill_urb(a_priv->bulk_urb); - } -}; - -static void agilent_82357a_release_urbs(struct agilent_82357a_priv *a_priv) -{ - if (a_priv) { - usb_free_urb(a_priv->interrupt_urb); - a_priv->interrupt_urb = NULL; - kfree(a_priv->interrupt_buffer); - } -} - -static int agilent_82357a_allocate_private(struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv; - - board->private_data = kzalloc(sizeof(struct agilent_82357a_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - a_priv = board->private_data; - mutex_init(&a_priv->bulk_transfer_lock); - mutex_init(&a_priv->bulk_alloc_lock); - mutex_init(&a_priv->control_alloc_lock); - mutex_init(&a_priv->interrupt_alloc_lock); - return 0; -} - -static void agilent_82357a_free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -#define INIT_NUM_REG_WRITES 18 -static int agilent_82357a_init(struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - struct agilent_82357a_register_pairlet hw_control; - struct agilent_82357a_register_pairlet writes[INIT_NUM_REG_WRITES]; - int retval; - unsigned int nanosec; - - writes[0].address = LED_CONTROL; - writes[0].value = FAIL_LED_ON; - writes[1].address = RESET_TO_POWERUP; - writes[1].value = RESET_SPACEBALL; - retval = agilent_82357a_write_registers(a_priv, writes, 2); - if (retval) { - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return -EIO; - } - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(usec_to_jiffies(2000))) - return -ERESTARTSYS; - writes[0].address = AUXCR; - writes[0].value = AUX_NBAF; - writes[1].address = AUXCR; - writes[1].value = AUX_HLDE; - writes[2].address = AUXCR; - writes[2].value = AUX_TON; - writes[3].address = AUXCR; - writes[3].value = AUX_LON; - writes[4].address = AUXCR; - writes[4].value = AUX_RSV2; - writes[5].address = AUXCR; - writes[5].value = AUX_INVAL; - writes[6].address = AUXCR; - writes[6].value = AUX_RPP; - writes[7].address = AUXCR; - writes[7].value = AUX_STDL; - writes[8].address = AUXCR; - writes[8].value = AUX_VSTDL; - writes[9].address = FAST_TALKER_T1; - nanosec = board->t1_nano_sec; - writes[9].value = nanosec_to_fast_talker_bits(&nanosec); - board->t1_nano_sec = nanosec; - writes[10].address = ADR; - writes[10].value = board->pad & ADDRESS_MASK; - writes[11].address = PPR; - writes[11].value = 0; - writes[12].address = SPMR; - writes[12].value = 0; - writes[13].address = PROTOCOL_CONTROL; - writes[13].value = WRITE_COMPLETE_INTERRUPT_EN; - writes[14].address = IMR0; - writes[14].value = HR_BOIE | HR_BIIE; - writes[15].address = IMR1; - writes[15].value = HR_SRQIE; - // turn off reset state - writes[16].address = AUXCR; - writes[16].value = AUX_CHIP_RESET; - writes[17].address = LED_CONTROL; - writes[17].value = FIRMWARE_LED_CONTROL; - retval = agilent_82357a_write_registers(a_priv, writes, INIT_NUM_REG_WRITES); - if (retval) { - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return -EIO; - } - hw_control.address = HW_CONTROL; - retval = agilent_82357a_read_registers(a_priv, &hw_control, 1, 1); - if (retval) { - dev_err(&usb_dev->dev, "read_registers() returned error\n"); - return -EIO; - } - a_priv->hw_control_bits = (hw_control.value & ~0x7) | NOT_TI_RESET | NOT_PARALLEL_POLL; - - return 0; -} - -static inline int agilent_82357a_device_match(struct usb_interface *interface, - const struct gpib_board_config *config) -{ - struct usb_device * const usbdev = interface_to_usbdev(interface); - - if (gpib_match_device_path(&interface->dev, config->device_path) == 0) - return 0; - if (config->serial_number && - strcmp(usbdev->serial, config->serial_number) != 0) - return 0; - - return 1; -} - -static int agilent_82357a_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - int retval; - int i; - unsigned int product_id; - struct agilent_82357a_priv *a_priv; - struct usb_device *usb_dev; - - if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) - return -ERESTARTSYS; - - retval = agilent_82357a_allocate_private(board); - if (retval < 0) { - mutex_unlock(&agilent_82357a_hotplug_lock); - return retval; - } - a_priv = board->private_data; - for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { - if (agilent_82357a_driver_interfaces[i] && - !usb_get_intfdata(agilent_82357a_driver_interfaces[i]) && - agilent_82357a_device_match(agilent_82357a_driver_interfaces[i], config)) { - a_priv->bus_interface = agilent_82357a_driver_interfaces[i]; - usb_set_intfdata(agilent_82357a_driver_interfaces[i], board); - usb_dev = interface_to_usbdev(a_priv->bus_interface); - break; - } - } - if (i == MAX_NUM_82357A_INTERFACES) { - dev_err(board->gpib_dev, - "No supported adapters found, have you loaded its firmware?\n"); - retval = -ENODEV; - goto attach_fail; - } - product_id = le16_to_cpu(interface_to_usbdev(a_priv->bus_interface)->descriptor.idProduct); - switch (product_id) { - case USB_DEVICE_ID_AGILENT_82357A: - a_priv->bulk_out_endpoint = AGILENT_82357A_BULK_OUT_ENDPOINT; - a_priv->interrupt_in_endpoint = AGILENT_82357A_INTERRUPT_IN_ENDPOINT; - break; - case USB_DEVICE_ID_AGILENT_82357B: - a_priv->bulk_out_endpoint = AGILENT_82357B_BULK_OUT_ENDPOINT; - a_priv->interrupt_in_endpoint = AGILENT_82357B_INTERRUPT_IN_ENDPOINT; - break; - default: - dev_err(&usb_dev->dev, "bug, unhandled product_id in switch?\n"); - retval = -EIO; - goto attach_fail; - } - - retval = agilent_82357a_setup_urbs(board); - if (retval < 0) - goto attach_fail; - - timer_setup(&a_priv->bulk_timer, agilent_82357a_timeout_handler, 0); - - board->t1_nano_sec = 800; - - retval = agilent_82357a_init(board); - - if (retval < 0) { - agilent_82357a_cleanup_urbs(a_priv); - agilent_82357a_release_urbs(a_priv); - goto attach_fail; - } - - dev_info(&usb_dev->dev, "bus %d dev num %d attached to gpib%d, interface %i\n", - usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); - mutex_unlock(&agilent_82357a_hotplug_lock); - return retval; - -attach_fail: - agilent_82357a_free_private(board); - mutex_unlock(&agilent_82357a_hotplug_lock); - return retval; -} - -static int agilent_82357a_go_idle(struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface); - struct agilent_82357a_register_pairlet writes[0x20]; - int retval; - - // turn on tms9914 reset state - writes[0].address = AUXCR; - writes[0].value = AUX_CS | AUX_CHIP_RESET; - a_priv->hw_control_bits &= ~NOT_TI_RESET; - writes[1].address = HW_CONTROL; - writes[1].value = a_priv->hw_control_bits; - writes[2].address = PROTOCOL_CONTROL; - writes[2].value = 0; - writes[3].address = IMR0; - writes[3].value = 0; - writes[4].address = IMR1; - writes[4].value = 0; - writes[5].address = LED_CONTROL; - writes[5].value = 0; - retval = agilent_82357a_write_registers(a_priv, writes, 6); - if (retval) { - dev_err(&usb_dev->dev, "write_registers() returned error\n"); - return -EIO; - } - return 0; -} - -static void agilent_82357a_detach(struct gpib_board *board) -{ - struct agilent_82357a_priv *a_priv; - - mutex_lock(&agilent_82357a_hotplug_lock); - - a_priv = board->private_data; - if (a_priv) { - if (a_priv->bus_interface) { - agilent_82357a_go_idle(board); - usb_set_intfdata(a_priv->bus_interface, NULL); - } - mutex_lock(&a_priv->control_alloc_lock); - mutex_lock(&a_priv->bulk_alloc_lock); - mutex_lock(&a_priv->interrupt_alloc_lock); - agilent_82357a_cleanup_urbs(a_priv); - agilent_82357a_release_urbs(a_priv); - agilent_82357a_free_private(board); - } - mutex_unlock(&agilent_82357a_hotplug_lock); -} - -static struct gpib_interface agilent_82357a_gpib_interface = { - .name = "agilent_82357a", - .attach = agilent_82357a_attach, - .detach = agilent_82357a_detach, - .read = agilent_82357a_read, - .write = agilent_82357a_write, - .command = agilent_82357a_command, - .take_control = agilent_82357a_take_control, - .go_to_standby = agilent_82357a_go_to_standby, - .request_system_control = agilent_82357a_request_system_control, - .interface_clear = agilent_82357a_interface_clear, - .remote_enable = agilent_82357a_remote_enable, - .enable_eos = agilent_82357a_enable_eos, - .disable_eos = agilent_82357a_disable_eos, - .parallel_poll = agilent_82357a_parallel_poll, - .parallel_poll_configure = agilent_82357a_parallel_poll_configure, - .parallel_poll_response = agilent_82357a_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = agilent_82357a_line_status, - .update_status = agilent_82357a_update_status, - .primary_address = agilent_82357a_primary_address, - .secondary_address = agilent_82357a_secondary_address, - .serial_poll_response = agilent_82357a_serial_poll_response, - .serial_poll_status = agilent_82357a_serial_poll_status, - .t1_delay = agilent_82357a_t1_delay, - .return_to_local = agilent_82357a_return_to_local, - .no_7_bit_eos = 1, - .skip_check_for_command_acceptors = 1 -}; - -// Table with the USB-devices: just now only testing IDs -static struct usb_device_id agilent_82357a_driver_device_table[] = { - {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357A)}, - {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357B)}, - {} /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, agilent_82357a_driver_device_table); - -static int agilent_82357a_driver_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int i; - char *path; - static const int path_length = 1024; - struct usb_device *usb_dev; - - if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) - return -ERESTARTSYS; - usb_dev = usb_get_dev(interface_to_usbdev(interface)); - for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { - if (!agilent_82357a_driver_interfaces[i]) { - agilent_82357a_driver_interfaces[i] = interface; - usb_set_intfdata(interface, NULL); - dev_dbg(&usb_dev->dev, "set bus interface %i to address 0x%p\n", - i, interface); - break; - } - } - if (i == MAX_NUM_82357A_INTERFACES) { - usb_put_dev(usb_dev); - mutex_unlock(&agilent_82357a_hotplug_lock); - dev_err(&usb_dev->dev, "out of space in agilent_82357a_driver_interfaces[]\n"); - return -1; - } - path = kmalloc(path_length, GFP_KERNEL); - if (!path) { - usb_put_dev(usb_dev); - mutex_unlock(&agilent_82357a_hotplug_lock); - return -ENOMEM; - } - usb_make_path(usb_dev, path, path_length); - dev_info(&usb_dev->dev, "probe succeeded for path: %s\n", path); - kfree(path); - mutex_unlock(&agilent_82357a_hotplug_lock); - return 0; -} - -static void agilent_82357a_driver_disconnect(struct usb_interface *interface) -{ - int i; - struct usb_device *usb_dev = interface_to_usbdev(interface); - - mutex_lock(&agilent_82357a_hotplug_lock); - - for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { - if (agilent_82357a_driver_interfaces[i] == interface) { - struct gpib_board *board = usb_get_intfdata(interface); - - if (board) { - struct agilent_82357a_priv *a_priv = board->private_data; - - if (a_priv) { - mutex_lock(&a_priv->control_alloc_lock); - mutex_lock(&a_priv->bulk_alloc_lock); - mutex_lock(&a_priv->interrupt_alloc_lock); - agilent_82357a_cleanup_urbs(a_priv); - a_priv->bus_interface = NULL; - mutex_unlock(&a_priv->interrupt_alloc_lock); - mutex_unlock(&a_priv->bulk_alloc_lock); - mutex_unlock(&a_priv->control_alloc_lock); - } - } - agilent_82357a_driver_interfaces[i] = NULL; - break; - } - } - if (i == MAX_NUM_82357A_INTERFACES) - dev_err(&usb_dev->dev, "unable to find interface - bug?\n"); - usb_put_dev(usb_dev); - - mutex_unlock(&agilent_82357a_hotplug_lock); -} - -static int agilent_82357a_driver_suspend(struct usb_interface *interface, pm_message_t message) -{ - int i, retval; - struct usb_device *usb_dev = interface_to_usbdev(interface); - - mutex_lock(&agilent_82357a_hotplug_lock); - - for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { - if (agilent_82357a_driver_interfaces[i] == interface) { - struct gpib_board *board = usb_get_intfdata(interface); - - if (board) { - struct agilent_82357a_priv *a_priv = board->private_data; - - if (a_priv) { - agilent_82357a_abort(a_priv, 0); - agilent_82357a_abort(a_priv, 0); - retval = agilent_82357a_go_idle(board); - if (retval) { - dev_err(&usb_dev->dev, "failed to go idle, retval=%i\n", - retval); - mutex_unlock(&agilent_82357a_hotplug_lock); - return retval; - } - mutex_lock(&a_priv->interrupt_alloc_lock); - agilent_82357a_cleanup_urbs(a_priv); - mutex_unlock(&a_priv->interrupt_alloc_lock); - dev_dbg(&usb_dev->dev, - "bus %d dev num %d gpib %d, interface %i suspended\n", - usb_dev->bus->busnum, usb_dev->devnum, - board->minor, i); - } - } - break; - } - } - - mutex_unlock(&agilent_82357a_hotplug_lock); - - return 0; -} - -static int agilent_82357a_driver_resume(struct usb_interface *interface) -{ - struct usb_device *usb_dev = interface_to_usbdev(interface); - struct gpib_board *board; - int i, retval = 0; - - mutex_lock(&agilent_82357a_hotplug_lock); - - for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { - if (agilent_82357a_driver_interfaces[i] == interface) { - board = usb_get_intfdata(interface); - if (board) - break; - } - } - if (i == MAX_NUM_82357A_INTERFACES) { - retval = -ENOENT; - goto resume_exit; - } - - struct agilent_82357a_priv *a_priv = board->private_data; - - if (a_priv) { - if (a_priv->interrupt_urb) { - mutex_lock(&a_priv->interrupt_alloc_lock); - retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL); - if (retval) { - dev_err(&usb_dev->dev, "failed to resubmit interrupt urb in resume, retval=%i\n", - retval); - mutex_unlock(&a_priv->interrupt_alloc_lock); - mutex_unlock(&agilent_82357a_hotplug_lock); - return retval; - } - mutex_unlock(&a_priv->interrupt_alloc_lock); - } - retval = agilent_82357a_init(board); - if (retval < 0) { - mutex_unlock(&agilent_82357a_hotplug_lock); - return retval; - } - // set/unset system controller - retval = agilent_82357a_request_system_control(board, board->master); - // toggle ifc if master - if (board->master) { - agilent_82357a_interface_clear(board, 1); - usleep_range(200, 250); - agilent_82357a_interface_clear(board, 0); - } - // assert/unassert REN - agilent_82357a_remote_enable(board, a_priv->ren_state); - - dev_dbg(&usb_dev->dev, - "bus %d dev num %d gpib%d, interface %i resumed\n", - usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); - } - -resume_exit: - mutex_unlock(&agilent_82357a_hotplug_lock); - - return retval; -} - -static struct usb_driver agilent_82357a_bus_driver = { - .name = DRV_NAME, - .probe = agilent_82357a_driver_probe, - .disconnect = agilent_82357a_driver_disconnect, - .suspend = agilent_82357a_driver_suspend, - .resume = agilent_82357a_driver_resume, - .id_table = agilent_82357a_driver_device_table, -}; - -static int __init agilent_82357a_init_module(void) -{ - int i; - int ret; - - for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) - agilent_82357a_driver_interfaces[i] = NULL; - - ret = usb_register(&agilent_82357a_bus_driver); - if (ret) { - pr_err("usb_register failed: error = %d\n", ret); - return ret; - } - - ret = gpib_register_driver(&agilent_82357a_gpib_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - usb_deregister(&agilent_82357a_bus_driver); - return ret; - } - - return 0; -} - -static void __exit agilent_82357a_exit_module(void) -{ - gpib_unregister_driver(&agilent_82357a_gpib_interface); - usb_deregister(&agilent_82357a_bus_driver); -} - -module_init(agilent_82357a_init_module); -module_exit(agilent_82357a_exit_module); diff --git a/drivers/staging/gpib/agilent_82357a/agilent_82357a.h b/drivers/staging/gpib/agilent_82357a/agilent_82357a.h deleted file mode 100644 index 33ac558e5552..000000000000 --- a/drivers/staging/gpib/agilent_82357a/agilent_82357a.h +++ /dev/null @@ -1,182 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2004 by Frank Mori Hess * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "gpibP.h" -#include "tms9914.h" - -enum usb_vendor_ids { - USB_VENDOR_ID_AGILENT = 0x0957 -}; - -enum usb_device_ids { - USB_DEVICE_ID_AGILENT_82357A = 0x0107, - USB_DEVICE_ID_AGILENT_82357A_PREINIT = 0x0007, // device id before firmware is loaded - USB_DEVICE_ID_AGILENT_82357B = 0x0718, // device id before firmware is loaded - USB_DEVICE_ID_AGILENT_82357B_PREINIT = 0x0518, // device id before firmware is loaded -}; - -enum endpoint_addresses { - AGILENT_82357_CONTROL_ENDPOINT = 0x0, - AGILENT_82357_BULK_IN_ENDPOINT = 0x2, - AGILENT_82357A_BULK_OUT_ENDPOINT = 0x4, - AGILENT_82357A_INTERRUPT_IN_ENDPOINT = 0x6, - AGILENT_82357B_BULK_OUT_ENDPOINT = 0x6, - AGILENT_82357B_INTERRUPT_IN_ENDPOINT = 0x8, -}; - -enum bulk_commands { - DATA_PIPE_CMD_WRITE = 0x1, - DATA_PIPE_CMD_READ = 0x3, - DATA_PIPE_CMD_WR_REGS = 0x4, - DATA_PIPE_CMD_RD_REGS = 0x5 -}; - -enum agilent_82357a_read_flags { - ARF_END_ON_EOI = 0x1, - ARF_NO_ADDRESS = 0x2, - ARF_END_ON_EOS_CHAR = 0x4, - ARF_SPOLL = 0x8 -}; - -enum agilent_82357a_trailing_read_flags { - ATRF_EOI = 0x1, - ATRF_ATN = 0x2, - ATRF_IFC = 0x4, - ATRF_EOS = 0x8, - ATRF_ABORT = 0x10, - ATRF_COUNT = 0x20, - ATRF_DEAD_BUS = 0x40, - ATRF_UNADDRESSED = 0x80 -}; - -enum agilent_82357a_write_flags { - AWF_SEND_EOI = 0x1, - AWF_NO_FAST_TALKER_FIRST_BYTE = 0x2, - AWF_NO_FAST_TALKER = 0x4, - AWF_NO_ADDRESS = 0x8, - AWF_ATN = 0x10, - AWF_SEPARATE_HEADER = 0x80 -}; - -enum agilent_82357a_interrupt_flag_bit_numbers { - AIF_SRQ_BN = 0, - AIF_WRITE_COMPLETE_BN = 1, - AIF_READ_COMPLETE_BN = 2, -}; - -enum agilent_82357_error_codes { - UGP_SUCCESS = 0, - UGP_ERR_INVALID_CMD = 1, - UGP_ERR_INVALID_PARAM = 2, - UGP_ERR_INVALID_REG = 3, - UGP_ERR_GPIB_READ = 4, - UGP_ERR_GPIB_WRITE = 5, - UGP_ERR_FLUSHING = 6, - UGP_ERR_FLUSHING_ALREADY = 7, - UGP_ERR_UNSUPPORTED = 8, - UGP_ERR_OTHER = 9 -}; - -enum agilent_82357_control_values { - XFER_ABORT = 0xa0, - XFER_STATUS = 0xb0, -}; - -enum xfer_status_bits { - XS_COMPLETED = 0x1, - XS_READ = 0x2, -}; - -enum xfer_status_completion_bits { - XSC_EOI = 0x1, - XSC_ATN = 0x2, - XSC_IFC = 0x4, - XSC_EOS = 0x8, - XSC_ABORT = 0x10, - XSC_COUNT = 0x20, - XSC_DEAD_BUS = 0x40, - XSC_BUS_NOT_ADDRESSED = 0x80 -}; - -enum xfer_abort_type { - XA_FLUSH = 0x1 -}; - -#define STATUS_DATA_LEN 8 -#define INTERRUPT_BUF_LEN 8 - -struct agilent_82357a_urb_ctx { - struct completion complete; - unsigned timed_out : 1; -}; - -// struct which defines local data for each 82357 device -struct agilent_82357a_priv { - struct usb_interface *bus_interface; - unsigned short eos_char; - unsigned short eos_mode; - unsigned short hw_control_bits; - unsigned long interrupt_flags; - struct urb *bulk_urb; - struct urb *interrupt_urb; - u8 *interrupt_buffer; - struct mutex bulk_transfer_lock; // bulk transfer lock - struct mutex bulk_alloc_lock; // bulk transfer allocation lock - struct mutex interrupt_alloc_lock; // interrupt allocation lock - struct mutex control_alloc_lock; // control message allocation lock - struct timer_list bulk_timer; - struct agilent_82357a_urb_ctx context; - unsigned int bulk_out_endpoint; - unsigned int interrupt_in_endpoint; - unsigned is_cic : 1; - unsigned ren_state : 1; -}; - -struct agilent_82357a_register_pairlet { - short address; - unsigned short value; -}; - -enum firmware_registers { - HW_CONTROL = 0xa, - LED_CONTROL = 0xb, - RESET_TO_POWERUP = 0xc, - PROTOCOL_CONTROL = 0xd, - FAST_TALKER_T1 = 0xe -}; - -enum hardware_control_bits { - NOT_TI_RESET = 0x1, - SYSTEM_CONTROLLER = 0x2, - NOT_PARALLEL_POLL = 0x4, - OSCILLATOR_5V_ON = 0x8, - OUTPUT_5V_ON = 0x20, - CPLD_3V_ON = 0x80, -}; - -enum led_control_bits { - FIRMWARE_LED_CONTROL = 0x1, - FAIL_LED_ON = 0x20, - READY_LED_ON = 0x40, - ACCESS_LED_ON = 0x80 -}; - -enum reset_to_powerup_bits { - RESET_SPACEBALL = 0x1, // wait 2 millisec after sending -}; - -enum protocol_control_bits { - WRITE_COMPLETE_INTERRUPT_EN = 0x1, -}; - -static const int agilent_82357a_control_request = 0x4; - diff --git a/drivers/staging/gpib/cb7210/Makefile b/drivers/staging/gpib/cb7210/Makefile deleted file mode 100644 index d239ae80b415..000000000000 --- a/drivers/staging/gpib/cb7210/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_GPIB_CB7210) += cb7210.o - - diff --git a/drivers/staging/gpib/cb7210/cb7210.c b/drivers/staging/gpib/cb7210/cb7210.c deleted file mode 100644 index 3e2397898a9b..000000000000 --- a/drivers/staging/gpib/cb7210/cb7210.c +++ /dev/null @@ -1,1598 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * Measurement Computing boards using cb7210.2 and cbi488.2 chips - * copyright : (C) 2001, 2002 by Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "cb7210.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gpib_pci_ids.h" -#include "quancom_pci.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver Measurement Computing boards using cb7210.2 and cbi488.2"); - -static int cb7210_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read); - - static inline int have_fifo_word(const struct cb7210_priv *cb_priv) -{ - if (((cb7210_read_byte(cb_priv, HS_STATUS)) & - (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY)) == - (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY)) - return 1; - else - return 0; -} - -static inline void input_fifo_enable(struct gpib_board *board, int enable) -{ - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - - if (enable) { - cb_priv->in_fifo_half_full = 0; - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - - cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT | - HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits, - HS_MODE); - - cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); - - cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); - - cb_priv->hs_mode_bits |= HS_RX_ENABLE; - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); - } else { - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - - cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, nec7210_iobase(cb_priv) + - HS_MODE); - - clear_bit(READ_READY_BN, &nec_priv->state); - } - - spin_unlock_irqrestore(&board->spinlock, flags); -} - -static int fifo_read(struct gpib_board *board, struct cb7210_priv *cb_priv, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - ssize_t retval = 0; - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - int hs_status; - u16 word; - unsigned long flags; - - *bytes_read = 0; - if (cb_priv->fifo_iobase == 0) { - dev_err(board->gpib_dev, "fifo iobase is zero!\n"); - return -EIO; - } - *end = 0; - if (length <= cb7210_fifo_size) { - dev_err(board->gpib_dev, " bug! fifo read length < fifo size\n"); - return -EINVAL; - } - - input_fifo_enable(board, 1); - - while (*bytes_read + cb7210_fifo_size < length) { - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); - - if (wait_event_interruptible(board->wait, - (cb_priv->in_fifo_half_full && - have_fifo_word(cb_priv)) || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - break; - } - - spin_lock_irqsave(&board->spinlock, flags); - - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - - while (have_fifo_word(cb_priv)) { - word = inw(cb_priv->fifo_iobase + DIR); - buffer[(*bytes_read)++] = word & 0xff; - buffer[(*bytes_read)++] = (word >> 8) & 0xff; - } - - cb_priv->in_fifo_half_full = 0; - - hs_status = cb7210_read_byte(cb_priv, HS_STATUS); - - spin_unlock_irqrestore(&board->spinlock, flags); - - if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) { - *end = 1; - break; - } - if (hs_status & HS_FIFO_FULL) - break; - if (test_bit(TIMO_NUM, &board->status)) { - retval = -ETIMEDOUT; - break; - } - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { - retval = -EINTR; - break; - } - } - hs_status = cb7210_read_byte(cb_priv, HS_STATUS); - if (hs_status & HS_RX_LSB_NOT_EMPTY) { - word = inw(cb_priv->fifo_iobase + DIR); - buffer[(*bytes_read)++] = word & 0xff; - } - - input_fifo_enable(board, 0); - - if (wait_event_interruptible(board->wait, - test_bit(READ_READY_BN, &nec_priv->state) || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - } - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - if (test_bit(READ_READY_BN, &nec_priv->state)) { - nec7210_set_handshake_mode(board, nec_priv, HR_HLDA); - buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end); - } - - return retval; -} - -static int cb7210_accel_read(struct gpib_board *board, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - ssize_t retval; - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - size_t num_bytes; - - *bytes_read = 0; - // deal with limitations of fifo - if (length < cb7210_fifo_size + 3 || (nec_priv->auxa_bits & HR_REOS)) - return cb7210_read(board, buffer, length, end, bytes_read); - *end = 0; - - nec7210_release_rfd_holdoff(board, nec_priv); - - if (wait_event_interruptible(board->wait, - test_bit(READ_READY_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - return -ERESTARTSYS; - } - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - return -EINTR; - - nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); - buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end); - if (*end) - return 0; - - nec7210_release_rfd_holdoff(board, nec_priv); - - retval = fifo_read(board, cb_priv, &buffer[*bytes_read], length - *bytes_read - 1, - end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0) - return retval; - if (*end) - return 0; - - retval = cb7210_read(board, &buffer[*bytes_read], 1, end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0) - return retval; - - return 0; -} - -static int output_fifo_empty(const struct cb7210_priv *cb_priv) -{ - if ((cb7210_read_byte(cb_priv, HS_STATUS) & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY)) - == 0) - return 1; - else - return 0; -} - -static inline void output_fifo_enable(struct gpib_board *board, int enable) -{ - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - - if (enable) { - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); - - cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT | - HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits, - HS_MODE); - - cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; - cb_priv->hs_mode_bits |= HS_TX_ENABLE; - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); - - cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); - - clear_bit(WRITE_READY_BN, &nec_priv->state); - - } else { - cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK; - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); - - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE); - } - - spin_unlock_irqrestore(&board->spinlock, flags); -} - -static int fifo_write(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - size_t count = 0; - ssize_t retval = 0; - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - unsigned int num_bytes, i; - unsigned long flags; - - *bytes_written = 0; - if (cb_priv->fifo_iobase == 0) { - dev_err(board->gpib_dev, "fifo iobase is zero!\n"); - return -EINVAL; - } - if (length == 0) - return 0; - - clear_bit(DEV_CLEAR_BN, &nec_priv->state); - clear_bit(BUS_ERROR_BN, &nec_priv->state); - - output_fifo_enable(board, 1); - - while (count < length) { - // wait until byte is ready to be sent - if (wait_event_interruptible(board->wait, - cb_priv->out_fifo_half_empty || - output_fifo_empty(cb_priv) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(BUS_ERROR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - break; - } - if (test_bit(TIMO_NUM, &board->status) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(BUS_ERROR_BN, &nec_priv->state)) - break; - - if (output_fifo_empty(cb_priv)) - num_bytes = cb7210_fifo_size - cb7210_fifo_width; - else - num_bytes = cb7210_fifo_size / 2; - if (num_bytes + count > length) - num_bytes = length - count; - if (num_bytes % cb7210_fifo_width) { - dev_err(board->gpib_dev, " bug! fifo write with odd number of bytes\n"); - retval = -EINVAL; - break; - } - - spin_lock_irqsave(&board->spinlock, flags); - for (i = 0; i < num_bytes / cb7210_fifo_width; i++) { - u16 word; - - word = buffer[count++] & 0xff; - word |= (buffer[count++] << 8) & 0xff00; - outw(word, cb_priv->fifo_iobase + CDOR); - } - cb_priv->out_fifo_half_empty = 0; - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits | - HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE); - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); - spin_unlock_irqrestore(&board->spinlock, flags); - } - // wait last byte has been sent - if (wait_event_interruptible(board->wait, - output_fifo_empty(cb_priv) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(BUS_ERROR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - } - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_bit(BUS_ERROR_BN, &nec_priv->state)) - retval = -EIO; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - - output_fifo_enable(board, 0); - - *bytes_written = count; - return retval; -} - -static int cb7210_accel_write(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written) -{ - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - unsigned long fast_chunk_size, leftover; - int retval; - size_t num_bytes; - - *bytes_written = 0; - if (length > cb7210_fifo_width) - fast_chunk_size = length - 1; - else - fast_chunk_size = 0; - fast_chunk_size -= fast_chunk_size % cb7210_fifo_width; - leftover = length - fast_chunk_size; - - retval = fifo_write(board, buffer, fast_chunk_size, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - - retval = nec7210_write(board, nec_priv, buffer + fast_chunk_size, leftover, - send_eoi, &num_bytes); - *bytes_written += num_bytes; - return retval; -} - -static int cb7210_line_status(const struct gpib_board *board) -{ - int status = VALID_ALL; - int bsr_bits; - struct cb7210_priv *cb_priv; - - cb_priv = board->private_data; - - bsr_bits = cb7210_paged_read_byte(cb_priv, BUS_STATUS, BUS_STATUS_PAGE); - - if ((bsr_bits & BSR_REN_BIT) == 0) - status |= BUS_REN; - if ((bsr_bits & BSR_IFC_BIT) == 0) - status |= BUS_IFC; - if ((bsr_bits & BSR_SRQ_BIT) == 0) - status |= BUS_SRQ; - if ((bsr_bits & BSR_EOI_BIT) == 0) - status |= BUS_EOI; - if ((bsr_bits & BSR_NRFD_BIT) == 0) - status |= BUS_NRFD; - if ((bsr_bits & BSR_NDAC_BIT) == 0) - status |= BUS_NDAC; - if ((bsr_bits & BSR_DAV_BIT) == 0) - status |= BUS_DAV; - if ((bsr_bits & BSR_ATN_BIT) == 0) - status |= BUS_ATN; - - return status; -} - -static int cb7210_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - unsigned int retval; - - retval = nec7210_t1_delay(board, nec_priv, nano_sec); - - if (nano_sec <= 350) { - write_byte(nec_priv, AUX_HI_SPEED, AUXMR); - retval = 350; - } else { - write_byte(nec_priv, AUX_LO_SPEED, AUXMR); - } - return retval; -} - -static irqreturn_t cb7210_locked_internal_interrupt(struct gpib_board *board); - -/* - * GPIB interrupt service routines - */ - -static irqreturn_t cb_pci_interrupt(int irq, void *arg) -{ - int bits; - struct gpib_board *board = arg; - struct cb7210_priv *priv = board->private_data; - - // first task check if this is really our interrupt in a shared irq environment - switch (priv->pci_chip) { - case PCI_CHIP_AMCC_S5933: - if ((inl(priv->amcc_iobase + INTCSR_REG) & - (INBOX_INTR_CS_BIT | INTR_ASSERTED_BIT)) == 0) - return IRQ_NONE; - - // read incoming mailbox to clear mailbox full flag - inl(priv->amcc_iobase + INCOMING_MAILBOX_REG(3)); - // clear amccs5933 interrupt - bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) | - INBOX_SELECT_BITS(3) | INBOX_INTR_CS_BIT; - outl(bits, priv->amcc_iobase + INTCSR_REG); - break; - case PCI_CHIP_QUANCOM: - if ((inb(nec7210_iobase(priv) + QUANCOM_IRQ_CONTROL_STATUS_REG) & - QUANCOM_IRQ_ASSERTED_BIT)) - outb(QUANCOM_IRQ_ENABLE_BIT, nec7210_iobase(priv) + - QUANCOM_IRQ_CONTROL_STATUS_REG); - break; - default: - break; - } - return cb7210_locked_internal_interrupt(arg); -} - -static irqreturn_t cb7210_internal_interrupt(struct gpib_board *board) -{ - int hs_status, status1, status2; - struct cb7210_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - int clear_bits; - - if ((priv->hs_mode_bits & HS_ENABLE_MASK)) { - status1 = 0; - hs_status = cb7210_read_byte(priv, HS_STATUS); - } else { - hs_status = 0; - status1 = read_byte(nec_priv, ISR1); - } - status2 = read_byte(nec_priv, ISR2); - nec7210_interrupt_have_status(board, nec_priv, status1, status2); - - dev_dbg(board->gpib_dev, "status 0x%x, mode 0x%x\n", hs_status, priv->hs_mode_bits); - - clear_bits = 0; - - if (hs_status & HS_HALF_FULL) { - if (priv->hs_mode_bits & HS_TX_ENABLE) - priv->out_fifo_half_empty = 1; - else if (priv->hs_mode_bits & HS_RX_ENABLE) - priv->in_fifo_half_full = 1; - clear_bits |= HS_CLR_HF_INT; - } - - if (hs_status & HS_SRQ_INT) { - set_bit(SRQI_NUM, &board->status); - clear_bits |= HS_CLR_SRQ_INT; - } - - if ((hs_status & HS_EOI_INT)) { - clear_bits |= HS_CLR_EOI_EMPTY_INT; - set_bit(RECEIVED_END_BN, &nec_priv->state); - if ((nec_priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE) - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - } - - if ((priv->hs_mode_bits & HS_TX_ENABLE) && - (hs_status & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY)) == 0) - clear_bits |= HS_CLR_EOI_EMPTY_INT; - - if (clear_bits) { - cb7210_write_byte(priv, priv->hs_mode_bits | clear_bits, HS_MODE); - cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE); - wake_up_interruptible(&board->wait); - } - - return IRQ_HANDLED; -} - -static irqreturn_t cb7210_locked_internal_interrupt(struct gpib_board *board) -{ - unsigned long flags; - irqreturn_t retval; - - spin_lock_irqsave(&board->spinlock, flags); - retval = cb7210_internal_interrupt(board); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -static irqreturn_t cb7210_interrupt(int irq, void *arg) -{ - return cb7210_internal_interrupt(arg); -} - -static int cb_pci_attach(struct gpib_board *board, const struct gpib_board_config *config); -static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_config *config); - -static void cb_pci_detach(struct gpib_board *board); -static void cb_isa_detach(struct gpib_board *board); - -// wrappers for interface functions -static int cb7210_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); -} - -static int cb7210_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); -} - -static int cb7210_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); -} - -static int cb7210_take_control(struct gpib_board *board, int synchronous) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_take_control(board, &priv->nec7210_priv, synchronous); -} - -static int cb7210_go_to_standby(struct gpib_board *board) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_go_to_standby(board, &priv->nec7210_priv); -} - -static int cb7210_request_system_control(struct gpib_board *board, int request_control) -{ - struct cb7210_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - - if (request_control) - priv->hs_mode_bits |= HS_SYS_CONTROL; - else - priv->hs_mode_bits &= ~HS_SYS_CONTROL; - - cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE); - return nec7210_request_system_control(board, nec_priv, request_control); -} - -static void cb7210_interface_clear(struct gpib_board *board, int assert) -{ - struct cb7210_priv *priv = board->private_data; - - nec7210_interface_clear(board, &priv->nec7210_priv, assert); -} - -static void cb7210_remote_enable(struct gpib_board *board, int enable) -{ - struct cb7210_priv *priv = board->private_data; - - nec7210_remote_enable(board, &priv->nec7210_priv, enable); -} - -static int cb7210_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); -} - -static void cb7210_disable_eos(struct gpib_board *board) -{ - struct cb7210_priv *priv = board->private_data; - - nec7210_disable_eos(board, &priv->nec7210_priv); -} - -static unsigned int cb7210_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); -} - -static int cb7210_primary_address(struct gpib_board *board, unsigned int address) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_primary_address(board, &priv->nec7210_priv, address); -} - -static int cb7210_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); -} - -static int cb7210_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_parallel_poll(board, &priv->nec7210_priv, result); -} - -static void cb7210_parallel_poll_configure(struct gpib_board *board, u8 configuration) -{ - struct cb7210_priv *priv = board->private_data; - - nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); -} - -static void cb7210_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct cb7210_priv *priv = board->private_data; - - nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); -} - -static void cb7210_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct cb7210_priv *priv = board->private_data; - - nec7210_serial_poll_response(board, &priv->nec7210_priv, status); -} - -static u8 cb7210_serial_poll_status(struct gpib_board *board) -{ - struct cb7210_priv *priv = board->private_data; - - return nec7210_serial_poll_status(board, &priv->nec7210_priv); -} - -static void cb7210_return_to_local(struct gpib_board *board) -{ - struct cb7210_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - - write_byte(nec_priv, AUX_RTL2, AUXMR); - udelay(1); - write_byte(nec_priv, AUX_RTL, AUXMR); -} - -static struct gpib_interface cb_pci_unaccel_interface = { - .name = "cbi_pci_unaccel", - .attach = cb_pci_attach, - .detach = cb_pci_detach, - .read = cb7210_read, - .write = cb7210_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static struct gpib_interface cb_pci_accel_interface = { - .name = "cbi_pci_accel", - .attach = cb_pci_attach, - .detach = cb_pci_detach, - .read = cb7210_accel_read, - .write = cb7210_accel_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static struct gpib_interface cb_pci_interface = { - .name = "cbi_pci", - .attach = cb_pci_attach, - .detach = cb_pci_detach, - .read = cb7210_accel_read, - .write = cb7210_accel_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static struct gpib_interface cb_isa_unaccel_interface = { - .name = "cbi_isa_unaccel", - .attach = cb_isa_attach, - .detach = cb_isa_detach, - .read = cb7210_read, - .write = cb7210_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static struct gpib_interface cb_isa_interface = { - .name = "cbi_isa", - .attach = cb_isa_attach, - .detach = cb_isa_detach, - .read = cb7210_accel_read, - .write = cb7210_accel_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static struct gpib_interface cb_isa_accel_interface = { - .name = "cbi_isa_accel", - .attach = cb_isa_attach, - .detach = cb_isa_detach, - .read = cb7210_accel_read, - .write = cb7210_accel_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static int cb7210_allocate_private(struct gpib_board *board) -{ - struct cb7210_priv *priv; - - board->private_data = kmalloc(sizeof(struct cb7210_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - priv = board->private_data; - memset(priv, 0, sizeof(struct cb7210_priv)); - init_nec7210_private(&priv->nec7210_priv); - return 0; -} - -static void cb7210_generic_detach(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -// generic part of attach functions shared by all cb7210 boards -static int cb7210_generic_attach(struct gpib_board *board) -{ - struct cb7210_priv *cb_priv; - struct nec7210_priv *nec_priv; - - board->status = 0; - - if (cb7210_allocate_private(board)) - return -ENOMEM; - cb_priv = board->private_data; - nec_priv = &cb_priv->nec7210_priv; - nec_priv->read_byte = nec7210_locking_ioport_read_byte; - nec_priv->write_byte = nec7210_locking_ioport_write_byte; - nec_priv->offset = cb7210_reg_offset; - nec_priv->type = CB7210; - return 0; -} - -static int cb7210_init(struct cb7210_priv *cb_priv, struct gpib_board *board) -{ - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - - cb7210_write_byte(cb_priv, HS_RESET7210, HS_INT_LEVEL); - cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL); - - nec7210_board_reset(nec_priv, board); - cb7210_write_byte(cb_priv, HS_TX_ENABLE | HS_RX_ENABLE | HS_CLR_SRQ_INT | - HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE); - - cb_priv->hs_mode_bits = HS_HF_INT_EN; - cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE); - - write_byte(nec_priv, AUX_LO_SPEED, AUXMR); - /* - * set clock register for maximum (20 MHz) driving frequency - * ICR should be set to clock in megahertz (1-15) and to zero - * for clocks faster than 15 MHz (max 20MHz) - */ - write_byte(nec_priv, ICR | 0, AUXMR); - - if (cb_priv->pci_chip == PCI_CHIP_QUANCOM) { - /* change interrupt polarity */ - nec_priv->auxb_bits |= HR_INV; - write_byte(nec_priv, nec_priv->auxb_bits, AUXMR); - } - nec7210_board_online(nec_priv, board); - - /* poll so we can detect assertion of ATN */ - if (gpib_request_pseudo_irq(board, cb_pci_interrupt)) { - pr_err("failed to allocate pseudo_irq\n"); - return -1; - } - return 0; -} - -static int cb_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct cb7210_priv *cb_priv; - struct nec7210_priv *nec_priv; - int isr_flags = 0; - int bits; - int retval; - - retval = cb7210_generic_attach(board); - if (retval) - return retval; - - cb_priv = board->private_data; - nec_priv = &cb_priv->nec7210_priv; - - cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS, - PCI_DEVICE_ID_CBOARDS_PCI_GPIB, NULL); - if (cb_priv->pci_device) - cb_priv->pci_chip = PCI_CHIP_AMCC_S5933; - if (!cb_priv->pci_device) { - cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS, - PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, NULL); - if (cb_priv->pci_device) - cb_priv->pci_chip = PCI_CHIP_AMCC_S5933; - } - if (!cb_priv->pci_device) { - cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_QUANCOM, - PCI_DEVICE_ID_QUANCOM_GPIB, NULL); - if (cb_priv->pci_device) { - cb_priv->pci_chip = PCI_CHIP_QUANCOM; - nec_priv->offset = 4; - } - } - if (!cb_priv->pci_device) { - dev_err(board->gpib_dev, "no supported boards found.\n"); - return -ENODEV; - } - - if (pci_enable_device(cb_priv->pci_device)) { - dev_err(board->gpib_dev, "error enabling pci device\n"); - return -EIO; - } - - if (pci_request_regions(cb_priv->pci_device, DRV_NAME)) - return -EBUSY; - switch (cb_priv->pci_chip) { - case PCI_CHIP_AMCC_S5933: - cb_priv->amcc_iobase = pci_resource_start(cb_priv->pci_device, 0); - nec_priv->iobase = pci_resource_start(cb_priv->pci_device, 1); - cb_priv->fifo_iobase = pci_resource_start(cb_priv->pci_device, 2); - break; - case PCI_CHIP_QUANCOM: - nec_priv->iobase = pci_resource_start(cb_priv->pci_device, 0); - cb_priv->fifo_iobase = nec_priv->iobase; - break; - default: - dev_err(board->gpib_dev, "bug! unhandled pci_chip=%i\n", cb_priv->pci_chip); - return -EIO; - } - isr_flags |= IRQF_SHARED; - if (request_irq(cb_priv->pci_device->irq, cb_pci_interrupt, isr_flags, DRV_NAME, board)) { - dev_err(board->gpib_dev, "can't request IRQ %d\n", - cb_priv->pci_device->irq); - return -EBUSY; - } - cb_priv->irq = cb_priv->pci_device->irq; - - switch (cb_priv->pci_chip) { - case PCI_CHIP_AMCC_S5933: - // make sure mailbox flags are clear - inl(cb_priv->amcc_iobase + INCOMING_MAILBOX_REG(3)); - // enable interrupts on amccs5933 chip - bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) | INBOX_SELECT_BITS(3) | - INBOX_INTR_CS_BIT; - outl(bits, cb_priv->amcc_iobase + INTCSR_REG); - break; - default: - break; - } - return cb7210_init(cb_priv, board); -} - -static void cb_pci_detach(struct gpib_board *board) -{ - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (cb_priv) { - gpib_free_pseudo_irq(board); - nec_priv = &cb_priv->nec7210_priv; - if (cb_priv->irq) { - // disable amcc interrupts - outl(0, cb_priv->amcc_iobase + INTCSR_REG); - free_irq(cb_priv->irq, board); - } - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - pci_release_regions(cb_priv->pci_device); - } - if (cb_priv->pci_device) - pci_dev_put(cb_priv->pci_device); - } - cb7210_generic_detach(board); -} - -static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - int isr_flags = 0; - struct cb7210_priv *cb_priv; - struct nec7210_priv *nec_priv; - unsigned int bits; - int retval; - - retval = cb7210_generic_attach(board); - if (retval) - return retval; - cb_priv = board->private_data; - nec_priv = &cb_priv->nec7210_priv; - if (!request_region(config->ibbase, cb7210_iosize, DRV_NAME)) { - dev_err(board->gpib_dev, "ioports starting at 0x%x are already in use\n", - config->ibbase); - return -EBUSY; - } - nec_priv->iobase = config->ibbase; - cb_priv->fifo_iobase = nec7210_iobase(cb_priv); - - bits = irq_bits(config->ibirq); - if (bits == 0) - dev_err(board->gpib_dev, "board incapable of using irq %i, try 2-5, 7, 10, or 11\n", - config->ibirq); - - // install interrupt handler - if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { - dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); - return -EBUSY; - } - cb_priv->irq = config->ibirq; - - return cb7210_init(cb_priv, board); -} - -static void cb_isa_detach(struct gpib_board *board) -{ - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (cb_priv) { - gpib_free_pseudo_irq(board); - nec_priv = &cb_priv->nec7210_priv; - if (cb_priv->irq) - free_irq(cb_priv->irq, board); - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - release_region(nec7210_iobase(cb_priv), cb7210_iosize); - } - } - cb7210_generic_detach(board); -} - -static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - return 0; -} - -static const struct pci_device_id cb7210_pci_table[] = { - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, cb7210_pci_table); - -static struct pci_driver cb7210_pci_driver = { - .name = DRV_NAME, - .id_table = cb7210_pci_table, - .probe = &cb7210_pci_probe -}; - -/*************************************************************************** - * Support for computer boards pcmcia-gpib card - * - * Based on gpib PCMCIA client driver written by Claus Schroeter - * (clausi@chemie.fu-berlin.de), which was adapted from the - * pcmcia skeleton example (presumably David Hinds) - ***************************************************************************/ - -#ifdef CONFIG_GPIB_PCMCIA - -#include -#include -#include -#include - -#include -#include - -/* - * The event() function is this driver's Card Services event handler. - * It will be called by Card Services when an appropriate card status - * event is received. The config() and release() entry points are - * used to configure or release a socket, in response to card insertion - * and ejection events. They are invoked from the gpib event - * handler. - */ - -static int cb_gpib_config(struct pcmcia_device *link); -static void cb_gpib_release(struct pcmcia_device *link); -static int cb_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config); -static void cb_pcmcia_detach(struct gpib_board *board); - -/* - * A linked list of "instances" of the gpib device. Each actual - * PCMCIA card corresponds to one device instance, and is described - * by one dev_link_t structure (defined in ds.h). - * - * You may not want to use a linked list for this -- for example, the - * memory card driver uses an array of dev_link_t pointers, where minor - * device numbers are used to derive the corresponding array index. - */ - -static struct pcmcia_device *curr_dev; - -/* - * A dev_link_t structure has fields for most things that are needed - * to keep track of a socket, but there will usually be some device - * specific information that also needs to be kept track of. The - * 'priv' pointer in a dev_link_t structure can be used to point to - * a device-specific private data structure, like this. - * - * A driver needs to provide a dev_node_t structure for each device - * on a card. In some cases, there is only one device per card (for - * example, ethernet cards, modems). In other cases, there may be - * many actual or logical devices (SCSI adapters, memory cards with - * multiple partitions). The dev_node_t structures need to be kept - * in a linked list starting at the 'dev' field of a dev_link_t - * structure. We allocate them in the card's private data structure, - * because they generally can't be allocated dynamically. - */ - -struct local_info { - struct pcmcia_device *p_dev; - struct gpib_board *dev; -}; - -/* - * gpib_attach() creates an "instance" of the driver, allocating - * local data structures for one device. The device is registered - * with Card Services. - * - * The dev_link structure is initialized, but we don't actually - * configure the card at this point -- we wait until we receive a - * card insertion event. - */ - -static int cb_gpib_probe(struct pcmcia_device *link) -{ - struct local_info *info; - int ret; - - /* Allocate space for private device-specific data */ - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->p_dev = link; - link->priv = info; - - /* The io structure describes IO port mapping */ - link->resource[0]->end = 16; - link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->resource[1]->end = 16; - link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; - link->io_lines = 10; - - /* General socket configuration */ - link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - link->config_index = 1; - link->config_regs = PRESENT_OPTION; - - /* Register with Card Services */ - curr_dev = link; - ret = cb_gpib_config(link); - if (ret) - goto free_info; - - return 0; - -free_info: - kfree(info); - return ret; -} - -/* - * This deletes a driver "instance". The device is de-registered - * with Card Services. If it has been released, all local data - * structures are freed. Otherwise, the structures will be freed - * when the device is released. - */ - -static void cb_gpib_remove(struct pcmcia_device *link) -{ - struct local_info *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - if (info->dev) - cb_pcmcia_detach(info->dev); - cb_gpib_release(link); - - //free_netdev(dev); - kfree(info); -} - -static int cb_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) -{ - return pcmcia_request_io(link); -} - -/* - * gpib_config() is scheduled to run after a CARD_INSERTION event - * is received, to configure the PCMCIA socket, and to make the - * ethernet device available to the system. - */ - -static int cb_gpib_config(struct pcmcia_device *link) -{ - int retval; - - retval = pcmcia_loop_config(link, &cb_gpib_config_iteration, NULL); - if (retval) { - dev_warn(&link->dev, "no configuration found\n"); - cb_gpib_release(link); - return -ENODEV; - } - - /* - * This actually configures the PCMCIA socket -- setting up - * the I/O windows and the interrupt mapping. - */ - retval = pcmcia_enable_device(link); - if (retval) { - dev_warn(&link->dev, "pcmcia_enable_device failed\n"); - cb_gpib_release(link); - return -ENODEV; - } - - return 0; -} /* gpib_config */ - -/* - * After a card is removed, gpib_release() will unregister the net - * device, and release the PCMCIA configuration. If the device is - * still open, this will be postponed until it is closed. - */ - -static void cb_gpib_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int cb_gpib_suspend(struct pcmcia_device *link) -{ - //struct local_info *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - if (link->open) - dev_warn(&link->dev, "Device still open\n"); - //netif_device_detach(dev); - - return 0; -} - -static int cb_gpib_resume(struct pcmcia_device *link) -{ - //struct local_info *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - /*if (link->open) { - * ni_gpib_probe(dev); / really? - * //netif_device_attach(dev); - * - */ - return cb_gpib_config(link); -} - -/*====================================================================*/ - -static struct pcmcia_device_id cb_pcmcia_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0005), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, cb_pcmcia_ids); - -static struct pcmcia_driver cb_gpib_cs_driver = { - .name = "cb_gpib_cs", - .owner = THIS_MODULE, - .id_table = cb_pcmcia_ids, - .probe = cb_gpib_probe, - .remove = cb_gpib_remove, - .suspend = cb_gpib_suspend, - .resume = cb_gpib_resume, -}; - -static void cb_pcmcia_cleanup_module(void) -{ - pcmcia_unregister_driver(&cb_gpib_cs_driver); -} - -static struct gpib_interface cb_pcmcia_unaccel_interface = { - .name = "cbi_pcmcia_unaccel", - .attach = cb_pcmcia_attach, - .detach = cb_pcmcia_detach, - .read = cb7210_read, - .write = cb7210_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static struct gpib_interface cb_pcmcia_interface = { - .name = "cbi_pcmcia", - .attach = cb_pcmcia_attach, - .detach = cb_pcmcia_detach, - .read = cb7210_accel_read, - .write = cb7210_accel_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static struct gpib_interface cb_pcmcia_accel_interface = { - .name = "cbi_pcmcia_accel", - .attach = cb_pcmcia_attach, - .detach = cb_pcmcia_detach, - .read = cb7210_accel_read, - .write = cb7210_accel_write, - .command = cb7210_command, - .take_control = cb7210_take_control, - .go_to_standby = cb7210_go_to_standby, - .request_system_control = cb7210_request_system_control, - .interface_clear = cb7210_interface_clear, - .remote_enable = cb7210_remote_enable, - .enable_eos = cb7210_enable_eos, - .disable_eos = cb7210_disable_eos, - .parallel_poll = cb7210_parallel_poll, - .parallel_poll_configure = cb7210_parallel_poll_configure, - .parallel_poll_response = cb7210_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = cb7210_line_status, - .update_status = cb7210_update_status, - .primary_address = cb7210_primary_address, - .secondary_address = cb7210_secondary_address, - .serial_poll_response = cb7210_serial_poll_response, - .serial_poll_status = cb7210_serial_poll_status, - .t1_delay = cb7210_t1_delay, - .return_to_local = cb7210_return_to_local, -}; - -static int cb_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct cb7210_priv *cb_priv; - struct nec7210_priv *nec_priv; - int retval; - - if (!curr_dev) { - dev_err(board->gpib_dev, "no cb pcmcia cards found\n"); - return -ENODEV; - } - - retval = cb7210_generic_attach(board); - if (retval) - return retval; - - cb_priv = board->private_data; - nec_priv = &cb_priv->nec7210_priv; - - if (!request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]), - DRV_NAME)) { - dev_err(board->gpib_dev, "ioports starting at 0x%lx are already in use\n", - (unsigned long)curr_dev->resource[0]->start); - return -EBUSY; - } - nec_priv->iobase = curr_dev->resource[0]->start; - cb_priv->fifo_iobase = curr_dev->resource[0]->start; - - if (request_irq(curr_dev->irq, cb7210_interrupt, IRQF_SHARED, DRV_NAME, board)) { - dev_err(board->gpib_dev, "failed to request IRQ %d\n", curr_dev->irq); - return -EBUSY; - } - cb_priv->irq = curr_dev->irq; - - return cb7210_init(cb_priv, board); -} - -static void cb_pcmcia_detach(struct gpib_board *board) -{ - struct cb7210_priv *cb_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (cb_priv) { - nec_priv = &cb_priv->nec7210_priv; - gpib_free_pseudo_irq(board); - if (cb_priv->irq) - free_irq(cb_priv->irq, board); - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - release_region(nec7210_iobase(cb_priv), cb7210_iosize); - } - } - cb7210_generic_detach(board); -} - -#endif /* CONFIG_GPIB_PCMCIA */ - -static int __init cb7210_init_module(void) -{ - int ret; - - ret = pci_register_driver(&cb7210_pci_driver); - if (ret) { - pr_err("pci_register_driver failed: error = %d\n", ret); - return ret; - } - - ret = gpib_register_driver(&cb_pci_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pci; - } - - ret = gpib_register_driver(&cb_isa_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_isa; - } - - ret = gpib_register_driver(&cb_pci_accel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pci_accel; - } - - ret = gpib_register_driver(&cb_pci_unaccel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pci_unaccel; - } - - ret = gpib_register_driver(&cb_isa_accel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_isa_accel; - } - - ret = gpib_register_driver(&cb_isa_unaccel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_isa_unaccel; - } - -#ifdef CONFIG_GPIB_PCMCIA - ret = gpib_register_driver(&cb_pcmcia_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pcmcia; - } - - ret = gpib_register_driver(&cb_pcmcia_accel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pcmcia_accel; - } - - ret = gpib_register_driver(&cb_pcmcia_unaccel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pcmcia_unaccel; - } - - ret = pcmcia_register_driver(&cb_gpib_cs_driver); - if (ret) { - pr_err("pcmcia_register_driver failed: error = %d\n", ret); - goto err_pcmcia_driver; - } -#endif - - return 0; - -#ifdef CONFIG_GPIB_PCMCIA -err_pcmcia_driver: - gpib_unregister_driver(&cb_pcmcia_unaccel_interface); -err_pcmcia_unaccel: - gpib_unregister_driver(&cb_pcmcia_accel_interface); -err_pcmcia_accel: - gpib_unregister_driver(&cb_pcmcia_interface); -err_pcmcia: -#endif - gpib_unregister_driver(&cb_isa_unaccel_interface); -err_isa_unaccel: - gpib_unregister_driver(&cb_isa_accel_interface); -err_isa_accel: - gpib_unregister_driver(&cb_pci_unaccel_interface); -err_pci_unaccel: - gpib_unregister_driver(&cb_pci_accel_interface); -err_pci_accel: - gpib_unregister_driver(&cb_isa_interface); -err_isa: - gpib_unregister_driver(&cb_pci_interface); -err_pci: - pci_unregister_driver(&cb7210_pci_driver); - - return ret; -} - -static void __exit cb7210_exit_module(void) -{ - gpib_unregister_driver(&cb_pci_interface); - gpib_unregister_driver(&cb_isa_interface); - gpib_unregister_driver(&cb_pci_accel_interface); - gpib_unregister_driver(&cb_pci_unaccel_interface); - gpib_unregister_driver(&cb_isa_accel_interface); - gpib_unregister_driver(&cb_isa_unaccel_interface); -#ifdef CONFIG_GPIB_PCMCIA - gpib_unregister_driver(&cb_pcmcia_interface); - gpib_unregister_driver(&cb_pcmcia_accel_interface); - gpib_unregister_driver(&cb_pcmcia_unaccel_interface); - cb_pcmcia_cleanup_module(); -#endif - - pci_unregister_driver(&cb7210_pci_driver); -} - -module_init(cb7210_init_module); -module_exit(cb7210_exit_module); diff --git a/drivers/staging/gpib/cb7210/cb7210.h b/drivers/staging/gpib/cb7210/cb7210.h deleted file mode 100644 index ddc841ff87ae..000000000000 --- a/drivers/staging/gpib/cb7210/cb7210.h +++ /dev/null @@ -1,203 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#include "nec7210.h" -#include "gpibP.h" -#include "amccs5933.h" - -#include -#include - -enum { - PCI_DEVICE_ID_CBOARDS_PCI_GPIB = 0x6, - PCI_DEVICE_ID_CBOARDS_CPCI_GPIB = 0xe, -}; - -enum pci_chip { - PCI_CHIP_NONE = 0, - PCI_CHIP_AMCC_S5933, - PCI_CHIP_QUANCOM -}; - -// struct which defines private_data for cb7210 boards -struct cb7210_priv { - struct nec7210_priv nec7210_priv; - struct pci_dev *pci_device; - // base address of amccs5933 pci chip - unsigned long amcc_iobase; - unsigned long fifo_iobase; - unsigned int irq; - enum pci_chip pci_chip; - u8 hs_mode_bits; - unsigned out_fifo_half_empty : 1; - unsigned in_fifo_half_full : 1; -}; - -// pci-gpib register offset -static const int cb7210_reg_offset = 1; - -// uses 10 ioports -static const int cb7210_iosize = 10; - -// fifo size in bytes -static const int cb7210_fifo_size = 2048; -static const int cb7210_fifo_width = 2; - -// cb7210 specific registers and bits -enum cb7210_regs { - BUS_STATUS = 0x7, -}; - -enum cb7210_page_in { - BUS_STATUS_PAGE = 1, -}; - -enum hs_regs { - // write registers - HS_MODE = 0x8, /* HS_MODE register */ - HS_INT_LEVEL = 0x9, /* HS_INT_LEVEL register */ - // read registers - HS_STATUS = 0x8, /* HS_STATUS register */ -}; - -static inline u32 nec7210_iobase(const struct cb7210_priv *cb_priv) -{ - return cb_priv->nec7210_priv.iobase; -} - -static inline int cb7210_page_in_bits(unsigned int page) -{ - return 0x50 | (page & 0xf); -} - -static inline u8 cb7210_paged_read_byte(struct cb7210_priv *cb_priv, - unsigned int register_num, unsigned int page) -{ - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - u8 retval; - unsigned long flags; - - spin_lock_irqsave(&nec_priv->register_page_lock, flags); - outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset); - udelay(1); - retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset); - spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); - return retval; -} - -// don't use for register_num < 8, since it doesn't lock -static inline u8 cb7210_read_byte(const struct cb7210_priv *cb_priv, - enum hs_regs register_num) -{ - const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - u8 retval; - - retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset); - return retval; -} - -static inline void cb7210_paged_write_byte(struct cb7210_priv *cb_priv, u8 data, - unsigned int register_num, unsigned int page) -{ - struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - unsigned long flags; - - spin_lock_irqsave(&nec_priv->register_page_lock, flags); - outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset); - udelay(1); - outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset); - spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); -} - -// don't use for register_num < 8, since it doesn't lock -static inline void cb7210_write_byte(const struct cb7210_priv *cb_priv, u8 data, - enum hs_regs register_num) -{ - const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv; - - outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset); -} - -enum bus_status_bits { - BSR_ATN_BIT = 0x1, - BSR_EOI_BIT = 0x2, - BSR_SRQ_BIT = 0x4, - BSR_IFC_BIT = 0x8, - BSR_REN_BIT = 0x10, - BSR_DAV_BIT = 0x20, - BSR_NRFD_BIT = 0x40, - BSR_NDAC_BIT = 0x80, -}; - -/* CBI 488.2 HS control */ - -/* - * when both bit 0 and 1 are set, it - * 1 clears the transmit state machine to an initial condition - * 2 clears any residual interrupts left latched on cbi488.2 - * 3 resets all control bits in HS_MODE to zero - * 4 enables TX empty interrupts - * when both bit 0 and 1 are zero, then the high speed mode is disabled - */ -enum hs_mode_bits { - HS_ENABLE_MASK = 0x3, - HS_TX_ENABLE = (1 << 0), - HS_RX_ENABLE = (1 << 1), - HS_HF_INT_EN = (1 << 3), - HS_CLR_SRQ_INT = (1 << 4), - HS_CLR_EOI_EMPTY_INT = (1 << 5), - HS_CLR_HF_INT = (1 << 6), - HS_SYS_CONTROL = (1 << 7), -}; - -/* CBI 488.2 status */ -enum hs_status_bits { - HS_FIFO_FULL = (1 << 0), - HS_HALF_FULL = (1 << 1), - HS_SRQ_INT = (1 << 2), - HS_EOI_INT = (1 << 3), - HS_TX_MSB_NOT_EMPTY = (1 << 4), - HS_RX_MSB_NOT_EMPTY = (1 << 5), - HS_TX_LSB_NOT_EMPTY = (1 << 6), - HS_RX_LSB_NOT_EMPTY = (1 << 7), -}; - -/* CBI488.2 hs_int_level register */ -enum hs_int_level_bits { - HS_RESET7210 = (1 << 7), -}; - -static inline unsigned int irq_bits(unsigned int irq) -{ - switch (irq) { - case 2: - case 3: - case 4: - case 5: - return irq - 1; - case 7: - return 0x5; - case 10: - return 0x6; - case 11: - return 0x7; - default: - return 0; - } -} - -enum cb7210_aux_cmds { -/* - * AUX_RTL2 is an undocumented aux command which causes cb7210 to assert - * (and keep asserted) local rtl message. This is used in conjunction - * with the (stupid) cb7210 implementation - * of the normal nec7210 AUX_RTL aux command, which - * causes the rtl message to toggle between on and off. - */ - AUX_RTL2 = 0xd, - AUX_LO_SPEED = 0x40, - AUX_HI_SPEED = 0x41, -}; diff --git a/drivers/staging/gpib/cec/Makefile b/drivers/staging/gpib/cec/Makefile deleted file mode 100644 index b7141e23d4e0..000000000000 --- a/drivers/staging/gpib/cec/Makefile +++ /dev/null @@ -1,3 +0,0 @@ - -obj-$(CONFIG_GPIB_CEC_PCI) += cec_gpib.o - diff --git a/drivers/staging/gpib/cec/cec.h b/drivers/staging/gpib/cec/cec.h deleted file mode 100644 index 3ce2869c7429..000000000000 --- a/drivers/staging/gpib/cec/cec.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#include "nec7210.h" -#include "gpibP.h" -#include "plx9050.h" - -struct cec_priv { - struct nec7210_priv nec7210_priv; - struct pci_dev *pci_device; - // base address for plx9052 pci chip - unsigned long plx_iobase; - unsigned int irq; -}; - -// offset between consecutive nec7210 registers -static const int cec_reg_offset = 1; diff --git a/drivers/staging/gpib/cec/cec_gpib.c b/drivers/staging/gpib/cec/cec_gpib.c deleted file mode 100644 index dbf9b95baabc..000000000000 --- a/drivers/staging/gpib/cec/cec_gpib.c +++ /dev/null @@ -1,393 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "cec.h" -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for CEC PCI and PCMCIA boards"); - -/* - * GPIB interrupt service routines - */ - -static irqreturn_t cec_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - struct cec_priv *priv = board->private_data; - unsigned long flags; - irqreturn_t retval; - - spin_lock_irqsave(&board->spinlock, flags); - retval = nec7210_interrupt(board, &priv->nec7210_priv); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -#define CEC_VENDOR_ID 0x12fc -#define CEC_DEV_ID 0x5cec -#define CEC_SUBID 0x9050 - -static int cec_pci_attach(struct gpib_board *board, const struct gpib_board_config *config); - -static void cec_pci_detach(struct gpib_board *board); - -// wrappers for interface functions -static int cec_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); -} - -static int cec_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); -} - -static int cec_command(struct gpib_board *board, u8 *buffer, - size_t length, size_t *bytes_written) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); -} - -static int cec_take_control(struct gpib_board *board, int synchronous) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_take_control(board, &priv->nec7210_priv, synchronous); -} - -static int cec_go_to_standby(struct gpib_board *board) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_go_to_standby(board, &priv->nec7210_priv); -} - -static int cec_request_system_control(struct gpib_board *board, int request_control) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_request_system_control(board, &priv->nec7210_priv, request_control); -} - -static void cec_interface_clear(struct gpib_board *board, int assert) -{ - struct cec_priv *priv = board->private_data; - - nec7210_interface_clear(board, &priv->nec7210_priv, assert); -} - -static void cec_remote_enable(struct gpib_board *board, int enable) -{ - struct cec_priv *priv = board->private_data; - - nec7210_remote_enable(board, &priv->nec7210_priv, enable); -} - -static int cec_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); -} - -static void cec_disable_eos(struct gpib_board *board) -{ - struct cec_priv *priv = board->private_data; - - nec7210_disable_eos(board, &priv->nec7210_priv); -} - -static unsigned int cec_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); -} - -static int cec_primary_address(struct gpib_board *board, unsigned int address) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_primary_address(board, &priv->nec7210_priv, address); -} - -static int cec_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); -} - -static int cec_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_parallel_poll(board, &priv->nec7210_priv, result); -} - -static void cec_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - struct cec_priv *priv = board->private_data; - - nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); -} - -static void cec_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct cec_priv *priv = board->private_data; - - nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); -} - -static void cec_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct cec_priv *priv = board->private_data; - - nec7210_serial_poll_response(board, &priv->nec7210_priv, status); -} - -static u8 cec_serial_poll_status(struct gpib_board *board) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_serial_poll_status(board, &priv->nec7210_priv); -} - -static int cec_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct cec_priv *priv = board->private_data; - - return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec); -} - -static void cec_return_to_local(struct gpib_board *board) -{ - struct cec_priv *priv = board->private_data; - - nec7210_return_to_local(board, &priv->nec7210_priv); -} - -static struct gpib_interface cec_pci_interface = { - .name = "cec_pci", - .attach = cec_pci_attach, - .detach = cec_pci_detach, - .read = cec_read, - .write = cec_write, - .command = cec_command, - .take_control = cec_take_control, - .go_to_standby = cec_go_to_standby, - .request_system_control = cec_request_system_control, - .interface_clear = cec_interface_clear, - .remote_enable = cec_remote_enable, - .enable_eos = cec_enable_eos, - .disable_eos = cec_disable_eos, - .parallel_poll = cec_parallel_poll, - .parallel_poll_configure = cec_parallel_poll_configure, - .parallel_poll_response = cec_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = NULL, // XXX - .update_status = cec_update_status, - .primary_address = cec_primary_address, - .secondary_address = cec_secondary_address, - .serial_poll_response = cec_serial_poll_response, - .serial_poll_status = cec_serial_poll_status, - .t1_delay = cec_t1_delay, - .return_to_local = cec_return_to_local, -}; - -static int cec_allocate_private(struct gpib_board *board) -{ - struct cec_priv *priv; - - board->private_data = kmalloc(sizeof(struct cec_priv), GFP_KERNEL); - if (!board->private_data) - return -1; - priv = board->private_data; - memset(priv, 0, sizeof(struct cec_priv)); - init_nec7210_private(&priv->nec7210_priv); - return 0; -} - -static void cec_free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static int cec_generic_attach(struct gpib_board *board) -{ - struct cec_priv *cec_priv; - struct nec7210_priv *nec_priv; - - board->status = 0; - - if (cec_allocate_private(board)) - return -ENOMEM; - cec_priv = board->private_data; - nec_priv = &cec_priv->nec7210_priv; - nec_priv->read_byte = nec7210_ioport_read_byte; - nec_priv->write_byte = nec7210_ioport_write_byte; - nec_priv->offset = cec_reg_offset; - nec_priv->type = NEC7210; // guess - return 0; -} - -static void cec_init(struct cec_priv *cec_priv, const struct gpib_board *board) -{ - struct nec7210_priv *nec_priv = &cec_priv->nec7210_priv; - - nec7210_board_reset(nec_priv, board); - - /* set internal counter register for 8 MHz input clock */ - write_byte(nec_priv, ICR | 8, AUXMR); - - nec7210_board_online(nec_priv, board); -} - -static int cec_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct cec_priv *cec_priv; - struct nec7210_priv *nec_priv; - int isr_flags = 0; - int retval; - - retval = cec_generic_attach(board); - if (retval) - return retval; - - cec_priv = board->private_data; - nec_priv = &cec_priv->nec7210_priv; - - // find board - cec_priv->pci_device = NULL; - while ((cec_priv->pci_device = - gpib_pci_get_device(config, CEC_VENDOR_ID, - CEC_DEV_ID, cec_priv->pci_device))) { - // check for board with plx9050 controller - if (cec_priv->pci_device->subsystem_device == CEC_SUBID) - break; - } - if (!cec_priv->pci_device) { - dev_err(board->gpib_dev, "no cec PCI board found\n"); - return -ENODEV; - } - - if (pci_enable_device(cec_priv->pci_device)) { - dev_err(board->gpib_dev, "error enabling pci device\n"); - return -EIO; - } - - if (pci_request_regions(cec_priv->pci_device, "cec-gpib")) - return -EBUSY; - - cec_priv->plx_iobase = pci_resource_start(cec_priv->pci_device, 1); - nec_priv->iobase = pci_resource_start(cec_priv->pci_device, 3); - - isr_flags |= IRQF_SHARED; - if (request_irq(cec_priv->pci_device->irq, cec_interrupt, isr_flags, DRV_NAME, board)) { - dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", cec_priv->pci_device->irq); - return -EBUSY; - } - cec_priv->irq = cec_priv->pci_device->irq; - if (gpib_request_pseudo_irq(board, cec_interrupt)) { - dev_err(board->gpib_dev, "failed to allocate pseudo irq\n"); - return -1; - } - cec_init(cec_priv, board); - - // enable interrupts on plx chip - outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT, - cec_priv->plx_iobase + PLX9050_INTCSR_REG); - - return 0; -} - -static void cec_pci_detach(struct gpib_board *board) -{ - struct cec_priv *cec_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (cec_priv) { - nec_priv = &cec_priv->nec7210_priv; - gpib_free_pseudo_irq(board); - if (cec_priv->irq) { - // disable plx9050 interrupts - outl(0, cec_priv->plx_iobase + PLX9050_INTCSR_REG); - free_irq(cec_priv->irq, board); - } - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - pci_release_regions(cec_priv->pci_device); - } - if (cec_priv->pci_device) - pci_dev_put(cec_priv->pci_device); - } - cec_free_private(board); -} - -static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - return 0; -} - -static const struct pci_device_id cec_pci_table[] = { - {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 }, - {0} -}; -MODULE_DEVICE_TABLE(pci, cec_pci_table); - -static struct pci_driver cec_pci_driver = { - .name = DRV_NAME, - .id_table = cec_pci_table, - .probe = &cec_pci_probe -}; - -static int __init cec_init_module(void) -{ - int result; - - result = pci_register_driver(&cec_pci_driver); - if (result) { - pr_err("pci_register_driver failed: error = %d\n", result); - return result; - } - - result = gpib_register_driver(&cec_pci_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - return result; - } - - return 0; -} - -static void cec_exit_module(void) -{ - gpib_unregister_driver(&cec_pci_interface); - - pci_unregister_driver(&cec_pci_driver); -} - -module_init(cec_init_module); -module_exit(cec_exit_module); diff --git a/drivers/staging/gpib/common/Makefile b/drivers/staging/gpib/common/Makefile deleted file mode 100644 index 460586edb574..000000000000 --- a/drivers/staging/gpib/common/Makefile +++ /dev/null @@ -1,6 +0,0 @@ - -obj-$(CONFIG_GPIB_COMMON) += gpib_common.o - -gpib_common-objs := gpib_os.o iblib.o - - diff --git a/drivers/staging/gpib/common/gpib_os.c b/drivers/staging/gpib/common/gpib_os.c deleted file mode 100644 index 9dbbac8b8436..000000000000 --- a/drivers/staging/gpib/common/gpib_os.c +++ /dev/null @@ -1,2271 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2001, 2004 by Frank Mori Hess - *************************************************************************** - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt - -#include "ibsys.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB base support"); -MODULE_ALIAS_CHARDEV_MAJOR(GPIB_CODE); - -static int board_type_ioctl(struct gpib_file_private *file_priv, - struct gpib_board *board, unsigned long arg); -static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, - unsigned long arg); -static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, - unsigned long arg); -static int command_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, - unsigned long arg); -static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg); -static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg); -static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg); -static int wait_ioctl(struct gpib_file_private *file_priv, - struct gpib_board *board, unsigned long arg); -static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg); -static int online_ioctl(struct gpib_board *board, unsigned long arg); -static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg); -static int take_control_ioctl(struct gpib_board *board, unsigned long arg); -static int line_status_ioctl(struct gpib_board *board, unsigned long arg); -static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg); -static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg); -static int eos_ioctl(struct gpib_board *board, unsigned long arg); -static int request_service_ioctl(struct gpib_board *board, unsigned long arg); -static int request_service2_ioctl(struct gpib_board *board, unsigned long arg); -static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg); -static int irq_ioctl(struct gpib_board_config *config, unsigned long arg); -static int dma_ioctl(struct gpib_board_config *config, unsigned long arg); -static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg); -static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg); -static int timeout_ioctl(struct gpib_board *board, unsigned long arg); -static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg); -static int board_info_ioctl(const struct gpib_board *board, unsigned long arg); -static int ppc_ioctl(struct gpib_board *board, unsigned long arg); -static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg); -static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg); -static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg); -static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg); -static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg); -static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg); -static int event_ioctl(struct gpib_board *board, unsigned long arg); -static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg); -static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg); - -static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board); - -static int pop_gpib_event_nolock(struct gpib_board *board, - struct gpib_event_queue *queue, short *event_type); - -/* - * Timer functions - */ - -/* Watchdog timeout routine */ - -static void watchdog_timeout(struct timer_list *t) -{ - struct gpib_board *board = timer_container_of(board, t, timer); - - set_bit(TIMO_NUM, &board->status); - wake_up_interruptible(&board->wait); -} - -/* install timer interrupt handler */ -void os_start_timer(struct gpib_board *board, unsigned int usec_timeout) -/* Starts the timeout task */ -{ - if (timer_pending(&board->timer)) { - dev_err(board->gpib_dev, "bug! timer already running?\n"); - return; - } - clear_bit(TIMO_NUM, &board->status); - - if (usec_timeout > 0) { - board->timer.function = watchdog_timeout; - /* set number of ticks */ - mod_timer(&board->timer, jiffies + usec_to_jiffies(usec_timeout)); - } -} - -void os_remove_timer(struct gpib_board *board) -/* Removes the timeout task */ -{ - if (timer_pending(&board->timer)) - timer_delete_sync(&board->timer); -} - -int io_timed_out(struct gpib_board *board) -{ - if (test_bit(TIMO_NUM, &board->status)) - return 1; - return 0; -} - -/* - * this is a function instead of a constant because of Suse - * defining HZ to be a function call to get_hz() - */ -static inline int pseudo_irq_period(void) -{ - return (HZ + 99) / 100; -} - -static void pseudo_irq_handler(struct timer_list *t) -{ - struct gpib_pseudo_irq *pseudo_irq = timer_container_of(pseudo_irq, t, - timer); - - if (pseudo_irq->handler) - pseudo_irq->handler(0, pseudo_irq->board); - else - pr_err("gpib: bug! pseudo_irq.handler is NULL\n"); - - if (atomic_read(&pseudo_irq->active)) - mod_timer(&pseudo_irq->timer, jiffies + pseudo_irq_period()); -} - -int gpib_request_pseudo_irq(struct gpib_board *board, irqreturn_t (*handler)(int, void *)) -{ - if (timer_pending(&board->pseudo_irq.timer) || board->pseudo_irq.handler) { - dev_err(board->gpib_dev, "only one pseudo interrupt per board allowed\n"); - return -1; - } - - board->pseudo_irq.handler = handler; - board->pseudo_irq.timer.function = pseudo_irq_handler; - board->pseudo_irq.board = board; - - atomic_set(&board->pseudo_irq.active, 1); - - mod_timer(&board->pseudo_irq.timer, jiffies + pseudo_irq_period()); - - return 0; -} -EXPORT_SYMBOL(gpib_request_pseudo_irq); - -void gpib_free_pseudo_irq(struct gpib_board *board) -{ - atomic_set(&board->pseudo_irq.active, 0); - - timer_delete_sync(&board->pseudo_irq.timer); - board->pseudo_irq.handler = NULL; -} -EXPORT_SYMBOL(gpib_free_pseudo_irq); - -static const unsigned int serial_timeout = 1000000; - -unsigned int num_status_bytes(const struct gpib_status_queue *dev) -{ - if (!dev) - return 0; - return dev->num_status_bytes; -} - -// push status byte onto back of status byte fifo -int push_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 poll_byte) -{ - struct list_head *head = &device->status_bytes; - struct gpib_status_byte *status; - static const unsigned int max_num_status_bytes = 1024; - int retval; - - if (num_status_bytes(device) >= max_num_status_bytes) { - u8 lost_byte; - - device->dropped_byte = 1; - retval = pop_status_byte(board, device, &lost_byte); - if (retval < 0) - return retval; - } - - status = kmalloc(sizeof(*status), GFP_KERNEL); - if (!status) - return -ENOMEM; - - INIT_LIST_HEAD(&status->list); - status->poll_byte = poll_byte; - - list_add_tail(&status->list, head); - - device->num_status_bytes++; - - dev_dbg(board->gpib_dev, "pushed status byte 0x%x, %i in queue\n", - (int)poll_byte, num_status_bytes(device)); - - return 0; -} - -// pop status byte from front of status byte fifo -int pop_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 *poll_byte) -{ - struct list_head *head = &device->status_bytes; - struct list_head *front = head->next; - struct gpib_status_byte *status; - - if (num_status_bytes(device) == 0) - return -EIO; - - if (front == head) - return -EIO; - - if (device->dropped_byte) { - device->dropped_byte = 0; - return -EPIPE; - } - - status = list_entry(front, struct gpib_status_byte, list); - *poll_byte = status->poll_byte; - - list_del(front); - kfree(status); - - device->num_status_bytes--; - - dev_dbg(board->gpib_dev, "popped status byte 0x%x, %i in queue\n", - (int)*poll_byte, num_status_bytes(device)); - - return 0; -} - -struct gpib_status_queue *get_gpib_status_queue(struct gpib_board *board, unsigned int pad, int sad) -{ - struct gpib_status_queue *device; - struct list_head *list_ptr; - const struct list_head *head = &board->device_list; - - for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { - device = list_entry(list_ptr, struct gpib_status_queue, list); - if (gpib_address_equal(device->pad, device->sad, pad, sad)) - return device; - } - - return NULL; -} - -int get_serial_poll_byte(struct gpib_board *board, unsigned int pad, int sad, - unsigned int usec_timeout, u8 *poll_byte) -{ - struct gpib_status_queue *device; - - device = get_gpib_status_queue(board, pad, sad); - if (num_status_bytes(device)) - return pop_status_byte(board, device, poll_byte); - else - return dvrsp(board, pad, sad, usec_timeout, poll_byte); -} - -int autopoll_all_devices(struct gpib_board *board) -{ - int retval; - - if (mutex_lock_interruptible(&board->user_mutex)) - return -ERESTARTSYS; - if (mutex_lock_interruptible(&board->big_gpib_mutex)) { - mutex_unlock(&board->user_mutex); - return -ERESTARTSYS; - } - - dev_dbg(board->gpib_dev, "autopoll has board lock\n"); - - retval = serial_poll_all(board, serial_timeout); - if (retval < 0) { - mutex_unlock(&board->big_gpib_mutex); - mutex_unlock(&board->user_mutex); - return retval; - } - - dev_dbg(board->gpib_dev, "complete\n"); - /* - * need to wake wait queue in case someone is - * waiting on RQS - */ - wake_up_interruptible(&board->wait); - mutex_unlock(&board->big_gpib_mutex); - mutex_unlock(&board->user_mutex); - - return retval; -} - -static int setup_serial_poll(struct gpib_board *board, unsigned int usec_timeout) -{ - u8 cmd_string[8]; - int i; - size_t bytes_written; - int ret; - - os_start_timer(board, usec_timeout); - ret = ibcac(board, 1, 1); - if (ret < 0) { - os_remove_timer(board); - return ret; - } - - i = 0; - cmd_string[i++] = UNL; - cmd_string[i++] = MLA(board->pad); /* controller's listen address */ - if (board->sad >= 0) - cmd_string[i++] = MSA(board->sad); - cmd_string[i++] = SPE; // serial poll enable - - ret = board->interface->command(board, cmd_string, i, &bytes_written); - if (ret < 0 || bytes_written < i) { - dev_dbg(board->gpib_dev, "failed to setup serial poll\n"); - os_remove_timer(board); - return -EIO; - } - os_remove_timer(board); - - return 0; -} - -static int read_serial_poll_byte(struct gpib_board *board, unsigned int pad, - int sad, unsigned int usec_timeout, u8 *result) -{ - u8 cmd_string[8]; - int end_flag; - int ret; - int i; - size_t nbytes; - - dev_dbg(board->gpib_dev, "entering pad=%i sad=%i\n", pad, sad); - - os_start_timer(board, usec_timeout); - ret = ibcac(board, 1, 1); - if (ret < 0) { - os_remove_timer(board); - return ret; - } - - i = 0; - // send talk address - cmd_string[i++] = MTA(pad); - if (sad >= 0) - cmd_string[i++] = MSA(sad); - - ret = board->interface->command(board, cmd_string, i, &nbytes); - if (ret < 0 || nbytes < i) { - dev_err(board->gpib_dev, "failed to setup serial poll\n"); - os_remove_timer(board); - return -EIO; - } - - ibgts(board); - - // read poll result - ret = board->interface->read(board, result, 1, &end_flag, &nbytes); - if (ret < 0 || nbytes < 1) { - dev_err(board->gpib_dev, "serial poll failed\n"); - os_remove_timer(board); - return -EIO; - } - os_remove_timer(board); - - return 0; -} - -static int cleanup_serial_poll(struct gpib_board *board, unsigned int usec_timeout) -{ - u8 cmd_string[8]; - int ret; - size_t bytes_written; - - os_start_timer(board, usec_timeout); - ret = ibcac(board, 1, 1); - if (ret < 0) { - os_remove_timer(board); - return ret; - } - - cmd_string[0] = SPD; /* disable serial poll bytes */ - cmd_string[1] = UNT; - ret = board->interface->command(board, cmd_string, 2, &bytes_written); - if (ret < 0 || bytes_written < 2) { - dev_err(board->gpib_dev, "failed to disable serial poll\n"); - os_remove_timer(board); - return -EIO; - } - os_remove_timer(board); - - return 0; -} - -static int serial_poll_single(struct gpib_board *board, unsigned int pad, int sad, - unsigned int usec_timeout, u8 *result) -{ - int retval, cleanup_retval; - - retval = setup_serial_poll(board, usec_timeout); - if (retval < 0) - return retval; - retval = read_serial_poll_byte(board, pad, sad, usec_timeout, result); - cleanup_retval = cleanup_serial_poll(board, usec_timeout); - if (retval < 0) - return retval; - if (cleanup_retval < 0) - return retval; - - return 0; -} - -int serial_poll_all(struct gpib_board *board, unsigned int usec_timeout) -{ - int retval = 0; - struct list_head *cur; - const struct list_head *head = NULL; - struct gpib_status_queue *device; - u8 result; - unsigned int num_bytes = 0; - - head = &board->device_list; - if (head->next == head) - return 0; - - retval = setup_serial_poll(board, usec_timeout); - if (retval < 0) - return retval; - - for (cur = head->next; cur != head; cur = cur->next) { - device = list_entry(cur, struct gpib_status_queue, list); - retval = read_serial_poll_byte(board, - device->pad, device->sad, usec_timeout, &result); - if (retval < 0) - continue; - if (result & request_service_bit) { - retval = push_status_byte(board, device, result); - if (retval < 0) - continue; - num_bytes++; - } - } - - retval = cleanup_serial_poll(board, usec_timeout); - if (retval < 0) - return retval; - - return num_bytes; -} - -/* - * DVRSP - * This function performs a serial poll of the device with primary - * address pad and secondary address sad. If the device has no - * secondary address, pass a negative number in for this argument. At the - * end of a successful serial poll the response is returned in result. - * SPD and UNT are sent at the completion of the poll. - */ - -int dvrsp(struct gpib_board *board, unsigned int pad, int sad, - unsigned int usec_timeout, u8 *result) -{ - int status = ibstatus(board); - int retval; - - if ((status & CIC) == 0) { - dev_err(board->gpib_dev, "not CIC during serial poll\n"); - return -1; - } - - if (pad > MAX_GPIB_PRIMARY_ADDRESS || sad > MAX_GPIB_SECONDARY_ADDRESS || sad < -1) { - dev_err(board->gpib_dev, "bad address for serial poll"); - return -1; - } - - retval = serial_poll_single(board, pad, sad, usec_timeout, result); - if (io_timed_out(board)) - retval = -ETIMEDOUT; - - return retval; -} - -static struct gpib_descriptor *handle_to_descriptor(const struct gpib_file_private *file_priv, - int handle) -{ - if (handle < 0 || handle >= GPIB_MAX_NUM_DESCRIPTORS) { - pr_err("gpib: invalid handle %i\n", handle); - return NULL; - } - - return file_priv->descriptors[handle]; -} - -static int init_gpib_file_private(struct gpib_file_private *priv) -{ - memset(priv, 0, sizeof(*priv)); - atomic_set(&priv->holding_mutex, 0); - priv->descriptors[0] = kmalloc(sizeof(struct gpib_descriptor), GFP_KERNEL); - if (!priv->descriptors[0]) { - pr_err("gpib: failed to allocate default board descriptor\n"); - return -ENOMEM; - } - init_gpib_descriptor(priv->descriptors[0]); - priv->descriptors[0]->is_board = 1; - mutex_init(&priv->descriptors_mutex); - return 0; -} - -int ibopen(struct inode *inode, struct file *filep) -{ - unsigned int minor = iminor(inode); - struct gpib_board *board; - struct gpib_file_private *priv; - - if (minor >= GPIB_MAX_NUM_BOARDS) { - pr_err("gpib: invalid minor number of device file\n"); - return -ENXIO; - } - - board = &board_array[minor]; - - filep->private_data = kmalloc(sizeof(struct gpib_file_private), GFP_KERNEL); - if (!filep->private_data) - return -ENOMEM; - - priv = filep->private_data; - init_gpib_file_private((struct gpib_file_private *)filep->private_data); - - if (board->use_count == 0) { - int retval; - - retval = request_module("gpib%i", minor); - if (retval) - dev_dbg(board->gpib_dev, "request module returned %i\n", retval); - } - if (board->interface) { - if (!try_module_get(board->provider_module)) { - dev_err(board->gpib_dev, "try_module_get() failed\n"); - return -EIO; - } - board->use_count++; - priv->got_module = 1; - } - return 0; -} - -int ibclose(struct inode *inode, struct file *filep) -{ - unsigned int minor = iminor(inode); - struct gpib_board *board; - struct gpib_file_private *priv = filep->private_data; - struct gpib_descriptor *desc; - - if (minor >= GPIB_MAX_NUM_BOARDS) { - pr_err("gpib: invalid minor number of device file\n"); - return -ENODEV; - } - - board = &board_array[minor]; - - if (priv) { - desc = handle_to_descriptor(priv, 0); - if (desc) { - if (desc->autopoll_enabled) { - dev_dbg(board->gpib_dev, "decrementing autospollers\n"); - if (board->autospollers > 0) - board->autospollers--; - else - dev_err(board->gpib_dev, - "Attempt to decrement zero autospollers\n"); - } - } else { - dev_err(board->gpib_dev, "Unexpected null gpib_descriptor\n"); - } - - cleanup_open_devices(priv, board); - - if (atomic_read(&priv->holding_mutex)) - mutex_unlock(&board->user_mutex); - - if (priv->got_module && board->use_count) { - module_put(board->provider_module); - --board->use_count; - } - - kfree(filep->private_data); - filep->private_data = NULL; - } - - return 0; -} - -long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - unsigned int minor = iminor(file_inode(filep)); - struct gpib_board *board; - struct gpib_file_private *file_priv = filep->private_data; - long retval = -ENOTTY; - - if (minor >= GPIB_MAX_NUM_BOARDS) { - pr_err("gpib: invalid minor number of device file\n"); - return -ENODEV; - } - board = &board_array[minor]; - - if (mutex_lock_interruptible(&board->big_gpib_mutex)) - return -ERESTARTSYS; - - dev_dbg(board->gpib_dev, "ioctl %d, interface=%s, use=%d, onl=%d\n", - cmd & 0xff, - board->interface ? board->interface->name : "", - board->use_count, - board->online); - - switch (cmd) { - case CFCBOARDTYPE: - retval = board_type_ioctl(file_priv, board, arg); - goto done; - case IBONL: - retval = online_ioctl(board, arg); - goto done; - default: - break; - } - if (!board->interface) { - dev_err(board->gpib_dev, "no gpib board configured\n"); - retval = -ENODEV; - goto done; - } - if (file_priv->got_module == 0) { - if (!try_module_get(board->provider_module)) { - dev_err(board->gpib_dev, "try_module_get() failed\n"); - retval = -EIO; - goto done; - } - file_priv->got_module = 1; - board->use_count++; - } - switch (cmd) { - case CFCBASE: - retval = iobase_ioctl(&board->config, arg); - goto done; - case CFCIRQ: - retval = irq_ioctl(&board->config, arg); - goto done; - case CFCDMA: - retval = dma_ioctl(&board->config, arg); - goto done; - case IBAUTOSPOLL: - retval = autospoll_ioctl(board, file_priv, arg); - goto done; - case IBBOARD_INFO: - retval = board_info_ioctl(board, arg); - goto done; - case IBMUTEX: - /* - * Need to unlock board->big_gpib_mutex before potentially locking board->user_mutex - * to maintain consistent locking order - */ - mutex_unlock(&board->big_gpib_mutex); - return mutex_ioctl(board, file_priv, arg); - case IBPAD: - retval = pad_ioctl(board, file_priv, arg); - goto done; - case IBSAD: - retval = sad_ioctl(board, file_priv, arg); - goto done; - case IBSELECT_PCI: - retval = select_pci_ioctl(&board->config, arg); - goto done; - case IBSELECT_DEVICE_PATH: - retval = select_device_path_ioctl(&board->config, arg); - goto done; - default: - break; - } - - if (!board->online) { - retval = -EINVAL; - goto done; - } - - switch (cmd) { - case IBEVENT: - retval = event_ioctl(board, arg); - goto done; - case IBCLOSEDEV: - retval = close_dev_ioctl(filep, board, arg); - goto done; - case IBOPENDEV: - retval = open_dev_ioctl(filep, board, arg); - goto done; - case IBSPOLL_BYTES: - retval = status_bytes_ioctl(board, arg); - goto done; - case IBWAIT: - retval = wait_ioctl(file_priv, board, arg); - if (retval == -ERESTARTSYS) - return retval; - goto done; - case IBLINES: - retval = line_status_ioctl(board, arg); - goto done; - case IBLOC: - board->interface->return_to_local(board); - retval = 0; - goto done; - default: - break; - } - - spin_lock(&board->locking_pid_spinlock); - if (current->pid != board->locking_pid) { - spin_unlock(&board->locking_pid_spinlock); - retval = -EPERM; - goto done; - } - spin_unlock(&board->locking_pid_spinlock); - - switch (cmd) { - case IB_T1_DELAY: - retval = t1_delay_ioctl(board, arg); - goto done; - case IBCAC: - retval = take_control_ioctl(board, arg); - goto done; - case IBCMD: - /* - * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex - * before we call them. - */ - mutex_unlock(&board->big_gpib_mutex); - return command_ioctl(file_priv, board, arg); - case IBEOS: - retval = eos_ioctl(board, arg); - goto done; - case IBGTS: - retval = ibgts(board); - goto done; - case IBPPC: - retval = ppc_ioctl(board, arg); - goto done; - case IBPP2_SET: - retval = set_local_ppoll_mode_ioctl(board, arg); - goto done; - case IBPP2_GET: - retval = get_local_ppoll_mode_ioctl(board, arg); - goto done; - case IBQUERY_BOARD_RSV: - retval = query_board_rsv_ioctl(board, arg); - goto done; - case IBRD: - /* - * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex - * before we call them. - */ - mutex_unlock(&board->big_gpib_mutex); - return read_ioctl(file_priv, board, arg); - case IBRPP: - retval = parallel_poll_ioctl(board, arg); - goto done; - case IBRSC: - retval = request_system_control_ioctl(board, arg); - goto done; - case IBRSP: - retval = serial_poll_ioctl(board, arg); - goto done; - case IBRSV: - retval = request_service_ioctl(board, arg); - goto done; - case IBRSV2: - retval = request_service2_ioctl(board, arg); - goto done; - case IBSIC: - retval = interface_clear_ioctl(board, arg); - goto done; - case IBSRE: - retval = remote_enable_ioctl(board, arg); - goto done; - case IBTMO: - retval = timeout_ioctl(board, arg); - goto done; - case IBWRT: - /* - * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex - * before we call them. - */ - mutex_unlock(&board->big_gpib_mutex); - return write_ioctl(file_priv, board, arg); - default: - retval = -ENOTTY; - goto done; - } - -done: - mutex_unlock(&board->big_gpib_mutex); - dev_dbg(board->gpib_dev, "ioctl done status = 0x%lx\n", board->status); - return retval; -} - -static int board_type_ioctl(struct gpib_file_private *file_priv, - struct gpib_board *board, unsigned long arg) -{ - struct list_head *list_ptr; - struct gpib_board_type_ioctl cmd; - int retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (board->online) - return -EBUSY; - - retval = copy_from_user(&cmd, (void __user *)arg, - sizeof(struct gpib_board_type_ioctl)); - if (retval) - return -EFAULT; - - for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers; - list_ptr = list_ptr->next) { - struct gpib_interface_list *entry; - - entry = list_entry(list_ptr, struct gpib_interface_list, list); - if (strcmp(entry->interface->name, cmd.name) == 0) { - int i; - int had_module = file_priv->got_module; - - if (board->use_count) { - for (i = 0; i < board->use_count; ++i) - module_put(board->provider_module); - board->interface = NULL; - file_priv->got_module = 0; - } - board->interface = entry->interface; - board->provider_module = entry->module; - for (i = 0; i < board->use_count; ++i) { - if (!try_module_get(entry->module)) { - board->use_count = i; - return -EIO; - } - } - if (had_module == 0) { - if (!try_module_get(entry->module)) - return -EIO; - ++board->use_count; - } - file_priv->got_module = 1; - return 0; - } - } - - return -EINVAL; -} - -static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, - unsigned long arg) -{ - struct gpib_read_write_ioctl read_cmd; - u8 __user *userbuf; - unsigned long remain; - int end_flag = 0; - int retval; - ssize_t read_ret = 0; - struct gpib_descriptor *desc; - size_t nbytes; - - retval = copy_from_user(&read_cmd, (void __user *)arg, sizeof(read_cmd)); - if (retval) - return -EFAULT; - - if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count) - return -EINVAL; - - desc = handle_to_descriptor(file_priv, read_cmd.handle); - if (!desc) - return -EINVAL; - - if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr))) - return -EFAULT; - - userbuf = (u8 __user *)(unsigned long)read_cmd.buffer_ptr; - userbuf += read_cmd.completed_transfer_count; - - remain = read_cmd.requested_transfer_count - read_cmd.completed_transfer_count; - - /* Check write access to buffer */ - if (!access_ok(userbuf, remain)) - return -EFAULT; - - atomic_set(&desc->io_in_progress, 1); - - /* Read buffer loads till we fill the user supplied buffer */ - while (remain > 0 && end_flag == 0) { - nbytes = 0; - read_ret = ibrd(board, board->buffer, (board->buffer_length < remain) ? - board->buffer_length : remain, &end_flag, &nbytes); - if (nbytes == 0) - break; - retval = copy_to_user(userbuf, board->buffer, nbytes); - if (retval) { - retval = -EFAULT; - break; - } - remain -= nbytes; - userbuf += nbytes; - if (read_ret < 0) - break; - } - read_cmd.completed_transfer_count = read_cmd.requested_transfer_count - remain; - read_cmd.end = end_flag; - /* - * suppress errors (for example due to timeout or interruption by device clear) - * if all bytes got sent. This prevents races that can occur in the various drivers - * if a device receives a device clear immediately after a transfer completes and - * the driver code wasn't careful enough to handle that case. - */ - if (remain == 0 || end_flag) - read_ret = 0; - if (retval == 0) - retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd)); - - atomic_set(&desc->io_in_progress, 0); - - wake_up_interruptible(&board->wait); - if (retval) - return -EFAULT; - - return read_ret; -} - -static int command_ioctl(struct gpib_file_private *file_priv, - struct gpib_board *board, unsigned long arg) -{ - struct gpib_read_write_ioctl cmd; - u8 __user *userbuf; - unsigned long remain; - int retval; - int fault = 0; - struct gpib_descriptor *desc; - size_t bytes_written; - int no_clear_io_in_prog; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - if (cmd.completed_transfer_count > cmd.requested_transfer_count) - return -EINVAL; - - desc = handle_to_descriptor(file_priv, cmd.handle); - if (!desc) - return -EINVAL; - - userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr; - userbuf += cmd.completed_transfer_count; - - no_clear_io_in_prog = cmd.end; - cmd.end = 0; - - remain = cmd.requested_transfer_count - cmd.completed_transfer_count; - - /* Check read access to buffer */ - if (!access_ok(userbuf, remain)) - return -EFAULT; - - /* - * Write buffer loads till we empty the user supplied buffer. - * Call drivers at least once, even if remain is zero, in - * order to allow them to insure previous commands were - * completely finished, in the case of a restarted ioctl. - */ - - atomic_set(&desc->io_in_progress, 1); - - do { - fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? - board->buffer_length : remain); - if (fault) { - retval = -EFAULT; - bytes_written = 0; - } else { - retval = ibcmd(board, board->buffer, (board->buffer_length < remain) ? - board->buffer_length : remain, &bytes_written); - } - remain -= bytes_written; - userbuf += bytes_written; - if (retval < 0) { - atomic_set(&desc->io_in_progress, 0); - - wake_up_interruptible(&board->wait); - break; - } - } while (remain > 0); - - cmd.completed_transfer_count = cmd.requested_transfer_count - remain; - - if (fault == 0) - fault = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); - - /* - * no_clear_io_in_prog (cmd.end) is true when io_in_progress should - * not be set to zero because the cmd in progress is the address setup - * operation for an async read or write. This causes CMPL not to be set - * in general_ibstatus until the async read or write completes. - */ - if (!no_clear_io_in_prog || fault) - atomic_set(&desc->io_in_progress, 0); - - wake_up_interruptible(&board->wait); - if (fault) - return -EFAULT; - - return retval; -} - -static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, - unsigned long arg) -{ - struct gpib_read_write_ioctl write_cmd; - u8 __user *userbuf; - unsigned long remain; - int retval = 0; - int fault; - struct gpib_descriptor *desc; - - fault = copy_from_user(&write_cmd, (void __user *)arg, sizeof(write_cmd)); - if (fault) - return -EFAULT; - - if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count) - return -EINVAL; - - desc = handle_to_descriptor(file_priv, write_cmd.handle); - if (!desc) - return -EINVAL; - - userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr; - userbuf += write_cmd.completed_transfer_count; - - remain = write_cmd.requested_transfer_count - write_cmd.completed_transfer_count; - - /* Check read access to buffer */ - if (!access_ok(userbuf, remain)) - return -EFAULT; - - atomic_set(&desc->io_in_progress, 1); - - /* Write buffer loads till we empty the user supplied buffer */ - while (remain > 0) { - int send_eoi; - size_t bytes_written = 0; - - send_eoi = remain <= board->buffer_length && write_cmd.end; - fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? - board->buffer_length : remain); - if (fault) { - retval = -EFAULT; - break; - } - retval = ibwrt(board, board->buffer, (board->buffer_length < remain) ? - board->buffer_length : remain, send_eoi, &bytes_written); - remain -= bytes_written; - userbuf += bytes_written; - if (retval < 0) - break; - } - write_cmd.completed_transfer_count = write_cmd.requested_transfer_count - remain; - /* - * suppress errors (for example due to timeout or interruption by device clear) - * if all bytes got sent. This prevents races that can occur in the various drivers - * if a device receives a device clear immediately after a transfer completes and - * the driver code wasn't careful enough to handle that case. - */ - if (remain == 0) - retval = 0; - if (fault == 0) - fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd)); - - atomic_set(&desc->io_in_progress, 0); - - wake_up_interruptible(&board->wait); - if (fault) - return -EFAULT; - - return retval; -} - -static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg) -{ - struct gpib_status_queue *device; - struct gpib_spoll_bytes_ioctl cmd; - int retval; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - device = get_gpib_status_queue(board, cmd.pad, cmd.sad); - if (!device) - cmd.num_bytes = 0; - else - cmd.num_bytes = num_status_bytes(device); - - retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); - if (retval) - return -EFAULT; - - return 0; -} - -static int increment_open_device_count(struct gpib_board *board, struct list_head *head, - unsigned int pad, int sad) -{ - struct list_head *list_ptr; - struct gpib_status_queue *device; - - /* - * first see if address has already been opened, then increment - * open count - */ - for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { - device = list_entry(list_ptr, struct gpib_status_queue, list); - if (gpib_address_equal(device->pad, device->sad, pad, sad)) { - dev_dbg(board->gpib_dev, "incrementing open count for pad %i, sad %i\n", - device->pad, device->sad); - device->reference_count++; - return 0; - } - } - - /* otherwise we need to allocate a new struct gpib_status_queue */ - device = kmalloc(sizeof(struct gpib_status_queue), GFP_ATOMIC); - if (!device) - return -ENOMEM; - init_gpib_status_queue(device); - device->pad = pad; - device->sad = sad; - device->reference_count = 1; - - list_add(&device->list, head); - - dev_dbg(board->gpib_dev, "opened pad %i, sad %i\n", device->pad, device->sad); - - return 0; -} - -static int subtract_open_device_count(struct gpib_board *board, struct list_head *head, - unsigned int pad, int sad, unsigned int count) -{ - struct gpib_status_queue *device; - struct list_head *list_ptr; - - for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { - device = list_entry(list_ptr, struct gpib_status_queue, list); - if (gpib_address_equal(device->pad, device->sad, pad, sad)) { - dev_dbg(board->gpib_dev, "decrementing open count for pad %i, sad %i\n", - device->pad, device->sad); - if (count > device->reference_count) { - dev_err(board->gpib_dev, "bug! in %s()\n", __func__); - return -EINVAL; - } - device->reference_count -= count; - if (device->reference_count == 0) { - dev_dbg(board->gpib_dev, "closing pad %i, sad %i\n", - device->pad, device->sad); - list_del(list_ptr); - kfree(device); - } - return 0; - } - } - dev_err(board->gpib_dev, "bug! tried to close address that was never opened!\n"); - return -EINVAL; -} - -static inline int decrement_open_device_count(struct gpib_board *board, struct list_head *head, - unsigned int pad, int sad) -{ - return subtract_open_device_count(board, head, pad, sad, 1); -} - -static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board) -{ - int retval = 0; - int i; - - for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) { - struct gpib_descriptor *desc; - - desc = file_priv->descriptors[i]; - if (!desc) - continue; - - if (desc->is_board == 0) { - retval = decrement_open_device_count(board, &board->device_list, desc->pad, - desc->sad); - if (retval < 0) - return retval; - } - kfree(desc); - file_priv->descriptors[i] = NULL; - } - - return 0; -} - -static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg) -{ - struct gpib_open_dev_ioctl open_dev_cmd; - int retval; - struct gpib_file_private *file_priv = filep->private_data; - int i; - - retval = copy_from_user(&open_dev_cmd, (void __user *)arg, sizeof(open_dev_cmd)); - if (retval) - return -EFAULT; - - if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) - return -ERESTARTSYS; - for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) - if (!file_priv->descriptors[i]) - break; - if (i == GPIB_MAX_NUM_DESCRIPTORS) { - mutex_unlock(&file_priv->descriptors_mutex); - return -ERANGE; - } - file_priv->descriptors[i] = kmalloc(sizeof(struct gpib_descriptor), GFP_KERNEL); - if (!file_priv->descriptors[i]) { - mutex_unlock(&file_priv->descriptors_mutex); - return -ENOMEM; - } - init_gpib_descriptor(file_priv->descriptors[i]); - - file_priv->descriptors[i]->pad = open_dev_cmd.pad; - file_priv->descriptors[i]->sad = open_dev_cmd.sad; - file_priv->descriptors[i]->is_board = open_dev_cmd.is_board; - mutex_unlock(&file_priv->descriptors_mutex); - - retval = increment_open_device_count(board, &board->device_list, open_dev_cmd.pad, - open_dev_cmd.sad); - if (retval < 0) - return retval; - - /* - * clear stuck srq state, since we may be able to find service request on - * the new device - */ - atomic_set(&board->stuck_srq, 0); - - open_dev_cmd.handle = i; - retval = copy_to_user((void __user *)arg, &open_dev_cmd, sizeof(open_dev_cmd)); - if (retval) - return -EFAULT; - - return 0; -} - -static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg) -{ - struct gpib_close_dev_ioctl cmd; - struct gpib_file_private *file_priv = filep->private_data; - int retval; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS) - return -EINVAL; - if (!file_priv->descriptors[cmd.handle]) - return -EINVAL; - - retval = decrement_open_device_count(board, &board->device_list, - file_priv->descriptors[cmd.handle]->pad, - file_priv->descriptors[cmd.handle]->sad); - if (retval < 0) - return retval; - - kfree(file_priv->descriptors[cmd.handle]); - file_priv->descriptors[cmd.handle] = NULL; - - return 0; -} - -static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg) -{ - struct gpib_serial_poll_ioctl serial_cmd; - int retval; - - retval = copy_from_user(&serial_cmd, (void __user *)arg, sizeof(serial_cmd)); - if (retval) - return -EFAULT; - - retval = get_serial_poll_byte(board, serial_cmd.pad, serial_cmd.sad, board->usec_timeout, - &serial_cmd.status_byte); - if (retval < 0) - return retval; - - retval = copy_to_user((void __user *)arg, &serial_cmd, sizeof(serial_cmd)); - if (retval) - return -EFAULT; - - return 0; -} - -static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, - unsigned long arg) -{ - struct gpib_wait_ioctl wait_cmd; - int retval; - struct gpib_descriptor *desc; - - retval = copy_from_user(&wait_cmd, (void __user *)arg, sizeof(wait_cmd)); - if (retval) - return -EFAULT; - - desc = handle_to_descriptor(file_priv, wait_cmd.handle); - if (!desc) - return -EINVAL; - - retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask, - wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc); - if (retval < 0) - return retval; - - retval = copy_to_user((void __user *)arg, &wait_cmd, sizeof(wait_cmd)); - if (retval) - return -EFAULT; - - return 0; -} - -static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg) -{ - u8 poll_byte; - int retval; - - retval = ibrpp(board, &poll_byte); - if (retval < 0) - return retval; - - retval = copy_to_user((void __user *)arg, &poll_byte, sizeof(poll_byte)); - if (retval) - return -EFAULT; - - return 0; -} - -static int online_ioctl(struct gpib_board *board, unsigned long arg) -{ - struct gpib_online_ioctl online_cmd; - int retval; - void __user *init_data = NULL; - - board->config.init_data = NULL; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - retval = copy_from_user(&online_cmd, (void __user *)arg, sizeof(online_cmd)); - if (retval) - return -EFAULT; - if (online_cmd.init_data_length > 0) { - board->config.init_data = vmalloc(online_cmd.init_data_length); - if (!board->config.init_data) - return -ENOMEM; - if (WARN_ON_ONCE(sizeof(init_data) > sizeof(online_cmd.init_data_ptr))) - return -EFAULT; - init_data = (void __user *)(unsigned long)(online_cmd.init_data_ptr); - retval = copy_from_user(board->config.init_data, init_data, - online_cmd.init_data_length); - if (retval) { - vfree(board->config.init_data); - return -EFAULT; - } - board->config.init_data_length = online_cmd.init_data_length; - } else { - board->config.init_data = NULL; - board->config.init_data_length = 0; - } - if (online_cmd.online) - retval = ibonline(board); - else - retval = iboffline(board); - if (board->config.init_data) { - vfree(board->config.init_data); - board->config.init_data = NULL; - board->config.init_data_length = 0; - } - return retval; -} - -static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg) -{ - int enable; - int retval; - - retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable)); - if (retval) - return -EFAULT; - - return ibsre(board, enable); -} - -static int take_control_ioctl(struct gpib_board *board, unsigned long arg) -{ - int synchronous; - int retval; - - retval = copy_from_user(&synchronous, (void __user *)arg, sizeof(synchronous)); - if (retval) - return -EFAULT; - - return ibcac(board, synchronous, 1); -} - -static int line_status_ioctl(struct gpib_board *board, unsigned long arg) -{ - short lines; - int retval; - - retval = iblines(board, &lines); - if (retval < 0) - return retval; - - retval = copy_to_user((void __user *)arg, &lines, sizeof(lines)); - if (retval) - return -EFAULT; - - return 0; -} - -static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg) -{ - struct gpib_pad_ioctl cmd; - int retval; - struct gpib_descriptor *desc; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - desc = handle_to_descriptor(file_priv, cmd.handle); - if (!desc) - return -EINVAL; - - if (desc->is_board) { - retval = ibpad(board, cmd.pad); - if (retval < 0) - return retval; - } else { - retval = decrement_open_device_count(board, &board->device_list, desc->pad, - desc->sad); - if (retval < 0) - return retval; - - desc->pad = cmd.pad; - - retval = increment_open_device_count(board, &board->device_list, desc->pad, - desc->sad); - if (retval < 0) - return retval; - } - - return 0; -} - -static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg) -{ - struct gpib_sad_ioctl cmd; - int retval; - struct gpib_descriptor *desc; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - desc = handle_to_descriptor(file_priv, cmd.handle); - if (!desc) - return -EINVAL; - - if (desc->is_board) { - retval = ibsad(board, cmd.sad); - if (retval < 0) - return retval; - } else { - retval = decrement_open_device_count(board, &board->device_list, desc->pad, - desc->sad); - if (retval < 0) - return retval; - - desc->sad = cmd.sad; - - retval = increment_open_device_count(board, &board->device_list, desc->pad, - desc->sad); - if (retval < 0) - return retval; - } - return 0; -} - -static int eos_ioctl(struct gpib_board *board, unsigned long arg) -{ - struct gpib_eos_ioctl eos_cmd; - int retval; - - retval = copy_from_user(&eos_cmd, (void __user *)arg, sizeof(eos_cmd)); - if (retval) - return -EFAULT; - - return ibeos(board, eos_cmd.eos, eos_cmd.eos_flags); -} - -static int request_service_ioctl(struct gpib_board *board, unsigned long arg) -{ - u8 status_byte; - int retval; - - retval = copy_from_user(&status_byte, (void __user *)arg, sizeof(status_byte)); - if (retval) - return -EFAULT; - - return ibrsv2(board, status_byte, status_byte & request_service_bit); -} - -static int request_service2_ioctl(struct gpib_board *board, unsigned long arg) -{ - struct gpib_request_service2 request_service2_cmd; - int retval; - - retval = copy_from_user(&request_service2_cmd, (void __user *)arg, - sizeof(struct gpib_request_service2)); - if (retval) - return -EFAULT; - - return ibrsv2(board, request_service2_cmd.status_byte, - request_service2_cmd.new_reason_for_service); -} - -static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg) -{ - u64 base_addr; - int retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - retval = copy_from_user(&base_addr, (void __user *)arg, sizeof(base_addr)); - if (retval) - return -EFAULT; - - if (WARN_ON_ONCE(sizeof(void *) > sizeof(base_addr))) - return -EFAULT; - config->ibbase = base_addr; - - return 0; -} - -static int irq_ioctl(struct gpib_board_config *config, unsigned long arg) -{ - unsigned int irq; - int retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - retval = copy_from_user(&irq, (void __user *)arg, sizeof(irq)); - if (retval) - return -EFAULT; - - config->ibirq = irq; - - return 0; -} - -static int dma_ioctl(struct gpib_board_config *config, unsigned long arg) -{ - unsigned int dma_channel; - int retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - retval = copy_from_user(&dma_channel, (void __user *)arg, sizeof(dma_channel)); - if (retval) - return -EFAULT; - - config->ibdma = dma_channel; - - return 0; -} - -static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg) -{ - short enable; - int retval; - struct gpib_descriptor *desc; - - retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable)); - if (retval) - return -EFAULT; - - desc = handle_to_descriptor(file_priv, 0); /* board handle is 0 */ - - if (enable) { - if (!desc->autopoll_enabled) { - board->autospollers++; - desc->autopoll_enabled = 1; - } - retval = 0; - } else { - if (desc->autopoll_enabled) { - desc->autopoll_enabled = 0; - if (board->autospollers > 0) { - board->autospollers--; - retval = 0; - } else { - dev_err(board->gpib_dev, - "tried to set number of autospollers negative\n"); - retval = -EINVAL; - } - } else { - dev_err(board->gpib_dev, "autopoll disable requested before enable\n"); - retval = -EINVAL; - } - } - return retval; -} - -static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, - unsigned long arg) -{ - int retval, lock_mutex; - - retval = copy_from_user(&lock_mutex, (void __user *)arg, sizeof(lock_mutex)); - if (retval) - return -EFAULT; - - if (lock_mutex) { - retval = mutex_lock_interruptible(&board->user_mutex); - if (retval) - return -ERESTARTSYS; - - spin_lock(&board->locking_pid_spinlock); - board->locking_pid = current->pid; - spin_unlock(&board->locking_pid_spinlock); - - atomic_set(&file_priv->holding_mutex, 1); - - dev_dbg(board->gpib_dev, "locked board mutex\n"); - } else { - spin_lock(&board->locking_pid_spinlock); - if (current->pid != board->locking_pid) { - dev_err(board->gpib_dev, "bug! pid %i tried to release mutex held by pid %i\n", - current->pid, board->locking_pid); - spin_unlock(&board->locking_pid_spinlock); - return -EPERM; - } - board->locking_pid = 0; - spin_unlock(&board->locking_pid_spinlock); - - atomic_set(&file_priv->holding_mutex, 0); - - mutex_unlock(&board->user_mutex); - dev_dbg(board->gpib_dev, "unlocked board mutex\n"); - } - return 0; -} - -static int timeout_ioctl(struct gpib_board *board, unsigned long arg) -{ - unsigned int timeout; - int retval; - - retval = copy_from_user(&timeout, (void __user *)arg, sizeof(timeout)); - if (retval) - return -EFAULT; - - board->usec_timeout = timeout; - dev_dbg(board->gpib_dev, "timeout set to %i usec\n", timeout); - - return 0; -} - -static int ppc_ioctl(struct gpib_board *board, unsigned long arg) -{ - struct gpib_ppoll_config_ioctl cmd; - int retval; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - if (cmd.set_ist) { - board->ist = 1; - board->interface->parallel_poll_response(board, board->ist); - } else if (cmd.clear_ist) { - board->ist = 0; - board->interface->parallel_poll_response(board, board->ist); - } - - if (cmd.config) { - retval = ibppc(board, cmd.config); - if (retval < 0) - return retval; - } - - return 0; -} - -static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg) -{ - short cmd; - int retval; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - if (!board->interface->local_parallel_poll_mode) - return -ENOENT; - board->local_ppoll_mode = cmd != 0; - board->interface->local_parallel_poll_mode(board, board->local_ppoll_mode); - - return 0; -} - -static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg) -{ - short cmd; - int retval; - - cmd = board->local_ppoll_mode; - retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); - if (retval) - return -EFAULT; - - return 0; -} - -static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg) -{ - int status; - int retval; - - status = board->interface->serial_poll_status(board); - - retval = copy_to_user((void __user *)arg, &status, sizeof(status)); - if (retval) - return -EFAULT; - - return 0; -} - -static int board_info_ioctl(const struct gpib_board *board, unsigned long arg) -{ - struct gpib_board_info_ioctl info = { }; - int retval; - - info.pad = board->pad; - info.sad = board->sad; - info.parallel_poll_configuration = board->parallel_poll_configuration; - info.is_system_controller = board->master; - if (board->autospollers) - info.autopolling = 1; - else - info.autopolling = 0; - info.t1_delay = board->t1_nano_sec; - info.ist = board->ist; - info.no_7_bit_eos = board->interface->no_7_bit_eos; - retval = copy_to_user((void __user *)arg, &info, sizeof(info)); - if (retval) - return -EFAULT; - - return 0; -} - -static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg) -{ - unsigned int usec_duration; - int retval; - - retval = copy_from_user(&usec_duration, (void __user *)arg, sizeof(usec_duration)); - if (retval) - return -EFAULT; - - return ibsic(board, usec_duration); -} - -static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg) -{ - struct gpib_select_pci_ioctl selection; - int retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - retval = copy_from_user(&selection, (void __user *)arg, sizeof(selection)); - if (retval) - return -EFAULT; - - config->pci_bus = selection.pci_bus; - config->pci_slot = selection.pci_slot; - - return 0; -} - -static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg) -{ - struct gpib_select_device_path_ioctl *selection; - int retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - selection = vmalloc(sizeof(struct gpib_select_device_path_ioctl)); - if (!selection) - return -ENOMEM; - - retval = copy_from_user(selection, (void __user *)arg, - sizeof(struct gpib_select_device_path_ioctl)); - if (retval) { - vfree(selection); - return -EFAULT; - } - - selection->device_path[sizeof(selection->device_path) - 1] = '\0'; - kfree(config->device_path); - config->device_path = NULL; - if (strlen(selection->device_path) > 0) - config->device_path = kstrdup(selection->device_path, GFP_KERNEL); - - vfree(selection); - return 0; -} - -unsigned int num_gpib_events(const struct gpib_event_queue *queue) -{ - return queue->num_events; -} - -static int push_gpib_event_nolock(struct gpib_board *board, short event_type) -{ - struct gpib_event_queue *queue = &board->event_queue; - struct list_head *head = &queue->event_head; - struct gpib_event *event; - static const unsigned int max_num_events = 1024; - int retval; - - if (num_gpib_events(queue) >= max_num_events) { - short lost_event; - - queue->dropped_event = 1; - retval = pop_gpib_event_nolock(board, queue, &lost_event); - if (retval < 0) - return retval; - } - - event = kmalloc(sizeof(struct gpib_event), GFP_ATOMIC); - if (!event) { - queue->dropped_event = 1; - dev_err(board->gpib_dev, "failed to allocate memory for event\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&event->list); - event->event_type = event_type; - - list_add_tail(&event->list, head); - - queue->num_events++; - - dev_dbg(board->gpib_dev, "pushed event %i, %i in queue\n", - (int)event_type, num_gpib_events(queue)); - - return 0; -} - -// push event onto back of event queue -int push_gpib_event(struct gpib_board *board, short event_type) -{ - unsigned long flags; - int retval; - - spin_lock_irqsave(&board->event_queue.lock, flags); - retval = push_gpib_event_nolock(board, event_type); - spin_unlock_irqrestore(&board->event_queue.lock, flags); - - if (event_type == EVENT_DEV_TRG) - board->status |= DTAS; - if (event_type == EVENT_DEV_CLR) - board->status |= DCAS; - - return retval; -} -EXPORT_SYMBOL(push_gpib_event); - -static int pop_gpib_event_nolock(struct gpib_board *board, - struct gpib_event_queue *queue, short *event_type) -{ - struct list_head *head = &queue->event_head; - struct list_head *front = head->next; - struct gpib_event *event; - - if (num_gpib_events(queue) == 0) { - *event_type = EVENT_NONE; - return 0; - } - - if (front == head) - return -EIO; - - if (queue->dropped_event) { - queue->dropped_event = 0; - return -EPIPE; - } - - event = list_entry(front, struct gpib_event, list); - *event_type = event->event_type; - - list_del(front); - kfree(event); - - queue->num_events--; - - dev_dbg(board->gpib_dev, "popped event %i, %i in queue\n", - (int)*event_type, num_gpib_events(queue)); - - return 0; -} - -// pop event from front of event queue -int pop_gpib_event(struct gpib_board *board, struct gpib_event_queue *queue, short *event_type) -{ - unsigned long flags; - int retval; - - spin_lock_irqsave(&queue->lock, flags); - retval = pop_gpib_event_nolock(board, queue, event_type); - spin_unlock_irqrestore(&queue->lock, flags); - return retval; -} - -static int event_ioctl(struct gpib_board *board, unsigned long arg) -{ - short user_event; - int retval; - short event; - - retval = pop_gpib_event(board, &board->event_queue, &event); - if (retval < 0) - return retval; - - user_event = event; - - retval = copy_to_user((void __user *)arg, &user_event, sizeof(user_event)); - if (retval) - return -EFAULT; - - return 0; -} - -static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg) -{ - int request_control; - int retval; - - retval = copy_from_user(&request_control, (void __user *)arg, sizeof(request_control)); - if (retval) - return -EFAULT; - - return ibrsc(board, request_control); -} - -static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg) -{ - unsigned int cmd; - unsigned int delay; - int retval; - - if (!board->interface->t1_delay) - return -ENOENT; - - retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); - if (retval) - return -EFAULT; - - delay = cmd; - - retval = board->interface->t1_delay(board, delay); - if (retval < 0) - return retval; - - board->t1_nano_sec = retval; - return 0; -} - -static const struct file_operations ib_fops = { - .owner = THIS_MODULE, - .llseek = NULL, - .unlocked_ioctl = &ibioctl, - .compat_ioctl = &ibioctl, - .open = &ibopen, - .release = &ibclose, -}; - -struct gpib_board board_array[GPIB_MAX_NUM_BOARDS]; - -LIST_HEAD(registered_drivers); - -void init_gpib_descriptor(struct gpib_descriptor *desc) -{ - desc->pad = 0; - desc->sad = -1; - desc->is_board = 0; - desc->autopoll_enabled = 0; - atomic_set(&desc->io_in_progress, 0); -} - -int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module) -{ - struct gpib_interface_list *entry; - - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - entry->interface = interface; - entry->module = provider_module; - list_add(&entry->list, ®istered_drivers); - - return 0; -} -EXPORT_SYMBOL(gpib_register_driver); - -void gpib_unregister_driver(struct gpib_interface *interface) -{ - int i; - struct list_head *list_ptr; - - for (i = 0; i < GPIB_MAX_NUM_BOARDS; i++) { - struct gpib_board *board = &board_array[i]; - - if (board->interface == interface) { - if (board->use_count > 0) - pr_warn("gpib: Warning: deregistered interface %s in use\n", - interface->name); - iboffline(board); - board->interface = NULL; - } - } - for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers;) { - struct gpib_interface_list *entry; - - entry = list_entry(list_ptr, struct gpib_interface_list, list); - list_ptr = list_ptr->next; - if (entry->interface == interface) { - list_del(&entry->list); - kfree(entry); - } - } -} -EXPORT_SYMBOL(gpib_unregister_driver); - -static void init_gpib_board_config(struct gpib_board_config *config) -{ - memset(config, 0, sizeof(struct gpib_board_config)); - config->pci_bus = -1; - config->pci_slot = -1; -} - -void init_gpib_board(struct gpib_board *board) -{ - board->interface = NULL; - board->provider_module = NULL; - board->buffer = NULL; - board->buffer_length = 0; - board->status = 0; - init_waitqueue_head(&board->wait); - mutex_init(&board->user_mutex); - mutex_init(&board->big_gpib_mutex); - board->locking_pid = 0; - spin_lock_init(&board->locking_pid_spinlock); - spin_lock_init(&board->spinlock); - timer_setup(&board->timer, NULL, 0); - board->dev = NULL; - board->gpib_dev = NULL; - init_gpib_board_config(&board->config); - board->private_data = NULL; - board->use_count = 0; - INIT_LIST_HEAD(&board->device_list); - board->pad = 0; - board->sad = -1; - board->usec_timeout = 3000000; - board->parallel_poll_configuration = 0; - board->online = 0; - board->autospollers = 0; - board->autospoll_task = NULL; - init_event_queue(&board->event_queue); - board->minor = -1; - init_gpib_pseudo_irq(&board->pseudo_irq); - board->master = 1; - atomic_set(&board->stuck_srq, 0); - board->local_ppoll_mode = 0; -} - -int gpib_allocate_board(struct gpib_board *board) -{ - if (!board->buffer) { - board->buffer_length = 0x4000; - board->buffer = vmalloc(board->buffer_length); - if (!board->buffer) { - board->buffer_length = 0; - return -ENOMEM; - } - } - return 0; -} - -void gpib_deallocate_board(struct gpib_board *board) -{ - short dummy; - - if (board->buffer) { - vfree(board->buffer); - board->buffer = NULL; - board->buffer_length = 0; - } - while (num_gpib_events(&board->event_queue)) - pop_gpib_event(board, &board->event_queue, &dummy); -} - -static void init_board_array(struct gpib_board *board_array, unsigned int length) -{ - int i; - - for (i = 0; i < length; i++) { - init_gpib_board(&board_array[i]); - board_array[i].minor = i; - } -} - -void init_gpib_status_queue(struct gpib_status_queue *device) -{ - INIT_LIST_HEAD(&device->list); - INIT_LIST_HEAD(&device->status_bytes); - device->num_status_bytes = 0; - device->reference_count = 0; - device->dropped_byte = 0; -} - -static struct class *gpib_class; - -static int __init gpib_common_init_module(void) -{ - int i; - - pr_info("GPIB core driver\n"); - init_board_array(board_array, GPIB_MAX_NUM_BOARDS); - if (register_chrdev(GPIB_CODE, "gpib", &ib_fops)) { - pr_err("gpib: can't get major %d\n", GPIB_CODE); - return -EIO; - } - gpib_class = class_create("gpib_common"); - if (IS_ERR(gpib_class)) { - pr_err("gpib: failed to create gpib class\n"); - unregister_chrdev(GPIB_CODE, "gpib"); - return PTR_ERR(gpib_class); - } - for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) - board_array[i].gpib_dev = device_create(gpib_class, NULL, - MKDEV(GPIB_CODE, i), NULL, "gpib%i", i); - - return 0; -} - -static void __exit gpib_common_exit_module(void) -{ - int i; - - for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) - device_destroy(gpib_class, MKDEV(GPIB_CODE, i)); - - class_destroy(gpib_class); - unregister_chrdev(GPIB_CODE, "gpib"); -} - -int gpib_match_device_path(struct device *dev, const char *device_path_in) -{ - if (device_path_in) { - char *device_path; - - device_path = kobject_get_path(&dev->kobj, GFP_KERNEL); - if (!device_path) { - dev_err(dev, "kobject_get_path returned NULL."); - return 0; - } - if (strcmp(device_path_in, device_path) != 0) { - kfree(device_path); - return 0; - } - kfree(device_path); - } - return 1; -} -EXPORT_SYMBOL(gpib_match_device_path); - -struct pci_dev *gpib_pci_get_device(const struct gpib_board_config *config, unsigned int vendor_id, - unsigned int device_id, struct pci_dev *from) -{ - struct pci_dev *pci_device = from; - - while ((pci_device = pci_get_device(vendor_id, device_id, pci_device))) { - if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) - continue; - if (config->pci_slot >= 0 && config->pci_slot != - PCI_SLOT(pci_device->devfn)) - continue; - if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) - continue; - return pci_device; - } - return NULL; -} -EXPORT_SYMBOL(gpib_pci_get_device); - -struct pci_dev *gpib_pci_get_subsys(const struct gpib_board_config *config, unsigned int vendor_id, - unsigned int device_id, unsigned int ss_vendor, - unsigned int ss_device, - struct pci_dev *from) -{ - struct pci_dev *pci_device = from; - - while ((pci_device = pci_get_subsys(vendor_id, device_id, - ss_vendor, ss_device, pci_device))) { - if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) - continue; - if (config->pci_slot >= 0 && config->pci_slot != - PCI_SLOT(pci_device->devfn)) - continue; - if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) - continue; - return pci_device; - } - return NULL; -} -EXPORT_SYMBOL(gpib_pci_get_subsys); - -module_init(gpib_common_init_module); -module_exit(gpib_common_exit_module); - diff --git a/drivers/staging/gpib/common/iblib.c b/drivers/staging/gpib/common/iblib.c deleted file mode 100644 index 7cbb6a467177..000000000000 --- a/drivers/staging/gpib/common/iblib.c +++ /dev/null @@ -1,717 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2001, 2002 by Frank Mori Hess - ***************************************************************************/ - -#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "ibsys.h" -#include -#include -#include - -/* - * IBCAC - * Return to the controller active state from the - * controller standby state, i.e., turn ATN on. Note - * that in order to enter the controller active state - * from the controller idle state, ibsic must be called. - * If sync is non-zero, attempt to take control synchronously. - * If fallback_to_async is non-zero, try to take control asynchronously - * if synchronous attempt fails. - */ -int ibcac(struct gpib_board *board, int sync, int fallback_to_async) -{ - int status = ibstatus(board); - int retval; - - if ((status & CIC) == 0) - return -EINVAL; - - if (status & ATN) - return 0; - - if (sync && (status & LACS) == 0) - /* - * tcs (take control synchronously) can only possibly work when - * controller is listener. Error code also needs to be -ETIMEDOUT - * or it will giveout without doing fallback. - */ - retval = -ETIMEDOUT; - else - retval = board->interface->take_control(board, sync); - - if (retval < 0 && fallback_to_async) { - if (sync && retval == -ETIMEDOUT) - retval = board->interface->take_control(board, 0); - } - board->interface->update_status(board, 0); - - return retval; -} - -/* - * After ATN is asserted, it should cause any connected devices - * to start listening for command bytes and leave acceptor idle state. - * So if ATN is asserted and neither NDAC or NRFD are asserted, - * then there are no devices and ibcmd should error out immediately. - * Some gpib hardware sees itself asserting NDAC/NRFD when it - * is controller in charge, in which case this check will - * do nothing useful (but shouldn't cause any harm either). - * Drivers that don't need this check (ni_usb for example) may - * set the skip_check_for_command_acceptors flag in their - * gpib_interface_struct to avoid useless overhead. - */ -static int check_for_command_acceptors(struct gpib_board *board) -{ - int lines; - - if (board->interface->skip_check_for_command_acceptors) - return 0; - if (!board->interface->line_status) - return 0; - - udelay(2); // allow time for devices to respond to ATN if it was just asserted - - lines = board->interface->line_status(board); - if (lines < 0) - return lines; - - if ((lines & VALID_NRFD) && (lines & VALID_NDAC)) { - if ((lines & BUS_NRFD) == 0 && (lines & BUS_NDAC) == 0) - return -ENOTCONN; - } - - return 0; -} - -/* - * IBCMD - * Write cnt command bytes from buf to the GPIB. The - * command operation terminates only on I/O complete. - * - * NOTE: - * 1. Prior to beginning the command, the interface is - * placed in the controller active state. - * 2. Before calling ibcmd for the first time, ibsic - * must be called to initialize the GPIB and enable - * the interface to leave the controller idle state. - */ -int ibcmd(struct gpib_board *board, u8 *buf, size_t length, size_t *bytes_written) -{ - ssize_t ret = 0; - int status; - - *bytes_written = 0; - - status = ibstatus(board); - - if ((status & CIC) == 0) - return -EINVAL; - - os_start_timer(board, board->usec_timeout); - - ret = ibcac(board, 1, 1); - if (ret == 0) { - ret = check_for_command_acceptors(board); - if (ret == 0) - ret = board->interface->command(board, buf, length, bytes_written); - } - - os_remove_timer(board); - - if (io_timed_out(board)) - ret = -ETIMEDOUT; - - return ret; -} - -/* - * IBGTS - * Go to the controller standby state from the controller - * active state, i.e., turn ATN off. - */ - -int ibgts(struct gpib_board *board) -{ - int status = ibstatus(board); - int retval; - - if ((status & CIC) == 0) - return -EINVAL; - - retval = board->interface->go_to_standby(board); /* go to standby */ - - board->interface->update_status(board, 0); - - return retval; -} - -static int autospoll_wait_should_wake_up(struct gpib_board *board) -{ - int retval; - - mutex_lock(&board->big_gpib_mutex); - - retval = board->master && board->autospollers > 0 && - !atomic_read(&board->stuck_srq) && - test_and_clear_bit(SRQI_NUM, &board->status); - - mutex_unlock(&board->big_gpib_mutex); - return retval; -} - -static int autospoll_thread(void *board_void) -{ - struct gpib_board *board = board_void; - int retval = 0; - - dev_dbg(board->gpib_dev, "entering autospoll thread\n"); - - while (1) { - wait_event_interruptible(board->wait, - kthread_should_stop() || - autospoll_wait_should_wake_up(board)); - dev_dbg(board->gpib_dev, "autospoll wait satisfied\n"); - if (kthread_should_stop()) - break; - - mutex_lock(&board->big_gpib_mutex); - /* make sure we are still good after we have lock */ - if (board->autospollers <= 0 || board->master == 0) { - mutex_unlock(&board->big_gpib_mutex); - continue; - } - mutex_unlock(&board->big_gpib_mutex); - - if (try_module_get(board->provider_module)) { - retval = autopoll_all_devices(board); - module_put(board->provider_module); - } else { - dev_err(board->gpib_dev, "try_module_get() failed!\n"); - } - if (retval <= 0) { - dev_err(board->gpib_dev, "stuck SRQ\n"); - - atomic_set(&board->stuck_srq, 1); // XXX could be better - set_bit(SRQI_NUM, &board->status); - } - } - return retval; -} - -int ibonline(struct gpib_board *board) -{ - int retval; - - if (board->online) - return -EBUSY; - if (!board->interface) - return -ENODEV; - retval = gpib_allocate_board(board); - if (retval < 0) - return retval; - - board->dev = NULL; - board->local_ppoll_mode = 0; - retval = board->interface->attach(board, &board->config); - if (retval < 0) { - board->interface->detach(board); - return retval; - } - /* - * nios2nommu on 2.6.11 uclinux kernel has weird problems - * with autospoll thread causing huge slowdowns - */ -#ifndef CONFIG_NIOS2 - board->autospoll_task = kthread_run(&autospoll_thread, board, - "gpib%d_autospoll_kthread", board->minor); - retval = IS_ERR(board->autospoll_task); - if (retval) { - dev_err(board->gpib_dev, "failed to create autospoll thread\n"); - board->interface->detach(board); - return retval; - } -#endif - board->online = 1; - dev_dbg(board->gpib_dev, "board online\n"); - - return 0; -} - -/* XXX need to make sure board is generally not in use (grab board lock?) */ -int iboffline(struct gpib_board *board) -{ - int retval; - - if (board->online == 0) - return 0; - if (!board->interface) - return -ENODEV; - - if (board->autospoll_task && !IS_ERR(board->autospoll_task)) { - retval = kthread_stop(board->autospoll_task); - if (retval) - dev_err(board->gpib_dev, "kthread_stop returned %i\n", retval); - board->autospoll_task = NULL; - } - - board->interface->detach(board); - gpib_deallocate_board(board); - board->online = 0; - dev_dbg(board->gpib_dev, "board offline\n"); - - return 0; -} - -/* - * IBLINES - * Poll the GPIB control lines and return their status in buf. - * - * LSB (bits 0-7) - VALID lines mask (lines that can be monitored). - * Next LSB (bits 8-15) - STATUS lines mask (lines that are currently set). - * - */ -int iblines(const struct gpib_board *board, short *lines) -{ - int retval; - - *lines = 0; - if (!board->interface->line_status) - return 0; - retval = board->interface->line_status(board); - if (retval < 0) - return retval; - *lines = retval; - return 0; -} - -/* - * IBRD - * Read up to 'length' bytes of data from the GPIB into buf. End - * on detection of END (EOI and or EOS) and set 'end_flag'. - * - * NOTE: - * 1. The interface is placed in the controller standby - * state prior to beginning the read. - * 2. Prior to calling ibrd, the intended devices as well - * as the interface board itself must be addressed by - * calling ibcmd. - */ - -int ibrd(struct gpib_board *board, u8 *buf, size_t length, int *end_flag, size_t *nbytes) -{ - ssize_t ret = 0; - int retval; - size_t bytes_read; - - *nbytes = 0; - *end_flag = 0; - if (length == 0) - return 0; - - if (board->master) { - retval = ibgts(board); - if (retval < 0) - return retval; - } - /* - * XXX resetting timer here could cause timeouts take longer than they should, - * since read_ioctl calls this - * function in a loop, there is probably a similar problem with writes/commands - */ - os_start_timer(board, board->usec_timeout); - - do { - ret = board->interface->read(board, buf, length - *nbytes, end_flag, &bytes_read); - if (ret < 0) - goto ibrd_out; - - buf += bytes_read; - *nbytes += bytes_read; - if (need_resched()) - schedule(); - } while (ret == 0 && *nbytes > 0 && *nbytes < length && *end_flag == 0); -ibrd_out: - os_remove_timer(board); - - return ret; -} - -/* - * IBRPP - * Conduct a parallel poll and return the byte in buf. - * - * NOTE: - * 1. Prior to conducting the poll the interface is placed - * in the controller active state. - */ -int ibrpp(struct gpib_board *board, u8 *result) -{ - int retval = 0; - - os_start_timer(board, board->usec_timeout); - retval = ibcac(board, 1, 1); - if (retval) - return -1; - - retval = board->interface->parallel_poll(board, result); - - os_remove_timer(board); - return retval; -} - -int ibppc(struct gpib_board *board, u8 configuration) -{ - configuration &= 0x1f; - board->interface->parallel_poll_configure(board, configuration); - board->parallel_poll_configuration = configuration; - - return 0; -} - -int ibrsv2(struct gpib_board *board, u8 status_byte, int new_reason_for_service) -{ - int board_status = ibstatus(board); - const unsigned int MSS = status_byte & request_service_bit; - - if ((board_status & CIC)) - return -EINVAL; - - if (MSS == 0 && new_reason_for_service) - return -EINVAL; - - if (board->interface->serial_poll_response2) { - board->interface->serial_poll_response2(board, status_byte, new_reason_for_service); - // fall back on simpler serial_poll_response if the behavior would be the same - } else if (board->interface->serial_poll_response && - (MSS == 0 || (MSS && new_reason_for_service))) { - board->interface->serial_poll_response(board, status_byte); - } else { - return -EOPNOTSUPP; - } - - return 0; -} - -/* - * IBSIC - * Send IFC for at least 100 microseconds. - * - * NOTE: - * 1. Ibsic must be called prior to the first call to - * ibcmd in order to initialize the bus and enable the - * interface to leave the controller idle state. - */ -int ibsic(struct gpib_board *board, unsigned int usec_duration) -{ - if (board->master == 0) - return -EINVAL; - - if (usec_duration < 100) - usec_duration = 100; - if (usec_duration > 1000) - usec_duration = 1000; - - dev_dbg(board->gpib_dev, "sending interface clear, delay = %ius\n", usec_duration); - board->interface->interface_clear(board, 1); - udelay(usec_duration); - board->interface->interface_clear(board, 0); - - return 0; -} - -int ibrsc(struct gpib_board *board, int request_control) -{ - int retval; - - if (!board->interface->request_system_control) - return -EPERM; - - retval = board->interface->request_system_control(board, request_control); - - if (retval) - return retval; - - board->master = request_control != 0; - - return 0; -} - -/* - * IBSRE - * Send REN true if v is non-zero or false if v is zero. - */ -int ibsre(struct gpib_board *board, int enable) -{ - if (board->master == 0) - return -EINVAL; - - board->interface->remote_enable(board, enable); /* set or clear REN */ - if (!enable) - usleep_range(100, 150); - - return 0; -} - -/* - * IBPAD - * change the GPIB address of the interface board. The address - * must be 0 through 30. ibonl resets the address to PAD. - */ -int ibpad(struct gpib_board *board, unsigned int addr) -{ - if (addr > MAX_GPIB_PRIMARY_ADDRESS) - return -EINVAL; - - board->pad = addr; - if (board->online) - board->interface->primary_address(board, board->pad); - dev_dbg(board->gpib_dev, "set primary addr to %i\n", board->pad); - return 0; -} - -/* - * IBSAD - * change the secondary GPIB address of the interface board. - * The address must be 0 through 30, or negative disables. ibonl resets the - * address to SAD. - */ -int ibsad(struct gpib_board *board, int addr) -{ - if (addr > MAX_GPIB_SECONDARY_ADDRESS) - return -EINVAL; - board->sad = addr; - if (board->online) { - if (board->sad >= 0) - board->interface->secondary_address(board, board->sad, 1); - else - board->interface->secondary_address(board, 0, 0); - } - dev_dbg(board->gpib_dev, "set secondary addr to %i\n", board->sad); - - return 0; -} - -/* - * IBEOS - * Set the end-of-string modes for I/O operations to v. - * - */ -int ibeos(struct gpib_board *board, int eos, int eosflags) -{ - int retval; - - if (eosflags & ~EOS_MASK) - return -EINVAL; - if (eosflags & REOS) { - retval = board->interface->enable_eos(board, eos, eosflags & BIN); - } else { - board->interface->disable_eos(board); - retval = 0; - } - return retval; -} - -int ibstatus(struct gpib_board *board) -{ - return general_ibstatus(board, NULL, 0, 0, NULL); -} - -int general_ibstatus(struct gpib_board *board, const struct gpib_status_queue *device, - int clear_mask, int set_mask, struct gpib_descriptor *desc) -{ - int status = 0; - short line_status; - - if (board->private_data) { - status = board->interface->update_status(board, clear_mask); - /* - * XXX should probably stop having drivers use TIMO bit in - * board->status to avoid confusion - */ - status &= ~TIMO; - /* get real SRQI status if we can */ - if (iblines(board, &line_status) == 0) { - if ((line_status & VALID_SRQ)) { - if ((line_status & BUS_SRQ)) - status |= SRQI; - else - status &= ~SRQI; - } - } - } - if (device) - if (num_status_bytes(device)) - status |= RQS; - - if (desc) { - if (set_mask & CMPL) - atomic_set(&desc->io_in_progress, 0); - else if (clear_mask & CMPL) - atomic_set(&desc->io_in_progress, 1); - - if (atomic_read(&desc->io_in_progress)) - status &= ~CMPL; - else - status |= CMPL; - } - if (num_gpib_events(&board->event_queue)) - status |= EVENT; - else - status &= ~EVENT; - - return status; -} - -struct wait_info { - struct gpib_board *board; - struct timer_list timer; - int timed_out; - unsigned long usec_timeout; -}; - -static void wait_timeout(struct timer_list *t) -{ - struct wait_info *winfo = timer_container_of(winfo, t, timer); - - winfo->timed_out = 1; - wake_up_interruptible(&winfo->board->wait); -} - -static void init_wait_info(struct wait_info *winfo) -{ - winfo->board = NULL; - winfo->timed_out = 0; - timer_setup_on_stack(&winfo->timer, wait_timeout, 0); -} - -static int wait_satisfied(struct wait_info *winfo, struct gpib_status_queue *status_queue, - int wait_mask, int *status, struct gpib_descriptor *desc) -{ - struct gpib_board *board = winfo->board; - int temp_status; - - if (mutex_lock_interruptible(&board->big_gpib_mutex)) - return -ERESTARTSYS; - - temp_status = general_ibstatus(board, status_queue, 0, 0, desc); - - mutex_unlock(&board->big_gpib_mutex); - - if (winfo->timed_out) - temp_status |= TIMO; - else - temp_status &= ~TIMO; - if (wait_mask & temp_status) { - *status = temp_status; - return 1; - } -// XXX does wait for END work? - return 0; -} - -/* install timer interrupt handler */ -static void start_wait_timer(struct wait_info *winfo) -/* Starts the timeout task */ -{ - winfo->timed_out = 0; - - if (winfo->usec_timeout > 0) - mod_timer(&winfo->timer, jiffies + usec_to_jiffies(winfo->usec_timeout)); -} - -static void remove_wait_timer(struct wait_info *winfo) -{ - timer_delete_sync(&winfo->timer); - timer_destroy_on_stack(&winfo->timer); -} - -/* - * IBWAIT - * Check or wait for a GPIB event to occur. The mask argument - * is a bit vector corresponding to the status bit vector. It - * has a bit set for each condition which can terminate the wait - * If the mask is 0 then - * no condition is waited for. - */ -int ibwait(struct gpib_board *board, int wait_mask, int clear_mask, int set_mask, - int *status, unsigned long usec_timeout, struct gpib_descriptor *desc) -{ - int retval = 0; - struct gpib_status_queue *status_queue; - struct wait_info winfo; - - if (desc->is_board) - status_queue = NULL; - else - status_queue = get_gpib_status_queue(board, desc->pad, desc->sad); - - if (wait_mask == 0) { - *status = general_ibstatus(board, status_queue, clear_mask, set_mask, desc); - return 0; - } - - mutex_unlock(&board->big_gpib_mutex); - - init_wait_info(&winfo); - winfo.board = board; - winfo.usec_timeout = usec_timeout; - start_wait_timer(&winfo); - - if (wait_event_interruptible(board->wait, wait_satisfied(&winfo, status_queue, - wait_mask, status, desc))) { - dev_dbg(board->gpib_dev, "wait interrupted\n"); - retval = -ERESTARTSYS; - } - remove_wait_timer(&winfo); - - if (retval) - return retval; - if (mutex_lock_interruptible(&board->big_gpib_mutex)) - return -ERESTARTSYS; - - /* make sure we only clear status bits that we are reporting */ - if (*status & clear_mask || set_mask) - general_ibstatus(board, status_queue, *status & clear_mask, set_mask, NULL); - - return 0; -} - -/* - * IBWRT - * Write cnt bytes of data from buf to the GPIB. The write - * operation terminates only on I/O complete. - * - * NOTE: - * 1. Prior to beginning the write, the interface is - * placed in the controller standby state. - * 2. Prior to calling ibwrt, the intended devices as - * well as the interface board itself must be - * addressed by calling ibcmd. - */ -int ibwrt(struct gpib_board *board, u8 *buf, size_t cnt, int send_eoi, size_t *bytes_written) -{ - int ret = 0; - int retval; - - if (cnt == 0) - return 0; - - if (board->master) { - retval = ibgts(board); - if (retval < 0) - return retval; - } - os_start_timer(board, board->usec_timeout); - ret = board->interface->write(board, buf, cnt, send_eoi, bytes_written); - - if (io_timed_out(board)) - ret = -ETIMEDOUT; - - os_remove_timer(board); - - return ret; -} - diff --git a/drivers/staging/gpib/common/ibsys.h b/drivers/staging/gpib/common/ibsys.h deleted file mode 100644 index e5a148f513a8..000000000000 --- a/drivers/staging/gpib/common/ibsys.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#include "gpibP.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MAX_GPIB_PRIMARY_ADDRESS 30 -#define MAX_GPIB_SECONDARY_ADDRESS 31 - -int gpib_allocate_board(struct gpib_board *board); -void gpib_deallocate_board(struct gpib_board *board); - -unsigned int num_status_bytes(const struct gpib_status_queue *dev); -int push_status_byte(struct gpib_board *board, struct gpib_status_queue *device, - u8 poll_byte); -int pop_status_byte(struct gpib_board *board, struct gpib_status_queue *device, - u8 *poll_byte); -struct gpib_status_queue *get_gpib_status_queue(struct gpib_board *board, - unsigned int pad, int sad); -int get_serial_poll_byte(struct gpib_board *board, unsigned int pad, int sad, - unsigned int usec_timeout, u8 *poll_byte); -int autopoll_all_devices(struct gpib_board *board); diff --git a/drivers/staging/gpib/eastwood/Makefile b/drivers/staging/gpib/eastwood/Makefile deleted file mode 100644 index 384825195f77..000000000000 --- a/drivers/staging/gpib/eastwood/Makefile +++ /dev/null @@ -1,3 +0,0 @@ - -obj-$(CONFIG_GPIB_FLUKE) += fluke_gpib.o - diff --git a/drivers/staging/gpib/eastwood/fluke_gpib.c b/drivers/staging/gpib/eastwood/fluke_gpib.c deleted file mode 100644 index 3ae848e3f738..000000000000 --- a/drivers/staging/gpib/eastwood/fluke_gpib.c +++ /dev/null @@ -1,1180 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * GPIB Driver for Fluke cda devices. Basically, its a driver for a (bugfixed) - * cb7210 connected to channel 0 of a pl330 dma controller. - * Author: Frank Mori Hess - * copyright: (C) 2006, 2010, 2015 Fluke Corporation - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "fluke_gpib.h" - -#include "gpibP.h" -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB Driver for Fluke cda devices"); - -static int fluke_attach_holdoff_all(struct gpib_board *board, - const struct gpib_board_config *config); -static int fluke_attach_holdoff_end(struct gpib_board *board, - const struct gpib_board_config *config); -static void fluke_detach(struct gpib_board *board); -static int fluke_config_dma(struct gpib_board *board, int output); -static irqreturn_t fluke_gpib_internal_interrupt(struct gpib_board *board); - -static struct platform_device *fluke_gpib_pdev; - -static u8 fluke_locking_read_byte(struct nec7210_priv *nec_priv, unsigned int register_number) -{ - u8 retval; - unsigned long flags; - - spin_lock_irqsave(&nec_priv->register_page_lock, flags); - retval = fluke_read_byte_nolock(nec_priv, register_number); - spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); - return retval; -} - -static void fluke_locking_write_byte(struct nec7210_priv *nec_priv, u8 byte, - unsigned int register_number) -{ - unsigned long flags; - - spin_lock_irqsave(&nec_priv->register_page_lock, flags); - fluke_write_byte_nolock(nec_priv, byte, register_number); - spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); -} - -// wrappers for interface functions -static int fluke_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); -} - -static int fluke_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); -} - -static int fluke_command(struct gpib_board *board, u8 *buffer, - size_t length, size_t *bytes_written) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); -} - -static int fluke_take_control(struct gpib_board *board, int synchronous) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_take_control(board, &priv->nec7210_priv, synchronous); -} - -static int fluke_go_to_standby(struct gpib_board *board) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_go_to_standby(board, &priv->nec7210_priv); -} - -static int fluke_request_system_control(struct gpib_board *board, int request_control) -{ - struct fluke_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - - return nec7210_request_system_control(board, nec_priv, request_control); -} - -static void fluke_interface_clear(struct gpib_board *board, int assert) -{ - struct fluke_priv *priv = board->private_data; - - nec7210_interface_clear(board, &priv->nec7210_priv, assert); -} - -static void fluke_remote_enable(struct gpib_board *board, int enable) -{ - struct fluke_priv *priv = board->private_data; - - nec7210_remote_enable(board, &priv->nec7210_priv, enable); -} - -static int fluke_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); -} - -static void fluke_disable_eos(struct gpib_board *board) -{ - struct fluke_priv *priv = board->private_data; - - nec7210_disable_eos(board, &priv->nec7210_priv); -} - -static unsigned int fluke_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); -} - -static int fluke_primary_address(struct gpib_board *board, unsigned int address) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_primary_address(board, &priv->nec7210_priv, address); -} - -static int fluke_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); -} - -static int fluke_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_parallel_poll(board, &priv->nec7210_priv, result); -} - -static void fluke_parallel_poll_configure(struct gpib_board *board, u8 configuration) -{ - struct fluke_priv *priv = board->private_data; - - nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); -} - -static void fluke_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct fluke_priv *priv = board->private_data; - - nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); -} - -static void fluke_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct fluke_priv *priv = board->private_data; - - nec7210_serial_poll_response(board, &priv->nec7210_priv, status); -} - -static u8 fluke_serial_poll_status(struct gpib_board *board) -{ - struct fluke_priv *priv = board->private_data; - - return nec7210_serial_poll_status(board, &priv->nec7210_priv); -} - -static void fluke_return_to_local(struct gpib_board *board) -{ - struct fluke_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - - write_byte(nec_priv, AUX_RTL2, AUXMR); - udelay(1); - write_byte(nec_priv, AUX_RTL, AUXMR); -} - -static int fluke_line_status(const struct gpib_board *board) -{ - int status = VALID_ALL; - int bsr_bits; - struct fluke_priv *e_priv; - - e_priv = board->private_data; - - bsr_bits = fluke_paged_read_byte(e_priv, BUS_STATUS, BUS_STATUS_PAGE); - - if ((bsr_bits & BSR_REN_BIT) == 0) - status |= BUS_REN; - if ((bsr_bits & BSR_IFC_BIT) == 0) - status |= BUS_IFC; - if ((bsr_bits & BSR_SRQ_BIT) == 0) - status |= BUS_SRQ; - if ((bsr_bits & BSR_EOI_BIT) == 0) - status |= BUS_EOI; - if ((bsr_bits & BSR_NRFD_BIT) == 0) - status |= BUS_NRFD; - if ((bsr_bits & BSR_NDAC_BIT) == 0) - status |= BUS_NDAC; - if ((bsr_bits & BSR_DAV_BIT) == 0) - status |= BUS_DAV; - if ((bsr_bits & BSR_ATN_BIT) == 0) - status |= BUS_ATN; - - return status; -} - -static int fluke_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned int retval; - - retval = nec7210_t1_delay(board, nec_priv, nano_sec); - - if (nano_sec <= 350) { - write_byte(nec_priv, AUX_HI_SPEED, AUXMR); - retval = 350; - } else { - write_byte(nec_priv, AUX_LO_SPEED, AUXMR); - } - return retval; -} - -static int lacs_or_read_ready(struct gpib_board *board) -{ - const struct fluke_priv *e_priv = board->private_data; - const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned long flags; - int retval; - - spin_lock_irqsave(&board->spinlock, flags); - retval = test_bit(LACS_NUM, &board->status) || test_bit(READ_READY_BN, &nec_priv->state); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -/* - * Wait until it is possible for a read to do something useful. This - * is not essential, it only exists to prevent RFD holdoff from being released pointlessly. - */ -static int wait_for_read(struct gpib_board *board) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - if (wait_event_interruptible(board->wait, - lacs_or_read_ready(board) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - return retval; -} - -/* - * Check if the SH state machine is in SGNS. We check twice since there is a very small chance - * we could be blowing through SGNS from SIDS to SDYS if there is already a - * byte available in the handshake state machine. We are interested - * in the case where the handshake is stuck in SGNS due to no byte being - * available to the chip (and thus we can be confident a dma transfer will - * result in at least one byte making it into the chip). This matters - * because we want to be confident before sending a "send eoi" auxilary - * command that we will be able to also put the associated data byte - * in the chip before any potential timeout. - */ -static int source_handshake_is_sgns(struct fluke_priv *e_priv) -{ - int i; - - for (i = 0; i < 2; ++i) { - if ((fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) & - SOURCE_HANDSHAKE_MASK) != SOURCE_HANDSHAKE_SGNS_BITS) { - return 0; - } - } - return 1; -} - -static int source_handshake_is_sids_or_sgns(struct fluke_priv *e_priv) -{ - unsigned int source_handshake_bits; - - source_handshake_bits = fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) & - SOURCE_HANDSHAKE_MASK; - - return (source_handshake_bits == SOURCE_HANDSHAKE_SGNS_BITS) || - (source_handshake_bits == SOURCE_HANDSHAKE_SIDS_BITS); -} - -/* - * Wait until the gpib chip is ready to accept a data out byte. - * If the chip is SGNS it is probably waiting for a a byte to - * be written to it. - */ -static int wait_for_data_out_ready(struct gpib_board *board) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - if (wait_event_interruptible(board->wait, - (test_bit(TACS_NUM, &board->status) && - source_handshake_is_sgns(e_priv)) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - return retval; -} - -static int wait_for_sids_or_sgns(struct gpib_board *board) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - if (wait_event_interruptible(board->wait, - source_handshake_is_sids_or_sgns(e_priv) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - return retval; -} - -static void fluke_dma_callback(void *arg) -{ - struct gpib_board *board = arg; - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE); - wake_up_interruptible(&board->wait); - - fluke_gpib_internal_interrupt(board); - clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); - clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); - - spin_unlock_irqrestore(&board->spinlock, flags); -} - -static int fluke_dma_write(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned long flags; - int retval = 0; - dma_addr_t address; - struct dma_async_tx_descriptor *tx_desc; - - *bytes_written = 0; - - if (WARN_ON_ONCE(length > e_priv->dma_buffer_size)) - return -EFAULT; - dmaengine_terminate_all(e_priv->dma_channel); - // write-clear counter - writel(0x0, e_priv->write_transfer_counter); - - memcpy(e_priv->dma_buffer, buffer, length); - address = dma_map_single(board->dev, e_priv->dma_buffer, - length, DMA_TO_DEVICE); - /* program dma controller */ - retval = fluke_config_dma(board, 1); - if (retval) - goto cleanup; - - tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx_desc) { - dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); - retval = -ENOMEM; - goto cleanup; - } - tx_desc->callback = fluke_dma_callback; - tx_desc->callback_param = board; - - spin_lock_irqsave(&board->spinlock, flags); - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); - dmaengine_submit(tx_desc); - dma_async_issue_pending(e_priv->dma_channel); - - clear_bit(WRITE_READY_BN, &nec_priv->state); - set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); - - spin_unlock_irqrestore(&board->spinlock, flags); - - // suspend until message is sent - if (wait_event_interruptible(board->wait, - ((readl(e_priv->write_transfer_counter) & - write_transfer_counter_mask) == length) || - test_bit(BUS_ERROR_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - } - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) - retval = -EIO; - // disable board's dma - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); - - dmaengine_terminate_all(e_priv->dma_channel); - // make sure fluke_dma_callback got called - if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state)) - fluke_dma_callback(board); - - /* - * if everything went fine, try to wait until last byte is actually - * transmitted across gpib (but don't try _too_ hard) - */ - if (retval == 0) - retval = wait_for_sids_or_sgns(board); - - *bytes_written = readl(e_priv->write_transfer_counter) & write_transfer_counter_mask; - if (WARN_ON_ONCE(*bytes_written > length)) - return -EFAULT; - -cleanup: - dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE); - return retval; -} - -static int fluke_accel_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - size_t remainder = length; - size_t transfer_size; - ssize_t retval = 0; - size_t dma_remainder = remainder; - - if (!e_priv->dma_channel) { - dev_err(board->gpib_dev, "No dma channel available, cannot do accel write."); - return -ENXIO; - } - - *bytes_written = 0; - if (length < 1) - return 0; - - clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME - - if (send_eoi) - --dma_remainder; - - while (dma_remainder > 0) { - size_t num_bytes; - - retval = wait_for_data_out_ready(board); - if (retval < 0) - break; - - transfer_size = (e_priv->dma_buffer_size < dma_remainder) ? - e_priv->dma_buffer_size : dma_remainder; - retval = fluke_dma_write(board, buffer, transfer_size, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - break; - dma_remainder -= num_bytes; - remainder -= num_bytes; - buffer += num_bytes; - if (need_resched()) - schedule(); - } - if (retval < 0) - return retval; - // handle sending of last byte with eoi - if (send_eoi) { - size_t num_bytes; - - if (WARN_ON_ONCE(remainder != 1)) - return -EFAULT; - - /* - * wait until we are sure we will be able to write the data byte - * into the chip before we send AUX_SEOI. This prevents a timeout - * scenerio where we send AUX_SEOI but then timeout without getting - * any bytes into the gpib chip. This will result in the first byte - * of the next write having a spurious EOI set on the first byte. - */ - retval = wait_for_data_out_ready(board); - if (retval < 0) - return retval; - - write_byte(nec_priv, AUX_SEOI, AUXMR); - retval = fluke_dma_write(board, buffer, remainder, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - remainder -= num_bytes; - } - return 0; -} - -static int fluke_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie) -{ - struct dma_tx_state state; - int result; - - result = dmaengine_pause(chan); - if (result < 0) { - pr_err("dma pause failed?\n"); - return result; - } - dmaengine_tx_status(chan, cookie, &state); - /* - * hardware doesn't support resume, so dont call this - * method unless the dma transfer is done. - */ - return state.residue; -} - -static int fluke_dma_read(struct gpib_board *board, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - unsigned long flags; - int residue; - dma_addr_t bus_address; - struct dma_async_tx_descriptor *tx_desc; - dma_cookie_t dma_cookie; - int i; - static const int timeout = 10; - - *bytes_read = 0; - *end = 0; - if (length == 0) - return 0; - - bus_address = dma_map_single(board->dev, e_priv->dma_buffer, - length, DMA_FROM_DEVICE); - - /* program dma controller */ - retval = fluke_config_dma(board, 0); - if (retval) { - dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); - return retval; - } - tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, - bus_address, length, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx_desc) { - dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); - dma_unmap_single(NULL, bus_address, length, DMA_FROM_DEVICE); - return -EIO; - } - tx_desc->callback = fluke_dma_callback; - tx_desc->callback_param = board; - - spin_lock_irqsave(&board->spinlock, flags); - // enable nec7210 dma - nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); - - dma_cookie = dmaengine_submit(tx_desc); - dma_async_issue_pending(e_priv->dma_channel); - - set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); - clear_bit(READ_READY_BN, &nec_priv->state); - - spin_unlock_irqrestore(&board->spinlock, flags); - // wait for data to transfer - if (wait_event_interruptible(board->wait, - test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0 || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - } - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - - /* - * If we woke up because of end, wait until the dma transfer has pulled - * the data byte associated with the end before we cancel the dma transfer. - */ - if (test_bit(RECEIVED_END_BN, &nec_priv->state)) { - for (i = 0; i < timeout; ++i) { - if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0) - break; - if ((read_byte(nec_priv, ADR0) & DATA_IN_STATUS) == 0) - break; - usleep_range(10, 15); - } - if (i == timeout) - pr_warn("fluke_gpib: timeout waiting for dma to transfer end data byte.\n"); - } - - // stop the dma transfer - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - /* - * delay a little just to make sure any bytes in dma controller's fifo get - * written to memory before we disable it - */ - usleep_range(10, 15); - residue = fluke_get_dma_residue(e_priv->dma_channel, dma_cookie); - if (WARN_ON_ONCE(residue > length || residue < 0)) - return -EFAULT; - *bytes_read += length - residue; - dmaengine_terminate_all(e_priv->dma_channel); - // make sure fluke_dma_callback got called - if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state)) - fluke_dma_callback(board); - - dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); - memcpy(buffer, e_priv->dma_buffer, *bytes_read); - - /* - * If we got an end interrupt, figure out if it was - * associated with the last byte we dma'd or with a - * byte still sitting on the cb7210. - */ - spin_lock_irqsave(&board->spinlock, flags); - if (test_bit(READ_READY_BN, &nec_priv->state) == 0) { - /* - * There is no byte sitting on the cb7210. If we - * saw an end interrupt, we need to deal with it now - */ - if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) - *end = 1; - } - spin_unlock_irqrestore(&board->spinlock, flags); - - return retval; -} - -static int fluke_accel_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - size_t remain = length; - size_t transfer_size; - int retval = 0; - size_t dma_nbytes; - - *end = 0; - *bytes_read = 0; - - smp_mb__before_atomic(); - clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME - smp_mb__after_atomic(); - - retval = wait_for_read(board); - if (retval < 0) - return retval; - - nec7210_release_rfd_holdoff(board, nec_priv); - - while (remain > 0) { - transfer_size = (e_priv->dma_buffer_size < remain) ? - e_priv->dma_buffer_size : remain; - retval = fluke_dma_read(board, buffer, transfer_size, end, &dma_nbytes); - remain -= dma_nbytes; - buffer += dma_nbytes; - *bytes_read += dma_nbytes; - if (*end) - break; - if (retval < 0) - return retval; - if (need_resched()) - schedule(); - } - - return retval; -} - -static struct gpib_interface fluke_unaccel_interface = { - .name = "fluke_unaccel", - .attach = fluke_attach_holdoff_all, - .detach = fluke_detach, - .read = fluke_read, - .write = fluke_write, - .command = fluke_command, - .take_control = fluke_take_control, - .go_to_standby = fluke_go_to_standby, - .request_system_control = fluke_request_system_control, - .interface_clear = fluke_interface_clear, - .remote_enable = fluke_remote_enable, - .enable_eos = fluke_enable_eos, - .disable_eos = fluke_disable_eos, - .parallel_poll = fluke_parallel_poll, - .parallel_poll_configure = fluke_parallel_poll_configure, - .parallel_poll_response = fluke_parallel_poll_response, - .line_status = fluke_line_status, - .update_status = fluke_update_status, - .primary_address = fluke_primary_address, - .secondary_address = fluke_secondary_address, - .serial_poll_response = fluke_serial_poll_response, - .serial_poll_status = fluke_serial_poll_status, - .t1_delay = fluke_t1_delay, - .return_to_local = fluke_return_to_local, -}; - -/* - * fluke_hybrid uses dma for writes but not for reads. Added - * to deal with occasional corruption of bytes seen when doing dma - * reads. From looking at the cb7210 vhdl, I believe the corruption - * is due to a hardware bug triggered by the cpu reading a cb7210 - * } - * register just as the dma controller is also doing a read. - */ - -static struct gpib_interface fluke_hybrid_interface = { - .name = "fluke_hybrid", - .attach = fluke_attach_holdoff_all, - .detach = fluke_detach, - .read = fluke_read, - .write = fluke_accel_write, - .command = fluke_command, - .take_control = fluke_take_control, - .go_to_standby = fluke_go_to_standby, - .request_system_control = fluke_request_system_control, - .interface_clear = fluke_interface_clear, - .remote_enable = fluke_remote_enable, - .enable_eos = fluke_enable_eos, - .disable_eos = fluke_disable_eos, - .parallel_poll = fluke_parallel_poll, - .parallel_poll_configure = fluke_parallel_poll_configure, - .parallel_poll_response = fluke_parallel_poll_response, - .line_status = fluke_line_status, - .update_status = fluke_update_status, - .primary_address = fluke_primary_address, - .secondary_address = fluke_secondary_address, - .serial_poll_response = fluke_serial_poll_response, - .serial_poll_status = fluke_serial_poll_status, - .t1_delay = fluke_t1_delay, - .return_to_local = fluke_return_to_local, -}; - -static struct gpib_interface fluke_interface = { - .name = "fluke", - .attach = fluke_attach_holdoff_end, - .detach = fluke_detach, - .read = fluke_accel_read, - .write = fluke_accel_write, - .command = fluke_command, - .take_control = fluke_take_control, - .go_to_standby = fluke_go_to_standby, - .request_system_control = fluke_request_system_control, - .interface_clear = fluke_interface_clear, - .remote_enable = fluke_remote_enable, - .enable_eos = fluke_enable_eos, - .disable_eos = fluke_disable_eos, - .parallel_poll = fluke_parallel_poll, - .parallel_poll_configure = fluke_parallel_poll_configure, - .parallel_poll_response = fluke_parallel_poll_response, - .line_status = fluke_line_status, - .update_status = fluke_update_status, - .primary_address = fluke_primary_address, - .secondary_address = fluke_secondary_address, - .serial_poll_response = fluke_serial_poll_response, - .serial_poll_status = fluke_serial_poll_status, - .t1_delay = fluke_t1_delay, - .return_to_local = fluke_return_to_local, -}; - -irqreturn_t fluke_gpib_internal_interrupt(struct gpib_board *board) -{ - int status0, status1, status2; - struct fluke_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - int retval = IRQ_NONE; - - if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS) - set_bit(READ_READY_BN, &nec_priv->state); - - status0 = fluke_paged_read_byte(priv, ISR0_IMR0, ISR0_IMR0_PAGE); - status1 = read_byte(nec_priv, ISR1); - status2 = read_byte(nec_priv, ISR2); - - if (status0 & FLUKE_IFCI_BIT) { - push_gpib_event(board, EVENT_IFC); - retval = IRQ_HANDLED; - } - - if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED) - retval = IRQ_HANDLED; - - if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS) { - if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state)) - set_bit(READ_READY_BN, &nec_priv->state); - else - clear_bit(READ_READY_BN, &nec_priv->state); - } - - if (retval == IRQ_HANDLED) - wake_up_interruptible(&board->wait); - - return retval; -} - -static irqreturn_t fluke_gpib_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - unsigned long flags; - irqreturn_t retval; - - spin_lock_irqsave(&board->spinlock, flags); - retval = fluke_gpib_internal_interrupt(board); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -static int fluke_allocate_private(struct gpib_board *board) -{ - struct fluke_priv *priv; - - board->private_data = kmalloc(sizeof(struct fluke_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - priv = board->private_data; - memset(priv, 0, sizeof(struct fluke_priv)); - init_nec7210_private(&priv->nec7210_priv); - priv->dma_buffer_size = 0x7ff; - priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL); - if (!priv->dma_buffer) - return -ENOMEM; - return 0; -} - -static void fluke_generic_detach(struct gpib_board *board) -{ - if (board->private_data) { - struct fluke_priv *e_priv = board->private_data; - - kfree(e_priv->dma_buffer); - kfree(board->private_data); - board->private_data = NULL; - } -} - -// generic part of attach functions shared by all cb7210 boards -static int fluke_generic_attach(struct gpib_board *board) -{ - struct fluke_priv *e_priv; - struct nec7210_priv *nec_priv; - int retval; - - board->status = 0; - - retval = fluke_allocate_private(board); - if (retval < 0) - return retval; - e_priv = board->private_data; - nec_priv = &e_priv->nec7210_priv; - nec_priv->read_byte = fluke_locking_read_byte; - nec_priv->write_byte = fluke_locking_write_byte; - nec_priv->offset = fluke_reg_offset; - nec_priv->type = CB7210; - return 0; -} - -static int fluke_config_dma(struct gpib_board *board, int output) -{ - struct fluke_priv *e_priv = board->private_data; - struct dma_slave_config config; - - config.src_maxburst = 1; - config.dst_maxburst = 1; - config.device_fc = true; - - if (output) { - config.direction = DMA_MEM_TO_DEV; - config.src_addr = 0; - config.dst_addr = e_priv->dma_port_res->start; - config.src_addr_width = 1; - config.dst_addr_width = 1; - } else { - config.direction = DMA_DEV_TO_MEM; - config.src_addr = e_priv->dma_port_res->start; - config.dst_addr = 0; - config.src_addr_width = 1; - config.dst_addr_width = 1; - } - return dmaengine_slave_config(e_priv->dma_channel, &config); -} - -static int fluke_init(struct fluke_priv *e_priv, struct gpib_board *board, int handshake_mode) -{ - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - - nec7210_board_reset(nec_priv, board); - write_byte(nec_priv, AUX_LO_SPEED, AUXMR); - /* - * set clock register for driving frequency - * ICR should be set to clock in megahertz (1-15) and to zero - * for clocks faster than 15 MHz (max 20MHz) - */ - write_byte(nec_priv, ICR | 10, AUXMR); - nec7210_set_handshake_mode(board, nec_priv, handshake_mode); - - nec7210_board_online(nec_priv, board); - - /* poll so we can detect ATN changes */ - if (gpib_request_pseudo_irq(board, fluke_gpib_interrupt)) { - dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n"); - return -EINVAL; - } - - fluke_paged_write_byte(e_priv, FLUKE_IFCIE_BIT, ISR0_IMR0, ISR0_IMR0_PAGE); - return 0; -} - -/* - * This function is passed to dma_request_channel() in order to - * select the pl330 dma channel which has been hardwired to - * the gpib controller. - */ -static bool gpib_dma_channel_filter(struct dma_chan *chan, void *filter_param) -{ - // select the channel which is wired to the gpib chip - return chan->chan_id == 0; -} - -static int fluke_attach_impl(struct gpib_board *board, const struct gpib_board_config *config, - unsigned int handshake_mode) -{ - struct fluke_priv *e_priv; - struct nec7210_priv *nec_priv; - int isr_flags = 0; - int retval; - int irq; - struct resource *res; - dma_cap_mask_t dma_cap; - - if (!fluke_gpib_pdev) { - dev_err(board->gpib_dev, "No fluke device was found, attach failed.\n"); - return -ENODEV; - } - - retval = fluke_generic_attach(board); - if (retval) - return retval; - - e_priv = board->private_data; - nec_priv = &e_priv->nec7210_priv; - nec_priv->offset = fluke_reg_offset; - board->dev = &fluke_gpib_pdev->dev; - - res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource\n"); - return -ENODEV; - } - - if (request_mem_region(res->start, - resource_size(res), - fluke_gpib_pdev->name) == NULL) { - dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); - return -ENXIO; - } - e_priv->gpib_iomem_res = res; - - nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, - resource_size(e_priv->gpib_iomem_res)); - if (!nec_priv->mmiobase) { - dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n"); - return -ENOMEM; - } - - res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for gpib dma port\n"); - return -ENODEV; - } - if (request_mem_region(res->start, - resource_size(res), - fluke_gpib_pdev->name) == NULL) { - dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); - return -ENXIO; - } - e_priv->dma_port_res = res; - - res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 2); - if (!res) { - dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for write transfer counter\n"); - return -ENODEV; - } - - if (request_mem_region(res->start, - resource_size(res), - fluke_gpib_pdev->name) == NULL) { - dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n"); - return -ENXIO; - } - e_priv->write_transfer_counter_res = res; - - e_priv->write_transfer_counter = ioremap(e_priv->write_transfer_counter_res->start, - resource_size(e_priv->write_transfer_counter_res)); - if (!e_priv->write_transfer_counter) { - dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n"); - return -ENOMEM; - } - - irq = platform_get_irq(fluke_gpib_pdev, 0); - if (irq < 0) - return -EBUSY; - retval = request_irq(irq, fluke_gpib_interrupt, isr_flags, fluke_gpib_pdev->name, board); - if (retval) { - dev_err(&fluke_gpib_pdev->dev, - "cannot register interrupt handler err=%d\n", - retval); - return retval; - } - e_priv->irq = irq; - - dma_cap_zero(dma_cap); - dma_cap_set(DMA_SLAVE, dma_cap); - e_priv->dma_channel = dma_request_channel(dma_cap, gpib_dma_channel_filter, NULL); - if (!e_priv->dma_channel) { - dev_err(board->gpib_dev, "failed to allocate a dma channel.\n"); - /* - * we don't error out here because unaccel interface will still - * work without dma - */ - } - - return fluke_init(e_priv, board, handshake_mode); -} - -int fluke_attach_holdoff_all(struct gpib_board *board, const struct gpib_board_config *config) -{ - return fluke_attach_impl(board, config, HR_HLDA); -} - -int fluke_attach_holdoff_end(struct gpib_board *board, const struct gpib_board_config *config) -{ - return fluke_attach_impl(board, config, HR_HLDE); -} - -void fluke_detach(struct gpib_board *board) -{ - struct fluke_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (e_priv) { - if (e_priv->dma_channel) - dma_release_channel(e_priv->dma_channel); - gpib_free_pseudo_irq(board); - nec_priv = &e_priv->nec7210_priv; - - if (nec_priv->mmiobase) { - fluke_paged_write_byte(e_priv, 0, ISR0_IMR0, ISR0_IMR0_PAGE); - nec7210_board_reset(nec_priv, board); - } - if (e_priv->irq) - free_irq(e_priv->irq, board); - if (e_priv->write_transfer_counter_res) { - release_mem_region(e_priv->write_transfer_counter_res->start, - resource_size(e_priv->write_transfer_counter_res)); - } - if (e_priv->dma_port_res) { - release_mem_region(e_priv->dma_port_res->start, - resource_size(e_priv->dma_port_res)); - } - if (e_priv->gpib_iomem_res) - release_mem_region(e_priv->gpib_iomem_res->start, - resource_size(e_priv->gpib_iomem_res)); - } - fluke_generic_detach(board); -} - -static int fluke_gpib_probe(struct platform_device *pdev) -{ - fluke_gpib_pdev = pdev; - return 0; -} - -static const struct of_device_id fluke_gpib_of_match[] = { - { .compatible = "flk,fgpib-4.0"}, - { {0} } -}; -MODULE_DEVICE_TABLE(of, fluke_gpib_of_match); - -static struct platform_driver fluke_gpib_platform_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = fluke_gpib_of_match, - }, - .probe = &fluke_gpib_probe -}; - -static int __init fluke_init_module(void) -{ - int result; - - result = platform_driver_register(&fluke_gpib_platform_driver); - if (result) { - pr_err("platform_driver_register failed: error = %d\n", result); - return result; - } - - result = gpib_register_driver(&fluke_unaccel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_unaccel; - } - - result = gpib_register_driver(&fluke_hybrid_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_hybrid; - } - - result = gpib_register_driver(&fluke_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_interface; - } - - return 0; - -err_interface: - gpib_unregister_driver(&fluke_hybrid_interface); -err_hybrid: - gpib_unregister_driver(&fluke_unaccel_interface); -err_unaccel: - platform_driver_unregister(&fluke_gpib_platform_driver); - - return result; -} - -static void __exit fluke_exit_module(void) -{ - gpib_unregister_driver(&fluke_unaccel_interface); - gpib_unregister_driver(&fluke_hybrid_interface); - gpib_unregister_driver(&fluke_interface); - platform_driver_unregister(&fluke_gpib_platform_driver); -} - -module_init(fluke_init_module); -module_exit(fluke_exit_module); diff --git a/drivers/staging/gpib/eastwood/fluke_gpib.h b/drivers/staging/gpib/eastwood/fluke_gpib.h deleted file mode 100644 index 493c200d0bbf..000000000000 --- a/drivers/staging/gpib/eastwood/fluke_gpib.h +++ /dev/null @@ -1,146 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * Author: Frank Mori Hess - * copyright: (C) 2006, 2010, 2015 Fluke Corporation - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include "nec7210.h" - -struct fluke_priv { - struct nec7210_priv nec7210_priv; - struct resource *gpib_iomem_res; - struct resource *write_transfer_counter_res; - struct resource *dma_port_res; - int irq; - struct dma_chan *dma_channel; - u8 *dma_buffer; - int dma_buffer_size; - void __iomem *write_transfer_counter; -}; - -// cb7210 specific registers and bits -enum cb7210_regs { - STATE1_REG = 0x4, - ISR0_IMR0 = 0x6, - BUS_STATUS = 0x7 -}; - -enum cb7210_page_in { - ISR0_IMR0_PAGE = 1, - BUS_STATUS_PAGE = 1, - STATE1_PAGE = 1 -}; - -/* IMR0 -- Interrupt Mode Register 0 */ -enum imr0_bits { - FLUKE_IFCIE_BIT = 0x8, /* interface clear interrupt */ -}; - -/* ISR0 -- Interrupt Status Register 0 */ -enum isr0_bits { - FLUKE_IFCI_BIT = 0x8, /* interface clear interrupt */ -}; - -enum state1_bits { - SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */ - SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */ - SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */ - SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */ - SOURCE_HANDSHAKE_MASK = 0x7 -}; - -/* - * we customized the cb7210 vhdl to give the "data in" status - * on the unused bit 7 of the address0 register. - */ -enum cb7210_address0 { - DATA_IN_STATUS = 0x80 -}; - -static inline int cb7210_page_in_bits(unsigned int page) -{ - return 0x50 | (page & 0xf); -} - -// don't use without locking nec_priv->register_page_lock -static inline u8 fluke_read_byte_nolock(struct nec7210_priv *nec_priv, - int register_num) -{ - u8 retval; - - retval = readl(nec_priv->mmiobase + register_num * nec_priv->offset); - return retval; -} - -// don't use without locking nec_priv->register_page_lock -static inline void fluke_write_byte_nolock(struct nec7210_priv *nec_priv, u8 data, - int register_num) -{ - writel(data, nec_priv->mmiobase + register_num * nec_priv->offset); -} - -static inline u8 fluke_paged_read_byte(struct fluke_priv *e_priv, - unsigned int register_num, unsigned int page) -{ - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - u8 retval; - unsigned long flags; - - spin_lock_irqsave(&nec_priv->register_page_lock, flags); - fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR); - udelay(1); - /* chip auto clears the page after a read */ - retval = fluke_read_byte_nolock(nec_priv, register_num); - spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); - return retval; -} - -static inline void fluke_paged_write_byte(struct fluke_priv *e_priv, u8 data, - unsigned int register_num, unsigned int page) -{ - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned long flags; - - spin_lock_irqsave(&nec_priv->register_page_lock, flags); - fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR); - udelay(1); - fluke_write_byte_nolock(nec_priv, data, register_num); - spin_unlock_irqrestore(&nec_priv->register_page_lock, flags); -} - -enum bus_status_bits { - BSR_ATN_BIT = 0x1, - BSR_EOI_BIT = 0x2, - BSR_SRQ_BIT = 0x4, - BSR_IFC_BIT = 0x8, - BSR_REN_BIT = 0x10, - BSR_DAV_BIT = 0x20, - BSR_NRFD_BIT = 0x40, - BSR_NDAC_BIT = 0x80, -}; - -enum cb7210_aux_cmds { -/* - * AUX_RTL2 is an undocumented aux command which causes cb7210 to assert - * (and keep asserted) local rtl message. This is used in conjunction - * with the (stupid) cb7210 implementation - * of the normal nec7210 AUX_RTL aux command, which - * causes the rtl message to toggle between on and off. - */ - AUX_RTL2 = 0xd, - AUX_NBAF = 0xe, // new byte available false (also clears seoi) - AUX_LO_SPEED = 0x40, - AUX_HI_SPEED = 0x41, -}; - -enum { - fluke_reg_offset = 4, - fluke_num_regs = 8, - write_transfer_counter_mask = 0x7ff, -}; diff --git a/drivers/staging/gpib/fmh_gpib/Makefile b/drivers/staging/gpib/fmh_gpib/Makefile deleted file mode 100644 index cc4d9e7cd5cd..000000000000 --- a/drivers/staging/gpib/fmh_gpib/Makefile +++ /dev/null @@ -1,2 +0,0 @@ - -obj-$(CONFIG_GPIB_FMH) += fmh_gpib.o diff --git a/drivers/staging/gpib/fmh_gpib/fmh_gpib.c b/drivers/staging/gpib/fmh_gpib/fmh_gpib.c deleted file mode 100644 index f7bfb4a8e553..000000000000 --- a/drivers/staging/gpib/fmh_gpib/fmh_gpib.c +++ /dev/null @@ -1,1754 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * GPIB Driver for fmh_gpib_core, see - * https://github.com/fmhess/fmh_gpib_core - * - * More specifically, it is a driver for the hardware arrangement described by - * src/examples/fmh_gpib_top.vhd in the fmh_gpib_core repository. - * - * Author: Frank Mori Hess - * Copyright: (C) 2006, 2010, 2015 Fluke Corporation - * (C) 2017 Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "fmh_gpib.h" - -#include "gpibP.h" -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB Driver for fmh_gpib_core"); -MODULE_AUTHOR("Frank Mori Hess "); - -static irqreturn_t fmh_gpib_interrupt(int irq, void *arg); -static int fmh_gpib_attach_holdoff_all(struct gpib_board *board, - const struct gpib_board_config *config); -static int fmh_gpib_attach_holdoff_end(struct gpib_board *board, - const struct gpib_board_config *config); -static void fmh_gpib_detach(struct gpib_board *board); -static int fmh_gpib_pci_attach_holdoff_all(struct gpib_board *board, - const struct gpib_board_config *config); -static int fmh_gpib_pci_attach_holdoff_end(struct gpib_board *board, - const struct gpib_board_config *config); -static void fmh_gpib_pci_detach(struct gpib_board *board); -static int fmh_gpib_config_dma(struct gpib_board *board, int output); -static irqreturn_t fmh_gpib_internal_interrupt(struct gpib_board *board); -static struct platform_driver fmh_gpib_platform_driver; -static struct pci_driver fmh_gpib_pci_driver; - -// wrappers for interface functions -static int fmh_gpib_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); -} - -static int fmh_gpib_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); -} - -static int fmh_gpib_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); -} - -static int fmh_gpib_take_control(struct gpib_board *board, int synchronous) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_take_control(board, &priv->nec7210_priv, synchronous); -} - -static int fmh_gpib_go_to_standby(struct gpib_board *board) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_go_to_standby(board, &priv->nec7210_priv); -} - -static int fmh_gpib_request_system_control(struct gpib_board *board, int request_control) -{ - struct fmh_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - - return nec7210_request_system_control(board, nec_priv, request_control); -} - -static void fmh_gpib_interface_clear(struct gpib_board *board, int assert) -{ - struct fmh_priv *priv = board->private_data; - - nec7210_interface_clear(board, &priv->nec7210_priv, assert); -} - -static void fmh_gpib_remote_enable(struct gpib_board *board, int enable) -{ - struct fmh_priv *priv = board->private_data; - - nec7210_remote_enable(board, &priv->nec7210_priv, enable); -} - -static int fmh_gpib_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); -} - -static void fmh_gpib_disable_eos(struct gpib_board *board) -{ - struct fmh_priv *priv = board->private_data; - - nec7210_disable_eos(board, &priv->nec7210_priv); -} - -static unsigned int fmh_gpib_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); -} - -static int fmh_gpib_primary_address(struct gpib_board *board, unsigned int address) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_primary_address(board, &priv->nec7210_priv, address); -} - -static int fmh_gpib_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); -} - -static int fmh_gpib_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_parallel_poll(board, &priv->nec7210_priv, result); -} - -static void fmh_gpib_parallel_poll_configure(struct gpib_board *board, u8 configuration) -{ - struct fmh_priv *priv = board->private_data; - - nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration); -} - -static void fmh_gpib_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct fmh_priv *priv = board->private_data; - - nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); -} - -static void fmh_gpib_local_parallel_poll_mode(struct gpib_board *board, int local) -{ - struct fmh_priv *priv = board->private_data; - - if (local) { - write_byte(&priv->nec7210_priv, AUX_I_REG | LOCAL_PPOLL_MODE_BIT, AUXMR); - } else { - /* - * For fmh_gpib_core, remote parallel poll config mode is unaffected by the - * state of the disable bit of the parallel poll register (unlike the tnt4882). - * So, we don't need to worry about that. - */ - write_byte(&priv->nec7210_priv, AUX_I_REG | 0x0, AUXMR); - } -} - -static void fmh_gpib_serial_poll_response2(struct gpib_board *board, u8 status, - int new_reason_for_service) -{ - struct fmh_priv *priv = board->private_data; - unsigned long flags; - const int MSS = status & request_service_bit; - const int reqt = MSS && new_reason_for_service; - const int reqf = MSS == 0; - - spin_lock_irqsave(&board->spinlock, flags); - if (reqt) { - priv->nec7210_priv.srq_pending = 1; - clear_bit(SPOLL_NUM, &board->status); - } else if (reqf) { - priv->nec7210_priv.srq_pending = 0; - } - - if (reqt) { - /* - * It may seem like a race to issue reqt before updating - * the status byte, but it is not. The chip does not - * issue the reqt until the SPMR is written to at - * a later time. - */ - write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR); - } else if (reqf) { - write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR); - } - /* - * We need to always zero bit 6 of the status byte before writing it to - * the SPMR to insure we are using - * serial poll mode SP1, and not accidentally triggering mode SP3. - */ - write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR); - spin_unlock_irqrestore(&board->spinlock, flags); -} - -static u8 fmh_gpib_serial_poll_status(struct gpib_board *board) -{ - struct fmh_priv *priv = board->private_data; - - return nec7210_serial_poll_status(board, &priv->nec7210_priv); -} - -static void fmh_gpib_return_to_local(struct gpib_board *board) -{ - struct fmh_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - - write_byte(nec_priv, AUX_RTL2, AUXMR); - udelay(1); - write_byte(nec_priv, AUX_RTL, AUXMR); -} - -static int fmh_gpib_line_status(const struct gpib_board *board) -{ - int status = VALID_ALL; - int bsr_bits; - struct fmh_priv *e_priv; - struct nec7210_priv *nec_priv; - - e_priv = board->private_data; - nec_priv = &e_priv->nec7210_priv; - - bsr_bits = read_byte(nec_priv, BUS_STATUS_REG); - - if ((bsr_bits & BSR_REN_BIT) == 0) - status |= BUS_REN; - if ((bsr_bits & BSR_IFC_BIT) == 0) - status |= BUS_IFC; - if ((bsr_bits & BSR_SRQ_BIT) == 0) - status |= BUS_SRQ; - if ((bsr_bits & BSR_EOI_BIT) == 0) - status |= BUS_EOI; - if ((bsr_bits & BSR_NRFD_BIT) == 0) - status |= BUS_NRFD; - if ((bsr_bits & BSR_NDAC_BIT) == 0) - status |= BUS_NDAC; - if ((bsr_bits & BSR_DAV_BIT) == 0) - status |= BUS_DAV; - if ((bsr_bits & BSR_ATN_BIT) == 0) - status |= BUS_ATN; - - return status; -} - -static int fmh_gpib_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned int retval; - - retval = nec7210_t1_delay(board, nec_priv, nano_sec); - - if (nano_sec <= 350) { - write_byte(nec_priv, AUX_HI_SPEED, AUXMR); - retval = 350; - } else { - write_byte(nec_priv, AUX_LO_SPEED, AUXMR); - } - return retval; -} - -static int lacs_or_read_ready(struct gpib_board *board) -{ - const struct fmh_priv *e_priv = board->private_data; - const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - retval = test_bit(LACS_NUM, &board->status) || - test_bit(READ_READY_BN, &nec_priv->state); - spin_unlock_irqrestore(&board->spinlock, flags); - - return retval; -} - -static int wait_for_read(struct gpib_board *board) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - if (wait_event_interruptible(board->wait, - lacs_or_read_ready(board) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - return retval; -} - -static int wait_for_rx_fifo_half_full_or_end(struct gpib_board *board) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - if (wait_event_interruptible(board->wait, - (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & - RX_FIFO_HALF_FULL) || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - return retval; -} - -/* - * Wait until the gpib chip is ready to accept a data out byte. - */ -static int wait_for_data_out_ready(struct gpib_board *board) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - if (wait_event_interruptible(board->wait, - (test_bit(TACS_NUM, &board->status) && - (read_byte(nec_priv, EXT_STATUS_1_REG) & - DATA_OUT_STATUS_BIT)) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - - return retval; -} - -static void fmh_gpib_dma_callback(void *arg) -{ - struct gpib_board *board = arg; - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE); - wake_up_interruptible(&board->wait); - - fmh_gpib_internal_interrupt(board); - - clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); - clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); - - spin_unlock_irqrestore(&board->spinlock, flags); -} - -/* - * returns true when all the bytes of a write have been transferred to - * the chip and successfully transferred out over the gpib bus. - */ -static int fmh_gpib_all_bytes_are_sent(struct fmh_priv *e_priv) -{ - if (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & fifo_xfer_counter_mask) - return 0; - - if ((read_byte(&e_priv->nec7210_priv, EXT_STATUS_1_REG) & DATA_OUT_STATUS_BIT) == 0) - return 0; - - return 1; -} - -static int fmh_gpib_dma_write(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned long flags; - int retval = 0; - dma_addr_t address; - struct dma_async_tx_descriptor *tx_desc; - - *bytes_written = 0; - if (WARN_ON_ONCE(length > e_priv->dma_buffer_size)) - return -EFAULT; - dmaengine_terminate_all(e_priv->dma_channel); - memcpy(e_priv->dma_buffer, buffer, length); - address = dma_map_single(board->dev, e_priv->dma_buffer, length, DMA_TO_DEVICE); - if (dma_mapping_error(board->dev, address)) - dev_err(board->gpib_dev, "dma mapping error in dma write!\n"); - /* program dma controller */ - retval = fmh_gpib_config_dma(board, 1); - if (retval) - goto cleanup; - - tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx_desc) { - dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); - retval = -ENOMEM; - goto cleanup; - } - tx_desc->callback = fmh_gpib_dma_callback; - tx_desc->callback_param = board; - - spin_lock_irqsave(&board->spinlock, flags); - fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); - fifos_write(e_priv, TX_FIFO_DMA_REQUEST_ENABLE | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); - - dmaengine_submit(tx_desc); - dma_async_issue_pending(e_priv->dma_channel); - clear_bit(WRITE_READY_BN, &nec_priv->state); - set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state); - - spin_unlock_irqrestore(&board->spinlock, flags); - - // suspend until message is sent - if (wait_event_interruptible(board->wait, - fmh_gpib_all_bytes_are_sent(e_priv) || - test_bit(BUS_ERROR_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) - retval = -EIO; - // disable board's dma - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); - fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); - - dmaengine_terminate_all(e_priv->dma_channel); - // make sure fmh_gpib_dma_callback got called - if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state)) - fmh_gpib_dma_callback(board); - - *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & - fifo_xfer_counter_mask); - if (WARN_ON_ONCE(*bytes_written > length)) - return -EFAULT; -cleanup: - dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE); - return retval; -} - -static int fmh_gpib_accel_write(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - size_t remainder = length; - size_t transfer_size; - ssize_t retval = 0; - size_t dma_remainder = remainder; - - if (!e_priv->dma_channel) { - dev_err(board->gpib_dev, "No dma channel available, cannot do accel write."); - return -ENXIO; - } - - *bytes_written = 0; - if (length < 1) - return 0; - - smp_mb__before_atomic(); - clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME - smp_mb__after_atomic(); - - if (send_eoi) - --dma_remainder; - - while (dma_remainder > 0) { - size_t num_bytes; - - retval = wait_for_data_out_ready(board); - if (retval < 0) - break; - - transfer_size = (e_priv->dma_buffer_size < dma_remainder) ? - e_priv->dma_buffer_size : dma_remainder; - retval = fmh_gpib_dma_write(board, buffer, transfer_size, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - break; - dma_remainder -= num_bytes; - remainder -= num_bytes; - buffer += num_bytes; - if (need_resched()) - schedule(); - } - if (retval < 0) - return retval; - // handle sending of last byte with eoi - if (send_eoi) { - size_t num_bytes; - - if (WARN_ON_ONCE(remainder != 1)) - return -EFAULT; - - /* - * wait until we are sure we will be able to write the data byte - * into the chip before we send AUX_SEOI. This prevents a timeout - * scenario where we send AUX_SEOI but then timeout without getting - * any bytes into the gpib chip. This will result in the first byte - * of the next write having a spurious EOI set on the first byte. - */ - retval = wait_for_data_out_ready(board); - if (retval < 0) - return retval; - - write_byte(nec_priv, AUX_SEOI, AUXMR); - retval = fmh_gpib_dma_write(board, buffer, remainder, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - remainder -= num_bytes; - } - return 0; -} - -static int fmh_gpib_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie) -{ - struct dma_tx_state state; - int result; - - result = dmaengine_pause(chan); - if (result < 0) { - pr_err("dma pause failed?\n"); - return result; - } - dmaengine_tx_status(chan, cookie, &state); - /* - * dma330 hardware doesn't support resume, so dont call this - * method unless the dma transfer is done. - */ - return state.residue; -} - -static int wait_for_tx_fifo_half_empty(struct gpib_board *board) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - if (wait_event_interruptible(board->wait, - (test_bit(TACS_NUM, &board->status) && - (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & - TX_FIFO_HALF_EMPTY)) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - - return retval; -} - -/* - * supports writing a chunk of data whose length must fit into the hardware'd xfer counter, - * called in a loop by fmh_gpib_fifo_write() - */ -static int fmh_gpib_fifo_write_countable(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - unsigned int remainder; - - *bytes_written = 0; - if (WARN_ON_ONCE(length > fifo_xfer_counter_mask)) - return -EFAULT; - - fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); - fifos_write(e_priv, TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); - - remainder = length; - while (remainder > 0) { - int i; - - fifos_write(e_priv, TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG); - retval = wait_for_tx_fifo_half_empty(board); - if (retval < 0) - goto cleanup; - - for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && remainder > 0; ++i) { - unsigned int data_value = *buffer; - - if (send_eoi && remainder == 1) - data_value |= FIFO_DATA_EOI_FLAG; - fifos_write(e_priv, data_value, FIFO_DATA_REG); - ++buffer; - --remainder; - } - } - - // suspend until last byte is sent - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE); - if (wait_event_interruptible(board->wait, - fmh_gpib_all_bytes_are_sent(e_priv) || - test_bit(BUS_ERROR_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) - retval = -EIO; - -cleanup: - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); - fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); - - *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & - fifo_xfer_counter_mask); - if (WARN_ON_ONCE(*bytes_written > length)) - return -EFAULT; - - return retval; -} - -static int fmh_gpib_fifo_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - size_t remainder = length; - size_t transfer_size; - ssize_t retval = 0; - - *bytes_written = 0; - if (length < 1) - return 0; - - clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME - - while (remainder > 0) { - size_t num_bytes; - int last_pass; - - retval = wait_for_data_out_ready(board); - if (retval < 0) - break; - - if (fifo_xfer_counter_mask < remainder) { - // round transfer size to a multiple of half fifo size - transfer_size = (fifo_xfer_counter_mask / - fmh_gpib_half_fifo_size(e_priv)) * - fmh_gpib_half_fifo_size(e_priv); - last_pass = 0; - } else { - transfer_size = remainder; - last_pass = 1; - } - retval = fmh_gpib_fifo_write_countable(board, buffer, transfer_size, - last_pass && send_eoi, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - break; - remainder -= num_bytes; - buffer += num_bytes; - if (need_resched()) - schedule(); - } - - return retval; -} - -static int fmh_gpib_dma_read(struct gpib_board *board, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - unsigned long flags; - int residue; - int wait_retval; - dma_addr_t bus_address; - struct dma_async_tx_descriptor *tx_desc; - dma_cookie_t dma_cookie; - - *bytes_read = 0; - *end = 0; - if (length == 0) - return 0; - - bus_address = dma_map_single(board->dev, e_priv->dma_buffer, - length, DMA_FROM_DEVICE); - if (dma_mapping_error(board->dev, bus_address)) - dev_err(board->gpib_dev, "dma mapping error in dma read!"); - - /* program dma controller */ - retval = fmh_gpib_config_dma(board, 0); - if (retval) { - dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); - return retval; - } - tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, bus_address, - length, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx_desc) { - dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n"); - dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); - return -EIO; - } - tx_desc->callback = fmh_gpib_dma_callback; - tx_desc->callback_param = board; - - spin_lock_irqsave(&board->spinlock, flags); - // enable nec7210 dma - fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); - fifos_write(e_priv, RX_FIFO_DMA_REQUEST_ENABLE | RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); - nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); - - dma_cookie = dmaengine_submit(tx_desc); - dma_async_issue_pending(e_priv->dma_channel); - - set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state); - - spin_unlock_irqrestore(&board->spinlock, flags); - - // wait for data to transfer - wait_retval = wait_event_interruptible(board->wait, - test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) - == 0 || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status)); - if (wait_retval) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - // stop the dma transfer - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); - /* - * give time for pl330 to transfer any in-flight data, since - * pl330 will throw it away when dmaengine_pause is called. - */ - usleep_range(10, 15); - residue = fmh_gpib_get_dma_residue(e_priv->dma_channel, dma_cookie); - if (WARN_ON_ONCE(residue > length || residue < 0)) - return -EFAULT; - *bytes_read += length - residue; - dmaengine_terminate_all(e_priv->dma_channel); - // make sure fmh_gpib_dma_callback got called - if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state)) - fmh_gpib_dma_callback(board); - - dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE); - memcpy(buffer, e_priv->dma_buffer, *bytes_read); - - /* Manually read any dregs out of fifo. */ - while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) { - if ((*bytes_read) >= length) { - dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d residue=%d\n", - (int)(*bytes_read), (int)length, (int)residue); - break; - } - buffer[(*bytes_read)++] = fifos_read(e_priv, FIFO_DATA_REG) & fifo_data_mask; - } - - /* - * If we got an end interrupt, figure out if it was - * associated with the last byte we dma'd or with a - * byte still sitting on the cb7210. - */ - spin_lock_irqsave(&board->spinlock, flags); - if (*bytes_read > 0 && test_bit(READ_READY_BN, &nec_priv->state) == 0) { - /* - * If there is no byte sitting on the cb7210 and we - * saw an end, we need to deal with it now - */ - if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) - *end = 1; - } - spin_unlock_irqrestore(&board->spinlock, flags); - - return retval; -} - -static void fmh_gpib_release_rfd_holdoff(struct gpib_board *board, struct fmh_priv *e_priv) -{ - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned int ext_status_1; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - - ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG); - - /* - * if there is an end byte sitting on the chip, don't release - * holdoff. We want it left set after we read out the end - * byte. - */ - if ((ext_status_1 & (DATA_IN_STATUS_BIT | END_STATUS_BIT)) != - (DATA_IN_STATUS_BIT | END_STATUS_BIT)) { - if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT) - write_byte(nec_priv, AUX_FH, AUXMR); - - /* - * Check if an end byte raced in before we executed the AUX_FH command. - * If it did, we want to make sure the rfd holdoff is in effect. The end - * byte can arrive since - * AUX_RFD_HOLDOFF_ASAP doesn't immediately force the acceptor handshake - * to leave ACRS. - */ - if ((read_byte(nec_priv, EXT_STATUS_1_REG) & - (RFD_HOLDOFF_STATUS_BIT | DATA_IN_STATUS_BIT | END_STATUS_BIT)) == - (DATA_IN_STATUS_BIT | END_STATUS_BIT)) { - write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - } else { - clear_bit(RFD_HOLDOFF_BN, &nec_priv->state); - } - } - spin_unlock_irqrestore(&board->spinlock, flags); -} - -static int fmh_gpib_accel_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - size_t remain = length; - size_t transfer_size; - int retval = 0; - size_t dma_nbytes; - unsigned long flags; - - smp_mb__before_atomic(); - clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME - smp_mb__after_atomic(); - *end = 0; - *bytes_read = 0; - - retval = wait_for_read(board); - if (retval < 0) - return retval; - - fmh_gpib_release_rfd_holdoff(board, e_priv); - while (remain > 0) { - transfer_size = (e_priv->dma_buffer_size < remain) ? - e_priv->dma_buffer_size : remain; - retval = fmh_gpib_dma_read(board, buffer, transfer_size, end, &dma_nbytes); - remain -= dma_nbytes; - buffer += dma_nbytes; - *bytes_read += dma_nbytes; - if (*end) - break; - if (retval < 0) - break; - if (need_resched()) - schedule(); - } - - spin_lock_irqsave(&board->spinlock, flags); - if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state) == 0) { - write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - } - spin_unlock_irqrestore(&board->spinlock, flags); - - return retval; -} - -/* - * Read a chunk of data whose length is within the limits of the hardware's - * xfer counter. Called in a loop from fmh_gpib_fifo_read(). - */ -static int fmh_gpib_fifo_read_countable(struct gpib_board *board, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - int retval = 0; - - *bytes_read = 0; - *end = 0; - if (length == 0) - return 0; - - fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG); - fifos_write(e_priv, RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); - nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); - - while (*bytes_read < length && *end == 0) { - int i; - - fifos_write(e_priv, RX_FIFO_HALF_FULL_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG); - retval = wait_for_rx_fifo_half_full_or_end(board); - if (retval < 0) - goto cleanup; - - for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && *end == 0; ++i) { - unsigned int data_value; - - data_value = fifos_read(e_priv, FIFO_DATA_REG); - buffer[(*bytes_read)++] = data_value & fifo_data_mask; - if (data_value & FIFO_DATA_EOI_FLAG) - *end = 1; - } - } - -cleanup: - // stop the transfer - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); - - /* Manually read any dregs out of fifo. */ - while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) { - unsigned int data_value; - - if ((*bytes_read) >= length) { - dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d\n", - (int)(*bytes_read), (int)length); - break; - } - data_value = fifos_read(e_priv, FIFO_DATA_REG); - buffer[(*bytes_read)++] = data_value & fifo_data_mask; - if (data_value & FIFO_DATA_EOI_FLAG) - *end = 1; - } - - return retval; -} - -static int fmh_gpib_fifo_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - size_t remain = length; - size_t transfer_size; - int retval = 0; - size_t nbytes; - unsigned long flags; - - clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME - *end = 0; - *bytes_read = 0; - - /* - * Do a little prep with data in interrupt so that following wait_for_read() - * will wake up if a data byte is received. - */ - nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, HR_DIIE); - fmh_gpib_interrupt(0, board); - - retval = wait_for_read(board); - if (retval < 0) - return retval; - - fmh_gpib_release_rfd_holdoff(board, e_priv); - while (remain > 0) { - if (fifo_xfer_counter_mask < remain) { - // round transfer size to a multiple of half fifo size - transfer_size = (fifo_xfer_counter_mask / - fmh_gpib_half_fifo_size(e_priv)) * - fmh_gpib_half_fifo_size(e_priv); - } else { - transfer_size = remain; - } - retval = fmh_gpib_fifo_read_countable(board, buffer, transfer_size, end, &nbytes); - remain -= nbytes; - buffer += nbytes; - *bytes_read += nbytes; - if (*end) - break; - if (retval < 0) - break; - if (need_resched()) - schedule(); - } - - if (*end == 0) { - spin_lock_irqsave(&board->spinlock, flags); - write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - spin_unlock_irqrestore(&board->spinlock, flags); - } - - return retval; -} - -static struct gpib_interface fmh_gpib_unaccel_interface = { - .name = "fmh_gpib_unaccel", - .attach = fmh_gpib_attach_holdoff_all, - .detach = fmh_gpib_detach, - .read = fmh_gpib_read, - .write = fmh_gpib_write, - .command = fmh_gpib_command, - .take_control = fmh_gpib_take_control, - .go_to_standby = fmh_gpib_go_to_standby, - .request_system_control = fmh_gpib_request_system_control, - .interface_clear = fmh_gpib_interface_clear, - .remote_enable = fmh_gpib_remote_enable, - .enable_eos = fmh_gpib_enable_eos, - .disable_eos = fmh_gpib_disable_eos, - .parallel_poll = fmh_gpib_parallel_poll, - .parallel_poll_configure = fmh_gpib_parallel_poll_configure, - .parallel_poll_response = fmh_gpib_parallel_poll_response, - .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, - .line_status = fmh_gpib_line_status, - .update_status = fmh_gpib_update_status, - .primary_address = fmh_gpib_primary_address, - .secondary_address = fmh_gpib_secondary_address, - .serial_poll_response2 = fmh_gpib_serial_poll_response2, - .serial_poll_status = fmh_gpib_serial_poll_status, - .t1_delay = fmh_gpib_t1_delay, - .return_to_local = fmh_gpib_return_to_local, -}; - -static struct gpib_interface fmh_gpib_interface = { - .name = "fmh_gpib", - .attach = fmh_gpib_attach_holdoff_end, - .detach = fmh_gpib_detach, - .read = fmh_gpib_accel_read, - .write = fmh_gpib_accel_write, - .command = fmh_gpib_command, - .take_control = fmh_gpib_take_control, - .go_to_standby = fmh_gpib_go_to_standby, - .request_system_control = fmh_gpib_request_system_control, - .interface_clear = fmh_gpib_interface_clear, - .remote_enable = fmh_gpib_remote_enable, - .enable_eos = fmh_gpib_enable_eos, - .disable_eos = fmh_gpib_disable_eos, - .parallel_poll = fmh_gpib_parallel_poll, - .parallel_poll_configure = fmh_gpib_parallel_poll_configure, - .parallel_poll_response = fmh_gpib_parallel_poll_response, - .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, - .line_status = fmh_gpib_line_status, - .update_status = fmh_gpib_update_status, - .primary_address = fmh_gpib_primary_address, - .secondary_address = fmh_gpib_secondary_address, - .serial_poll_response2 = fmh_gpib_serial_poll_response2, - .serial_poll_status = fmh_gpib_serial_poll_status, - .t1_delay = fmh_gpib_t1_delay, - .return_to_local = fmh_gpib_return_to_local, -}; - -static struct gpib_interface fmh_gpib_pci_interface = { - .name = "fmh_gpib_pci", - .attach = fmh_gpib_pci_attach_holdoff_end, - .detach = fmh_gpib_pci_detach, - .read = fmh_gpib_fifo_read, - .write = fmh_gpib_fifo_write, - .command = fmh_gpib_command, - .take_control = fmh_gpib_take_control, - .go_to_standby = fmh_gpib_go_to_standby, - .request_system_control = fmh_gpib_request_system_control, - .interface_clear = fmh_gpib_interface_clear, - .remote_enable = fmh_gpib_remote_enable, - .enable_eos = fmh_gpib_enable_eos, - .disable_eos = fmh_gpib_disable_eos, - .parallel_poll = fmh_gpib_parallel_poll, - .parallel_poll_configure = fmh_gpib_parallel_poll_configure, - .parallel_poll_response = fmh_gpib_parallel_poll_response, - .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, - .line_status = fmh_gpib_line_status, - .update_status = fmh_gpib_update_status, - .primary_address = fmh_gpib_primary_address, - .secondary_address = fmh_gpib_secondary_address, - .serial_poll_response2 = fmh_gpib_serial_poll_response2, - .serial_poll_status = fmh_gpib_serial_poll_status, - .t1_delay = fmh_gpib_t1_delay, - .return_to_local = fmh_gpib_return_to_local, -}; - -static struct gpib_interface fmh_gpib_pci_unaccel_interface = { - .name = "fmh_gpib_pci_unaccel", - .attach = fmh_gpib_pci_attach_holdoff_all, - .detach = fmh_gpib_pci_detach, - .read = fmh_gpib_read, - .write = fmh_gpib_write, - .command = fmh_gpib_command, - .take_control = fmh_gpib_take_control, - .go_to_standby = fmh_gpib_go_to_standby, - .request_system_control = fmh_gpib_request_system_control, - .interface_clear = fmh_gpib_interface_clear, - .remote_enable = fmh_gpib_remote_enable, - .enable_eos = fmh_gpib_enable_eos, - .disable_eos = fmh_gpib_disable_eos, - .parallel_poll = fmh_gpib_parallel_poll, - .parallel_poll_configure = fmh_gpib_parallel_poll_configure, - .parallel_poll_response = fmh_gpib_parallel_poll_response, - .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode, - .line_status = fmh_gpib_line_status, - .update_status = fmh_gpib_update_status, - .primary_address = fmh_gpib_primary_address, - .secondary_address = fmh_gpib_secondary_address, - .serial_poll_response2 = fmh_gpib_serial_poll_response2, - .serial_poll_status = fmh_gpib_serial_poll_status, - .t1_delay = fmh_gpib_t1_delay, - .return_to_local = fmh_gpib_return_to_local, -}; - -irqreturn_t fmh_gpib_internal_interrupt(struct gpib_board *board) -{ - unsigned int status0, status1, status2, ext_status_1, fifo_status; - struct fmh_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - int retval = IRQ_NONE; - - status0 = read_byte(nec_priv, ISR0_IMR0_REG); - status1 = read_byte(nec_priv, ISR1); - status2 = read_byte(nec_priv, ISR2); - fifo_status = fifos_read(priv, FIFO_CONTROL_STATUS_REG); - - if (status0 & IFC_INTERRUPT_BIT) { - push_gpib_event(board, EVENT_IFC); - retval = IRQ_HANDLED; - } - - if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED) - retval = IRQ_HANDLED; - - ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG); - - if (ext_status_1 & DATA_IN_STATUS_BIT) - set_bit(READ_READY_BN, &nec_priv->state); - else - clear_bit(READ_READY_BN, &nec_priv->state); - - if (ext_status_1 & DATA_OUT_STATUS_BIT) - set_bit(WRITE_READY_BN, &nec_priv->state); - else - clear_bit(WRITE_READY_BN, &nec_priv->state); - - if (ext_status_1 & COMMAND_OUT_STATUS_BIT) - set_bit(COMMAND_READY_BN, &nec_priv->state); - else - clear_bit(COMMAND_READY_BN, &nec_priv->state); - - if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT) - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - else - clear_bit(RFD_HOLDOFF_BN, &nec_priv->state); - - if (ext_status_1 & END_STATUS_BIT) { - /* - * only set RECEIVED_END while there is still a data - * byte sitting in the chip, to avoid spuriously - * setting it multiple times after it has been cleared - * during a read. - */ - if (ext_status_1 & DATA_IN_STATUS_BIT) - set_bit(RECEIVED_END_BN, &nec_priv->state); - } else { - clear_bit(RECEIVED_END_BN, &nec_priv->state); - } - - if ((fifo_status & TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED) && - (fifo_status & TX_FIFO_HALF_EMPTY)) { - /* - * We really only want to clear the - * TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE bit in the - * FIFO_CONTROL_STATUS_REG. Since we are not being - * careful, this also has a side effect of disabling - * DMA requests and the RX fifo interrupt. That is - * fine though, since they should never be in use at - * the same time as the TX fifo interrupt. - */ - fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG); - retval = IRQ_HANDLED; - } - - if ((fifo_status & RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED) && - (fifo_status & RX_FIFO_HALF_FULL)) { - /* - * We really only want to clear the - * RX_FIFO_HALF_FULL_INTERRUPT_ENABLE bit in the - * FIFO_CONTROL_STATUS_REG. Since we are not being - * careful, this also has a side effect of disabling - * DMA requests and the TX fifo interrupt. That is - * fine though, since they should never be in use at - * the same time as the RX fifo interrupt. - */ - fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG); - retval = IRQ_HANDLED; - } - - if (retval == IRQ_HANDLED) - wake_up_interruptible(&board->wait); - - return retval; -} - -irqreturn_t fmh_gpib_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - unsigned long flags; - irqreturn_t retval; - - spin_lock_irqsave(&board->spinlock, flags); - retval = fmh_gpib_internal_interrupt(board); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -static int fmh_gpib_allocate_private(struct gpib_board *board) -{ - struct fmh_priv *priv; - - board->private_data = kmalloc(sizeof(struct fmh_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - priv = board->private_data; - memset(priv, 0, sizeof(struct fmh_priv)); - init_nec7210_private(&priv->nec7210_priv); - priv->dma_buffer_size = 0x800; - priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL); - if (!priv->dma_buffer) - return -ENOMEM; - return 0; -} - -static void fmh_gpib_generic_detach(struct gpib_board *board) -{ - if (board->private_data) { - struct fmh_priv *e_priv = board->private_data; - - kfree(e_priv->dma_buffer); - kfree(board->private_data); - board->private_data = NULL; - } - if (board->dev) - dev_set_drvdata(board->dev, NULL); -} - -// generic part of attach functions -static int fmh_gpib_generic_attach(struct gpib_board *board) -{ - struct fmh_priv *e_priv; - struct nec7210_priv *nec_priv; - int retval; - - board->status = 0; - - retval = fmh_gpib_allocate_private(board); - if (retval < 0) - return retval; - e_priv = board->private_data; - nec_priv = &e_priv->nec7210_priv; - nec_priv->read_byte = gpib_cs_read_byte; - nec_priv->write_byte = gpib_cs_write_byte; - nec_priv->offset = 1; - nec_priv->type = CB7210; - return 0; -} - -static int fmh_gpib_config_dma(struct gpib_board *board, int output) -{ - struct fmh_priv *e_priv = board->private_data; - struct dma_slave_config config; - - config.device_fc = true; - - if (e_priv->dma_burst_length < 1) { - config.src_maxburst = 1; - config.dst_maxburst = 1; - } else { - config.src_maxburst = e_priv->dma_burst_length; - config.dst_maxburst = e_priv->dma_burst_length; - } - - config.src_addr_width = 1; - config.dst_addr_width = 1; - - if (output) { - config.direction = DMA_MEM_TO_DEV; - config.src_addr = 0; - config.dst_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset; - } else { - config.direction = DMA_DEV_TO_MEM; - config.src_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset; - config.dst_addr = 0; - } - return dmaengine_slave_config(e_priv->dma_channel, &config); -} - -static int fmh_gpib_init(struct fmh_priv *e_priv, struct gpib_board *board, int handshake_mode) -{ - struct nec7210_priv *nec_priv = &e_priv->nec7210_priv; - unsigned long flags; - unsigned int fifo_status_bits; - - fifos_write(e_priv, RX_FIFO_CLEAR | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG); - - nec7210_board_reset(nec_priv, board); - write_byte(nec_priv, AUX_LO_SPEED, AUXMR); - nec7210_set_handshake_mode(board, nec_priv, handshake_mode); - - /* Hueristically check if hardware supports fifo half full/empty interrupts */ - fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG); - e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) && - (fifo_status_bits & TX_FIFO_HALF_EMPTY); - - nec7210_board_online(nec_priv, board); - - write_byte(nec_priv, IFC_INTERRUPT_ENABLE_BIT | ATN_INTERRUPT_ENABLE_BIT, ISR0_IMR0_REG); - - spin_lock_irqsave(&board->spinlock, flags); - write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR); - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - spin_unlock_irqrestore(&board->spinlock, flags); - return 0; -} - -/* Match callback for driver_find_device */ -static int fmh_gpib_device_match(struct device *dev, const void *data) -{ - const struct gpib_board_config *config = data; - - if (dev_get_drvdata(dev)) - return 0; - - if (gpib_match_device_path(dev, config->device_path) == 0) - return 0; - - // driver doesn't support selection by serial number - if (config->serial_number) - return 0; - - dev_dbg(dev, "matched: %s\n", of_node_full_name(dev_of_node((dev)))); - return 1; -} - -static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_board_config *config, - unsigned int handshake_mode, int acquire_dma) -{ - struct fmh_priv *e_priv; - struct nec7210_priv *nec_priv; - int retval; - int irq; - struct resource *res; - struct platform_device *pdev; - - board->dev = driver_find_device(&fmh_gpib_platform_driver.driver, - NULL, (const void *)config, &fmh_gpib_device_match); - if (!board->dev) { - dev_err(board->gpib_dev, "No matching fmh_gpib_core device was found, attach failed."); - return -ENODEV; - } - // currently only used to mark the device as already attached - dev_set_drvdata(board->dev, board); - pdev = to_platform_device(board->dev); - - retval = fmh_gpib_generic_attach(board); - if (retval) - return retval; - - e_priv = board->private_data; - nec_priv = &e_priv->nec7210_priv; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpib_control_status"); - if (!res) { - dev_err(board->dev, "Unable to locate mmio resource\n"); - return -ENODEV; - } - - if (request_mem_region(res->start, - resource_size(res), - pdev->name) == NULL) { - dev_err(board->dev, "cannot claim registers\n"); - return -ENXIO; - } - e_priv->gpib_iomem_res = res; - - nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, - resource_size(e_priv->gpib_iomem_res)); - if (!nec_priv->mmiobase) { - dev_err(board->dev, "Could not map I/O memory\n"); - return -ENOMEM; - } - dev_dbg(board->dev, "iobase %pr remapped to %p\n", - e_priv->gpib_iomem_res, nec_priv->mmiobase); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma_fifos"); - if (!res) { - dev_err(board->dev, "Unable to locate mmio resource for gpib dma port\n"); - return -ENODEV; - } - if (request_mem_region(res->start, - resource_size(res), - pdev->name) == NULL) { - dev_err(board->dev, "cannot claim registers\n"); - return -ENXIO; - } - e_priv->dma_port_res = res; - e_priv->fifo_base = ioremap(e_priv->dma_port_res->start, - resource_size(e_priv->dma_port_res)); - if (!e_priv->fifo_base) { - dev_err(board->dev, "Could not map I/O memory for fifos\n"); - return -ENOMEM; - } - dev_dbg(board->dev, "dma fifos 0x%lx remapped to %p, length=%ld\n", - (unsigned long)e_priv->dma_port_res->start, e_priv->fifo_base, - (unsigned long)resource_size(e_priv->dma_port_res)); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return -EBUSY; - retval = request_irq(irq, fmh_gpib_interrupt, IRQF_SHARED, pdev->name, board); - if (retval) { - dev_err(board->dev, - "cannot register interrupt handler err=%d\n", - retval); - return retval; - } - e_priv->irq = irq; - - if (acquire_dma) { - e_priv->dma_channel = dma_request_slave_channel(board->dev, "rxtx"); - if (!e_priv->dma_channel) { - dev_err(board->dev, "failed to acquire dma channel \"rxtx\".\n"); - return -EIO; - } - } - /* - * in the future we might want to know the half-fifo size - * (dma_burst_length) even when not using dma, so go ahead an - * initialize it unconditionally. - */ - e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) & - fifo_max_burst_length_mask; - - return fmh_gpib_init(e_priv, board, handshake_mode); -} - -int fmh_gpib_attach_holdoff_all(struct gpib_board *board, const struct gpib_board_config *config) -{ - return fmh_gpib_attach_impl(board, config, HR_HLDA, 0); -} - -int fmh_gpib_attach_holdoff_end(struct gpib_board *board, const struct gpib_board_config *config) -{ - return fmh_gpib_attach_impl(board, config, HR_HLDE, 1); -} - -void fmh_gpib_detach(struct gpib_board *board) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (e_priv) { - if (e_priv->dma_channel) - dma_release_channel(e_priv->dma_channel); - nec_priv = &e_priv->nec7210_priv; - - if (e_priv->irq) - free_irq(e_priv->irq, board); - if (e_priv->fifo_base) - fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); - if (nec_priv->mmiobase) { - write_byte(nec_priv, 0, ISR0_IMR0_REG); - nec7210_board_reset(nec_priv, board); - } - if (e_priv->fifo_base) - iounmap(e_priv->fifo_base); - if (nec_priv->mmiobase) - iounmap(nec_priv->mmiobase); - if (e_priv->dma_port_res) { - release_mem_region(e_priv->dma_port_res->start, - resource_size(e_priv->dma_port_res)); - } - if (e_priv->gpib_iomem_res) - release_mem_region(e_priv->gpib_iomem_res->start, - resource_size(e_priv->gpib_iomem_res)); - } - fmh_gpib_generic_detach(board); - - if (board->dev) { - put_device(board->dev); - board->dev = NULL; - } -} - -static int fmh_gpib_pci_attach_impl(struct gpib_board *board, - const struct gpib_board_config *config, - unsigned int handshake_mode) -{ - struct fmh_priv *e_priv; - struct nec7210_priv *nec_priv; - int retval; - struct pci_dev *pci_device; - - retval = fmh_gpib_generic_attach(board); - if (retval) - return retval; - - e_priv = board->private_data; - nec_priv = &e_priv->nec7210_priv; - - // find board - pci_device = gpib_pci_get_device(config, BOGUS_PCI_VENDOR_ID_FLUKE, - BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, NULL); - if (!pci_device) { - dev_err(board->gpib_dev, "No matching fmh_gpib_core pci device was found, attach failed."); - return -ENODEV; - } - board->dev = &pci_device->dev; - - // bladerunner prototype has offset of 4 between gpib control/status registers - nec_priv->offset = 4; - - if (pci_enable_device(pci_device)) { - dev_err(board->dev, "error enabling pci device\n"); - return -EIO; - } - if (pci_request_regions(pci_device, KBUILD_MODNAME)) { - dev_err(board->dev, "pci_request_regions failed\n"); - return -EIO; - } - e_priv->gpib_iomem_res = &pci_device->resource[gpib_control_status_pci_resource_index]; - e_priv->dma_port_res = &pci_device->resource[gpib_fifo_pci_resource_index]; - - nec_priv->mmiobase = ioremap(pci_resource_start(pci_device, - gpib_control_status_pci_resource_index), - pci_resource_len(pci_device, - gpib_control_status_pci_resource_index)); - dev_dbg(board->dev, "base address for gpib control/status registers remapped to 0x%p\n", - nec_priv->mmiobase); - - if (e_priv->dma_port_res->flags & IORESOURCE_MEM) { - e_priv->fifo_base = ioremap(pci_resource_start(pci_device, - gpib_fifo_pci_resource_index), - pci_resource_len(pci_device, - gpib_fifo_pci_resource_index)); - dev_dbg(board->dev, "base address for gpib fifo registers remapped to 0x%p\n", - e_priv->fifo_base); - } else { - e_priv->fifo_base = NULL; - dev_dbg(board->dev, "hardware has no gpib fifo registers.\n"); - } - - if (pci_device->irq) { - retval = request_irq(pci_device->irq, fmh_gpib_interrupt, IRQF_SHARED, - KBUILD_MODNAME, board); - if (retval) { - dev_err(board->dev, "cannot register interrupt handler err=%d\n", retval); - return retval; - } - } - e_priv->irq = pci_device->irq; - - e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) & - fifo_max_burst_length_mask; - - return fmh_gpib_init(e_priv, board, handshake_mode); -} - -int fmh_gpib_pci_attach_holdoff_all(struct gpib_board *board, - const struct gpib_board_config *config) -{ - return fmh_gpib_pci_attach_impl(board, config, HR_HLDA); -} - -int fmh_gpib_pci_attach_holdoff_end(struct gpib_board *board, - const struct gpib_board_config *config) -{ - int retval; - struct fmh_priv *e_priv; - - retval = fmh_gpib_pci_attach_impl(board, config, HR_HLDE); - e_priv = board->private_data; - if (retval == 0 && e_priv && e_priv->supports_fifo_interrupts == 0) { - dev_err(board->gpib_dev, "your fmh_gpib_core does not appear to support fifo interrupts. Try the fmh_gpib_pci_unaccel board type instead."); - return -EIO; - } - return retval; -} - -void fmh_gpib_pci_detach(struct gpib_board *board) -{ - struct fmh_priv *e_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (e_priv) { - nec_priv = &e_priv->nec7210_priv; - - if (e_priv->irq) - free_irq(e_priv->irq, board); - if (e_priv->fifo_base) - fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG); - if (nec_priv->mmiobase) { - write_byte(nec_priv, 0, ISR0_IMR0_REG); - nec7210_board_reset(nec_priv, board); - } - if (e_priv->fifo_base) - iounmap(e_priv->fifo_base); - if (nec_priv->mmiobase) - iounmap(nec_priv->mmiobase); - if (e_priv->dma_port_res || e_priv->gpib_iomem_res) - pci_release_regions(to_pci_dev(board->dev)); - if (board->dev) - pci_dev_put(to_pci_dev(board->dev)); - } - fmh_gpib_generic_detach(board); -} - -static int fmh_gpib_platform_probe(struct platform_device *pdev) -{ - return 0; -} - -static const struct of_device_id fmh_gpib_of_match[] = { - { .compatible = "fmhess,fmh_gpib_core"}, - { {0} } -}; -MODULE_DEVICE_TABLE(of, fmh_gpib_of_match); - -static struct platform_driver fmh_gpib_platform_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = fmh_gpib_of_match, - }, - .probe = &fmh_gpib_platform_probe -}; - -static int fmh_gpib_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - return 0; -} - -static const struct pci_device_id fmh_gpib_pci_match[] = { - { BOGUS_PCI_VENDOR_ID_FLUKE, BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, 0, 0, 0 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, fmh_gpib_pci_match); - -static struct pci_driver fmh_gpib_pci_driver = { - .name = DRV_NAME, - .id_table = fmh_gpib_pci_match, - .probe = &fmh_gpib_pci_probe -}; - -static int __init fmh_gpib_init_module(void) -{ - int result; - - result = platform_driver_register(&fmh_gpib_platform_driver); - if (result) { - pr_err("platform_driver_register failed: error = %d\n", result); - return result; - } - - result = pci_register_driver(&fmh_gpib_pci_driver); - if (result) { - pr_err("pci_register_driver failed: error = %d\n", result); - goto err_pci_driver; - } - - result = gpib_register_driver(&fmh_gpib_unaccel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_unaccel; - } - - result = gpib_register_driver(&fmh_gpib_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_interface; - } - - result = gpib_register_driver(&fmh_gpib_pci_unaccel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_pci_unaccel; - } - - result = gpib_register_driver(&fmh_gpib_pci_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_pci; - } - - return 0; - -err_pci: - gpib_unregister_driver(&fmh_gpib_pci_unaccel_interface); -err_pci_unaccel: - gpib_unregister_driver(&fmh_gpib_interface); -err_interface: - gpib_unregister_driver(&fmh_gpib_unaccel_interface); -err_unaccel: - pci_unregister_driver(&fmh_gpib_pci_driver); -err_pci_driver: - platform_driver_unregister(&fmh_gpib_platform_driver); - - return result; -} - -static void __exit fmh_gpib_exit_module(void) -{ - gpib_unregister_driver(&fmh_gpib_pci_interface); - gpib_unregister_driver(&fmh_gpib_pci_unaccel_interface); - gpib_unregister_driver(&fmh_gpib_unaccel_interface); - gpib_unregister_driver(&fmh_gpib_interface); - - pci_unregister_driver(&fmh_gpib_pci_driver); - platform_driver_unregister(&fmh_gpib_platform_driver); -} - -module_init(fmh_gpib_init_module); -module_exit(fmh_gpib_exit_module); diff --git a/drivers/staging/gpib/fmh_gpib/fmh_gpib.h b/drivers/staging/gpib/fmh_gpib/fmh_gpib.h deleted file mode 100644 index e7602d7e1401..000000000000 --- a/drivers/staging/gpib/fmh_gpib/fmh_gpib.h +++ /dev/null @@ -1,177 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * Author: Frank Mori Hess - * Copyright: (C) 2006, 2010, 2015 Fluke Corporation - * (C) 2017 Frank Mori Hess - ***************************************************************************/ - -#include -#include -#include -#include -#include "nec7210.h" - -static const int fifo_reg_offset = 2; - -static const int gpib_control_status_pci_resource_index; -static const int gpib_fifo_pci_resource_index = 1; - -/* We don't have a real pci vendor/device id, the following will need to be - * patched to match prototype hardware. - */ -#define BOGUS_PCI_VENDOR_ID_FLUKE 0xffff -#define BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER 0x0 - -struct fmh_priv { - struct nec7210_priv nec7210_priv; - struct resource *gpib_iomem_res; - struct resource *write_transfer_counter_res; - struct resource *dma_port_res; - int irq; - struct dma_chan *dma_channel; - u8 *dma_buffer; - int dma_buffer_size; - int dma_burst_length; - void __iomem *fifo_base; - unsigned supports_fifo_interrupts : 1; -}; - -static inline int fmh_gpib_half_fifo_size(struct fmh_priv *priv) -{ - return priv->dma_burst_length; -} - -// registers beyond the nec7210 register set -enum fmh_gpib_regs { - EXT_STATUS_1_REG = 0x9, - STATE1_REG = 0xc, - ISR0_IMR0_REG = 0xe, - BUS_STATUS_REG = 0xf -}; - -/* IMR0 -- Interrupt Mode Register 0 */ -enum imr0_bits { - ATN_INTERRUPT_ENABLE_BIT = 0x4, - IFC_INTERRUPT_ENABLE_BIT = 0x8 -}; - -/* ISR0 -- Interrupt Status Register 0 */ -enum isr0_bits { - ATN_INTERRUPT_BIT = 0x4, - IFC_INTERRUPT_BIT = 0x8 -}; - -enum state1_bits { - SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */ - SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */ - SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */ - SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */ - SOURCE_HANDSHAKE_MASK = 0x7 -}; - -enum fmh_gpib_auxmr_bits { - AUX_I_REG = 0xe0, -}; - -enum aux_reg_i_bits { - LOCAL_PPOLL_MODE_BIT = 0x4 -}; - -enum ext_status_1_bits { - DATA_IN_STATUS_BIT = 0x01, - DATA_OUT_STATUS_BIT = 0x02, - COMMAND_OUT_STATUS_BIT = 0x04, - RFD_HOLDOFF_STATUS_BIT = 0x08, - END_STATUS_BIT = 0x10 -}; - -/* dma fifo reg and bits */ -enum dma_fifo_regs { - FIFO_DATA_REG = 0x0, - FIFO_CONTROL_STATUS_REG = 0x1, - FIFO_XFER_COUNTER_REG = 0x2, - FIFO_MAX_BURST_LENGTH_REG = 0x3 -}; - -enum fifo_data_bits { - FIFO_DATA_EOI_FLAG = 0x100 -}; - -enum fifo_control_bits { - TX_FIFO_DMA_REQUEST_ENABLE = 0x0001, - TX_FIFO_CLEAR = 0x0002, - TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE = 0x0008, - RX_FIFO_DMA_REQUEST_ENABLE = 0x0100, - RX_FIFO_CLEAR = 0x0200, - RX_FIFO_HALF_FULL_INTERRUPT_ENABLE = 0x0800 -}; - -enum fifo_status_bits { - TX_FIFO_EMPTY = 0x0001, - TX_FIFO_FULL = 0x0002, - TX_FIFO_HALF_EMPTY = 0x0004, - TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED = 0x0008, - TX_FIFO_DMA_REQUEST_IS_ENABLED = 0x0010, - RX_FIFO_EMPTY = 0x0100, - RX_FIFO_FULL = 0x0200, - RX_FIFO_HALF_FULL = 0x0400, - RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED = 0x0800, - RX_FIFO_DMA_REQUEST_IS_ENABLED = 0x1000 -}; - -static const unsigned int fifo_data_mask = 0x00ff; -static const unsigned int fifo_xfer_counter_mask = 0x0fff; -static const unsigned int fifo_max_burst_length_mask = 0x00ff; - -static inline u8 gpib_cs_read_byte(struct nec7210_priv *nec_priv, - unsigned int register_num) -{ - return readb(nec_priv->mmiobase + register_num * nec_priv->offset); -} - -static inline void gpib_cs_write_byte(struct nec7210_priv *nec_priv, u8 data, - unsigned int register_num) -{ - writeb(data, nec_priv->mmiobase + register_num * nec_priv->offset); -} - -static inline uint16_t fifos_read(struct fmh_priv *fmh_priv, int register_num) -{ - if (!fmh_priv->fifo_base) - return 0; - return readw(fmh_priv->fifo_base + register_num * fifo_reg_offset); -} - -static inline void fifos_write(struct fmh_priv *fmh_priv, uint16_t data, int register_num) -{ - if (!fmh_priv->fifo_base) - return; - writew(data, fmh_priv->fifo_base + register_num * fifo_reg_offset); -} - -enum bus_status_bits { - BSR_ATN_BIT = 0x01, - BSR_EOI_BIT = 0x02, - BSR_SRQ_BIT = 0x04, - BSR_IFC_BIT = 0x08, - BSR_REN_BIT = 0x10, - BSR_DAV_BIT = 0x20, - BSR_NRFD_BIT = 0x40, - BSR_NDAC_BIT = 0x80, -}; - -enum fmh_gpib_aux_cmds { - /* AUX_RTL2 is an auxiliary command which causes the cb7210 to assert - * (and keep asserted) the local rtl message. This is used in conjunction - * with the normal nec7210 AUX_RTL command, which - * pulses the rtl message, having the effect of clearing rtl if it was left - * asserted by AUX_RTL2. - */ - AUX_RTL2 = 0x0d, - AUX_RFD_HOLDOFF_ASAP = 0x15, - AUX_REQT = 0x18, - AUX_REQF = 0x19, - AUX_LO_SPEED = 0x40, - AUX_HI_SPEED = 0x41 -}; diff --git a/drivers/staging/gpib/gpio/Makefile b/drivers/staging/gpib/gpio/Makefile deleted file mode 100644 index 00ea52abdda7..000000000000 --- a/drivers/staging/gpib/gpio/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - -obj-$(CONFIG_GPIB_GPIO) += gpib_bitbang.o - - diff --git a/drivers/staging/gpib/gpio/gpib_bitbang.c b/drivers/staging/gpib/gpio/gpib_bitbang.c deleted file mode 100644 index 374cd61355e9..000000000000 --- a/drivers/staging/gpib/gpio/gpib_bitbang.c +++ /dev/null @@ -1,1469 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/************************************************************************* - * This code has been developed at the Institute of Sensor and Actuator * - * Systems (Technical University of Vienna, Austria) to enable the GPIO * - * lines (e.g. of a raspberry pi) to function as a GPIO master device * - * * - * authors : Thomas Klima * - * Marcello Carla' * - * Dave Penkler * - * * - * copyright : (C) 2016 Thomas Klima * - * * - *************************************************************************/ - -/* - * limitations: - * works only on RPi - * cannot function as non-CIC system controller with SN7516x because - * SN75161B cannot simultaneously make ATN input with IFC and REN as - * outputs. - * not implemented: - * parallel poll - * return2local - * device support (non master operation) - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define NAME KBUILD_MODNAME - -#define ENABLE_IRQ(IRQ, TYPE) irq_set_irq_type(IRQ, TYPE) -#define DISABLE_IRQ(IRQ) irq_set_irq_type(IRQ, IRQ_TYPE_NONE) - -/* - * Debug print levels: - * 0 = load/unload info and errors that make the driver fail; - * 1 = + warnings for unforeseen events that may break the current - * operation and lead to a timeout, but do not affect the - * driver integrity (mainly unexpected interrupts); - * 2 = + trace of function calls; - * 3 = + trace of protocol codes; - * 4 = + trace of interrupt operation. - */ -#define dbg_printk(level, frm, ...) \ - do { if (debug >= (level)) \ - dev_dbg(board->gpib_dev, frm, ## __VA_ARGS__); } \ - while (0) - -#define LINVAL gpiod_get_value(DAV), \ - gpiod_get_value(NRFD), \ - gpiod_get_value(NDAC), \ - gpiod_get_value(SRQ) -#define LINFMT "DAV: %d NRFD:%d NDAC: %d SRQ: %d" - -#include "gpibP.h" -#include "gpib_state_machines.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int sn7516x_used = 1, sn7516x; -module_param(sn7516x_used, int, 0660); - -#define PINMAP_0 "elektronomikon" -#define PINMAP_1 "gpib4pi-1.1" -#define PINMAP_2 "yoga" -static char *pin_map = PINMAP_0; -module_param(pin_map, charp, 0660); -MODULE_PARM_DESC(pin_map, " valid values: " PINMAP_0 " " PINMAP_1 " " PINMAP_2); - -/********************************************** - * Signal pairing and pin wiring between the * - * Raspberry-Pi connector and the GPIB bus * - * * - * signal pin wiring * - * GPIB Pi-gpio GPIB -> RPi * - ********************************************** - */ -enum lines_t { - D01_pin_nr = 20, /* 1 -> 38 */ - D02_pin_nr = 26, /* 2 -> 37 */ - D03_pin_nr = 16, /* 3 -> 36 */ - D04_pin_nr = 19, /* 4 -> 35 */ - D05_pin_nr = 13, /* 13 -> 33 */ - D06_pin_nr = 12, /* 14 -> 32 */ - D07_pin_nr = 6, /* 15 -> 31 */ - D08_pin_nr = 5, /* 16 -> 29 */ - EOI_pin_nr = 9, /* 5 -> 21 */ - DAV_pin_nr = 10, /* 6 -> 19 */ - NRFD_pin_nr = 24, /* 7 -> 18 */ - NDAC_pin_nr = 23, /* 8 -> 16 */ - IFC_pin_nr = 22, /* 9 -> 15 */ - SRQ_pin_nr = 11, /* 10 -> 23 */ - _ATN_pin_nr = 25, /* 11 -> 22 */ - REN_pin_nr = 27, /* 17 -> 13 */ -/* - * GROUND PINS - * 12,18,19,20,21,22,23,24 => 14,20,25,30,34,39 - */ - -/* - * These lines are used to control the external - * SN75160/161 driver chips when used. - * When not used there is reduced fan out; - * currently tested with up to 4 devices. - */ - -/* Pi GPIO RPI 75161B 75160B Description */ - PE_pin_nr = 7, /* 26 -> nc 11 Pullup Enable */ - DC_pin_nr = 8, /* 24 -> 12 nc Direction control */ - TE_pin_nr = 18, /* 12 -> 2 1 Talk Enable */ - ACT_LED_pin_nr = 4, /* 7 -> LED */ - -/* YOGA adapter uses different pinout to ease layout */ - YOGA_D03_pin_nr = 13, - YOGA_D04_pin_nr = 12, - YOGA_D05_pin_nr = 21, - YOGA_D06_pin_nr = 19, -}; - -/* - * GPIO descriptors and pins - WARNING: STRICTLY KEEP ITEMS ORDER - */ - -#define GPIB_PINS 16 -#define SN7516X_PINS 4 -#define NUM_PINS (GPIB_PINS + SN7516X_PINS) - -#define ACT_LED_ON do { \ - if (ACT_LED) \ - gpiod_direction_output(ACT_LED, 1); \ - } while (0) -#define ACT_LED_OFF do { \ - if (ACT_LED) \ - gpiod_direction_output(ACT_LED, 0); \ - } while (0) - -static struct gpio_desc *all_descriptors[GPIB_PINS + SN7516X_PINS]; - -#define D01 all_descriptors[0] -#define D02 all_descriptors[1] -#define D03 all_descriptors[2] -#define D04 all_descriptors[3] -#define D05 all_descriptors[4] -#define D06 all_descriptors[5] -#define D07 all_descriptors[6] -#define D08 all_descriptors[7] - -#define EOI all_descriptors[8] -#define NRFD all_descriptors[9] -#define IFC all_descriptors[10] -#define _ATN all_descriptors[11] -#define REN all_descriptors[12] -#define DAV all_descriptors[13] -#define NDAC all_descriptors[14] -#define SRQ all_descriptors[15] - -#define PE all_descriptors[16] -#define DC all_descriptors[17] -#define TE all_descriptors[18] -#define ACT_LED all_descriptors[19] - -/* YOGA adapter uses a global enable for the buffer chips, re-using the TE pin */ -#define YOGA_ENABLE TE - -static int gpios_vector[] = { - D01_pin_nr, - D02_pin_nr, - D03_pin_nr, - D04_pin_nr, - D05_pin_nr, - D06_pin_nr, - D07_pin_nr, - D08_pin_nr, - - EOI_pin_nr, - NRFD_pin_nr, - IFC_pin_nr, - _ATN_pin_nr, - REN_pin_nr, - DAV_pin_nr, - NDAC_pin_nr, - SRQ_pin_nr, - - PE_pin_nr, - DC_pin_nr, - TE_pin_nr, - ACT_LED_pin_nr -}; - -/* Lookup table for general GPIOs */ - -static struct gpiod_lookup_table gpib_gpio_table_1 = { - // for bcm2835/6 - .dev_id = "", // device id of board device - .table = { - GPIO_LOOKUP_IDX("GPIO_GCLK", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("SPI_CE1_N", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("SPI_CE0_N", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("SPI_MISO", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("SPI_MOSI", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("SPI_SCLK", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table gpib_gpio_table_0 = { - .dev_id = "", // device id of board device - .table = { - // for bcm27xx based pis (b b+ 2b 3b 3b+ 4 5) - GPIO_LOOKUP_IDX("GPIO4", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO7", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO8", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO9", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO10", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO11", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table *lookup_tables[] = { - &gpib_gpio_table_0, - &gpib_gpio_table_1, - NULL -}; - -/* struct which defines private_data for gpio driver */ - -struct bb_priv { - int irq_NRFD; - int irq_NDAC; - int irq_DAV; - int irq_SRQ; - int dav_mode; /* dav interrupt mode 0/1 -> edge/levels */ - int nrfd_mode; /* nrfd interrupt mode 0/1 -> edge/levels */ - int ndac_mode; /* nrfd interrupt mode 0/1 -> edge/levels */ - int dav_tx; /* keep trace of DAV status while sending */ - int dav_rx; /* keep trace of DAV status while receiving */ - u8 eos; /* eos character */ - short eos_flags; /* eos mode */ - short eos_check; /* eos check required in current operation ... */ - short eos_check_8; /* ... with byte comparison */ - short eos_mask_7; /* ... with 7 bit masked character */ - short int end; - int request; - int count; - int direction; - int t1_delay; - u8 *rbuf; - u8 *wbuf; - int end_flag; - int r_busy; /* 0==idle 1==busy */ - int w_busy; - int write_done; - int cmd; /* 1 = cmd write in progress */ - size_t w_cnt; - size_t length; - u8 *w_buf; - spinlock_t rw_lock; /* protect mods to rw_lock */ - int phase; - int ndac_idle; - int ndac_seq; - int nrfd_idle; - int nrfd_seq; - int dav_seq; - long all_irqs; - int dav_idle; - - enum talker_function_state talker_state; - enum listener_function_state listener_state; -}; - -static inline long usec_diff(struct timespec64 *a, struct timespec64 *b); -static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length, - int cmd, int eoi); -static void set_data_lines(u8 byte); -static u8 get_data_lines(void); -static void set_data_lines_input(void); -static void set_data_lines_output(void); -static inline int check_for_eos(struct bb_priv *priv, u8 byte); -static void set_atn(struct gpib_board *board, int atn_asserted); - -static inline void SET_DIR_WRITE(struct bb_priv *priv); -static inline void SET_DIR_READ(struct bb_priv *priv); - -#define DIR_READ 0 -#define DIR_WRITE 1 - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB helper functions for bitbanging I/O"); - -/**** global variables ****/ -static int debug; -module_param(debug, int, 0644); - -static char printable(char x) -{ - if (x < 32 || x > 126) - return ' '; - return x; -} - -/*************************************************************************** - * * - * READ * - * * - ***************************************************************************/ - -static int bb_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct bb_priv *priv = board->private_data; - unsigned long flags; - int retval = 0; - - ACT_LED_ON; - SET_DIR_READ(priv); - - dbg_printk(2, "board: %p lock %d length: %zu\n", - board, mutex_is_locked(&board->user_mutex), length); - - priv->end = 0; - priv->count = 0; - priv->rbuf = buffer; - if (length == 0) - goto read_end; - priv->request = length; - priv->eos_check = (priv->eos_flags & REOS) == 0; /* do eos check */ - priv->eos_check_8 = priv->eos_flags & BIN; /* over 8 bits */ - priv->eos_mask_7 = priv->eos & 0x7f; /* with this 7 bit eos */ - - dbg_printk(3, ".........." LINFMT "\n", LINVAL); - - spin_lock_irqsave(&priv->rw_lock, flags); - priv->dav_mode = 1; - priv->dav_rx = 1; - ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_LEVEL_LOW); - priv->end_flag = 0; - gpiod_set_value(NRFD, 1); // ready for data - priv->r_busy = 1; - priv->phase = 100; - spin_unlock_irqrestore(&priv->rw_lock, flags); - - /* wait for the interrupt routines finish their work */ - - retval = wait_event_interruptible(board->wait, - (priv->end_flag || board->status & TIMO)); - - dbg_printk(3, "awake from wait queue: %d\n", retval); - - if (retval == 0 && board->status & TIMO) { - retval = -ETIMEDOUT; - dbg_printk(1, "timeout\n"); - } else if (retval) { - retval = -ERESTARTSYS; - } - - DISABLE_IRQ(priv->irq_DAV); - spin_lock_irqsave(&priv->rw_lock, flags); - gpiod_set_value(NRFD, 0); // DIR_READ line state - priv->r_busy = 0; - spin_unlock_irqrestore(&priv->rw_lock, flags); - -read_end: - ACT_LED_OFF; - *bytes_read = priv->count; - *end = priv->end; - priv->r_busy = 0; - dbg_printk(2, "return: %d eoi|eos: %d count: %d\n\n", retval, priv->end, priv->count); - return retval; -} - -/*************************************************************************** - * * - * READ interrupt routine (DAV line) * - * * - ***************************************************************************/ - -static irqreturn_t bb_DAV_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - struct bb_priv *priv = board->private_data; - int val; - unsigned long flags; - - spin_lock_irqsave(&priv->rw_lock, flags); - - priv->all_irqs++; - - if (priv->dav_mode) { - ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_EDGE_BOTH); - priv->dav_mode = 0; - } - - if (priv->r_busy == 0) { - dbg_printk(1, "interrupt while idle after %d at %d\n", - priv->count, priv->phase); - priv->dav_idle++; - priv->phase = 200; - goto dav_exit; /* idle */ - } - - val = gpiod_get_value(DAV); - if (val == priv->dav_rx) { - dbg_printk(1, "out of order DAV interrupt %d/%d after %zu/%zu at %d cmd %d " - LINFMT ".\n", val, priv->dav_rx, priv->w_cnt, priv->length, - priv->phase, priv->cmd, LINVAL); - priv->dav_seq++; - } - priv->dav_rx = val; - - dbg_printk(3, "> irq: %d DAV: %d st: %4lx dir: %d busy: %d:%d\n", - irq, val, board->status, priv->direction, priv->r_busy, priv->w_busy); - - if (val == 0) { - gpiod_set_value(NRFD, 0); // not ready for data - priv->rbuf[priv->count++] = get_data_lines(); - priv->end = !gpiod_get_value(EOI); - gpiod_set_value(NDAC, 1); // data accepted - priv->end |= check_for_eos(priv, priv->rbuf[priv->count - 1]); - priv->end_flag = ((priv->count >= priv->request) || priv->end); - priv->phase = 210; - } else { - gpiod_set_value(NDAC, 0); // data not accepted - if (priv->end_flag) { - priv->r_busy = 0; - wake_up_interruptible(&board->wait); - priv->phase = 220; - } else { - gpiod_set_value(NRFD, 1); // ready for data - priv->phase = 230; - } - } - -dav_exit: - spin_unlock_irqrestore(&priv->rw_lock, flags); - dbg_printk(3, "< irq: %d count %d\n", irq, priv->count); - return IRQ_HANDLED; -} - -/*************************************************************************** - * * - * WRITE * - * * - ***************************************************************************/ - -static int bb_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - unsigned long flags; - int retval = 0; - - struct bb_priv *priv = board->private_data; - - ACT_LED_ON; - - priv->w_cnt = 0; - priv->w_buf = buffer; - dbg_printk(2, "board %p lock %d length: %zu\n", - board, mutex_is_locked(&board->user_mutex), length); - - if (debug > 1) - bb_buffer_print(board, buffer, length, priv->cmd, send_eoi); - priv->count = 0; - priv->phase = 300; - - if (length == 0) - goto write_end; - priv->end = send_eoi; - priv->length = length; - - SET_DIR_WRITE(priv); - - dbg_printk(2, "Enabling interrupts - NRFD: %d NDAC: %d\n", - gpiod_get_value(NRFD), gpiod_get_value(NDAC)); - - if (gpiod_get_value(NRFD) && gpiod_get_value(NDAC)) { /* check for listener */ - retval = -ENOTCONN; - goto write_end; - } - - spin_lock_irqsave(&priv->rw_lock, flags); - priv->w_busy = 1; /* make the interrupt routines active */ - priv->write_done = 0; - priv->nrfd_mode = 1; - priv->ndac_mode = 1; - priv->dav_tx = 1; - ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_LEVEL_HIGH); - ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_LEVEL_HIGH); - spin_unlock_irqrestore(&priv->rw_lock, flags); - - /* wait for the interrupt routines finish their work */ - - retval = wait_event_interruptible(board->wait, - priv->write_done || (board->status & TIMO)); - - dbg_printk(3, "awake from wait queue: %d\n", retval); - - if (retval == 0) { - if (board->status & TIMO) { - retval = -ETIMEDOUT; - dbg_printk(1, "timeout after %zu/%zu at %d " LINFMT " eoi: %d\n", - priv->w_cnt, length, priv->phase, LINVAL, send_eoi); - } else { - retval = priv->w_cnt; - } - } else { - retval = -ERESTARTSYS; - } - - DISABLE_IRQ(priv->irq_NRFD); - DISABLE_IRQ(priv->irq_NDAC); - - spin_lock_irqsave(&priv->rw_lock, flags); - priv->w_busy = 0; - gpiod_set_value(DAV, 1); // DIR_WRITE line state - gpiod_set_value(EOI, 1); // De-assert EOI (in case) - spin_unlock_irqrestore(&priv->rw_lock, flags); - -write_end: - *bytes_written = priv->w_cnt; - ACT_LED_OFF; - dbg_printk(2, "sent %zu bytes\r\n\r\n", *bytes_written); - priv->phase = 310; - return retval; -} - -/*************************************************************************** - * * - * WRITE interrupt routine (NRFD line) * - * * - ***************************************************************************/ - -static irqreturn_t bb_NRFD_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - struct bb_priv *priv = board->private_data; - unsigned long flags; - int nrfd; - - spin_lock_irqsave(&priv->rw_lock, flags); - - nrfd = gpiod_get_value(NRFD); - priv->all_irqs++; - - dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n", - irq, nrfd, gpiod_get_value(NDAC), board->status, priv->direction, - priv->w_busy, priv->r_busy); - - if (priv->nrfd_mode) { - ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_EDGE_RISING); - priv->nrfd_mode = 0; - } - - if (priv->w_busy == 0) { - dbg_printk(1, "interrupt while idle after %zu/%zu at %d\n", - priv->w_cnt, priv->length, priv->phase); - priv->nrfd_idle++; - goto nrfd_exit; /* idle */ - } - if (nrfd == 0) { - dbg_printk(1, "out of order interrupt after %zu/%zu at %d cmd %d " LINFMT ".\n", - priv->w_cnt, priv->length, priv->phase, priv->cmd, LINVAL); - priv->phase = 400; - priv->nrfd_seq++; - goto nrfd_exit; - } - if (!priv->dav_tx) { - dbg_printk(1, "DAV low after %zu/%zu cmd %d " LINFMT ". No action.\n", - priv->w_cnt, priv->length, priv->cmd, LINVAL); - priv->dav_seq++; - goto nrfd_exit; - } - - if (priv->w_cnt >= priv->length) { // test for missed NDAC end of transfer - dev_err(board->gpib_dev, "Unexpected NRFD exit\n"); - priv->write_done = 1; - priv->w_busy = 0; - wake_up_interruptible(&board->wait); - goto nrfd_exit; - } - - dbg_printk(3, "sending %zu\n", priv->w_cnt); - - set_data_lines(priv->w_buf[priv->w_cnt++]); // put the data on the lines - - if (priv->w_cnt == priv->length && priv->end) { - dbg_printk(3, "Asserting EOI\n"); - gpiod_set_value(EOI, 0); // Assert EOI - } - - gpiod_set_value(DAV, 0); // Data available - priv->dav_tx = 0; - priv->phase = 410; - -nrfd_exit: - spin_unlock_irqrestore(&priv->rw_lock, flags); - - return IRQ_HANDLED; -} - -/*************************************************************************** - * * - * WRITE interrupt routine (NDAC line) * - * * - ***************************************************************************/ - -static irqreturn_t bb_NDAC_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - struct bb_priv *priv = board->private_data; - unsigned long flags; - int ndac; - - spin_lock_irqsave(&priv->rw_lock, flags); - - ndac = gpiod_get_value(NDAC); - priv->all_irqs++; - dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n", - irq, gpiod_get_value(NRFD), ndac, board->status, priv->direction, - priv->w_busy, priv->r_busy); - - if (priv->ndac_mode) { - ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_EDGE_RISING); - priv->ndac_mode = 0; - } - - if (priv->w_busy == 0) { - dbg_printk(1, "interrupt while idle.\n"); - priv->ndac_idle++; - goto ndac_exit; - } - if (ndac == 0) { - dbg_printk(1, "out of order interrupt at %zu:%d.\n", priv->w_cnt, priv->phase); - priv->phase = 500; - priv->ndac_seq++; - goto ndac_exit; - } - if (priv->dav_tx) { - dbg_printk(1, "DAV high after %zu/%zu cmd %d " LINFMT ". No action.\n", - priv->w_cnt, priv->length, priv->cmd, LINVAL); - priv->dav_seq++; - goto ndac_exit; - } - - dbg_printk(3, "accepted %zu\n", priv->w_cnt - 1); - - gpiod_set_value(DAV, 1); // Data not available - priv->dav_tx = 1; - priv->phase = 510; - - if (priv->w_cnt >= priv->length) { // test for end of transfer - priv->write_done = 1; - priv->w_busy = 0; - wake_up_interruptible(&board->wait); - } - -ndac_exit: - spin_unlock_irqrestore(&priv->rw_lock, flags); - return IRQ_HANDLED; -} - -/*************************************************************************** - * * - * interrupt routine for SRQ line * - * * - ***************************************************************************/ - -static irqreturn_t bb_SRQ_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - - int val = gpiod_get_value(SRQ); - - dbg_printk(3, "> %d st: %4lx\n", val, board->status); - - if (!val) - set_bit(SRQI_NUM, &board->status); /* set_bit() is atomic */ - - wake_up_interruptible(&board->wait); - - return IRQ_HANDLED; -} - -static int bb_command(struct gpib_board *board, u8 *buffer, - size_t length, size_t *bytes_written) -{ - int ret; - struct bb_priv *priv = board->private_data; - int i; - - dbg_printk(2, "%p %p\n", buffer, board->buffer); - - /* the _ATN line has already been asserted by bb_take_control() */ - - priv->cmd = 1; - - ret = bb_write(board, buffer, length, 0, bytes_written); // no eoi - - for (i = 0; i < length; i++) { - if (buffer[i] == UNT) { - priv->talker_state = talker_idle; - } else { - if (buffer[i] == UNL) { - priv->listener_state = listener_idle; - } else { - if (buffer[i] == (MTA(board->pad))) { - priv->talker_state = talker_addressed; - priv->listener_state = listener_idle; - } else if (buffer[i] == (MLA(board->pad))) { - priv->listener_state = listener_addressed; - priv->talker_state = talker_idle; - } - } - } - } - - /* the _ATN line will be released by bb_go_to_stby */ - - priv->cmd = 0; - - return ret; -} - -/*************************************************************************** - * * - * Buffer print with decode for debug/trace * - * * - ***************************************************************************/ - -static char *cmd_string[32] = { - "", // 0x00 - "GTL", // 0x01 - "", // 0x02 - "", // 0x03 - "SDC", // 0x04 - "PPC", // 0x05 - "", // 0x06 - "", // 0x07 - "GET", // 0x08 - "TCT", // 0x09 - "", // 0x0a - "", // 0x0b - "", // 0x0c - "", // 0x0d - "", // 0x0e - "", // 0x0f - "", // 0x10 - "LLO", // 0x11 - "", // 0x12 - "", // 0x13 - "DCL", // 0x14 - "PPU", // 0x15 - "", // 0x16 - "", // 0x17 - "SPE", // 0x18 - "SPD", // 0x19 - "", // 0x1a - "", // 0x1b - "", // 0x1c - "", // 0x1d - "", // 0x1e - "CFE" // 0x1f -}; - -static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length, - int cmd, int eoi) -{ - int i; - - if (cmd) { - dbg_printk(2, "\n", length); - for (i = 0; i < length; i++) { - if (buffer[i] < 0x20) { - dbg_printk(3, "0x%x=%s\n", buffer[i], cmd_string[buffer[i]]); - } else if (buffer[i] == 0x3f) { - dbg_printk(3, "0x%x=%s\n", buffer[i], "UNL"); - } else if (buffer[i] == 0x5f) { - dbg_printk(3, "0x%x=%s\n", buffer[i], "UNT"); - } else if (buffer[i] < 0x60) { - dbg_printk(3, "0x%x=%s%d\n", buffer[i], - (buffer[i] & 0x40) ? "TLK" : "LSN", buffer[i] & 0x1F); - } else { - dbg_printk(3, "0x%x\n", buffer[i]); - } - } - } else { - dbg_printk(2, "\n", length, (eoi) ? "w.EOI" : " "); - for (i = 0; i < length; i++) - dbg_printk(2, "%3d 0x%x->%c\n", i, buffer[i], printable(buffer[i])); - } -} - -/*************************************************************************** - * * - * STATUS Management * - * * - ***************************************************************************/ -static void set_atn(struct gpib_board *board, int atn_asserted) -{ - struct bb_priv *priv = board->private_data; - - if (priv->listener_state != listener_idle && - priv->talker_state != talker_idle) { - dev_err(board->gpib_dev, "listener/talker state machine conflict\n"); - } - if (atn_asserted) { - if (priv->listener_state == listener_active) - priv->listener_state = listener_addressed; - if (priv->talker_state == talker_active) - priv->talker_state = talker_addressed; - SET_DIR_WRITE(priv); // need to be able to read bus NRFD/NDAC - } else { - if (priv->listener_state == listener_addressed) { - priv->listener_state = listener_active; - SET_DIR_READ(priv); // make sure holdoff is active when we unassert ATN - } - if (priv->talker_state == talker_addressed) - priv->talker_state = talker_active; - } - gpiod_direction_output(_ATN, !atn_asserted); -} - -static int bb_take_control(struct gpib_board *board, int synchronous) -{ - dbg_printk(2, "%d\n", synchronous); - set_atn(board, 1); - return 0; -} - -static int bb_go_to_standby(struct gpib_board *board) -{ - dbg_printk(2, "\n"); - set_atn(board, 0); - return 0; -} - -static int bb_request_system_control(struct gpib_board *board, int request_control) -{ - struct bb_priv *priv = board->private_data; - - dbg_printk(2, "%d\n", request_control); - if (!request_control) - return -EINVAL; - - gpiod_direction_output(REN, 1); /* user space must enable REN if needed */ - gpiod_direction_output(IFC, 1); /* user space must toggle IFC if needed */ - if (sn7516x) - gpiod_direction_output(DC, 0); /* enable ATN as output on SN75161/2 */ - - gpiod_direction_input(SRQ); - - ENABLE_IRQ(priv->irq_SRQ, IRQ_TYPE_EDGE_FALLING); - - return 0; -} - -static void bb_interface_clear(struct gpib_board *board, int assert) -{ - struct bb_priv *priv = board->private_data; - - dbg_printk(2, "%d\n", assert); - if (assert) { - gpiod_direction_output(IFC, 0); - priv->talker_state = talker_idle; - priv->listener_state = listener_idle; - set_bit(CIC_NUM, &board->status); - } else { - gpiod_direction_output(IFC, 1); - } -} - -static void bb_remote_enable(struct gpib_board *board, int enable) -{ - dbg_printk(2, "%d\n", enable); - if (enable) { - set_bit(REM_NUM, &board->status); - gpiod_direction_output(REN, 0); - } else { - clear_bit(REM_NUM, &board->status); - gpiod_direction_output(REN, 1); - } -} - -static int bb_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct bb_priv *priv = board->private_data; - - dbg_printk(2, "%s\n", "EOS_en"); - priv->eos = eos_byte; - priv->eos_flags = REOS; - if (compare_8_bits) - priv->eos_flags |= BIN; - - return 0; -} - -static void bb_disable_eos(struct gpib_board *board) -{ - struct bb_priv *priv = board->private_data; - - dbg_printk(2, "\n"); - priv->eos_flags &= ~REOS; -} - -static unsigned int bb_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct bb_priv *priv = board->private_data; - - board->status &= ~clear_mask; - - if (gpiod_get_value(SRQ)) /* SRQ asserted low */ - clear_bit(SRQI_NUM, &board->status); - else - set_bit(SRQI_NUM, &board->status); - if (gpiod_get_value(_ATN)) /* ATN asserted low */ - clear_bit(ATN_NUM, &board->status); - else - set_bit(ATN_NUM, &board->status); - if (priv->talker_state == talker_active || - priv->talker_state == talker_addressed) - set_bit(TACS_NUM, &board->status); - else - clear_bit(TACS_NUM, &board->status); - - if (priv->listener_state == listener_active || - priv->listener_state == listener_addressed) - set_bit(LACS_NUM, &board->status); - else - clear_bit(LACS_NUM, &board->status); - - dbg_printk(2, "0x%lx mask 0x%x\n", board->status, clear_mask); - - return board->status; -} - -static int bb_primary_address(struct gpib_board *board, unsigned int address) -{ - dbg_printk(2, "%d\n", address); - board->pad = address; - return 0; -} - -static int bb_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - dbg_printk(2, "%d %d\n", address, enable); - if (enable) - board->sad = address; - return 0; -} - -static int bb_parallel_poll(struct gpib_board *board, u8 *result) -{ - return -ENOENT; -} - -static void bb_parallel_poll_configure(struct gpib_board *board, u8 config) -{ -} - -static void bb_parallel_poll_response(struct gpib_board *board, int ist) -{ -} - -static void bb_serial_poll_response(struct gpib_board *board, u8 status) -{ -} - -static u8 bb_serial_poll_status(struct gpib_board *board) -{ - return 0; // -ENOENT; -} - -static int bb_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct bb_priv *priv = board->private_data; - - if (nano_sec <= 350) - priv->t1_delay = 350; - else if (nano_sec <= 1100) - priv->t1_delay = 1100; - else - priv->t1_delay = 2000; - - dbg_printk(2, "t1 delay set to %d nanosec\n", priv->t1_delay); - - return priv->t1_delay; -} - -static void bb_return_to_local(struct gpib_board *board) -{ -} - -static int bb_line_status(const struct gpib_board *board) -{ - int line_status = VALID_ALL; - - if (gpiod_get_value(REN) == 0) - line_status |= BUS_REN; - if (gpiod_get_value(IFC) == 0) - line_status |= BUS_IFC; - if (gpiod_get_value(NDAC) == 0) - line_status |= BUS_NDAC; - if (gpiod_get_value(NRFD) == 0) - line_status |= BUS_NRFD; - if (gpiod_get_value(DAV) == 0) - line_status |= BUS_DAV; - if (gpiod_get_value(EOI) == 0) - line_status |= BUS_EOI; - if (gpiod_get_value(_ATN) == 0) - line_status |= BUS_ATN; - if (gpiod_get_value(SRQ) == 0) - line_status |= BUS_SRQ; - - dbg_printk(2, "status lines: %4x\n", line_status); - - return line_status; -} - -/*************************************************************************** - * * - * Module Management * - * * - ***************************************************************************/ - -static int allocate_private(struct gpib_board *board) -{ - board->private_data = kzalloc(sizeof(struct bb_priv), GFP_KERNEL); - if (!board->private_data) - return -1; - return 0; -} - -static void free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static int bb_get_irq(struct gpib_board *board, char *name, - struct gpio_desc *gpio, int *irq, - irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags) -{ - if (!gpio) - return -1; - gpiod_direction_input(gpio); - *irq = gpiod_to_irq(gpio); - dbg_printk(2, "IRQ %s: %d\n", name, *irq); - if (*irq < 0) { - dev_err(board->gpib_dev, "can't get IRQ for %s\n", name); - return -1; - } - if (request_threaded_irq(*irq, handler, thread_fn, flags, name, board)) { - dev_err(board->gpib_dev, "can't request IRQ for %s %d\n", name, *irq); - *irq = 0; - return -1; - } - DISABLE_IRQ(*irq); - return 0; -} - -static void bb_free_irq(struct gpib_board *board, int *irq, char *name) -{ - if (*irq) { - free_irq(*irq, board); - dbg_printk(2, "IRQ %d(%s) freed\n", *irq, name); - *irq = 0; - } -} - -static void release_gpios(void) -{ - int j; - - for (j = 0 ; j < NUM_PINS ; j++) { - if (all_descriptors[j]) { - gpiod_put(all_descriptors[j]); - all_descriptors[j] = NULL; - } - } -} - -static int allocate_gpios(struct gpib_board *board) -{ - int j; - int table_index = 0; - char name[256]; - struct gpio_desc *desc; - struct gpiod_lookup_table *lookup_table; - - if (!board->gpib_dev) { - pr_err("NULL gpib dev for board\n"); - return -ENOENT; - } - - lookup_table = lookup_tables[table_index]; - lookup_table->dev_id = dev_name(board->gpib_dev); - gpiod_add_lookup_table(lookup_table); - dbg_printk(1, "Allocating gpios using table index %d\n", table_index); - - for (j = 0 ; j < NUM_PINS ; j++) { - if (gpios_vector[j] < 0) - continue; - /* name not really used in gpiod_get_index() */ - sprintf(name, "GPIO%d", gpios_vector[j]); -try_again: - dbg_printk(1, "Allocating gpio %s pin no %d\n", name, gpios_vector[j]); - desc = gpiod_get_index(board->gpib_dev, name, gpios_vector[j], GPIOD_IN); - - if (IS_ERR(desc)) { - gpiod_remove_lookup_table(lookup_table); - table_index++; - lookup_table = lookup_tables[table_index]; - if (!lookup_table) { - dev_err(board->gpib_dev, "Unable to obtain gpio descriptor for pin %d error %ld\n", - gpios_vector[j], PTR_ERR(desc)); - goto alloc_gpios_fail; - } - dbg_printk(1, "Allocation failed, now using table_index %d\n", table_index); - lookup_table->dev_id = dev_name(board->gpib_dev); - gpiod_add_lookup_table(lookup_table); - goto try_again; - } - all_descriptors[j] = desc; - } - - gpiod_remove_lookup_table(lookup_table); - - return 0; - -alloc_gpios_fail: - release_gpios(); - return -1; -} - -static void bb_detach(struct gpib_board *board) -{ - struct bb_priv *priv = board->private_data; - - dbg_printk(2, "Enter with data %p\n", board->private_data); - if (!board->private_data) - return; - - bb_free_irq(board, &priv->irq_DAV, NAME "_DAV"); - bb_free_irq(board, &priv->irq_NRFD, NAME "_NRFD"); - bb_free_irq(board, &priv->irq_NDAC, NAME "_NDAC"); - bb_free_irq(board, &priv->irq_SRQ, NAME "_SRQ"); - - if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */ - gpiod_set_value(YOGA_ENABLE, 0); - } - - release_gpios(); - - dbg_printk(2, "detached board: %d\n", board->minor); - dbg_printk(0, "NRFD: idle %d, seq %d, NDAC: idle %d, seq %d DAV: idle %d seq: %d all: %ld", - priv->nrfd_idle, priv->nrfd_seq, - priv->ndac_idle, priv->ndac_seq, - priv->dav_idle, priv->dav_seq, priv->all_irqs); - - free_private(board); -} - -static int bb_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct bb_priv *priv; - int retval = 0; - - dbg_printk(2, "%s\n", "Enter ..."); - - board->status = 0; - - if (allocate_private(board)) - return -ENOMEM; - priv = board->private_data; - priv->direction = -1; - priv->t1_delay = 2000; - priv->listener_state = listener_idle; - priv->talker_state = talker_idle; - - sn7516x = sn7516x_used; - if (strcmp(PINMAP_0, pin_map) == 0) { - if (!sn7516x) { - gpios_vector[&(PE) - &all_descriptors[0]] = -1; - gpios_vector[&(DC) - &all_descriptors[0]] = -1; - gpios_vector[&(TE) - &all_descriptors[0]] = -1; - } - } else if (strcmp(PINMAP_1, pin_map) == 0) { - if (!sn7516x) { - gpios_vector[&(PE) - &all_descriptors[0]] = -1; - gpios_vector[&(DC) - &all_descriptors[0]] = -1; - gpios_vector[&(TE) - &all_descriptors[0]] = -1; - } - gpios_vector[&(REN) - &all_descriptors[0]] = 0; /* 27 -> 0 REN on GPIB pin 0 */ - } else if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */ - sn7516x = 0; - gpios_vector[&(D03) - &all_descriptors[0]] = YOGA_D03_pin_nr; - gpios_vector[&(D04) - &all_descriptors[0]] = YOGA_D04_pin_nr; - gpios_vector[&(D05) - &all_descriptors[0]] = YOGA_D05_pin_nr; - gpios_vector[&(D06) - &all_descriptors[0]] = YOGA_D06_pin_nr; - gpios_vector[&(PE) - &all_descriptors[0]] = -1; - gpios_vector[&(DC) - &all_descriptors[0]] = -1; - } else { - dev_err(board->gpib_dev, "Unrecognized pin map %s\n", pin_map); - goto bb_attach_fail; - } - dbg_printk(0, "Using pin map \"%s\" %s\n", pin_map, (sn7516x) ? - " with SN7516x driver support" : ""); - - if (allocate_gpios(board)) - goto bb_attach_fail; - -/* - * Configure SN7516X control lines. - * drive ATN, IFC and REN as outputs only when master - * i.e. system controller. In this mode can only be the CIC - * When not master then enable device mode ATN, IFC & REN as inputs - */ - if (sn7516x) { - gpiod_direction_output(DC, 0); - gpiod_direction_output(TE, 1); - gpiod_direction_output(PE, 1); - } -/* Set main control lines to a known state */ - gpiod_direction_output(IFC, 1); - gpiod_direction_output(REN, 1); - gpiod_direction_output(_ATN, 1); - - if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA: enable level shifters */ - gpiod_direction_output(YOGA_ENABLE, 1); - } - - spin_lock_init(&priv->rw_lock); - - /* request DAV interrupt for read */ - if (bb_get_irq(board, NAME "_DAV", DAV, &priv->irq_DAV, bb_DAV_interrupt, NULL, - IRQF_TRIGGER_NONE)) - goto bb_attach_fail_r; - - /* request NRFD interrupt for write */ - if (bb_get_irq(board, NAME "_NRFD", NRFD, &priv->irq_NRFD, bb_NRFD_interrupt, NULL, - IRQF_TRIGGER_NONE)) - goto bb_attach_fail_r; - - /* request NDAC interrupt for write */ - if (bb_get_irq(board, NAME "_NDAC", NDAC, &priv->irq_NDAC, bb_NDAC_interrupt, NULL, - IRQF_TRIGGER_NONE)) - goto bb_attach_fail_r; - - /* request SRQ interrupt for Service Request */ - if (bb_get_irq(board, NAME "_SRQ", SRQ, &priv->irq_SRQ, bb_SRQ_interrupt, NULL, - IRQF_TRIGGER_NONE)) - goto bb_attach_fail_r; - - dbg_printk(0, "attached board %d\n", board->minor); - goto bb_attach_out; - -bb_attach_fail_r: - release_gpios(); -bb_attach_fail: - retval = -1; -bb_attach_out: - return retval; -} - -static struct gpib_interface bb_interface = { - .name = NAME, - .attach = bb_attach, - .detach = bb_detach, - .read = bb_read, - .write = bb_write, - .command = bb_command, - .take_control = bb_take_control, - .go_to_standby = bb_go_to_standby, - .request_system_control = bb_request_system_control, - .interface_clear = bb_interface_clear, - .remote_enable = bb_remote_enable, - .enable_eos = bb_enable_eos, - .disable_eos = bb_disable_eos, - .parallel_poll = bb_parallel_poll, - .parallel_poll_configure = bb_parallel_poll_configure, - .parallel_poll_response = bb_parallel_poll_response, - .line_status = bb_line_status, - .update_status = bb_update_status, - .primary_address = bb_primary_address, - .secondary_address = bb_secondary_address, - .serial_poll_response = bb_serial_poll_response, - .serial_poll_status = bb_serial_poll_status, - .t1_delay = bb_t1_delay, - .return_to_local = bb_return_to_local, -}; - -static int __init bb_init_module(void) -{ - int result = gpib_register_driver(&bb_interface, THIS_MODULE); - - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - return result; - } - - return 0; -} - -static void __exit bb_exit_module(void) -{ - gpib_unregister_driver(&bb_interface); -} - -module_init(bb_init_module); -module_exit(bb_exit_module); - -/*************************************************************************** - * * - * UTILITY Functions * - * * - ***************************************************************************/ -inline long usec_diff(struct timespec64 *a, struct timespec64 *b) -{ - return ((a->tv_sec - b->tv_sec) * 1000000 + - (a->tv_nsec - b->tv_nsec) / 1000); -} - -static inline int check_for_eos(struct bb_priv *priv, u8 byte) -{ - if (priv->eos_check) - return 0; - - if (priv->eos_check_8) { - if (priv->eos == byte) - return 1; - } else { - if (priv->eos_mask_7 == (byte & 0x7f)) - return 1; - } - return 0; -} - -static void set_data_lines_output(void) -{ - gpiod_direction_output(D01, 1); - gpiod_direction_output(D02, 1); - gpiod_direction_output(D03, 1); - gpiod_direction_output(D04, 1); - gpiod_direction_output(D05, 1); - gpiod_direction_output(D06, 1); - gpiod_direction_output(D07, 1); - gpiod_direction_output(D08, 1); -} - -static void set_data_lines(u8 byte) -{ - gpiod_set_value(D01, !(byte & 0x01)); - gpiod_set_value(D02, !(byte & 0x02)); - gpiod_set_value(D03, !(byte & 0x04)); - gpiod_set_value(D04, !(byte & 0x08)); - gpiod_set_value(D05, !(byte & 0x10)); - gpiod_set_value(D06, !(byte & 0x20)); - gpiod_set_value(D07, !(byte & 0x40)); - gpiod_set_value(D08, !(byte & 0x80)); -} - -static u8 get_data_lines(void) -{ - u8 ret; - - ret = gpiod_get_value(D01); - ret |= gpiod_get_value(D02) << 1; - ret |= gpiod_get_value(D03) << 2; - ret |= gpiod_get_value(D04) << 3; - ret |= gpiod_get_value(D05) << 4; - ret |= gpiod_get_value(D06) << 5; - ret |= gpiod_get_value(D07) << 6; - ret |= gpiod_get_value(D08) << 7; - return ~ret; -} - -static void set_data_lines_input(void) -{ - gpiod_direction_input(D01); - gpiod_direction_input(D02); - gpiod_direction_input(D03); - gpiod_direction_input(D04); - gpiod_direction_input(D05); - gpiod_direction_input(D06); - gpiod_direction_input(D07); - gpiod_direction_input(D08); -} - -static inline void SET_DIR_WRITE(struct bb_priv *priv) -{ - if (priv->direction == DIR_WRITE) - return; - - gpiod_direction_input(NRFD); - gpiod_direction_input(NDAC); - set_data_lines_output(); - gpiod_direction_output(DAV, 1); - gpiod_direction_output(EOI, 1); - - if (sn7516x) { - gpiod_set_value(PE, 1); /* set data lines to transmit on sn75160b */ - gpiod_set_value(TE, 1); /* set NDAC and NRFD to receive and DAV to transmit */ - } - - priv->direction = DIR_WRITE; -} - -static inline void SET_DIR_READ(struct bb_priv *priv) -{ - if (priv->direction == DIR_READ) - return; - - gpiod_direction_input(DAV); - gpiod_direction_input(EOI); - - set_data_lines_input(); - - if (sn7516x) { - gpiod_set_value(PE, 0); /* set data lines to receive on sn75160b */ - gpiod_set_value(TE, 0); /* set NDAC and NRFD to transmit and DAV to receive */ - } - - gpiod_direction_output(NRFD, 0); /* hold off the talker */ - gpiod_direction_output(NDAC, 0); /* data not accepted */ - - priv->direction = DIR_READ; -} diff --git a/drivers/staging/gpib/hp_82335/Makefile b/drivers/staging/gpib/hp_82335/Makefile deleted file mode 100644 index 305ce44ee48a..000000000000 --- a/drivers/staging/gpib/hp_82335/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - -obj-$(CONFIG_GPIB_HP82335) += hp82335.o - - diff --git a/drivers/staging/gpib/hp_82335/hp82335.c b/drivers/staging/gpib/hp_82335/hp82335.c deleted file mode 100644 index d0e47ef77c87..000000000000 --- a/drivers/staging/gpib/hp_82335/hp82335.c +++ /dev/null @@ -1,371 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess * - ***************************************************************************/ - -/* - * should enable ATN interrupts (and update board->status on occurrence), - * implement recovery from bus errors (if necessary) - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "hp82335.h" -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for HP 82335 interface cards"); - -static int hp82335_attach(struct gpib_board *board, const struct gpib_board_config *config); -static void hp82335_detach(struct gpib_board *board); -static irqreturn_t hp82335_interrupt(int irq, void *arg); - -// wrappers for interface functions -static int hp82335_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); -} - -static int hp82335_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); -} - -static int hp82335_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); -} - -static int hp82335_take_control(struct gpib_board *board, int synchronous) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_take_control(board, &priv->tms9914_priv, synchronous); -} - -static int hp82335_go_to_standby(struct gpib_board *board) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_go_to_standby(board, &priv->tms9914_priv); -} - -static int hp82335_request_system_control(struct gpib_board *board, int request_control) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_request_system_control(board, &priv->tms9914_priv, request_control); -} - -static void hp82335_interface_clear(struct gpib_board *board, int assert) -{ - struct hp82335_priv *priv = board->private_data; - - tms9914_interface_clear(board, &priv->tms9914_priv, assert); -} - -static void hp82335_remote_enable(struct gpib_board *board, int enable) -{ - struct hp82335_priv *priv = board->private_data; - - tms9914_remote_enable(board, &priv->tms9914_priv, enable); -} - -static int hp82335_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); -} - -static void hp82335_disable_eos(struct gpib_board *board) -{ - struct hp82335_priv *priv = board->private_data; - - tms9914_disable_eos(board, &priv->tms9914_priv); -} - -static unsigned int hp82335_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); -} - -static int hp82335_primary_address(struct gpib_board *board, unsigned int address) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_primary_address(board, &priv->tms9914_priv, address); -} - -static int hp82335_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); -} - -static int hp82335_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_parallel_poll(board, &priv->tms9914_priv, result); -} - -static void hp82335_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - struct hp82335_priv *priv = board->private_data; - - tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); -} - -static void hp82335_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct hp82335_priv *priv = board->private_data; - - tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); -} - -static void hp82335_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct hp82335_priv *priv = board->private_data; - - tms9914_serial_poll_response(board, &priv->tms9914_priv, status); -} - -static u8 hp82335_serial_poll_status(struct gpib_board *board) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_serial_poll_status(board, &priv->tms9914_priv); -} - -static int hp82335_line_status(const struct gpib_board *board) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_line_status(board, &priv->tms9914_priv); -} - -static int hp82335_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct hp82335_priv *priv = board->private_data; - - return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); -} - -static void hp82335_return_to_local(struct gpib_board *board) -{ - struct hp82335_priv *priv = board->private_data; - - tms9914_return_to_local(board, &priv->tms9914_priv); -} - -static struct gpib_interface hp82335_interface = { - .name = "hp82335", - .attach = hp82335_attach, - .detach = hp82335_detach, - .read = hp82335_read, - .write = hp82335_write, - .command = hp82335_command, - .request_system_control = hp82335_request_system_control, - .take_control = hp82335_take_control, - .go_to_standby = hp82335_go_to_standby, - .interface_clear = hp82335_interface_clear, - .remote_enable = hp82335_remote_enable, - .enable_eos = hp82335_enable_eos, - .disable_eos = hp82335_disable_eos, - .parallel_poll = hp82335_parallel_poll, - .parallel_poll_configure = hp82335_parallel_poll_configure, - .parallel_poll_response = hp82335_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = hp82335_line_status, - .update_status = hp82335_update_status, - .primary_address = hp82335_primary_address, - .secondary_address = hp82335_secondary_address, - .serial_poll_response = hp82335_serial_poll_response, - .serial_poll_status = hp82335_serial_poll_status, - .t1_delay = hp82335_t1_delay, - .return_to_local = hp82335_return_to_local, -}; - -static int hp82335_allocate_private(struct gpib_board *board) -{ - board->private_data = kzalloc(sizeof(struct hp82335_priv), GFP_KERNEL); - if (!board->private_data) - return -1; - return 0; -} - -static void hp82335_free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static inline unsigned int tms9914_to_hp82335_offset(unsigned int register_num) -{ - return 0x1ff8 + register_num; -} - -static u8 hp82335_read_byte(struct tms9914_priv *priv, unsigned int register_num) -{ - return tms9914_iomem_read_byte(priv, tms9914_to_hp82335_offset(register_num)); -} - -static void hp82335_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) -{ - tms9914_iomem_write_byte(priv, data, tms9914_to_hp82335_offset(register_num)); -} - -static void hp82335_clear_interrupt(struct hp82335_priv *hp_priv) -{ - struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; - - writeb(0, tms_priv->mmiobase + HPREG_INTR_CLEAR); -} - -static int hp82335_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct hp82335_priv *hp_priv; - struct tms9914_priv *tms_priv; - int retval; - const unsigned long upper_iomem_base = config->ibbase + hp82335_rom_size; - - board->status = 0; - - if (hp82335_allocate_private(board)) - return -ENOMEM; - hp_priv = board->private_data; - tms_priv = &hp_priv->tms9914_priv; - tms_priv->read_byte = hp82335_read_byte; - tms_priv->write_byte = hp82335_write_byte; - tms_priv->offset = 1; - - switch (config->ibbase) { - case 0xc4000: - case 0xc8000: - case 0xcc000: - case 0xd0000: - case 0xd4000: - case 0xd8000: - case 0xdc000: - case 0xe0000: - case 0xe4000: - case 0xe8000: - case 0xec000: - case 0xf0000: - case 0xf4000: - case 0xf8000: - case 0xfc000: - break; - default: - dev_err(board->gpib_dev, "invalid base io address 0x%x\n", config->ibbase); - return -EINVAL; - } - if (!request_mem_region(upper_iomem_base, hp82335_upper_iomem_size, "hp82335")) { - dev_err(board->gpib_dev, "failed to allocate io memory region 0x%lx-0x%lx\n", - upper_iomem_base, upper_iomem_base + hp82335_upper_iomem_size - 1); - return -EBUSY; - } - hp_priv->raw_iobase = upper_iomem_base; - tms_priv->mmiobase = ioremap(upper_iomem_base, hp82335_upper_iomem_size); - - retval = request_irq(config->ibirq, hp82335_interrupt, 0, DRV_NAME, board); - if (retval) { - dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq); - return retval; - } - hp_priv->irq = config->ibirq; - - tms9914_board_reset(tms_priv); - - hp82335_clear_interrupt(hp_priv); - - writeb(INTR_ENABLE, tms_priv->mmiobase + HPREG_CCR); - - tms9914_online(board, tms_priv); - - return 0; -} - -static void hp82335_detach(struct gpib_board *board) -{ - struct hp82335_priv *hp_priv = board->private_data; - struct tms9914_priv *tms_priv; - - if (hp_priv) { - tms_priv = &hp_priv->tms9914_priv; - if (hp_priv->irq) - free_irq(hp_priv->irq, board); - if (tms_priv->mmiobase) { - writeb(0, tms_priv->mmiobase + HPREG_CCR); - tms9914_board_reset(tms_priv); - iounmap(tms_priv->mmiobase); - } - if (hp_priv->raw_iobase) - release_mem_region(hp_priv->raw_iobase, hp82335_upper_iomem_size); - } - hp82335_free_private(board); -} - -static int __init hp82335_init_module(void) -{ - int result = gpib_register_driver(&hp82335_interface, THIS_MODULE); - - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - return result; - } - - return 0; -} - -static void __exit hp82335_exit_module(void) -{ - gpib_unregister_driver(&hp82335_interface); -} - -module_init(hp82335_init_module); -module_exit(hp82335_exit_module); - -/* - * GPIB interrupt service routines - */ - -static irqreturn_t hp82335_interrupt(int irq, void *arg) -{ - int status1, status2; - struct gpib_board *board = arg; - struct hp82335_priv *priv = board->private_data; - unsigned long flags; - irqreturn_t retval; - - spin_lock_irqsave(&board->spinlock, flags); - status1 = read_byte(&priv->tms9914_priv, ISR0); - status2 = read_byte(&priv->tms9914_priv, ISR1); - hp82335_clear_interrupt(priv); - retval = tms9914_interrupt_have_status(board, &priv->tms9914_priv, status1, status2); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - diff --git a/drivers/staging/gpib/hp_82335/hp82335.h b/drivers/staging/gpib/hp_82335/hp82335.h deleted file mode 100644 index 0c252a712ec9..000000000000 --- a/drivers/staging/gpib/hp_82335/hp82335.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess * - ***************************************************************************/ - -#ifndef _HP82335_H -#define _HP82335_H - -#include "tms9914.h" -#include "gpibP.h" - -// struct which defines private_data for board -struct hp82335_priv { - struct tms9914_priv tms9914_priv; - unsigned int irq; - unsigned long raw_iobase; -}; - -// size of io memory region used -static const int hp82335_rom_size = 0x2000; -static const int hp82335_upper_iomem_size = 0x2000; - -// hp82335 register offsets -enum hp_read_regs { - HPREG_CSR = 0x17f8, - HPREG_STATUS = 0x1ffc, -}; - -enum hp_write_regs { - HPREG_INTR_CLEAR = 0x17f7, - HPREG_CCR = HPREG_CSR, -}; - -enum ccr_bits { - DMA_ENABLE = (1 << 0), /* DMA enable */ - DMA_CHAN_SELECT = (1 << 1), /* DMA channel select O=3,1=2 */ - INTR_ENABLE = (1 << 2), /* interrupt enable */ - SYS_DISABLE = (1 << 3), /* system controller disable */ -}; - -enum csr_bits { - SWITCH6 = (1 << 0), /* switch 6 position */ - SWITCH5 = (1 << 1), /* switch 5 position */ - SYS_CONTROLLER = (1 << 2), /* system controller bit */ - DMA_ENABLE_STATUS = (1 << 4), /* DMA enabled */ - DMA_CHAN_STATUS = (1 << 5), /* DMA channel 0=3,1=2 */ - INTR_ENABLE_STATUS = (1 << 6), /* Interrupt enable */ - INTR_PENDING = (1 << 7), /* Interrupt Pending */ -}; - -#endif // _HP82335_H diff --git a/drivers/staging/gpib/hp_82341/Makefile b/drivers/staging/gpib/hp_82341/Makefile deleted file mode 100644 index 21367310a17e..000000000000 --- a/drivers/staging/gpib/hp_82341/Makefile +++ /dev/null @@ -1,2 +0,0 @@ - -obj-$(CONFIG_GPIB_HP82341) += hp_82341.o diff --git a/drivers/staging/gpib/hp_82341/hp_82341.c b/drivers/staging/gpib/hp_82341/hp_82341.c deleted file mode 100644 index 1a2ad0560e14..000000000000 --- a/drivers/staging/gpib/hp_82341/hp_82341.c +++ /dev/null @@ -1,907 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * Driver for hp 82341a/b/c/d boards. * - * Might be worth merging with Agilent 82350b driver. * - * copyright : (C) 2002, 2005 by Frank Mori Hess * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "hp_82341.h" -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for hp 82341a/b/c/d boards"); - -static unsigned short read_and_clear_event_status(struct gpib_board *board); -static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count); -static int read_transfer_counter(struct hp_82341_priv *hp_priv); -static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written); -static irqreturn_t hp_82341_interrupt(int irq, void *arg); - -static int hp_82341_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read) -{ - struct hp_82341_priv *hp_priv = board->private_data; - struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; - int retval = 0; - unsigned short event_status; - int i; - int num_fifo_bytes; - // hardware doesn't support checking for end-of-string character when using fifo - if (tms_priv->eos_flags & REOS) - return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); - - clear_bit(DEV_CLEAR_BN, &tms_priv->state); - - read_and_clear_event_status(board); - *end = 0; - *bytes_read = 0; - if (length == 0) - return 0; - // disable fifo for the moment - outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); - /* - * Handle corner case of board not in holdoff and one byte has slipped in already. - * Also, board sometimes has problems (spurious 1 byte reads) when read fifo is - * started up with board in TACS under certain data holdoff conditions. - * Doing a 1 byte tms9914-style read avoids these problems. - */ - if (/*tms_priv->holdoff_active == 0 && */length > 1) { - size_t num_bytes; - - retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0) - dev_err(board->gpib_dev, "tms9914_read failed retval=%i\n", retval); - if (retval < 0 || *end) - return retval; - ++buffer; - --length; - } - tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); - tms9914_release_holdoff(tms_priv); - outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); - i = 0; - num_fifo_bytes = length - 1; - while (i < num_fifo_bytes && *end == 0) { - int block_size; - int j; - int count; - - block_size = min(num_fifo_bytes - i, hp_82341_fifo_size); - set_transfer_counter(hp_priv, block_size); - outb(ENABLE_TI_BUFFER_BIT | DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + - BUFFER_CONTROL_REG); - if (inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) - outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); - - clear_bit(READ_READY_BN, &tms_priv->state); - - retval = wait_event_interruptible(board->wait, - ((event_status = - read_and_clear_event_status(board)) & - (TERMINAL_COUNT_EVENT_BIT | - BUFFER_END_EVENT_BIT)) || - test_bit(DEV_CLEAR_BN, &tms_priv->state) || - test_bit(TIMO_NUM, &board->status)); - if (retval) { - retval = -ERESTARTSYS; - break; - } - // have to disable buffer before we can read from buffer port - outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); - count = block_size - read_transfer_counter(hp_priv); - j = 0; - while (j < count && i < num_fifo_bytes) { - unsigned short data_word = inw(hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); - - buffer[i++] = data_word & 0xff; - ++j; - if (j < count && i < num_fifo_bytes) { - buffer[i++] = (data_word >> 8) & 0xff; - ++j; - } - } - if (event_status & BUFFER_END_EVENT_BIT) { - clear_bit(RECEIVED_END_BN, &tms_priv->state); - - *end = 1; - tms_priv->holdoff_active = 1; - } - if (test_bit(TIMO_NUM, &board->status)) { - retval = -ETIMEDOUT; - break; - } - if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { - retval = -EINTR; - break; - } - } - *bytes_read += i; - buffer += i; - length -= i; - if (retval < 0) - return retval; - // read last byte if we havn't received an END yet - if (*end == 0) { - size_t num_bytes; - // try to make sure we holdoff after last byte read - retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0) - return retval; - } - return 0; -} - -static int restart_write_fifo(struct gpib_board *board, struct hp_82341_priv *hp_priv) -{ - struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; - - if ((inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) == 0) - return 0; - while (1) { - int status; - - // restart doesn't work if data holdoff is in effect - status = tms9914_line_status(board, tms_priv); - if ((status & BUS_NRFD) == 0) { - outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); - return 0; - } - if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) - return -EINTR; - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - if (msleep_interruptible(1)) - return -EINTR; - } - return 0; -} - -static int hp_82341_accel_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - struct hp_82341_priv *hp_priv = board->private_data; - struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; - int i, j; - unsigned short event_status; - int retval = 0; - int fifo_xfer_len = length; - - *bytes_written = 0; - if (send_eoi) - --fifo_xfer_len; - - clear_bit(DEV_CLEAR_BN, &tms_priv->state); - - read_and_clear_event_status(board); - outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); - outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); - for (i = 0; i < fifo_xfer_len;) { - int block_size; - - block_size = min(fifo_xfer_len - i, hp_82341_fifo_size); - set_transfer_counter(hp_priv, block_size); - // load data into board's fifo - for (j = 0; j < block_size;) { - unsigned short data_word = buffer[i++]; - ++j; - if (j < block_size) { - data_word |= buffer[i++] << 8; - ++j; - } - outw(data_word, hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); - } - clear_bit(WRITE_READY_BN, &tms_priv->state); - outb(ENABLE_TI_BUFFER_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); - retval = restart_write_fifo(board, hp_priv); - if (retval < 0) { - dev_err(board->gpib_dev, "failed to restart write stream\n"); - break; - } - retval = wait_event_interruptible(board->wait, - ((event_status = - read_and_clear_event_status(board)) & - TERMINAL_COUNT_EVENT_BIT) || - test_bit(DEV_CLEAR_BN, &tms_priv->state) || - test_bit(TIMO_NUM, &board->status)); - outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); - *bytes_written += block_size - read_transfer_counter(hp_priv); - if (retval) { - retval = -ERESTARTSYS; - break; - } - if (test_bit(TIMO_NUM, &board->status)) { - retval = -ETIMEDOUT; - break; - } - if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { - retval = -EINTR; - break; - } - } - if (retval) - return retval; - if (send_eoi) { - size_t num_bytes; - - retval = hp_82341_write(board, buffer + fifo_xfer_len, 1, 1, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - } - return 0; -} - -static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config); - -static void hp_82341_detach(struct gpib_board *board); - -// wrappers for interface functions -static int hp_82341_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); -} - -static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); -} - -static int hp_82341_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); -} - -static int hp_82341_take_control(struct gpib_board *board, int synchronous) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_take_control(board, &priv->tms9914_priv, synchronous); -} - -static int hp_82341_go_to_standby(struct gpib_board *board) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_go_to_standby(board, &priv->tms9914_priv); -} - -static int hp_82341_request_system_control(struct gpib_board *board, int request_control) -{ - struct hp_82341_priv *priv = board->private_data; - - if (request_control) - priv->mode_control_bits |= SYSTEM_CONTROLLER_BIT; - else - priv->mode_control_bits &= ~SYSTEM_CONTROLLER_BIT; - outb(priv->mode_control_bits, priv->iobase[0] + MODE_CONTROL_STATUS_REG); - return tms9914_request_system_control(board, &priv->tms9914_priv, request_control); -} - -static void hp_82341_interface_clear(struct gpib_board *board, int assert) -{ - struct hp_82341_priv *priv = board->private_data; - - tms9914_interface_clear(board, &priv->tms9914_priv, assert); -} - -static void hp_82341_remote_enable(struct gpib_board *board, int enable) -{ - struct hp_82341_priv *priv = board->private_data; - - tms9914_remote_enable(board, &priv->tms9914_priv, enable); -} - -static int hp_82341_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); -} - -static void hp_82341_disable_eos(struct gpib_board *board) -{ - struct hp_82341_priv *priv = board->private_data; - - tms9914_disable_eos(board, &priv->tms9914_priv); -} - -static unsigned int hp_82341_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); -} - -static int hp_82341_primary_address(struct gpib_board *board, unsigned int address) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_primary_address(board, &priv->tms9914_priv, address); -} - -static int hp_82341_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); -} - -static int hp_82341_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_parallel_poll(board, &priv->tms9914_priv, result); -} - -static void hp_82341_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - struct hp_82341_priv *priv = board->private_data; - - tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); -} - -static void hp_82341_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct hp_82341_priv *priv = board->private_data; - - tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); -} - -static void hp_82341_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct hp_82341_priv *priv = board->private_data; - - tms9914_serial_poll_response(board, &priv->tms9914_priv, status); -} - -static u8 hp_82341_serial_poll_status(struct gpib_board *board) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_serial_poll_status(board, &priv->tms9914_priv); -} - -static int hp_82341_line_status(const struct gpib_board *board) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_line_status(board, &priv->tms9914_priv); -} - -static int hp_82341_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct hp_82341_priv *priv = board->private_data; - - return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); -} - -static void hp_82341_return_to_local(struct gpib_board *board) -{ - struct hp_82341_priv *priv = board->private_data; - - tms9914_return_to_local(board, &priv->tms9914_priv); -} - -static struct gpib_interface hp_82341_unaccel_interface = { - .name = "hp_82341_unaccel", - .attach = hp_82341_attach, - .detach = hp_82341_detach, - .read = hp_82341_read, - .write = hp_82341_write, - .command = hp_82341_command, - .request_system_control = hp_82341_request_system_control, - .take_control = hp_82341_take_control, - .go_to_standby = hp_82341_go_to_standby, - .interface_clear = hp_82341_interface_clear, - .remote_enable = hp_82341_remote_enable, - .enable_eos = hp_82341_enable_eos, - .disable_eos = hp_82341_disable_eos, - .parallel_poll = hp_82341_parallel_poll, - .parallel_poll_configure = hp_82341_parallel_poll_configure, - .parallel_poll_response = hp_82341_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = hp_82341_line_status, - .update_status = hp_82341_update_status, - .primary_address = hp_82341_primary_address, - .secondary_address = hp_82341_secondary_address, - .serial_poll_response = hp_82341_serial_poll_response, - .serial_poll_status = hp_82341_serial_poll_status, - .t1_delay = hp_82341_t1_delay, - .return_to_local = hp_82341_return_to_local, -}; - -static struct gpib_interface hp_82341_interface = { - .name = "hp_82341", - .attach = hp_82341_attach, - .detach = hp_82341_detach, - .read = hp_82341_accel_read, - .write = hp_82341_accel_write, - .command = hp_82341_command, - .request_system_control = hp_82341_request_system_control, - .take_control = hp_82341_take_control, - .go_to_standby = hp_82341_go_to_standby, - .interface_clear = hp_82341_interface_clear, - .remote_enable = hp_82341_remote_enable, - .enable_eos = hp_82341_enable_eos, - .disable_eos = hp_82341_disable_eos, - .parallel_poll = hp_82341_parallel_poll, - .parallel_poll_configure = hp_82341_parallel_poll_configure, - .parallel_poll_response = hp_82341_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = hp_82341_line_status, - .update_status = hp_82341_update_status, - .primary_address = hp_82341_primary_address, - .secondary_address = hp_82341_secondary_address, - .serial_poll_response = hp_82341_serial_poll_response, - .t1_delay = hp_82341_t1_delay, - .return_to_local = hp_82341_return_to_local, -}; - -static int hp_82341_allocate_private(struct gpib_board *board) -{ - board->private_data = kzalloc(sizeof(struct hp_82341_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - return 0; -} - -static void hp_82341_free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static u8 hp_82341_read_byte(struct tms9914_priv *priv, unsigned int register_num) -{ - return inb(priv->iobase + register_num); -} - -static void hp_82341_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) -{ - outb(data, priv->iobase + register_num); -} - -static int hp_82341_find_isapnp_board(struct pnp_dev **dev) -{ - *dev = pnp_find_dev(NULL, ISAPNP_VENDOR('H', 'W', 'P'), - ISAPNP_FUNCTION(0x1411), NULL); - if (!*dev || !(*dev)->card) { - pr_err("failed to find isapnp board\n"); - return -ENODEV; - } - if (pnp_device_attach(*dev) < 0) { - pr_err("board already active, skipping\n"); - return -EBUSY; - } - if (pnp_activate_dev(*dev) < 0) { - pnp_device_detach(*dev); - pr_err("failed to activate(), aborting\n"); - return -EAGAIN; - } - if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { - pnp_device_detach(*dev); - pr_err("invalid port or irq, aborting\n"); - return -ENOMEM; - } - return 0; -} - -static int xilinx_ready(struct hp_82341_priv *hp_priv) -{ - switch (hp_priv->hw_version) { - case HW_VERSION_82341C: - if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & XILINX_READY_BIT) - return 1; - else - return 0; - break; - case HW_VERSION_82341D: - if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_READY_BIT) - return 1; - else - return 0; - default: - pr_err("bug! unknown hw_version\n"); - break; - } - return 0; -} - -static int xilinx_done(struct hp_82341_priv *hp_priv) -{ - switch (hp_priv->hw_version) { - case HW_VERSION_82341C: - if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & DONE_PGL_BIT) - return 1; - else - return 0; - case HW_VERSION_82341D: - if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_DONE_BIT) - return 1; - else - return 0; - default: - pr_err("bug! unknown hw_version\n"); - break; - } - return 0; -} - -static int irq_valid(struct hp_82341_priv *hp_priv, int irq) -{ - switch (hp_priv->hw_version) { - case HW_VERSION_82341C: - switch (irq) { - case 3: - case 5: - case 7: - case 9: - case 10: - case 11: - case 12: - case 15: - return 1; - default: - pr_err("invalid irq=%i for 82341C, irq must be 3, 5, 7, 9, 10, 11, 12, or 15.\n", - irq); - return 0; - } - break; - case HW_VERSION_82341D: - return 1; - default: - pr_err("bug! unknown hw_version\n"); - break; - } - return 0; -} - -static int hp_82341_load_firmware_array(struct hp_82341_priv *hp_priv, - const unsigned char *firmware_data, - unsigned int firmware_length) -{ - int i, j; - static const int timeout = 100; - - for (i = 0; i < firmware_length; ++i) { - for (j = 0; j < timeout; ++j) { - if (need_resched()) - schedule(); - if (xilinx_ready(hp_priv)) - break; - usleep_range(10, 15); - } - if (j == timeout) { - pr_err("timed out waiting for Xilinx ready.\n"); - return -ETIMEDOUT; - } - outb(firmware_data[i], hp_priv->iobase[0] + XILINX_DATA_REG); - } - for (j = 0; j < timeout; ++j) { - if (xilinx_done(hp_priv)) - break; - if (need_resched()) - schedule(); - usleep_range(10, 15); - } - if (j == timeout) { - pr_err("timed out waiting for Xilinx done.\n"); - return -ETIMEDOUT; - } - return 0; -} - -static int hp_82341_load_firmware(struct hp_82341_priv *hp_priv, - const struct gpib_board_config *config) -{ - if (config->init_data_length == 0) { - if (xilinx_done(hp_priv)) - return 0; - pr_err("board needs be initialized with firmware upload.\n" - "\tUse the --init-data option of gpib_config.\n"); - return -EINVAL; - } - switch (hp_priv->hw_version) { - case HW_VERSION_82341C: - if (config->init_data_length != hp_82341c_firmware_length) { - pr_err("bad firmware length=%i for 82341c (expected %i).\n", - config->init_data_length, hp_82341c_firmware_length); - return -EINVAL; - } - break; - case HW_VERSION_82341D: - if (config->init_data_length != hp_82341d_firmware_length) { - pr_err("bad firmware length=%i for 82341d (expected %i).\n", - config->init_data_length, hp_82341d_firmware_length); - return -EINVAL; - } - break; - default: - pr_err("bug! unknown hw_version\n"); - break; - } - return hp_82341_load_firmware_array(hp_priv, config->init_data, config->init_data_length); -} - -static void set_xilinx_not_prog(struct hp_82341_priv *hp_priv, int assert) -{ - switch (hp_priv->hw_version) { - case HW_VERSION_82341C: - if (assert) - hp_priv->config_control_bits |= DONE_PGL_BIT; - else - hp_priv->config_control_bits &= ~DONE_PGL_BIT; - outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); - break; - case HW_VERSION_82341D: - if (assert) - isapnp_write_byte(PIO_DATA_REG, HP_82341D_NOT_PROG_BIT); - else - isapnp_write_byte(PIO_DATA_REG, 0x0); - break; - default: - break; - } -} - -// clear xilinx firmware -static int clear_xilinx(struct hp_82341_priv *hp_priv) -{ - set_xilinx_not_prog(hp_priv, 1); - if (msleep_interruptible(1)) - return -EINTR; - set_xilinx_not_prog(hp_priv, 0); - if (msleep_interruptible(1)) - return -EINTR; - set_xilinx_not_prog(hp_priv, 1); - if (msleep_interruptible(1)) - return -EINTR; - return 0; -} - -static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct hp_82341_priv *hp_priv; - struct tms9914_priv *tms_priv; - u32 start_addr; - u32 iobase; - int irq; - int i; - int retval; - - board->status = 0; - if (hp_82341_allocate_private(board)) - return -ENOMEM; - hp_priv = board->private_data; - tms_priv = &hp_priv->tms9914_priv; - tms_priv->read_byte = hp_82341_read_byte; - tms_priv->write_byte = hp_82341_write_byte; - tms_priv->offset = 1; - - if (config->ibbase == 0) { - struct pnp_dev *dev; - int retval = hp_82341_find_isapnp_board(&dev); - - if (retval < 0) - return retval; - hp_priv->pnp_dev = dev; - iobase = pnp_port_start(dev, 0); - irq = pnp_irq(dev, 0); - hp_priv->hw_version = HW_VERSION_82341D; - hp_priv->io_region_offset = 0x8; - } else { - iobase = config->ibbase; - irq = config->ibirq; - hp_priv->hw_version = HW_VERSION_82341C; - hp_priv->io_region_offset = 0x400; - } - for (i = 0; i < hp_82341_num_io_regions; ++i) { - start_addr = iobase + i * hp_priv->io_region_offset; - if (!request_region(start_addr, hp_82341_region_iosize, DRV_NAME)) { - dev_err(board->gpib_dev, "failed to allocate io ports 0x%x-0x%x\n", - start_addr, - start_addr + hp_82341_region_iosize - 1); - return -EIO; - } - hp_priv->iobase[i] = start_addr; - } - tms_priv->iobase = hp_priv->iobase[2]; - if (hp_priv->hw_version == HW_VERSION_82341D) { - retval = isapnp_cfg_begin(hp_priv->pnp_dev->card->number, - hp_priv->pnp_dev->number); - if (retval < 0) { - dev_err(board->gpib_dev, "isapnp_cfg_begin returned error\n"); - return retval; - } - isapnp_write_byte(PIO_DIRECTION_REG, HP_82341D_XILINX_READY_BIT | - HP_82341D_XILINX_DONE_BIT); - } - retval = clear_xilinx(hp_priv); - if (retval < 0) - return retval; - retval = hp_82341_load_firmware(hp_priv, config); - if (hp_priv->hw_version == HW_VERSION_82341D) - isapnp_cfg_end(); - if (retval < 0) - return retval; - if (irq_valid(hp_priv, irq) == 0) - return -EINVAL; - if (request_irq(irq, hp_82341_interrupt, 0, DRV_NAME, board)) { - dev_err(board->gpib_dev, "failed to allocate IRQ %d\n", irq); - return -EIO; - } - hp_priv->irq = irq; - hp_priv->config_control_bits &= ~IRQ_SELECT_MASK; - hp_priv->config_control_bits |= IRQ_SELECT_BITS(irq); - outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); - hp_priv->mode_control_bits |= ENABLE_IRQ_CONFIG_BIT; - outb(hp_priv->mode_control_bits, hp_priv->iobase[0] + MODE_CONTROL_STATUS_REG); - tms9914_board_reset(tms_priv); - outb(ENABLE_BUFFER_END_EVENT_BIT | ENABLE_TERMINAL_COUNT_EVENT_BIT | - ENABLE_TI_INTERRUPT_EVENT_BIT, hp_priv->iobase[0] + EVENT_ENABLE_REG); - outb(ENABLE_BUFFER_END_INTERRUPT_BIT | ENABLE_TERMINAL_COUNT_INTERRUPT_BIT | - ENABLE_TI_INTERRUPT_BIT, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); - // write clear event register - outb((TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | - BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), - hp_priv->iobase[0] + EVENT_STATUS_REG); - - tms9914_online(board, tms_priv); - - return 0; -} - -static void hp_82341_detach(struct gpib_board *board) -{ - struct hp_82341_priv *hp_priv = board->private_data; - struct tms9914_priv *tms_priv; - int i; - - if (hp_priv) { - tms_priv = &hp_priv->tms9914_priv; - if (hp_priv->iobase[0]) { - outb(0, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); - if (tms_priv->iobase) - tms9914_board_reset(tms_priv); - if (hp_priv->irq) - free_irq(hp_priv->irq, board); - } - for (i = 0; i < hp_82341_num_io_regions; ++i) { - if (hp_priv->iobase[i]) - release_region(hp_priv->iobase[i], hp_82341_region_iosize); - } - if (hp_priv->pnp_dev) - pnp_device_detach(hp_priv->pnp_dev); - } - hp_82341_free_private(board); -} - -#if 0 -/* unused, will be needed when the driver is turned into a pnp_driver */ -static const struct pnp_device_id hp_82341_pnp_table[] = { - {.id = "HWP1411"}, - {.id = ""} -}; -MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table); -#endif - -static int __init hp_82341_init_module(void) -{ - int ret; - - ret = gpib_register_driver(&hp_82341_unaccel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - return ret; - } - - ret = gpib_register_driver(&hp_82341_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - gpib_unregister_driver(&hp_82341_unaccel_interface); - return ret; - } - - return 0; -} - -static void __exit hp_82341_exit_module(void) -{ - gpib_unregister_driver(&hp_82341_interface); - gpib_unregister_driver(&hp_82341_unaccel_interface); -} - -module_init(hp_82341_init_module); -module_exit(hp_82341_exit_module); - -/* - * GPIB interrupt service routines - */ -static unsigned short read_and_clear_event_status(struct gpib_board *board) -{ - struct hp_82341_priv *hp_priv = board->private_data; - unsigned long flags; - unsigned short status; - - spin_lock_irqsave(&board->spinlock, flags); - status = hp_priv->event_status_bits; - hp_priv->event_status_bits = 0; - spin_unlock_irqrestore(&board->spinlock, flags); - return status; -} - -static irqreturn_t hp_82341_interrupt(int irq, void *arg) -{ - int status1, status2; - struct gpib_board *board = arg; - struct hp_82341_priv *hp_priv = board->private_data; - struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; - unsigned long flags; - irqreturn_t retval = IRQ_NONE; - int event_status; - - spin_lock_irqsave(&board->spinlock, flags); - event_status = inb(hp_priv->iobase[0] + EVENT_STATUS_REG); - if (event_status & INTERRUPT_PENDING_EVENT_BIT) - retval = IRQ_HANDLED; - // write-clear status bits - if (event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | - BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT)) { - outb(event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | - BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), - hp_priv->iobase[0] + EVENT_STATUS_REG); - hp_priv->event_status_bits |= event_status; - } - if (event_status & TI_INTERRUPT_EVENT_BIT) { - status1 = read_byte(tms_priv, ISR0); - status2 = read_byte(tms_priv, ISR1); - tms9914_interrupt_have_status(board, tms_priv, status1, status2); - } - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -static int read_transfer_counter(struct hp_82341_priv *hp_priv) -{ - int lo, mid, value; - - lo = inb(hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); - mid = inb(hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); - value = (lo & 0xff) | ((mid << 8) & 0x7f00); - value = ~(value - 1) & 0x7fff; - return value; -} - -static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count) -{ - int complement = -count; - - outb(complement & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); - outb((complement >> 8) & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); - // I don't think the hi count reg is even used, but oh well - outb((complement >> 16) & 0xf, hp_priv->iobase[1] + TRANSFER_COUNT_HIGH_REG); -} - diff --git a/drivers/staging/gpib/hp_82341/hp_82341.h b/drivers/staging/gpib/hp_82341/hp_82341.h deleted file mode 100644 index 859ef2899acb..000000000000 --- a/drivers/staging/gpib/hp_82341/hp_82341.h +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002, 2005 by Frank Mori Hess * - ***************************************************************************/ - -#include "tms9914.h" -#include "gpibP.h" - -enum hp_82341_hardware_version { - HW_VERSION_UNKNOWN, - HW_VERSION_82341C, - HW_VERSION_82341D, -}; - -// struct which defines private_data for board -struct hp_82341_priv { - struct tms9914_priv tms9914_priv; - unsigned int irq; - unsigned short config_control_bits; - unsigned short mode_control_bits; - unsigned short event_status_bits; - struct pnp_dev *pnp_dev; - unsigned long iobase[4]; - unsigned long io_region_offset; - enum hp_82341_hardware_version hw_version; -}; - -static const int hp_82341_region_iosize = 0x8; -static const int hp_82341_num_io_regions = 4; -static const int hp_82341_fifo_size = 0xffe; -static const int hp_82341c_firmware_length = 5764; -static const int hp_82341d_firmware_length = 5302; - -// hp 82341 register offsets -enum hp_82341_region_0_registers { - CONFIG_CONTROL_STATUS_REG = 0x0, - MODE_CONTROL_STATUS_REG = 0x1, - MONITOR_REG = 0x2, // after initialization - XILINX_DATA_REG = 0x2, // before initialization, write only - INTERRUPT_ENABLE_REG = 0x3, - EVENT_STATUS_REG = 0x4, - EVENT_ENABLE_REG = 0x5, - STREAM_STATUS_REG = 0x7, -}; - -enum hp_82341_region_1_registers { - ID0_REG = 0x2, - ID1_REG = 0x3, - TRANSFER_COUNT_LOW_REG = 0x4, - TRANSFER_COUNT_MID_REG = 0x5, - TRANSFER_COUNT_HIGH_REG = 0x6, -}; - -enum hp_82341_region_3_registers { - BUFFER_PORT_LOW_REG = 0x0, - BUFFER_PORT_HIGH_REG = 0x1, - ID2_REG = 0x2, - ID3_REG = 0x3, - BUFFER_FLUSH_REG = 0x4, - BUFFER_CONTROL_REG = 0x7 -}; - -enum config_control_status_bits { - IRQ_SELECT_MASK = 0x7, - DMA_CONFIG_MASK = 0x18, - ENABLE_DMA_CONFIG_BIT = 0x20, - XILINX_READY_BIT = 0x40, // read only - DONE_PGL_BIT = 0x80 -}; - -static inline unsigned int IRQ_SELECT_BITS(int irq) -{ - switch (irq) { - case 3: - return 0x3; - case 5: - return 0x2; - case 7: - return 0x1; - case 9: - return 0x0; - case 10: - return 0x7; - case 11: - return 0x6; - case 12: - return 0x5; - case 15: - return 0x4; - default: - return 0x0; - } -}; - -enum mode_control_status_bits { - SLOT8_BIT = 0x1, // read only - ACTIVE_CONTROLLER_BIT = 0x2, // read only - ENABLE_DMA_BIT = 0x4, - SYSTEM_CONTROLLER_BIT = 0x8, - MONITOR_BIT = 0x10, - ENABLE_IRQ_CONFIG_BIT = 0x20, - ENABLE_TI_STREAM_BIT = 0x40 -}; - -enum monitor_bits { - MONITOR_INTERRUPT_PENDING_BIT = 0x1, // read only - MONITOR_CLEAR_HOLDOFF_BIT = 0x2, // write only - MONITOR_PPOLL_BIT = 0x4, // write clear - MONITOR_SRQ_BIT = 0x8, // write clear - MONITOR_IFC_BIT = 0x10, // write clear - MONITOR_REN_BIT = 0x20, // write clear - MONITOR_END_BIT = 0x40, // write clear - MONITOR_DAV_BIT = 0x80 // write clear -}; - -enum interrupt_enable_bits { - ENABLE_TI_INTERRUPT_BIT = 0x1, - ENABLE_POINTERS_EQUAL_INTERRUPT_BIT = 0x4, - ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10, - ENABLE_TERMINAL_COUNT_INTERRUPT_BIT = 0x20, - ENABLE_DMA_TERMINAL_COUNT_INTERRUPT_BIT = 0x80, -}; - -enum event_status_bits { - TI_INTERRUPT_EVENT_BIT = 0x1, // write clear - INTERRUPT_PENDING_EVENT_BIT = 0x2, // read only - POINTERS_EQUAL_EVENT_BIT = 0x4, // write clear - BUFFER_END_EVENT_BIT = 0x10, // write clear - TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear - DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear -}; - -enum event_enable_bits { - ENABLE_TI_INTERRUPT_EVENT_BIT = 0x1, // write clear - ENABLE_POINTERS_EQUAL_EVENT_BIT = 0x4, // write clear - ENABLE_BUFFER_END_EVENT_BIT = 0x10, // write clear - ENABLE_TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear - ENABLE_DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear -}; - -enum stream_status_bits { - HALTED_STATUS_BIT = 0x1, // read - RESTART_STREAM_BIT = 0x1 // write -}; - -enum buffer_control_bits { - DIRECTION_GPIB_TO_HOST_BIT = 0x20, // transfer direction (set for gpib to host) - ENABLE_TI_BUFFER_BIT = 0x40, // enable fifo - FAST_WR_EN_BIT = 0x80, // 350 ns t1 delay? -}; - -// registers accessible through isapnp chip on 82341d -enum hp_82341d_pnp_registers { - PIO_DATA_REG = 0x20, // read/write pio data lines - PIO_DIRECTION_REG = 0x21, // set pio data line directions (set for input) -}; - -enum hp_82341d_pnp_pio_bits { - HP_82341D_XILINX_READY_BIT = 0x1, - HP_82341D_XILINX_DONE_BIT = 0x2, - // use register layout compatible with C and older versions instead of 32 contiguous ioports - HP_82341D_LEGACY_MODE_BIT = 0x4, - HP_82341D_NOT_PROG_BIT = 0x8, // clear to reinitialize xilinx -}; diff --git a/drivers/staging/gpib/include/amcc5920.h b/drivers/staging/gpib/include/amcc5920.h deleted file mode 100644 index 7a88bd282feb..000000000000 --- a/drivers/staging/gpib/include/amcc5920.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * Header for amcc5920 pci chip - * - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -// plx pci chip registers and bits -enum amcc_registers { - AMCC_INTCS_REG = 0x38, - AMCC_PASS_THRU_REG = 0x60, -}; - -enum amcc_incsr_bits { - AMCC_ADDON_INTR_ENABLE_BIT = 0x2000, - AMCC_ADDON_INTR_ACTIVE_BIT = 0x400000, - AMCC_INTR_ACTIVE_BIT = 0x800000, -}; - -static const int bits_per_region = 8; - -static inline uint32_t amcc_wait_state_bits(unsigned int region, unsigned int num_wait_states) -{ - return (num_wait_states & 0x7) << (--region * bits_per_region); -}; - -enum amcc_prefetch_bits { - PREFETCH_DISABLED = 0x0, - PREFETCH_SMALL = 0x8, - PREFETCH_MEDIUM = 0x10, - PREFETCH_LARGE = 0x18, -}; - -static inline uint32_t amcc_prefetch_bits(unsigned int region, enum amcc_prefetch_bits prefetch) -{ - return prefetch << (--region * bits_per_region); -}; - -static inline uint32_t amcc_PTADR_mode_bit(unsigned int region) -{ - return 0x80 << (--region * bits_per_region); -}; - -static inline uint32_t amcc_disable_write_fifo_bit(unsigned int region) -{ - return 0x20 << (--region * bits_per_region); -}; - diff --git a/drivers/staging/gpib/include/amccs5933.h b/drivers/staging/gpib/include/amccs5933.h deleted file mode 100644 index d7f63c795096..000000000000 --- a/drivers/staging/gpib/include/amccs5933.h +++ /dev/null @@ -1,59 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * Registers and bits for amccs5933 pci chip - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -// register offsets -enum { - MBEF_REG = 0x34, // mailbux empty/full - INTCSR_REG = 0x38, // interrupt control and status - BMCSR_REG = 0x3c, // bus master control and status -}; - -// incoming mailbox 0-3 register offsets -extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox) -{ - return (0x10 + 4 * mailbox); -}; - -// bit definitions - -// INTCSR bits -enum { - OUTBOX_EMPTY_INTR_BIT = 0x10, // enable outbox empty interrupt - INBOX_FULL_INTR_BIT = 0x1000, // enable inbox full interrupt - INBOX_INTR_CS_BIT = 0x20000, // read, or write clear inbox full interrupt - INTR_ASSERTED_BIT = 0x800000, // read only, interrupt asserted -}; - -// select byte 0 to 3 of incoming mailbox -extern inline int INBOX_BYTE_BITS(unsigned int byte) -{ - return (byte & 0x3) << 8; -}; - -// select incoming mailbox 0 to 3 -extern inline int INBOX_SELECT_BITS(unsigned int mailbox) -{ - return (mailbox & 0x3) << 10; -}; - -// select byte 0 to 3 of outgoing mailbox -extern inline int OUTBOX_BYTE_BITS(unsigned int byte) -{ - return (byte & 0x3); -}; - -// select outgoing mailbox 0 to 3 -extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox) -{ - return (mailbox & 0x3) << 2; -}; - -// BMCSR bits -enum { - MBOX_FLAGS_RESET_BIT = 0x08000000, // resets mailbox empty/full flags -}; - diff --git a/drivers/staging/gpib/include/gpibP.h b/drivers/staging/gpib/include/gpibP.h deleted file mode 100644 index 1b27f37e0ba0..000000000000 --- a/drivers/staging/gpib/include/gpibP.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002,2003 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _GPIB_P_H -#define _GPIB_P_H - -#include - -#include "gpib_types.h" -#include "gpib_proto.h" -#include "gpib_cmd.h" -#include "gpib.h" -#include "gpib_ioctl.h" - -#include -#include -#include - -int gpib_register_driver(struct gpib_interface *interface, struct module *mod); -void gpib_unregister_driver(struct gpib_interface *interface); -struct pci_dev *gpib_pci_get_device(const struct gpib_board_config *config, unsigned int vendor_id, - unsigned int device_id, struct pci_dev *from); -struct pci_dev *gpib_pci_get_subsys(const struct gpib_board_config *config, unsigned int vendor_id, - unsigned int device_id, unsigned int ss_vendor, - unsigned int ss_device, struct pci_dev *from); -unsigned int num_gpib_events(const struct gpib_event_queue *queue); -int push_gpib_event(struct gpib_board *board, short event_type); -int pop_gpib_event(struct gpib_board *board, struct gpib_event_queue *queue, short *event_type); -int gpib_request_pseudo_irq(struct gpib_board *board, irqreturn_t (*handler)(int, void *)); -void gpib_free_pseudo_irq(struct gpib_board *board); -int gpib_match_device_path(struct device *dev, const char *device_path_in); - -extern struct gpib_board board_array[GPIB_MAX_NUM_BOARDS]; - -extern struct list_head registered_drivers; - -#endif // _GPIB_P_H - diff --git a/drivers/staging/gpib/include/gpib_cmd.h b/drivers/staging/gpib/include/gpib_cmd.h deleted file mode 100644 index 9e96a3bfa22d..000000000000 --- a/drivers/staging/gpib/include/gpib_cmd.h +++ /dev/null @@ -1,112 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef _GPIB_CMD_H -#define _GPIB_CMD_H - -#include - -/* Command byte definitions tests and functions */ - -/* mask of bits that actually matter in a command byte */ -enum { - gpib_command_mask = 0x7f, -}; - -/* Possible GPIB command messages */ - -enum cmd_byte { - GTL = 0x1, /* go to local */ - SDC = 0x4, /* selected device clear */ - PP_CONFIG = 0x5, - GET = 0x8, /* group execute trigger */ - TCT = 0x9, /* take control */ - LLO = 0x11, /* local lockout */ - DCL = 0x14, /* device clear */ - PPU = 0x15, /* parallel poll unconfigure */ - SPE = 0x18, /* serial poll enable */ - SPD = 0x19, /* serial poll disable */ - CFE = 0x1f, /* configure enable */ - LAD = 0x20, /* value to be 'ored' in to obtain listen address */ - UNL = 0x3F, /* unlisten */ - TAD = 0x40, /* value to be 'ored' in to obtain talk address */ - UNT = 0x5F, /* untalk */ - SAD = 0x60, /* my secondary address (base) */ - PPE = 0x60, /* parallel poll enable (base) */ - PPD = 0x70 /* parallel poll disable */ -}; - -/* confine address to range 0 to 30. */ -static inline unsigned int gpib_address_restrict(u32 addr) -{ - addr &= 0x1f; - if (addr == 0x1f) - addr = 0; - return addr; -} - -static inline u8 MLA(u32 addr) -{ - return gpib_address_restrict(addr) | LAD; -} - -static inline u8 MTA(u32 addr) -{ - return gpib_address_restrict(addr) | TAD; -} - -static inline u8 MSA(u32 addr) -{ - return (addr & 0x1f) | SAD; -} - -static inline s32 gpib_address_equal(u32 pad1, s32 sad1, u32 pad2, s32 sad2) -{ - if (pad1 == pad2) { - if (sad1 == sad2) - return 1; - if (sad1 < 0 && sad2 < 0) - return 1; - } - - return 0; -} - -static inline s32 is_PPE(u8 command) -{ - return (command & 0x70) == 0x60; -} - -static inline s32 is_PPD(u8 command) -{ - return (command & 0x70) == 0x70; -} - -static inline s32 in_addressed_command_group(u8 command) -{ - return (command & 0x70) == 0x0; -} - -static inline s32 in_universal_command_group(u8 command) -{ - return (command & 0x70) == 0x10; -} - -static inline s32 in_listen_address_group(u8 command) -{ - return (command & 0x60) == 0x20; -} - -static inline s32 in_talk_address_group(u8 command) -{ - return (command & 0x60) == 0x40; -} - -static inline s32 in_primary_command_group(u8 command) -{ - return in_addressed_command_group(command) || - in_universal_command_group(command) || - in_listen_address_group(command) || - in_talk_address_group(command); -} - -#endif /* _GPIB_CMD_H */ diff --git a/drivers/staging/gpib/include/gpib_pci_ids.h b/drivers/staging/gpib/include/gpib_pci_ids.h deleted file mode 100644 index 52dcab07a7d1..000000000000 --- a/drivers/staging/gpib/include/gpib_pci_ids.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef __GPIB_PCI_IDS_H -#define __GPIB_PCI_IDS_H - -#ifndef PCI_VENDOR_ID_AMCC -#define PCI_VENDOR_ID_AMCC 0x10e8 -#endif - -#ifndef PCI_VENDOR_ID_CBOARDS -#define PCI_VENDOR_ID_CBOARDS 0x1307 -#endif - -#ifndef PCI_VENDOR_ID_QUANCOM -#define PCI_VENDOR_ID_QUANCOM 0x8008 -#endif - -#ifndef PCI_DEVICE_ID_QUANCOM_GPIB -#define PCI_DEVICE_ID_QUANCOM_GPIB 0x3302 -#endif - -#endif // __GPIB_PCI_IDS_H - diff --git a/drivers/staging/gpib/include/gpib_proto.h b/drivers/staging/gpib/include/gpib_proto.h deleted file mode 100644 index 42e736e3b7cd..000000000000 --- a/drivers/staging/gpib/include/gpib_proto.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef GPIB_PROTO_INCLUDED -#define GPIB_PROTO_INCLUDED - -#include - -int ibopen(struct inode *inode, struct file *filep); -int ibclose(struct inode *inode, struct file *file); -long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg); -void os_start_timer(struct gpib_board *board, unsigned int usec_timeout); -void os_remove_timer(struct gpib_board *board); -void init_gpib_board(struct gpib_board *board); -static inline unsigned long usec_to_jiffies(unsigned int usec) -{ - unsigned long usec_per_jiffy = 1000000 / HZ; - - return 1 + (usec + usec_per_jiffy - 1) / usec_per_jiffy; -}; - -int serial_poll_all(struct gpib_board *board, unsigned int usec_timeout); -void init_gpib_descriptor(struct gpib_descriptor *desc); -int dvrsp(struct gpib_board *board, unsigned int pad, int sad, - unsigned int usec_timeout, u8 *result); -int ibcac(struct gpib_board *board, int sync, int fallback_to_async); -int ibcmd(struct gpib_board *board, u8 *buf, size_t length, size_t *bytes_written); -int ibgts(struct gpib_board *board); -int ibonline(struct gpib_board *board); -int iboffline(struct gpib_board *board); -int iblines(const struct gpib_board *board, short *lines); -int ibrd(struct gpib_board *board, u8 *buf, size_t length, int *end_flag, size_t *bytes_read); -int ibrpp(struct gpib_board *board, u8 *buf); -int ibrsv2(struct gpib_board *board, u8 status_byte, int new_reason_for_service); -int ibrsc(struct gpib_board *board, int request_control); -int ibsic(struct gpib_board *board, unsigned int usec_duration); -int ibsre(struct gpib_board *board, int enable); -int ibpad(struct gpib_board *board, unsigned int addr); -int ibsad(struct gpib_board *board, int addr); -int ibeos(struct gpib_board *board, int eos, int eosflags); -int ibwait(struct gpib_board *board, int wait_mask, int clear_mask, int set_mask, - int *status, unsigned long usec_timeout, struct gpib_descriptor *desc); -int ibwrt(struct gpib_board *board, u8 *buf, size_t cnt, int send_eoi, size_t *bytes_written); -int ibstatus(struct gpib_board *board); -int general_ibstatus(struct gpib_board *board, const struct gpib_status_queue *device, - int clear_mask, int set_mask, struct gpib_descriptor *desc); -int io_timed_out(struct gpib_board *board); -int ibppc(struct gpib_board *board, u8 configuration); - -#endif /* GPIB_PROTO_INCLUDED */ diff --git a/drivers/staging/gpib/include/gpib_state_machines.h b/drivers/staging/gpib/include/gpib_state_machines.h deleted file mode 100644 index 7488c00f191e..000000000000 --- a/drivers/staging/gpib/include/gpib_state_machines.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2006 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _GPIB_STATE_MACHINES_H -#define _GPIB_STATE_MACHINES_H - -enum talker_function_state { - talker_idle, - talker_addressed, - talker_active, - serial_poll_active -}; - -enum listener_function_state { - listener_idle, - listener_addressed, - listener_active -}; - -#endif // _GPIB_STATE_MACHINES_H diff --git a/drivers/staging/gpib/include/gpib_types.h b/drivers/staging/gpib/include/gpib_types.h deleted file mode 100644 index 998abb379749..000000000000 --- a/drivers/staging/gpib/include/gpib_types.h +++ /dev/null @@ -1,381 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _GPIB_TYPES_H -#define _GPIB_TYPES_H - -#ifdef __KERNEL__ -#include "gpib.h" -#include -#include -#include -#include -#include -#include -#include - -struct gpib_board; - -/* config parameters that are only used by driver attach functions */ -struct gpib_board_config { - /* firmware blob */ - void *init_data; - int init_data_length; - /* IO base address to use for non-pnp cards (set by core, driver should make local copy) */ - u32 ibbase; - void __iomem *mmibbase; - /* IRQ to use for non-pnp cards (set by core, driver should make local copy) */ - unsigned int ibirq; - /* dma channel to use for non-pnp cards (set by core, driver should make local copy) */ - unsigned int ibdma; - /* - * pci bus of card, useful for distinguishing multiple identical pci cards - * (negative means don't care) - */ - int pci_bus; - /* - * pci slot of card, useful for distinguishing multiple identical pci cards - * (negative means don't care) - */ - int pci_slot; - /* sysfs device path of hardware to attach */ - char *device_path; - /* serial number of hardware to attach */ - char *serial_number; -}; - -/* - * struct gpib_interface defines the interface - * between the board-specific details dealt with in the drivers - * and generic interface provided by gpib-common. - * This really should be in a different header file. - */ -struct gpib_interface { - /* name of board */ - char *name; - /* attach() initializes board and allocates resources */ - int (*attach)(struct gpib_board *board, const struct gpib_board_config *config); - /* detach() shuts down board and frees resources */ - void (*detach)(struct gpib_board *board); - /* - * read() should read at most 'length' bytes from the bus into - * 'buffer'. It should return when it fills the buffer or - * encounters an END (EOI and or EOS if appropriate). It should set 'end' - * to be nonzero if the read was terminated by an END, otherwise 'end' - * should be zero. - * Ultimately, this will be changed into or replaced by an asynchronous - * read. Zero return value for success, negative - * return indicates error. - * nbytes returns number of bytes read - */ - int (*read)(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read); - /* - * write() should write 'length' bytes from buffer to the bus. - * If the boolean value send_eoi is nonzero, then EOI should - * be sent along with the last byte. Returns number of bytes - * written or negative value on error. - */ - int (*write)(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written); - /* - * command() writes the command bytes in 'buffer' to the bus - * Returns zero on success or negative value on error. - */ - int (*command)(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written); - /* - * Take control (assert ATN). If 'asyncronous' is nonzero, take - * control asyncronously (assert ATN immediately without waiting - * for other processes to complete first). Should not return - * until board becomes controller in charge. Returns zero no success, - * nonzero on error. - */ - int (*take_control)(struct gpib_board *board, int asyncronous); - /* - * De-assert ATN. Returns zero on success, nonzer on error. - */ - int (*go_to_standby)(struct gpib_board *board); - /* request/release control of the IFC and REN lines (system controller) */ - int (*request_system_control)(struct gpib_board *board, int request_control); - /* - * Asserts or de-asserts 'interface clear' (IFC) depending on - * boolean value of 'assert' - */ - void (*interface_clear)(struct gpib_board *board, int assert); - /* - * Sends remote enable command if 'enable' is nonzero, disables remote mode - * if 'enable' is zero - */ - void (*remote_enable)(struct gpib_board *board, int enable); - /* - * enable END for reads, when byte 'eos' is received. If - * 'compare_8_bits' is nonzero, then all 8 bits are compared - * with the eos bytes. Otherwise only the 7 least significant - * bits are compared. - */ - int (*enable_eos)(struct gpib_board *board, u8 eos, int compare_8_bits); - /* disable END on eos byte (END on EOI only)*/ - void (*disable_eos)(struct gpib_board *board); - /* configure parallel poll */ - void (*parallel_poll_configure)(struct gpib_board *board, u8 configuration); - /* conduct parallel poll */ - int (*parallel_poll)(struct gpib_board *board, u8 *result); - /* set/clear ist (individual status bit) */ - void (*parallel_poll_response)(struct gpib_board *board, int ist); - /* select local parallel poll configuration mode PP2 versus remote PP1 */ - void (*local_parallel_poll_mode)(struct gpib_board *board, int local); - /* - * Returns current status of the bus lines. Should be set to - * NULL if your board does not have the ability to query the - * state of the bus lines. - */ - int (*line_status)(const struct gpib_board *board); - /* - * updates and returns the board's current status. - * The meaning of the bits are specified in gpib_user.h - * in the IBSTA section. The driver does not need to - * worry about setting the CMPL, END, TIMO, or ERR bits. - */ - unsigned int (*update_status)(struct gpib_board *board, unsigned int clear_mask); - /* - * Sets primary address 0-30 for gpib interface card. - */ - int (*primary_address)(struct gpib_board *board, unsigned int address); - /* - * Sets and enables, or disables secondary address 0-30 - * for gpib interface card. - */ - int (*secondary_address)(struct gpib_board *board, unsigned int address, - int enable); - /* - * Sets the byte the board should send in response to a serial poll. - * This function should also start or stop requests for service via - * IEEE 488.2 reqt/reqf, based on MSS (bit 6 of the status_byte). - * If the more flexible serial_poll_response2 is implemented by the - * driver, then this method should be left NULL since it will not - * be used. This method can generate spurious service requests - * which are allowed by IEEE 488.2, but not ideal. - * - * This method should implement the serial poll response method described - * by IEEE 488.2 section 11.3.3.4.3 "Allowed Coupled Control of - * STB, reqt, and reqf". - */ - void (*serial_poll_response)(struct gpib_board *board, u8 status_byte); - /* - * Sets the byte the board should send in response to a serial poll. - * This function should also request service via IEEE 488.2 reqt/reqf - * based on MSS (bit 6 of the status_byte) and new_reason_for_service. - * reqt should be set true if new_reason_for_service is true, - * and reqf should be set true if MSS is false. This function - * will never be called with MSS false and new_reason_for_service - * true simultaneously, so don't worry about that case. - * - * This method implements the serial poll response method described - * by IEEE 488.2 section 11.3.3.4.1 "Preferred Implementation". - * - * If this method is left NULL by the driver, then the user library - * function ibrsv2 will not work. - */ - void (*serial_poll_response2)(struct gpib_board *board, u8 status_byte, - int new_reason_for_service); - /* - * returns the byte the board will send in response to a serial poll. - */ - u8 (*serial_poll_status)(struct gpib_board *board); - /* adjust T1 delay */ - int (*t1_delay)(struct gpib_board *board, unsigned int nano_sec); - /* go to local mode */ - void (*return_to_local)(struct gpib_board *board); - /* board does not support 7 bit eos comparisons */ - unsigned no_7_bit_eos : 1; - /* skip check for listeners before trying to send command bytes */ - unsigned skip_check_for_command_acceptors : 1; -}; - -struct gpib_event_queue { - struct list_head event_head; - spinlock_t lock; // for access to event list - unsigned int num_events; - unsigned dropped_event : 1; -}; - -static inline void init_event_queue(struct gpib_event_queue *queue) -{ - INIT_LIST_HEAD(&queue->event_head); - queue->num_events = 0; - queue->dropped_event = 0; - spin_lock_init(&queue->lock); -} - -/* struct for supporting polling operation when irq is not available */ -struct gpib_pseudo_irq { - struct timer_list timer; - irqreturn_t (*handler)(int irq, void *arg); - struct gpib_board *board; - atomic_t active; -}; - -static inline void init_gpib_pseudo_irq(struct gpib_pseudo_irq *pseudo_irq) -{ - pseudo_irq->handler = NULL; - timer_setup(&pseudo_irq->timer, NULL, 0); - atomic_set(&pseudo_irq->active, 0); -} - -/* list so we can make a linked list of drivers */ -struct gpib_interface_list { - struct list_head list; - struct gpib_interface *interface; - struct module *module; -}; - -/* - * One struct gpib_board is allocated for each physical board in the computer. - * It provides storage for variables local to each board, and interface - * functions for performing operations on the board - */ -struct gpib_board { - /* functions used by this board */ - struct gpib_interface *interface; - /* - * Pointer to module whose use count we should increment when - * interface is in use - */ - struct module *provider_module; - /* buffer used to store read/write data for this board */ - u8 *buffer; - /* length of buffer */ - unsigned int buffer_length; - /* - * Used to hold the board's current status (see update_status() above) - */ - unsigned long status; - /* - * Driver should only sleep on this wait queue. It is special in that the - * core will wake this queue and set the TIMO bit in 'status' when the - * watchdog timer times out. - */ - wait_queue_head_t wait; - /* - * Lock that only allows one process to access this board at a time. - * Has to be first in any locking order, since it can be locked over - * multiple ioctls. - */ - struct mutex user_mutex; - /* - * Mutex which compensates for removal of "big kernel lock" from kernel. - * Should not be held for extended waits. - */ - struct mutex big_gpib_mutex; - /* pid of last process to lock the board mutex */ - pid_t locking_pid; - /* lock for setting locking pid */ - spinlock_t locking_pid_spinlock; - /* Spin lock for dealing with races with the interrupt handler */ - spinlock_t spinlock; - /* Watchdog timer to enable timeouts */ - struct timer_list timer; - /* device of attached driver if any */ - struct device *dev; - /* gpib_common device gpibN */ - struct device *gpib_dev; - /* - * 'private_data' can be used as seen fit by the driver to - * store additional variables for this board - */ - void *private_data; - /* Number of open file descriptors using this board */ - unsigned int use_count; - /* list of open devices connected to this board */ - struct list_head device_list; - /* primary address */ - unsigned int pad; - /* secondary address */ - int sad; - /* timeout for io operations, in microseconds */ - unsigned int usec_timeout; - /* board's parallel poll configuration byte */ - u8 parallel_poll_configuration; - /* t1 delay we are using */ - unsigned int t1_nano_sec; - /* Count that keeps track of whether board is up and running or not */ - unsigned int online; - /* number of processes trying to autopoll */ - int autospollers; - /* autospoll kernel thread */ - struct task_struct *autospoll_task; - /* queue for recording received trigger/clear/ifc events */ - struct gpib_event_queue event_queue; - /* minor number for this board's device file */ - int minor; - /* struct to deal with polling mode*/ - struct gpib_pseudo_irq pseudo_irq; - /* error dong autopoll */ - atomic_t stuck_srq; - struct gpib_board_config config; - /* Flag that indicates whether board is system controller of the bus */ - unsigned master : 1; - /* individual status bit */ - unsigned ist : 1; - /* - * one means local parallel poll mode ieee 488.1 PP2 (or no parallel poll PP0), - * zero means remote parallel poll configuration mode ieee 488.1 PP1 - */ - unsigned local_ppoll_mode : 1; -}; - -/* element of event queue */ -struct gpib_event { - struct list_head list; - short event_type; -}; - -/* - * Each board has a list of gpib_status_queue to keep track of all open devices - * on the bus, so we know what address to poll when we get a service request - */ -struct gpib_status_queue { - /* list_head so we can make a linked list of devices */ - struct list_head list; - unsigned int pad; /* primary gpib address */ - int sad; /* secondary gpib address (negative means disabled) */ - /* stores serial poll bytes for this device */ - struct list_head status_bytes; - unsigned int num_status_bytes; - /* number of times this address is opened */ - unsigned int reference_count; - /* flags loss of status byte error due to limit on size of queue */ - unsigned dropped_byte : 1; -}; - -struct gpib_status_byte { - struct list_head list; - u8 poll_byte; -}; - -void init_gpib_status_queue(struct gpib_status_queue *device); - -/* Used to store device-descriptor-specific information */ -struct gpib_descriptor { - unsigned int pad; /* primary gpib address */ - int sad; /* secondary gpib address (negative means disabled) */ - atomic_t io_in_progress; - unsigned is_board : 1; - unsigned autopoll_enabled : 1; -}; - -struct gpib_file_private { - atomic_t holding_mutex; - struct gpib_descriptor *descriptors[GPIB_MAX_NUM_DESCRIPTORS]; - /* locked while descriptors are being allocated/deallocated */ - struct mutex descriptors_mutex; - unsigned got_module : 1; -}; - -#endif /* __KERNEL__ */ - -#endif /* _GPIB_TYPES_H */ diff --git a/drivers/staging/gpib/include/nec7210.h b/drivers/staging/gpib/include/nec7210.h deleted file mode 100644 index 9835aa5ef4ff..000000000000 --- a/drivers/staging/gpib/include/nec7210.h +++ /dev/null @@ -1,141 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _NEC7210_H -#define _NEC7210_H - -#include "gpib_state_machines.h" -#include -#include -#include -#include - -#include "gpib_types.h" -#include "nec7210_registers.h" - -/* struct used to provide variables local to a nec7210 chip */ -struct nec7210_priv { -#ifdef CONFIG_HAS_IOPORT - u32 iobase; -#endif - void __iomem *mmiobase; - unsigned int offset; // offset between successive nec7210 io addresses - unsigned int dma_channel; - u8 *dma_buffer; - unsigned int dma_buffer_length; // length of dma buffer - dma_addr_t dma_buffer_addr; // bus address of board->buffer for use with dma - // software copy of bits written to registers - u8 reg_bits[8]; - u8 auxa_bits; // bits written to auxiliary register A - u8 auxb_bits; // bits written to auxiliary register B - // used to keep track of board's state, bit definitions given below - unsigned long state; - // lock for chips that extend the nec7210 registers by paging in alternate regs - spinlock_t register_page_lock; - // wrappers for outb, inb, readb, or writeb - u8 (*read_byte)(struct nec7210_priv *priv, unsigned int register_number); - void (*write_byte)(struct nec7210_priv *priv, u8 byte, unsigned int register_number); - enum nec7210_chipset type; - enum talker_function_state talker_state; - enum listener_function_state listener_state; - void *private; - unsigned srq_pending : 1; -}; - -static inline void init_nec7210_private(struct nec7210_priv *priv) -{ - memset(priv, 0, sizeof(struct nec7210_priv)); - spin_lock_init(&priv->register_page_lock); -} - -// slightly shorter way to access read_byte and write_byte -static inline u8 read_byte(struct nec7210_priv *priv, unsigned int register_number) -{ - return priv->read_byte(priv, register_number); -} - -static inline void write_byte(struct nec7210_priv *priv, u8 byte, unsigned int register_number) -{ - priv->write_byte(priv, byte, register_number); -} - -// struct nec7210_priv.state bit numbers -enum { - PIO_IN_PROGRESS_BN, // pio transfer in progress - DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress - DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress - READ_READY_BN, // board has data byte available to read - WRITE_READY_BN, // board is ready to send a data byte - COMMAND_READY_BN, // board is ready to send a command byte - RECEIVED_END_BN, // received END - BUS_ERROR_BN, // output error has occurred - RFD_HOLDOFF_BN, // rfd holdoff in effect - DEV_CLEAR_BN, // device clear received - ADR_CHANGE_BN, // address state change occurred -}; - -// interface functions -int nec7210_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length, int *end, size_t *bytes_read); -int nec7210_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written); -int nec7210_command(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length, size_t *bytes_written); -int nec7210_take_control(struct gpib_board *board, struct nec7210_priv *priv, int syncronous); -int nec7210_go_to_standby(struct gpib_board *board, struct nec7210_priv *priv); -int nec7210_request_system_control(struct gpib_board *board, - struct nec7210_priv *priv, int request_control); -void nec7210_interface_clear(struct gpib_board *board, struct nec7210_priv *priv, int assert); -void nec7210_remote_enable(struct gpib_board *board, struct nec7210_priv *priv, int enable); -int nec7210_enable_eos(struct gpib_board *board, struct nec7210_priv *priv, u8 eos_bytes, - int compare_8_bits); -void nec7210_disable_eos(struct gpib_board *board, struct nec7210_priv *priv); -unsigned int nec7210_update_status(struct gpib_board *board, struct nec7210_priv *priv, - unsigned int clear_mask); -unsigned int nec7210_update_status_nolock(struct gpib_board *board, struct nec7210_priv *priv); -int nec7210_primary_address(const struct gpib_board *board, - struct nec7210_priv *priv, unsigned int address); -int nec7210_secondary_address(const struct gpib_board *board, struct nec7210_priv *priv, - unsigned int address, int enable); -int nec7210_parallel_poll(struct gpib_board *board, struct nec7210_priv *priv, u8 *result); -void nec7210_serial_poll_response(struct gpib_board *board, - struct nec7210_priv *priv, u8 status); -void nec7210_parallel_poll_configure(struct gpib_board *board, - struct nec7210_priv *priv, unsigned int configuration); -void nec7210_parallel_poll_response(struct gpib_board *board, - struct nec7210_priv *priv, int ist); -u8 nec7210_serial_poll_status(struct gpib_board *board, struct nec7210_priv *priv); -int nec7210_t1_delay(struct gpib_board *board, - struct nec7210_priv *priv, unsigned int nano_sec); -void nec7210_return_to_local(const struct gpib_board *board, struct nec7210_priv *priv); - -// utility functions -void nec7210_board_reset(struct nec7210_priv *priv, const struct gpib_board *board); -void nec7210_board_online(struct nec7210_priv *priv, const struct gpib_board *board); -unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg, - unsigned int mask, unsigned int bits); -void nec7210_set_handshake_mode(struct gpib_board *board, struct nec7210_priv *priv, int mode); -void nec7210_release_rfd_holdoff(struct gpib_board *board, struct nec7210_priv *priv); -u8 nec7210_read_data_in(struct gpib_board *board, struct nec7210_priv *priv, int *end); - -// wrappers for io functions -u8 nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num); -void nec7210_ioport_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num); -u8 nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num); -void nec7210_iomem_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num); -u8 nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num); -void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, u8 data, - unsigned int register_num); -u8 nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num); -void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data, - unsigned int register_num); - -// interrupt service routine -irqreturn_t nec7210_interrupt(struct gpib_board *board, struct nec7210_priv *priv); -irqreturn_t nec7210_interrupt_have_status(struct gpib_board *board, - struct nec7210_priv *priv, int status1, int status2); - -#endif //_NEC7210_H diff --git a/drivers/staging/gpib/include/nec7210_registers.h b/drivers/staging/gpib/include/nec7210_registers.h deleted file mode 100644 index 067983d7a07f..000000000000 --- a/drivers/staging/gpib/include/nec7210_registers.h +++ /dev/null @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _NEC7210_REGISTERS_H -#define _NEC7210_REGISTERS_H - -enum nec7210_chipset { - NEC7210, // The original - TNT4882, // NI - NAT4882, // NI - CB7210, // measurement computing - IOT7210, // iotech - IGPIB7210, // Ines - TNT5004, // NI (minor differences to TNT4882) -}; - -/* - * nec7210 register numbers (might need to be multiplied by - * a board-dependent offset to get actually io address offset) - */ -// write registers -enum nec7210_write_regs { - CDOR, // command/data out - IMR1, // interrupt mask 1 - IMR2, // interrupt mask 2 - SPMR, // serial poll mode - ADMR, // address mode - AUXMR, // auxiliary mode - ADR, // address - EOSR, // end-of-string - - // nec7210 has 8 registers - nec7210_num_registers = 8, -}; - -// read registers -enum nec7210_read_regs { - DIR, // data in - ISR1, // interrupt status 1 - ISR2, // interrupt status 2 - SPSR, // serial poll status - ADSR, // address status - CPTR, // command pass though - ADR0, // address 1 - ADR1, // address 2 -}; - -// bit definitions common to nec-7210 compatible registers - -// ISR1: interrupt status register 1 -enum isr1_bits { - HR_DI = (1 << 0), - HR_DO = (1 << 1), - HR_ERR = (1 << 2), - HR_DEC = (1 << 3), - HR_END = (1 << 4), - HR_DET = (1 << 5), - HR_APT = (1 << 6), - HR_CPT = (1 << 7), -}; - -// IMR1: interrupt mask register 1 -enum imr1_bits { - HR_DIIE = (1 << 0), - HR_DOIE = (1 << 1), - HR_ERRIE = (1 << 2), - HR_DECIE = (1 << 3), - HR_ENDIE = (1 << 4), - HR_DETIE = (1 << 5), - HR_APTIE = (1 << 6), - HR_CPTIE = (1 << 7), -}; - -// ISR2, interrupt status register 2 -enum isr2_bits { - HR_ADSC = (1 << 0), - HR_REMC = (1 << 1), - HR_LOKC = (1 << 2), - HR_CO = (1 << 3), - HR_REM = (1 << 4), - HR_LOK = (1 << 5), - HR_SRQI = (1 << 6), - HR_INT = (1 << 7), -}; - -// IMR2, interrupt mask register 2 -enum imr2_bits { - // all the bits in this register that enable interrupts - IMR2_ENABLE_INTR_MASK = 0x4f, - HR_ACIE = (1 << 0), - HR_REMIE = (1 << 1), - HR_LOKIE = (1 << 2), - HR_COIE = (1 << 3), - HR_DMAI = (1 << 4), - HR_DMAO = (1 << 5), - HR_SRQIE = (1 << 6), -}; - -// SPSR, serial poll status register -enum spsr_bits { - HR_PEND = (1 << 6), -}; - -// SPMR, serial poll mode register -enum spmr_bits { - HR_RSV = (1 << 6), -}; - -// ADSR, address status register -enum adsr_bits { - HR_MJMN = (1 << 0), - HR_TA = (1 << 1), - HR_LA = (1 << 2), - HR_TPAS = (1 << 3), - HR_LPAS = (1 << 4), - HR_SPMS = (1 << 5), - HR_NATN = (1 << 6), - HR_CIC = (1 << 7), -}; - -// ADMR, address mode register -enum admr_bits { - HR_ADM0 = (1 << 0), - HR_ADM1 = (1 << 1), - HR_TRM0 = (1 << 4), - HR_TRM1 = (1 << 5), - HR_TRM_EOIOE_TRIG = 0, - HR_TRM_CIC_TRIG = HR_TRM0, - HR_TRM_CIC_EOIOE = HR_TRM1, - HR_TRM_CIC_PE = HR_TRM0 | HR_TRM1, - HR_LON = (1 << 6), - HR_TON = (1 << 7), -}; - -// ADR, bits used in address0, address1 and address0/1 registers -enum adr_bits { - ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits */ - HR_DL = (1 << 5), - HR_DT = (1 << 6), - HR_ARS = (1 << 7), -}; - -// ADR1, address1 register -enum adr1_bits { - HR_EOI = (1 << 7), -}; - -// AUXMR, auxiliary mode register -enum auxmr_bits { - ICR = 0x20, - PPR = 0x60, - AUXRA = 0x80, - AUXRB = 0xa0, - AUXRE = 0xc0, -}; - -// auxra, auxiliary register A -enum auxra_bits { - HR_HANDSHAKE_MASK = 0x3, - HR_HLDA = 0x1, - HR_HLDE = 0x2, - HR_LCM = 0x3, /* auxra listen continuous */ - HR_REOS = 0x4, - HR_XEOS = 0x8, - HR_BIN = 0x10, -}; - -// auxrb, auxiliary register B -enum auxrb_bits { - HR_CPTE = (1 << 0), - HR_SPEOI = (1 << 1), - HR_TRI = (1 << 2), - HR_INV = (1 << 3), - HR_ISS = (1 << 4), -}; - -enum auxre_bits { - HR_DAC_HLD_DCAS = 0x1, /* perform DAC holdoff on receiving clear */ - HR_DAC_HLD_DTAS = 0x2, /* perform DAC holdoff on receiving trigger */ -}; - -// parallel poll register -enum ppr_bits { - HR_PPS = (1 << 3), - HR_PPU = (1 << 4), -}; - -/* 7210 Auxiliary Commands */ -enum aux_cmds { - AUX_PON = 0x0, /* Immediate Execute pon */ - AUX_CPPF = 0x1, /* Clear Parallel Poll Flag */ - AUX_CR = 0x2, /* Chip Reset */ - AUX_FH = 0x3, /* Finish Handshake */ - AUX_TRIG = 0x4, /* Trigger */ - AUX_RTL = 0x5, /* Return to local */ - AUX_SEOI = 0x6, /* Send EOI */ - AUX_NVAL = 0x7, /* Non-Valid Secondary Command or Address */ - AUX_SPPF = 0x9, /* Set Parallel Poll Flag */ - AUX_VAL = 0xf, /* Valid Secondary Command or Address */ - AUX_GTS = 0x10, /* Go To Standby */ - AUX_TCA = 0x11, /* Take Control Asynchronously */ - AUX_TCS = 0x12, /* Take Control Synchronously */ - AUX_LTN = 0x13, /* Listen */ - AUX_DSC = 0x14, /* Disable System Control */ - AUX_CIFC = 0x16, /* Clear IFC */ - AUX_CREN = 0x17, /* Clear REN */ - AUX_TCSE = 0x1a, /* Take Control Synchronously on End */ - AUX_LTNC = 0x1b, /* Listen in Continuous Mode */ - AUX_LUN = 0x1c, /* Local Unlisten */ - AUX_EPP = 0x1d, /* Execute Parallel Poll */ - AUX_SIFC = 0x1e, /* Set IFC */ - AUX_SREN = 0x1f, /* Set REN */ -}; - -#endif //_NEC7210_REGISTERS_H diff --git a/drivers/staging/gpib/include/plx9050.h b/drivers/staging/gpib/include/plx9050.h deleted file mode 100644 index c911b285a0ca..000000000000 --- a/drivers/staging/gpib/include/plx9050.h +++ /dev/null @@ -1,72 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * Header for plx9050 pci chip - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _PLX9050_GPIB_H -#define _PLX9050_GPIB_H - -// plx pci chip registers and bits -enum { - PLX9050_INTCSR_REG = 0x4c, - PLX9050_CNTRL_REG = 0x50 -}; - -enum plx9050_intcsr_bits { - PLX9050_LINTR1_EN_BIT = 0x1, - PLX9050_LINTR1_POLARITY_BIT = 0x2, - PLX9050_LINTR1_STATUS_BIT = 0x4, - PLX9050_LINTR2_EN_BIT = 0x8, - PLX9050_LINTR2_POLARITY_BIT = 0x10, - PLX9050_LINTR2_STATUS_BIT = 0x20, - PLX9050_PCI_INTR_EN_BIT = 0x40, - PLX9050_SOFT_INTR_BIT = 0x80, - PLX9050_LINTR1_SELECT_ENABLE_BIT = 0x100, // 9052 extension - PLX9050_LINTR2_SELECT_ENABLE_BIT = 0x200, // 9052 extension - PLX9050_LINTR1_EDGE_CLEAR_BIT = 0x400, // 9052 extension - PLX9050_LINTR2_EDGE_CLEAR_BIT = 0x800, // 9052 extension -}; - -enum plx9050_cntrl_bits { - PLX9050_WAITO_NOT_USER0_SELECT_BIT = 0x1, - PLX9050_USER0_OUTPUT_BIT = 0x2, - PLX9050_USER0_DATA_BIT = 0x4, - PLX9050_LLOCK_NOT_USER1_SELECT_BIT = 0x8, - PLX9050_USER1_OUTPUT_BIT = 0x10, - PLX9050_USER1_DATA_BIT = 0x20, - PLX9050_CS2_NOT_USER2_SELECT_BIT = 0x40, - PLX9050_USER2_OUTPUT_BIT = 0x80, - PLX9050_USER2_DATA_BIT = 0x100, - PLX9050_CS3_NOT_USER3_SELECT_BIT = 0x200, - PLX9050_USER3_OUTPUT_BIT = 0x400, - PLX9050_USER3_DATA_BIT = 0x800, - PLX9050_PCIBAR_ENABLE_MASK = 0x3000, - PLX9050_PCIBAR_MEMORY_AND_IO_ENABLE_BITS = 0x0, - PLX9050_PCIBAR_MEMORY_NO_IO_ENABLE_BITS = 0x1000, - PLX9050_PCIBAR_IO_NO_MEMORY_ENABLE_BITS = 0x2000, - PLX9050_PCIBAR_MEMORY_AND_IO_TOO_ENABLE_BITS = 0x3000, - PLX9050_PCI_READ_MODE_BIT = 0x4000, - PLX9050_PCI_READ_WITH_WRITE_FLUSH_MODE_BIT = 0x8000, - PLX9050_PCI_READ_NO_FLUSH_MODE_BIT = 0x10000, - PLX9050_PCI_READ_NO_WRITE_MODE_BIT = 0x20000, - PLX9050_PCI_WRITE_MODE_BIT = 0x40000, - PLX9050_PCI_RETRY_DELAY_MASK = 0x780000, - PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT = 0x800000, - PLX9050_EEPROM_CLOCK_BIT = 0x1000000, - PLX9050_EEPROM_CHIP_SELECT_BIT = 0x2000000, - PLX9050_WRITE_TO_EEPROM_BIT = 0x4000000, - PLX9050_READ_EEPROM_DATA_BIT = 0x8000000, - PLX9050_EEPROM_VALID_BIT = 0x10000000, - PLX9050_RELOAD_CONFIG_REGISTERS_BIT = 0x20000000, - PLX9050_PCI_SOFTWARE_RESET_BIT = 0x40000000, - PLX9050_MASK_REVISION_BIT = 0x80000000 -}; - -static inline unsigned int PLX9050_PCI_RETRY_DELAY_BITS(unsigned int clocks) -{ - return ((clocks / 8) << 19) & PLX9050_PCI_RETRY_DELAY_MASK; -} - -#endif // _PLX9050_GPIB_H diff --git a/drivers/staging/gpib/include/quancom_pci.h b/drivers/staging/gpib/include/quancom_pci.h deleted file mode 100644 index cdaf0d056be9..000000000000 --- a/drivers/staging/gpib/include/quancom_pci.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * Quancom pci stuff - * copyright (C) 2005 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _QUANCOM_PCI_H -#define _QUANCOM_PCI_H - -/* quancom registers */ -enum quancom_regs { - QUANCOM_IRQ_CONTROL_STATUS_REG = 0xfc, -}; - -enum quancom_irq_control_status_bits { - QUANCOM_IRQ_ASSERTED_BIT = 0x1, /* readable */ - /* (any write to the register clears the interrupt)*/ - QUANCOM_IRQ_ENABLE_BIT = 0x4, /* writeable */ -}; - -#endif // _QUANCOM_PCI_H diff --git a/drivers/staging/gpib/include/tms9914.h b/drivers/staging/gpib/include/tms9914.h deleted file mode 100644 index e66b75e0fda8..000000000000 --- a/drivers/staging/gpib/include/tms9914.h +++ /dev/null @@ -1,280 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _TMS9914_H -#define _TMS9914_H - -#include -#include -#include "gpib_state_machines.h" -#include "gpib_types.h" - -enum tms9914_holdoff_mode { - TMS9914_HOLDOFF_NONE, - TMS9914_HOLDOFF_EOI, - TMS9914_HOLDOFF_ALL, -}; - -/* struct used to provide variables local to a tms9914 chip */ -struct tms9914_priv { -#ifdef CONFIG_HAS_IOPORT - u32 iobase; -#endif - void __iomem *mmiobase; - unsigned int offset; // offset between successive tms9914 io addresses - unsigned int dma_channel; - // software copy of bits written to interrupt mask registers - u8 imr0_bits, imr1_bits; - // bits written to address mode register - u8 admr_bits; - u8 auxa_bits; // bits written to auxiliary register A - // used to keep track of board's state, bit definitions given below - unsigned long state; - u8 eos; // eos character - short eos_flags; - u8 spoll_status; - enum tms9914_holdoff_mode holdoff_mode; - unsigned int ppoll_line; - enum talker_function_state talker_state; - enum listener_function_state listener_state; - unsigned ppoll_sense : 1; - unsigned ppoll_enable : 1; - unsigned ppoll_configure_state : 1; - unsigned primary_listen_addressed : 1; - unsigned primary_talk_addressed : 1; - unsigned holdoff_on_end : 1; - unsigned holdoff_on_all : 1; - unsigned holdoff_active : 1; - // wrappers for outb, inb, readb, or writeb - u8 (*read_byte)(struct tms9914_priv *priv, unsigned int register_number); - void (*write_byte)(struct tms9914_priv *priv, u8 byte, unsigned int - register_number); -}; - -// slightly shorter way to access read_byte and write_byte -static inline u8 read_byte(struct tms9914_priv *priv, unsigned int register_number) -{ - return priv->read_byte(priv, register_number); -} - -static inline void write_byte(struct tms9914_priv *priv, u8 byte, unsigned int register_number) -{ - priv->write_byte(priv, byte, register_number); -} - -// struct tms9914_priv.state bit numbers -enum { - PIO_IN_PROGRESS_BN, // pio transfer in progress - DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress - DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress - READ_READY_BN, // board has data byte available to read - WRITE_READY_BN, // board is ready to send a data byte - COMMAND_READY_BN, // board is ready to send a command byte - RECEIVED_END_BN, // received END - BUS_ERROR_BN, // bus error - DEV_CLEAR_BN, // device clear received -}; - -// interface functions -int tms9914_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, - size_t length, int *end, size_t *bytes_read); -int tms9914_write(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written); -int tms9914_command(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, - size_t length, size_t *bytes_written); -int tms9914_take_control(struct gpib_board *board, struct tms9914_priv *priv, int syncronous); -/* - * alternate version of tms9914_take_control which works around buggy tcs - * implementation. - */ -int tms9914_take_control_workaround(struct gpib_board *board, struct tms9914_priv *priv, - int syncronous); -int tms9914_go_to_standby(struct gpib_board *board, struct tms9914_priv *priv); -int tms9914_request_system_control(struct gpib_board *board, struct tms9914_priv *priv, - int request_control); -void tms9914_interface_clear(struct gpib_board *board, struct tms9914_priv *priv, int assert); -void tms9914_remote_enable(struct gpib_board *board, struct tms9914_priv *priv, int enable); -int tms9914_enable_eos(struct gpib_board *board, struct tms9914_priv *priv, u8 eos_bytes, - int compare_8_bits); -void tms9914_disable_eos(struct gpib_board *board, struct tms9914_priv *priv); -unsigned int tms9914_update_status(struct gpib_board *board, struct tms9914_priv *priv, - unsigned int clear_mask); -int tms9914_primary_address(struct gpib_board *board, - struct tms9914_priv *priv, unsigned int address); -int tms9914_secondary_address(struct gpib_board *board, struct tms9914_priv *priv, - unsigned int address, int enable); -int tms9914_parallel_poll(struct gpib_board *board, struct tms9914_priv *priv, u8 *result); -void tms9914_parallel_poll_configure(struct gpib_board *board, - struct tms9914_priv *priv, u8 config); -void tms9914_parallel_poll_response(struct gpib_board *board, - struct tms9914_priv *priv, int ist); -void tms9914_serial_poll_response(struct gpib_board *board, - struct tms9914_priv *priv, u8 status); -u8 tms9914_serial_poll_status(struct gpib_board *board, struct tms9914_priv *priv); -int tms9914_line_status(const struct gpib_board *board, struct tms9914_priv *priv); -unsigned int tms9914_t1_delay(struct gpib_board *board, struct tms9914_priv *priv, - unsigned int nano_sec); -void tms9914_return_to_local(const struct gpib_board *board, struct tms9914_priv *priv); - -// utility functions -void tms9914_board_reset(struct tms9914_priv *priv); -void tms9914_online(struct gpib_board *board, struct tms9914_priv *priv); -void tms9914_release_holdoff(struct tms9914_priv *priv); -void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode); - -// wrappers for io functions -u8 tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num); -void tms9914_ioport_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num); -u8 tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num); -void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num); - -// interrupt service routine -irqreturn_t tms9914_interrupt(struct gpib_board *board, struct tms9914_priv *priv); -irqreturn_t tms9914_interrupt_have_status(struct gpib_board *board, struct tms9914_priv *priv, - int status1, int status2); - -// tms9914 has 8 registers -enum { - ms9914_num_registers = 8, -}; - -/* - * tms9914 register numbers (might need to be multiplied by - * a board-dependent offset to get actually io address offset) - */ -// write registers -enum { - IMR0 = 0, /* interrupt mask 0 */ - IMR1 = 1, /* interrupt mask 1 */ - AUXCR = 3, /* auxiliary command */ - ADR = 4, /* address register */ - SPMR = 5, /* serial poll mode register */ - PPR = 6, /* parallel poll */ - CDOR = 7, /* data out register */ -}; - -// read registers -enum { - ISR0 = 0, /* interrupt status 0 */ - ISR1 = 1, /* interrupt status 1 */ - ADSR = 2, /* address status */ - BSR = 3, /* bus status */ - CPTR = 6, /* command pass thru */ - DIR = 7, /* data in register */ -}; - -// bit definitions common to tms9914 compatible registers - -/* ISR0 - Register bits */ -enum isr0_bits { - HR_MAC = (1 << 0), /* My Address Change */ - HR_RLC = (1 << 1), /* Remote/Local change */ - HR_SPAS = (1 << 2), /* Serial Poll active State */ - HR_END = (1 << 3), /* END (EOI or EOS) */ - HR_BO = (1 << 4), /* Byte Out */ - HR_BI = (1 << 5), /* Byte In */ -}; - -/* IMR0 - Register bits */ -enum imr0_bits { - HR_MACIE = (1 << 0), /* */ - HR_RLCIE = (1 << 1), /* */ - HR_SPASIE = (1 << 2), /* */ - HR_ENDIE = (1 << 3), /* */ - HR_BOIE = (1 << 4), /* */ - HR_BIIE = (1 << 5), /* */ -}; - -/* ISR1 - Register bits */ -enum isr1_bits { - HR_IFC = (1 << 0), /* IFC asserted */ - HR_SRQ = (1 << 1), /* SRQ asserted */ - HR_MA = (1 << 2), /* My Address */ - HR_DCAS = (1 << 3), /* Device Clear active State */ - HR_APT = (1 << 4), /* Address pass Through */ - HR_UNC = (1 << 5), /* Unrecognized Command */ - HR_ERR = (1 << 6), /* Data Transmission Error */ - HR_GET = (1 << 7), /* Group execute Trigger */ -}; - -/* IMR1 - Register bits */ -enum imr1_bits { - HR_IFCIE = (1 << 0), /* */ - HR_SRQIE = (1 << 1), /* */ - HR_MAIE = (1 << 2), /* */ - HR_DCASIE = (1 << 3), /* */ - HR_APTIE = (1 << 4), /* */ - HR_UNCIE = (1 << 5), /* */ - HR_ERRIE = (1 << 6), /* */ - HR_GETIE = (1 << 7), /* */ -}; - -/* ADSR - Register bits */ -enum adsr_bits { - HR_ULPA = (1 << 0), /* Store last address LSB */ - HR_TA = (1 << 1), /* Talker Adressed */ - HR_LA = (1 << 2), /* Listener adressed */ - HR_TPAS = (1 << 3), /* talker primary address state */ - HR_LPAS = (1 << 4), /* listener " */ - HR_ATN = (1 << 5), /* ATN active */ - HR_LLO = (1 << 6), /* LLO active */ - HR_REM = (1 << 7), /* REM active */ -}; - -/* ADR - Register bits */ -enum adr_bits { - ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits for ADR */ - HR_DAT = (1 << 5), /* disable talker */ - HR_DAL = (1 << 6), /* disable listener */ - HR_EDPA = (1 << 7), /* enable dual primary addressing */ -}; - -enum bus_status_bits { - BSR_REN_BIT = 0x1, - BSR_IFC_BIT = 0x2, - BSR_SRQ_BIT = 0x4, - BSR_EOI_BIT = 0x8, - BSR_NRFD_BIT = 0x10, - BSR_NDAC_BIT = 0x20, - BSR_DAV_BIT = 0x40, - BSR_ATN_BIT = 0x80, -}; - -/*---------------------------------------------------------*/ -/* TMS 9914 Auxiliary Commands */ -/*---------------------------------------------------------*/ - -enum aux_cmd_bits { - AUX_CS = 0x80, /* set bit instead of clearing it, used with commands marked 'd' below */ - AUX_CHIP_RESET = 0x0, /* d Chip reset */ - AUX_INVAL = 0x1, /* release dac holdoff, invalid command byte */ - AUX_VAL = (AUX_INVAL | AUX_CS), /* release dac holdoff, valid command byte */ - AUX_RHDF = 0x2, /* X Release RFD holdoff */ - AUX_HLDA = 0x3, /* d holdoff on all data */ - AUX_HLDE = 0x4, /* d holdoff on EOI only */ - AUX_NBAF = 0x5, /* X Set new byte available false */ - AUX_FGET = 0x6, /* d force GET */ - AUX_RTL = 0x7, /* d return to local */ - AUX_SEOI = 0x8, /* X send EOI with next byte */ - AUX_LON = 0x9, /* d Listen only */ - AUX_TON = 0xa, /* d Talk only */ - AUX_GTS = 0xb, /* X goto standby */ - AUX_TCA = 0xc, /* X take control asynchronously */ - AUX_TCS = 0xd, /* X take " synchronously */ - AUX_RPP = 0xe, /* d Request parallel poll */ - AUX_SIC = 0xf, /* d send interface clear */ - AUX_SRE = 0x10, /* d send remote enable */ - AUX_RQC = 0x11, /* X request control */ - AUX_RLC = 0x12, /* X release control */ - AUX_DAI = 0x13, /* d disable all interrupts */ - AUX_PTS = 0x14, /* X pass through next secondary */ - AUX_STDL = 0x15, /* d short T1 delay */ - AUX_SHDW = 0x16, /* d shadow handshake */ - AUX_VSTDL = 0x17, /* d very short T1 delay (smj9914 extension) */ - AUX_RSV2 = 0x18, /* d request service bit 2 (smj9914 extension) */ -}; - -#endif //_TMS9914_H diff --git a/drivers/staging/gpib/include/tnt4882_registers.h b/drivers/staging/gpib/include/tnt4882_registers.h deleted file mode 100644 index d54c4cc61168..000000000000 --- a/drivers/staging/gpib/include/tnt4882_registers.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2002, 2004 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _TNT4882_REGISTERS_H -#define _TNT4882_REGISTERS_H - -// tnt4882 register offsets -enum { - ACCWR = 0x5, - // offset of auxiliary command register in 9914 mode - AUXCR = 0x6, - INTRT = 0x7, - // register number for auxiliary command register when swap bit is set (9914 mode) - SWAPPED_AUXCR = 0xa, - HSSEL = 0xd, // handshake select register - CNT2 = 0x9, - CNT3 = 0xb, - CFG = 0x10, - SASR = 0x1b, - IMR0 = 0x1d, - IMR3 = 0x12, - CNT0 = 0x14, - CNT1 = 0x16, - KEYREG = 0x17, // key control register (7210 mode only) - CSR = KEYREG, - FIFOB = 0x18, - FIFOA = 0x19, - CCR = 0x1a, // carry cycle register - CMDR = 0x1c, // command register - TIMER = 0x1e, // timer register - - STS1 = 0x10, // T488 Status Register 1 - STS2 = 0x1c, // T488 Status Register 2 - ISR0 = IMR0, - ISR3 = 0x1a, // T488 Interrupt Status Register 3 - BCR = 0x1f, // bus control/status register - BSR = BCR, -}; - -enum { - tnt_pagein_offset = 0x11, -}; - -/*============================================================*/ - -/* TURBO-488 registers bit definitions */ - -enum bus_control_status_bits { - BCSR_REN_BIT = 0x1, - BCSR_IFC_BIT = 0x2, - BCSR_SRQ_BIT = 0x4, - BCSR_EOI_BIT = 0x8, - BCSR_NRFD_BIT = 0x10, - BCSR_NDAC_BIT = 0x20, - BCSR_DAV_BIT = 0x40, - BCSR_ATN_BIT = 0x80, -}; - -/* CFG -- Configuration Register (write only) */ -enum cfg_bits { - TNT_COMMAND = 0x80, /* bytes are command bytes instead of data bytes - * (tnt4882 one-chip and newer only?) - */ - TNT_TLCHE = (1 << 6), /* halt transfer on imr0, imr1, or imr2 interrupt */ - TNT_IN = (1 << 5), /* transfer is GPIB read */ - TNT_A_B = (1 << 4), /* order to use fifos 1=fifo A first(big endian), - * 0=fifo b first(little endian) - */ - TNT_CCEN = (1 << 3), /* enable carry cycle */ - TNT_TMOE = (1 << 2), /* enable CPU bus time limit */ - TNT_TIM_BYTN = (1 << 1), /* tmot reg is: 1=125ns clocks, 0=num bytes */ - TNT_B_16BIT = (1 << 0), /* 1=FIFO is 16-bit register, 0=8-bit */ -}; - -/* CMDR -- Command Register */ -enum cmdr_bits { - CLRSC = 0x2, /* clear the system controller bit */ - SETSC = 0x3, /* set the system controller bit */ - GO = 0x4, /* start fifos */ - STOP = 0x8, /* stop fifos */ - RESET_FIFO = 0x10, /* reset the FIFOs */ - SOFT_RESET = 0x22, /* issue a software reset */ - HARD_RESET = 0x40 /* 500x only? */ -}; - -/* HSSEL -- handshake select register (write only) */ -enum hssel_bits { - TNT_ONE_CHIP_BIT = 0x1, - NODMA = 0x10, - TNT_GO2SIDS_BIT = 0x20, -}; - -/* IMR0 -- Interrupt Mode Register 0 */ -enum imr0_bits { - TNT_SYNCIE_BIT = 0x1, /* handshake sync */ - TNT_TOIE_BIT = 0x2, /* timeout */ - TNT_ATNIE_BIT = 0x4, /* ATN interrupt */ - TNT_IFCIE_BIT = 0x8, /* interface clear interrupt */ - TNT_BTO_BIT = 0x10, /* byte timeout */ - TNT_NLEN_BIT = 0x20, /* treat new line as EOS char */ - TNT_STBOIE_BIT = 0x40, /* status byte out */ - TNT_IMR0_ALWAYS_BITS = 0x80, /* always set this bit on write */ -}; - -/* ISR0 -- Interrupt Status Register 0 */ -enum isr0_bits { - TNT_SYNC_BIT = 0x1, /* handshake sync */ - TNT_TO_BIT = 0x2, /* timeout */ - TNT_ATNI_BIT = 0x4, /* ATN interrupt */ - TNT_IFCI_BIT = 0x8, /* interface clear interrupt */ - TNT_EOS_BIT = 0x10, /* end of string */ - TNT_NL_BIT = 0x20, /* new line receive */ - TNT_STBO_BIT = 0x40, /* status byte out */ - TNT_NBA_BIT = 0x80, /* new byte available */ -}; - -/* ISR3 -- Interrupt Status Register 3 (read only) */ -enum isr3_bits { - HR_DONE = (1 << 0), /* transfer done */ - HR_TLCI = (1 << 1), /* isr0, isr1, or isr2 interrupt asserted */ - HR_NEF = (1 << 2), /* NOT empty fifo */ - HR_NFF = (1 << 3), /* NOT full fifo */ - HR_STOP = (1 << 4), /* fifo empty or STOP command issued */ - HR_SRQI_CIC = (1 << 5), /* SRQ asserted and we are CIC (500x only?)*/ - HR_INTR = (1 << 7), /* isr3 interrupt active */ -}; - -enum keyreg_bits { - MSTD = 0x20, /* enable 350ns T1 delay */ -}; - -/* STS1 -- Status Register 1 (read only) */ -enum sts1_bits { - S_DONE = 0x80, /* DMA done */ - S_SC = 0x40, /* is system controller */ - S_IN = 0x20, /* DMA in (to memory) */ - S_DRQ = 0x10, /* DRQ line (for diagnostics) */ - S_STOP = 0x08, /* DMA stopped */ - S_NDAV = 0x04, /* inverse of DAV */ - S_HALT = 0x02, /* status of transfer machine */ - S_GSYNC = 0x01, /* indicates if GPIB is in sync w I/O */ -}; - -/* STS2 -- Status Register 2 */ -enum sts2_bits { - AFFN = (1 << 3), /* "A full FIFO NOT" (0=FIFO full) */ - AEFN = (1 << 2), /* "A empty FIFO NOT" (0=FIFO empty) */ - BFFN = (1 << 1), /* "B full FIFO NOT" (0=FIFO full) */ - BEFN = (1 << 0), /* "B empty FIFO NOT" (0=FIFO empty) */ -}; - -// Auxiliary commands -enum tnt4882_aux_cmds { - AUX_9914 = 0x15, // switch to 9914 mode - AUX_REQT = 0x18, - AUX_REQF = 0x19, - AUX_PAGEIN = 0x50, // page in alternate registers - AUX_HLDI = 0x51, // rfd holdoff immediately - AUX_CLEAR_END = 0x55, - AUX_7210 = 0x99, // switch to 7210 mode -}; - -enum tnt4882_aux_regs { - AUXRG = 0x40, - AUXRI = 0xe0, -}; - -enum auxg_bits { - /* no talking when no listeners bit (prevents bus errors when data written at wrong time) */ - NTNL_BIT = 0x8, - RPP2_BIT = 0x4, /* set/clear local rpp message */ - CHES_BIT = 0x1, /*clear holdoff on end select bit*/ -}; - -enum auxi_bits { - SISB = 0x1, // static interrupt bits (don't clear isr1, isr2 on read) - PP2 = 0x4, // ignore remote parallel poll configuration - USTD = 0x8, // ultra short (1100 nanosec) T1 delay -}; - -enum sasr_bits { - ACRDY_BIT = 0x4, /* acceptor ready state */ - ADHS_BIT = 0x8, /* acceptor data holdoff state */ - ANHS2_BIT = 0x10, /* acceptor not ready holdoff immediately state */ - ANHS1_BIT = 0x20, /* acceptor not ready holdoff state */ - AEHS_BIT = 0x40, /* acceptor end holdoff state */ -}; - -#endif // _TNT4882_REGISTERS_H diff --git a/drivers/staging/gpib/ines/Makefile b/drivers/staging/gpib/ines/Makefile deleted file mode 100644 index 88241f15ecea..000000000000 --- a/drivers/staging/gpib/ines/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_GPIB_INES) += ines_gpib.o - - diff --git a/drivers/staging/gpib/ines/ines.h b/drivers/staging/gpib/ines/ines.h deleted file mode 100644 index 6ad57e9a1216..000000000000 --- a/drivers/staging/gpib/ines/ines.h +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * Header for ines GPIB boards - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _INES_GPIB_H -#define _INES_GPIB_H - -#include "nec7210.h" -#include "gpibP.h" -#include "plx9050.h" -#include "amcc5920.h" -#include "quancom_pci.h" -#include - -enum ines_pci_chip { - PCI_CHIP_NONE, - PCI_CHIP_PLX9050, - PCI_CHIP_AMCC5920, - PCI_CHIP_QUANCOM, - PCI_CHIP_QUICKLOGIC5030, -}; - -struct ines_priv { - struct nec7210_priv nec7210_priv; - struct pci_dev *pci_device; - // base address for plx9052 pci chip - unsigned long plx_iobase; - // base address for amcc5920 pci chip - unsigned long amcc_iobase; - unsigned int irq; - enum ines_pci_chip pci_chip_type; - u8 extend_mode_bits; -}; - -/* inb/outb wrappers */ -static inline unsigned int ines_inb(struct ines_priv *priv, unsigned int register_number) -{ - return inb(priv->nec7210_priv.iobase + - register_number * priv->nec7210_priv.offset); -} - -static inline void ines_outb(struct ines_priv *priv, unsigned int value, - unsigned int register_number) -{ - outb(value, priv->nec7210_priv.iobase + - register_number * priv->nec7210_priv.offset); -} - -enum ines_regs { - // read - FIFO_STATUS = 0x8, - ISR3 = 0x9, - ISR4 = 0xa, - IN_FIFO_COUNT = 0x10, - OUT_FIFO_COUNT = 0x11, - EXTEND_STATUS = 0xf, - - // write - XDMA_CONTROL = 0x8, - IMR3 = ISR3, - IMR4 = ISR4, - IN_FIFO_WATERMARK = IN_FIFO_COUNT, - OUT_FIFO_WATERMARK = OUT_FIFO_COUNT, - EXTEND_MODE = 0xf, - - // read-write - XFER_COUNT_LOWER = 0xb, - XFER_COUNT_UPPER = 0xc, - BUS_CONTROL_MONITOR = 0x13, -}; - -enum isr3_imr3_bits { - HW_TIMEOUT_BIT = 0x1, - XFER_COUNT_BIT = 0x2, - CMD_RECEIVED_BIT = 0x4, - TCT_RECEIVED_BIT = 0x8, - IFC_ACTIVE_BIT = 0x10, - ATN_ACTIVE_BIT = 0x20, - FIFO_ERROR_BIT = 0x40, -}; - -enum isr4_imr4_bits { - IN_FIFO_WATERMARK_BIT = 0x1, - OUT_FIFO_WATERMARK_BIT = 0x2, - IN_FIFO_FULL_BIT = 0x4, - OUT_FIFO_EMPTY_BIT = 0x8, - IN_FIFO_READY_BIT = 0x10, - OUT_FIFO_READY_BIT = 0x20, - IN_FIFO_EXIT_WATERMARK_BIT = 0x40, - OUT_FIFO_EXIT_WATERMARK_BIT = 0x80, -}; - -enum extend_mode_bits { - TR3_TRIG_ENABLE_BIT = 0x1, // enable generation of trigger pulse T/R3 pin - // clear message available status bit when chip writes byte with EOI true - MAV_ENABLE_BIT = 0x2, - EOS1_ENABLE_BIT = 0x4, // enable eos register 1 - EOS2_ENABLE_BIT = 0x8, // enable eos register 2 - EOIDIS_BIT = 0x10, // disable EOI interrupt when doing rfd holdoff on end? - XFER_COUNTER_ENABLE_BIT = 0x20, - XFER_COUNTER_OUTPUT_BIT = 0x40, // use counter for output, clear for input - // when xfer counter hits 0, assert EOI on write or RFD holdoff on read - LAST_BYTE_HANDLING_BIT = 0x80, -}; - -enum extend_status_bits { - OUTPUT_MESSAGE_IN_PROGRESS_BIT = 0x1, - SCSEL_BIT = 0x2, // statue of SCSEL pin - LISTEN_DISABLED = 0x4, - IN_FIFO_EMPTY_BIT = 0x8, - OUT_FIFO_FULL_BIT = 0x10, -}; - -// ines adds fifo enable bits to address mode register -enum ines_admr_bits { - IN_FIFO_ENABLE_BIT = 0x8, - OUT_FIFO_ENABLE_BIT = 0x4, -}; - -enum xdma_control_bits { - DMA_OUTPUT_BIT = 0x1, // use dma for output, clear for input - ENABLE_SYNC_DMA_BIT = 0x2, - DMA_ACCESS_EVERY_CYCLE = 0x4, // dma accesses fifo every cycle, clear for every other cycle - DMA_16BIT = 0x8, // clear for 8 bit transfers -}; - -enum bus_control_monitor_bits { - BCM_DAV_BIT = 0x1, - BCM_NRFD_BIT = 0x2, - BCM_NDAC_BIT = 0x4, - BCM_IFC_BIT = 0x8, - BCM_ATN_BIT = 0x10, - BCM_SRQ_BIT = 0x20, - BCM_REN_BIT = 0x40, - BCM_EOI_BIT = 0x80, -}; - -enum ines_aux_reg_bits { - INES_AUXD = 0x40, -}; - -enum ines_aux_cmds { - INES_RFD_HLD_IMMEDIATE = 0x4, - INES_AUX_CLR_OUT_FIFO = 0x5, - INES_AUX_CLR_IN_FIFO = 0x6, - INES_AUX_XMODE = 0xa, -}; - -enum ines_auxd_bits { - INES_FOLLOWING_T1_MASK = 0x3, - INES_FOLLOWING_T1_500ns = 0x0, - INES_FOLLOWING_T1_350ns = 0x1, - INES_FOLLOWING_T1_250ns = 0x2, - INES_INITIAL_TI_MASK = 0xc, - INES_INITIAL_T1_2000ns = 0x0, - INES_INITIAL_T1_1100ns = 0x4, - INES_INITIAL_T1_700ns = 0x8, - INES_T6_2us = 0x0, - INES_T6_50us = 0x10, -}; - -#endif // _INES_GPIB_H diff --git a/drivers/staging/gpib/ines/ines_gpib.c b/drivers/staging/gpib/ines/ines_gpib.c deleted file mode 100644 index a3cf846fd0f9..000000000000 --- a/drivers/staging/gpib/ines/ines_gpib.c +++ /dev/null @@ -1,1500 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 1999 Axel Dziemba (axel.dziemba@ines.de) - * (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include "ines.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gpib_pci_ids.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for Ines iGPIB 72010"); - -static irqreturn_t ines_interrupt(struct gpib_board *board); - -static int ines_line_status(const struct gpib_board *board) -{ - int status = VALID_ALL; - int bcm_bits; - struct ines_priv *ines_priv; - - ines_priv = board->private_data; - - bcm_bits = ines_inb(ines_priv, BUS_CONTROL_MONITOR); - - if (bcm_bits & BCM_REN_BIT) - status |= BUS_REN; - if (bcm_bits & BCM_IFC_BIT) - status |= BUS_IFC; - if (bcm_bits & BCM_SRQ_BIT) - status |= BUS_SRQ; - if (bcm_bits & BCM_EOI_BIT) - status |= BUS_EOI; - if (bcm_bits & BCM_NRFD_BIT) - status |= BUS_NRFD; - if (bcm_bits & BCM_NDAC_BIT) - status |= BUS_NDAC; - if (bcm_bits & BCM_DAV_BIT) - status |= BUS_DAV; - if (bcm_bits & BCM_ATN_BIT) - status |= BUS_ATN; - - return status; -} - -static void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count) -{ - if (count > 0xffff) { - pr_err("bug! tried to set xfer counter > 0xffff\n"); - return; - } - ines_outb(priv, (count >> 8) & 0xff, XFER_COUNT_UPPER); - ines_outb(priv, count & 0xff, XFER_COUNT_LOWER); -} - -static int ines_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct ines_priv *ines_priv = board->private_data; - struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; - unsigned int retval; - - retval = nec7210_t1_delay(board, nec_priv, nano_sec); - - if (nano_sec <= 250) { - write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_250ns | - INES_INITIAL_T1_2000ns, AUXMR); - retval = 250; - } else if (nano_sec <= 350) { - write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_350ns | - INES_INITIAL_T1_2000ns, AUXMR); - retval = 350; - } else { - write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_500ns | - INES_INITIAL_T1_2000ns, AUXMR); - retval = 500; - } - - return retval; -} - -static inline unsigned short num_in_fifo_bytes(struct ines_priv *ines_priv) -{ - return ines_inb(ines_priv, IN_FIFO_COUNT); -} - -static ssize_t pio_read(struct gpib_board *board, struct ines_priv *ines_priv, u8 *buffer, - size_t length, size_t *nbytes) -{ - ssize_t retval = 0; - unsigned int num_fifo_bytes, i; - struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; - - *nbytes = 0; - while (*nbytes < length) { - if (wait_event_interruptible(board->wait, - num_in_fifo_bytes(ines_priv) || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - return -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - return -EINTR; - - num_fifo_bytes = num_in_fifo_bytes(ines_priv); - if (num_fifo_bytes + *nbytes > length) - num_fifo_bytes = length - *nbytes; - - for (i = 0; i < num_fifo_bytes; i++) - buffer[(*nbytes)++] = read_byte(nec_priv, DIR); - if (test_bit(RECEIVED_END_BN, &nec_priv->state) && - num_in_fifo_bytes(ines_priv) == 0) - break; - if (need_resched()) - schedule(); - } - /* make sure RECEIVED_END is in sync */ - ines_interrupt(board); - return retval; -} - -static int ines_accel_read(struct gpib_board *board, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - ssize_t retval = 0; - struct ines_priv *ines_priv = board->private_data; - struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; - int counter_setting; - - *end = 0; - *bytes_read = 0; - if (length == 0) - return 0; - - clear_bit(DEV_CLEAR_BN, &nec_priv->state); - - write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); - - // clear in fifo - nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, 0); - nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, IN_FIFO_ENABLE_BIT); - - ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT; - ines_priv->extend_mode_bits &= ~XFER_COUNTER_OUTPUT_BIT & ~XFER_COUNTER_ENABLE_BIT; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - - counter_setting = length - num_in_fifo_bytes(ines_priv); - if (counter_setting > 0) { - ines_set_xfer_counter(ines_priv, length); - ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - - // holdoff on END - nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); - /* release rfd holdoff */ - write_byte(nec_priv, AUX_FH, AUXMR); - } - - retval = pio_read(board, ines_priv, buffer, length, bytes_read); - ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - if (retval < 0) { - write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); - return retval; - } - if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) - *end = 1; - - return retval; -} - -static const int out_fifo_size = 0xff; - -static inline unsigned short num_out_fifo_bytes(struct ines_priv *ines_priv) -{ - return ines_inb(ines_priv, OUT_FIFO_COUNT); -} - -static int ines_write_wait(struct gpib_board *board, struct ines_priv *ines_priv, - unsigned int fifo_threshold) -{ - struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; - - // wait until byte is ready to be sent - if (wait_event_interruptible(board->wait, - num_out_fifo_bytes(ines_priv) < fifo_threshold || - test_bit(BUS_ERROR_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - return -ERESTARTSYS; - - if (test_bit(BUS_ERROR_BN, &nec_priv->state)) - return -EIO; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - return -EINTR; - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - - return 0; -} - -static int ines_accel_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - size_t count = 0; - ssize_t retval = 0; - struct ines_priv *ines_priv = board->private_data; - struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; - unsigned int num_bytes, i; - - *bytes_written = 0; - // clear out fifo - nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, 0); - nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, OUT_FIFO_ENABLE_BIT); - - ines_priv->extend_mode_bits |= XFER_COUNTER_OUTPUT_BIT; - ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; - ines_priv->extend_mode_bits &= ~LAST_BYTE_HANDLING_BIT; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - - ines_set_xfer_counter(ines_priv, length); - if (send_eoi) - ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT; - ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - - while (count < length) { - retval = ines_write_wait(board, ines_priv, out_fifo_size); - if (retval < 0) - break; - - num_bytes = out_fifo_size - num_out_fifo_bytes(ines_priv); - if (num_bytes + count > length) - num_bytes = length - count; - for (i = 0; i < num_bytes; i++) - write_byte(nec_priv, buffer[count++], CDOR); - } - if (retval < 0) { - ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - *bytes_written = length - num_out_fifo_bytes(ines_priv); - return retval; - } - // wait last byte has been sent - retval = ines_write_wait(board, ines_priv, 1); - ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - *bytes_written = length - num_out_fifo_bytes(ines_priv); - - return retval; -} - -static irqreturn_t ines_pci_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - struct ines_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - - if (priv->pci_chip_type == PCI_CHIP_QUANCOM) { - if ((inb(nec_priv->iobase + - QUANCOM_IRQ_CONTROL_STATUS_REG) & - QUANCOM_IRQ_ASSERTED_BIT)) - outb(QUANCOM_IRQ_ENABLE_BIT, nec_priv->iobase + - QUANCOM_IRQ_CONTROL_STATUS_REG); - } - - return ines_interrupt(board); -} - -static irqreturn_t ines_interrupt(struct gpib_board *board) -{ - struct ines_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - unsigned int isr3_bits, isr4_bits; - unsigned long flags; - int wake = 0; - - spin_lock_irqsave(&board->spinlock, flags); - - nec7210_interrupt(board, nec_priv); - isr3_bits = ines_inb(priv, ISR3); - isr4_bits = ines_inb(priv, ISR4); - if (isr3_bits & IFC_ACTIVE_BIT) { - push_gpib_event(board, EVENT_IFC); - wake++; - } - if (isr3_bits & FIFO_ERROR_BIT) - dev_err(board->gpib_dev, "fifo error\n"); - if (isr3_bits & XFER_COUNT_BIT) - wake++; - - if (isr4_bits & (IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT | OUT_FIFO_WATERMARK_BIT | - OUT_FIFO_EMPTY_BIT)) - wake++; - - if (wake) - wake_up_interruptible(&board->wait); - spin_unlock_irqrestore(&board->spinlock, flags); - return IRQ_HANDLED; -} - -static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_config *config); -static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config); -static int ines_isa_attach(struct gpib_board *board, const struct gpib_board_config *config); - -static void ines_pci_detach(struct gpib_board *board); -static void ines_isa_detach(struct gpib_board *board); - -enum ines_pci_vendor_ids { - PCI_VENDOR_ID_INES_QUICKLOGIC = 0x16da -}; - -enum ines_pci_device_ids { - PCI_DEVICE_ID_INES_GPIB_AMCC = 0x8507, - PCI_DEVICE_ID_INES_GPIB_QL5030 = 0x11, -}; - -enum ines_pci_subdevice_ids { - PCI_SUBDEVICE_ID_INES_GPIB = 0x1072 -}; - -static struct pci_device_id ines_pci_table[] = { - {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC, - PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0}, - {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, - PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0}, - {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)}, - {0} -}; -MODULE_DEVICE_TABLE(pci, ines_pci_table); - -struct ines_pci_id { - unsigned int vendor_id; - unsigned int device_id; - int subsystem_vendor_id; - int subsystem_device_id; - unsigned int gpib_region; - unsigned int io_offset; - enum ines_pci_chip pci_chip_type; -}; - -static struct ines_pci_id pci_ids[] = { - {.vendor_id = PCI_VENDOR_ID_PLX, - .device_id = PCI_DEVICE_ID_PLX_9050, - .subsystem_vendor_id = PCI_VENDOR_ID_PLX, - .subsystem_device_id = PCI_SUBDEVICE_ID_INES_GPIB, - .gpib_region = 2, - .io_offset = 1, - .pci_chip_type = PCI_CHIP_PLX9050, - }, - {.vendor_id = PCI_VENDOR_ID_AMCC, - .device_id = PCI_DEVICE_ID_INES_GPIB_AMCC, - .subsystem_vendor_id = PCI_VENDOR_ID_AMCC, - .subsystem_device_id = PCI_SUBDEVICE_ID_INES_GPIB, - .gpib_region = 1, - .io_offset = 1, - .pci_chip_type = PCI_CHIP_AMCC5920, - }, - {.vendor_id = PCI_VENDOR_ID_INES_QUICKLOGIC, - .device_id = PCI_DEVICE_ID_INES_GPIB_QL5030, - .subsystem_vendor_id = PCI_VENDOR_ID_INES_QUICKLOGIC, - .subsystem_device_id = PCI_DEVICE_ID_INES_GPIB_QL5030, - .gpib_region = 1, - .io_offset = 1, - .pci_chip_type = PCI_CHIP_QUICKLOGIC5030, - }, - {.vendor_id = PCI_VENDOR_ID_QUANCOM, - .device_id = PCI_DEVICE_ID_QUANCOM_GPIB, - .subsystem_vendor_id = -1, - .subsystem_device_id = -1, - .gpib_region = 0, - .io_offset = 4, - .pci_chip_type = PCI_CHIP_QUANCOM, - }, -}; - -static const int num_pci_chips = ARRAY_SIZE(pci_ids); - -// wrappers for interface functions -static int ines_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - struct ines_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - ssize_t retval; - int dummy; - - retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); - if (retval < 0) { - write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); - - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - - nec7210_read_data_in(board, nec_priv, &dummy); - } - return retval; -} - -static int ines_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); -} - -static int ines_command(struct gpib_board *board, u8 *buffer, size_t length, size_t *bytes_written) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); -} - -static int ines_take_control(struct gpib_board *board, int synchronous) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_take_control(board, &priv->nec7210_priv, synchronous); -} - -static int ines_go_to_standby(struct gpib_board *board) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_go_to_standby(board, &priv->nec7210_priv); -} - -static int ines_request_system_control(struct gpib_board *board, int request_control) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_request_system_control(board, &priv->nec7210_priv, request_control); -} - -static void ines_interface_clear(struct gpib_board *board, int assert) -{ - struct ines_priv *priv = board->private_data; - - nec7210_interface_clear(board, &priv->nec7210_priv, assert); -} - -static void ines_remote_enable(struct gpib_board *board, int enable) -{ - struct ines_priv *priv = board->private_data; - - nec7210_remote_enable(board, &priv->nec7210_priv, enable); -} - -static int ines_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); -} - -static void ines_disable_eos(struct gpib_board *board) -{ - struct ines_priv *priv = board->private_data; - - nec7210_disable_eos(board, &priv->nec7210_priv); -} - -static unsigned int ines_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); -} - -static int ines_primary_address(struct gpib_board *board, unsigned int address) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_primary_address(board, &priv->nec7210_priv, address); -} - -static int ines_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); -} - -static int ines_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_parallel_poll(board, &priv->nec7210_priv, result); -} - -static void ines_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - struct ines_priv *priv = board->private_data; - - nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); -} - -static void ines_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct ines_priv *priv = board->private_data; - - nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); -} - -static void ines_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct ines_priv *priv = board->private_data; - - nec7210_serial_poll_response(board, &priv->nec7210_priv, status); -} - -static u8 ines_serial_poll_status(struct gpib_board *board) -{ - struct ines_priv *priv = board->private_data; - - return nec7210_serial_poll_status(board, &priv->nec7210_priv); -} - -static void ines_return_to_local(struct gpib_board *board) -{ - struct ines_priv *priv = board->private_data; - - nec7210_return_to_local(board, &priv->nec7210_priv); -} - -static struct gpib_interface ines_pci_unaccel_interface = { - .name = "ines_pci_unaccel", - .attach = ines_pci_attach, - .detach = ines_pci_detach, - .read = ines_read, - .write = ines_write, - .command = ines_command, - .take_control = ines_take_control, - .go_to_standby = ines_go_to_standby, - .request_system_control = ines_request_system_control, - .interface_clear = ines_interface_clear, - .remote_enable = ines_remote_enable, - .enable_eos = ines_enable_eos, - .disable_eos = ines_disable_eos, - .parallel_poll = ines_parallel_poll, - .parallel_poll_configure = ines_parallel_poll_configure, - .parallel_poll_response = ines_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ines_line_status, - .update_status = ines_update_status, - .primary_address = ines_primary_address, - .secondary_address = ines_secondary_address, - .serial_poll_response = ines_serial_poll_response, - .serial_poll_status = ines_serial_poll_status, - .t1_delay = ines_t1_delay, - .return_to_local = ines_return_to_local, -}; - -static struct gpib_interface ines_pci_interface = { - .name = "ines_pci", - .attach = ines_pci_accel_attach, - .detach = ines_pci_detach, - .read = ines_accel_read, - .write = ines_accel_write, - .command = ines_command, - .take_control = ines_take_control, - .go_to_standby = ines_go_to_standby, - .request_system_control = ines_request_system_control, - .interface_clear = ines_interface_clear, - .remote_enable = ines_remote_enable, - .enable_eos = ines_enable_eos, - .disable_eos = ines_disable_eos, - .parallel_poll = ines_parallel_poll, - .parallel_poll_configure = ines_parallel_poll_configure, - .parallel_poll_response = ines_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ines_line_status, - .update_status = ines_update_status, - .primary_address = ines_primary_address, - .secondary_address = ines_secondary_address, - .serial_poll_response = ines_serial_poll_response, - .serial_poll_status = ines_serial_poll_status, - .t1_delay = ines_t1_delay, - .return_to_local = ines_return_to_local, -}; - -static struct gpib_interface ines_pci_accel_interface = { - .name = "ines_pci_accel", - .attach = ines_pci_accel_attach, - .detach = ines_pci_detach, - .read = ines_accel_read, - .write = ines_accel_write, - .command = ines_command, - .take_control = ines_take_control, - .go_to_standby = ines_go_to_standby, - .request_system_control = ines_request_system_control, - .interface_clear = ines_interface_clear, - .remote_enable = ines_remote_enable, - .enable_eos = ines_enable_eos, - .disable_eos = ines_disable_eos, - .parallel_poll = ines_parallel_poll, - .parallel_poll_configure = ines_parallel_poll_configure, - .parallel_poll_response = ines_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ines_line_status, - .update_status = ines_update_status, - .primary_address = ines_primary_address, - .secondary_address = ines_secondary_address, - .serial_poll_response = ines_serial_poll_response, - .serial_poll_status = ines_serial_poll_status, - .t1_delay = ines_t1_delay, - .return_to_local = ines_return_to_local, -}; - -static struct gpib_interface ines_isa_interface = { - .name = "ines_isa", - .attach = ines_isa_attach, - .detach = ines_isa_detach, - .read = ines_accel_read, - .write = ines_accel_write, - .command = ines_command, - .take_control = ines_take_control, - .go_to_standby = ines_go_to_standby, - .request_system_control = ines_request_system_control, - .interface_clear = ines_interface_clear, - .remote_enable = ines_remote_enable, - .enable_eos = ines_enable_eos, - .disable_eos = ines_disable_eos, - .parallel_poll = ines_parallel_poll, - .parallel_poll_configure = ines_parallel_poll_configure, - .parallel_poll_response = ines_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ines_line_status, - .update_status = ines_update_status, - .primary_address = ines_primary_address, - .secondary_address = ines_secondary_address, - .serial_poll_response = ines_serial_poll_response, - .serial_poll_status = ines_serial_poll_status, - .t1_delay = ines_t1_delay, - .return_to_local = ines_return_to_local, -}; - -static int ines_allocate_private(struct gpib_board *board) -{ - struct ines_priv *priv; - - board->private_data = kmalloc(sizeof(struct ines_priv), GFP_KERNEL); - if (!board->private_data) - return -1; - priv = board->private_data; - memset(priv, 0, sizeof(struct ines_priv)); - init_nec7210_private(&priv->nec7210_priv); - return 0; -} - -static void ines_free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static int ines_generic_attach(struct gpib_board *board) -{ - struct ines_priv *ines_priv; - struct nec7210_priv *nec_priv; - - board->status = 0; - - if (ines_allocate_private(board)) - return -ENOMEM; - ines_priv = board->private_data; - nec_priv = &ines_priv->nec7210_priv; - nec_priv->read_byte = nec7210_ioport_read_byte; - nec_priv->write_byte = nec7210_ioport_write_byte; - nec_priv->offset = 1; - nec_priv->type = IGPIB7210; - ines_priv->pci_chip_type = PCI_CHIP_NONE; - - return 0; -} - -static void ines_online(struct ines_priv *ines_priv, const struct gpib_board *board, int use_accel) -{ - struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv; - - /* ines doesn't seem to use internal count register */ - write_byte(nec_priv, ICR | 0, AUXMR); - - write_byte(nec_priv, INES_AUX_XMODE, AUXMR); - write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR); - - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - - write_byte(nec_priv, INES_AUXD | 0, AUXMR); - ines_outb(ines_priv, 0, XDMA_CONTROL); - ines_priv->extend_mode_bits = 0; - ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE); - if (use_accel) { - ines_outb(ines_priv, 0x80, OUT_FIFO_WATERMARK); - ines_outb(ines_priv, 0x80, IN_FIFO_WATERMARK); - ines_outb(ines_priv, IFC_ACTIVE_BIT | ATN_ACTIVE_BIT | - FIFO_ERROR_BIT | XFER_COUNT_BIT, IMR3); - ines_outb(ines_priv, IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT | - OUT_FIFO_WATERMARK_BIT | OUT_FIFO_EMPTY_BIT, IMR4); - } else { - nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT | OUT_FIFO_ENABLE_BIT, 0); - ines_outb(ines_priv, IFC_ACTIVE_BIT | FIFO_ERROR_BIT, IMR3); - ines_outb(ines_priv, 0, IMR4); - } - - nec7210_board_online(nec_priv, board); - if (use_accel) - nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, 0); -} - -static int ines_common_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct ines_priv *ines_priv; - struct nec7210_priv *nec_priv; - int isr_flags = 0; - int retval; - struct ines_pci_id found_id; - unsigned int i; - struct pci_dev *pdev; - - memset(&found_id, 0, sizeof(found_id)); - - retval = ines_generic_attach(board); - if (retval) - return retval; - - ines_priv = board->private_data; - nec_priv = &ines_priv->nec7210_priv; - - // find board - ines_priv->pci_device = NULL; - for (i = 0; i < num_pci_chips && !ines_priv->pci_device; i++) { - pdev = NULL; - do { - if (pci_ids[i].subsystem_vendor_id >= 0 && - pci_ids[i].subsystem_device_id >= 0) - pdev = pci_get_subsys(pci_ids[i].vendor_id, pci_ids[i].device_id, - pci_ids[i].subsystem_vendor_id, - pci_ids[i].subsystem_device_id, pdev); - else - pdev = pci_get_device(pci_ids[i].vendor_id, pci_ids[i].device_id, - pdev); - if (!pdev) - break; - if (config->pci_bus >= 0 && config->pci_bus != pdev->bus->number) - continue; - if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(pdev->devfn)) - continue; - found_id = pci_ids[i]; - ines_priv->pci_device = pdev; - break; - } while (1); - } - if (!ines_priv->pci_device) { - dev_err(board->gpib_dev, "could not find ines PCI board\n"); - return -1; - } - - if (pci_enable_device(ines_priv->pci_device)) { - dev_err(board->gpib_dev, "error enabling pci device\n"); - return -1; - } - - if (pci_request_regions(ines_priv->pci_device, DRV_NAME)) - return -1; - nec_priv->iobase = pci_resource_start(ines_priv->pci_device, - found_id.gpib_region); - - ines_priv->pci_chip_type = found_id.pci_chip_type; - nec_priv->offset = found_id.io_offset; - switch (ines_priv->pci_chip_type) { - case PCI_CHIP_PLX9050: - ines_priv->plx_iobase = pci_resource_start(ines_priv->pci_device, 1); - break; - case PCI_CHIP_AMCC5920: - ines_priv->amcc_iobase = pci_resource_start(ines_priv->pci_device, 0); - break; - case PCI_CHIP_QUANCOM: - break; - case PCI_CHIP_QUICKLOGIC5030: - break; - default: - dev_err(board->gpib_dev, "unspecified chip type? (bug)\n"); - nec_priv->iobase = 0; - pci_release_regions(ines_priv->pci_device); - return -1; - } - - nec7210_board_reset(nec_priv, board); -#ifdef QUANCOM_PCI - if (ines_priv->pci_chip_type == PCI_CHIP_QUANCOM) { - /* change interrupt polarity */ - nec_priv->auxb_bits |= HR_INV; - ines_outb(ines_priv, nec_priv->auxb_bits, AUXMR); - } -#endif - isr_flags |= IRQF_SHARED; - if (request_irq(ines_priv->pci_device->irq, ines_pci_interrupt, isr_flags, - DRV_NAME, board)) { - dev_err(board->gpib_dev, "can't request IRQ %d\n", ines_priv->pci_device->irq); - return -1; - } - ines_priv->irq = ines_priv->pci_device->irq; - - // enable interrupts on pci chip - switch (ines_priv->pci_chip_type) { - case PCI_CHIP_PLX9050: - outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT, - ines_priv->plx_iobase + PLX9050_INTCSR_REG); - break; - case PCI_CHIP_AMCC5920: - { - static const int region = 1; - static const int num_wait_states = 7; - u32 bits; - - bits = amcc_prefetch_bits(region, PREFETCH_DISABLED); - bits |= amcc_PTADR_mode_bit(region); - bits |= amcc_disable_write_fifo_bit(region); - bits |= amcc_wait_state_bits(region, num_wait_states); - outl(bits, ines_priv->amcc_iobase + AMCC_PASS_THRU_REG); - outl(AMCC_ADDON_INTR_ENABLE_BIT, ines_priv->amcc_iobase + AMCC_INTCS_REG); - } - break; - case PCI_CHIP_QUANCOM: - outb(QUANCOM_IRQ_ENABLE_BIT, nec_priv->iobase + - QUANCOM_IRQ_CONTROL_STATUS_REG); - break; - case PCI_CHIP_QUICKLOGIC5030: - break; - default: - dev_err(board->gpib_dev, "unspecified chip type? (bug)\n"); - return -1; - } - - return 0; -} - -static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct ines_priv *ines_priv; - int retval; - - retval = ines_common_pci_attach(board, config); - if (retval < 0) - return retval; - - ines_priv = board->private_data; - ines_online(ines_priv, board, 0); - - return 0; -} - -static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct ines_priv *ines_priv; - int retval; - - retval = ines_common_pci_attach(board, config); - if (retval < 0) - return retval; - - ines_priv = board->private_data; - ines_online(ines_priv, board, 1); - - return 0; -} - -static const int ines_isa_iosize = 0x20; - -static int ines_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct ines_priv *ines_priv; - struct nec7210_priv *nec_priv; - int isr_flags = 0; - int retval; - - retval = ines_generic_attach(board); - if (retval) - return retval; - - ines_priv = board->private_data; - nec_priv = &ines_priv->nec7210_priv; - - if (!request_region(config->ibbase, ines_isa_iosize, DRV_NAME)) { - dev_err(board->gpib_dev, "ioports at 0x%x already in use\n", - config->ibbase); - return -EBUSY; - } - nec_priv->iobase = config->ibbase; - nec_priv->offset = 1; - nec7210_board_reset(nec_priv, board); - if (request_irq(config->ibirq, ines_pci_interrupt, isr_flags, DRV_NAME, board)) { - dev_err(board->gpib_dev, "failed to allocate IRQ %d\n", config->ibirq); - return -1; - } - ines_priv->irq = config->ibirq; - ines_online(ines_priv, board, 1); - return 0; -} - -static void ines_pci_detach(struct gpib_board *board) -{ - struct ines_priv *ines_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (ines_priv) { - nec_priv = &ines_priv->nec7210_priv; - if (ines_priv->irq) { - // disable interrupts - switch (ines_priv->pci_chip_type) { - case PCI_CHIP_AMCC5920: - if (ines_priv->plx_iobase) - outl(0, ines_priv->plx_iobase + PLX9050_INTCSR_REG); - break; - case PCI_CHIP_QUANCOM: - if (nec_priv->iobase) - outb(0, nec_priv->iobase + - QUANCOM_IRQ_CONTROL_STATUS_REG); - break; - default: - break; - } - free_irq(ines_priv->irq, board); - } - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - pci_release_regions(ines_priv->pci_device); - } - if (ines_priv->pci_device) - pci_dev_put(ines_priv->pci_device); - } - ines_free_private(board); -} - -static void ines_isa_detach(struct gpib_board *board) -{ - struct ines_priv *ines_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (ines_priv) { - nec_priv = &ines_priv->nec7210_priv; - if (ines_priv->irq) - free_irq(ines_priv->irq, board); - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - release_region(nec_priv->iobase, ines_isa_iosize); - } - } - ines_free_private(board); -} - -static int ines_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - return 0; -} - -static struct pci_driver ines_pci_driver = { - .name = "ines_gpib", - .id_table = ines_pci_table, - .probe = &ines_pci_probe -}; - -#ifdef CONFIG_GPIB_PCMCIA - -#include -#include -#include -#include - -#include -#include -#include - -static const int ines_pcmcia_iosize = 0x20; - -/* - * The event() function is this driver's Card Services event handler. - * It will be called by Card Services when an appropriate card status - * event is received. The config() and release() entry points are - * used to configure or release a socket, in response to card insertion - * and ejection events. They are invoked from the gpib event - * handler. - */ - -static int ines_gpib_config(struct pcmcia_device *link); -static void ines_gpib_release(struct pcmcia_device *link); -static int ines_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config); -static int ines_pcmcia_accel_attach(struct gpib_board *board, - const struct gpib_board_config *config); -static void ines_pcmcia_detach(struct gpib_board *board); -static int ines_common_pcmcia_attach(struct gpib_board *board); -/* - * A linked list of "instances" of the gpib device. Each actual - * PCMCIA card corresponds to one device instance, and is described - * by one dev_link_t structure (defined in ds.h). - * - * You may not want to use a linked list for this -- for example, the - * memory card driver uses an array of dev_link_t pointers, where minor - * device numbers are used to derive the corresponding array index. - */ - -static struct pcmcia_device *curr_dev; - -/* - * A dev_link_t structure has fields for most things that are needed - * to keep track of a socket, but there will usually be some device - * specific information that also needs to be kept track of. The - * 'priv' pointer in a dev_link_t structure can be used to point to - * a device-specific private data structure, like this. - * - * A driver needs to provide a dev_node_t structure for each device - * on a card. In some cases, there is only one device per card (for - * example, ethernet cards, modems). In other cases, there may be - * many actual or logical devices (SCSI adapters, memory cards with - * multiple partitions). The dev_node_t structures need to be kept - * in a linked list starting at the 'dev' field of a dev_link_t - * structure. We allocate them in the card's private data structure, - * because they generally can't be allocated dynamically. - */ - -struct local_info { - struct pcmcia_device *p_dev; - struct gpib_board *dev; - u_short manfid; - u_short cardid; -}; - -/* - * gpib_attach() creates an "instance" of the driver, allocating - * local data structures for one device. The device is registered - * with Card Services. - * - * The dev_link structure is initialized, but we don't actually - * configure the card at this point -- we wait until we receive a - * card insertion event. - */ -static int ines_gpib_probe(struct pcmcia_device *link) -{ - struct local_info *info; - -// int ret, i; - - /* Allocate space for private device-specific data */ - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->p_dev = link; - link->priv = info; - - /* The io structure describes IO port mapping */ - link->resource[0]->end = 32; - link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->io_lines = 5; - - /* General socket configuration */ - link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - /* Register with Card Services */ - curr_dev = link; - return ines_gpib_config(link); -} - -/* - * This deletes a driver "instance". The device is de-registered - * with Card Services. If it has been released, all local data - * structures are freed. Otherwise, the structures will be freed - * when the device is released. - */ -static void ines_gpib_remove(struct pcmcia_device *link) -{ - struct local_info *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - if (info->dev) - ines_pcmcia_detach(info->dev); - ines_gpib_release(link); - - //free_netdev(dev); - kfree(info); -} - -static int ines_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) -{ - return pcmcia_request_io(link); -} - -/* - * gpib_config() is scheduled to run after a CARD_INSERTION event - * is received, to configure the PCMCIA socket, and to make the - * device available to the system. - */ -static int ines_gpib_config(struct pcmcia_device *link) -{ - int retval; - void __iomem *virt; - - retval = pcmcia_loop_config(link, &ines_gpib_config_iteration, NULL); - if (retval) { - dev_warn(&link->dev, "no configuration found\n"); - ines_gpib_release(link); - return -ENODEV; - } - - dev_dbg(&link->dev, "ines_cs: manufacturer: 0x%x card: 0x%x\n", - link->manf_id, link->card_id); - - /* - * for the ines card we have to setup the configuration registers in - * attribute memory here - */ - link->resource[2]->flags |= WIN_MEMORY_TYPE_AM | WIN_DATA_WIDTH_8 | WIN_ENABLE; - link->resource[2]->end = 0x1000; - retval = pcmcia_request_window(link, link->resource[2], 250); - if (retval) { - dev_warn(&link->dev, "pcmcia_request_window failed\n"); - ines_gpib_release(link); - return -ENODEV; - } - retval = pcmcia_map_mem_page(link, link->resource[2], 0); - if (retval) { - dev_warn(&link->dev, "pcmcia_map_mem_page failed\n"); - ines_gpib_release(link); - return -ENODEV; - } - virt = ioremap(link->resource[2]->start, resource_size(link->resource[2])); - writeb((link->resource[2]->start >> 2) & 0xff, virt + 0xf0); // IOWindow base - iounmap(virt); - - /* - * This actually configures the PCMCIA socket -- setting up - * the I/O windows and the interrupt mapping. - */ - retval = pcmcia_enable_device(link); - if (retval) { - ines_gpib_release(link); - return -ENODEV; - } - return 0; -} /* gpib_config */ - -/* - * After a card is removed, gpib_release() will unregister the net - * device, and release the PCMCIA configuration. If the device is - * still open, this will be postponed until it is closed. - */ - -static void ines_gpib_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} /* gpib_release */ - -static int ines_gpib_suspend(struct pcmcia_device *link) -{ - //struct local_info *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - if (link->open) - dev_err(&link->dev, "Device still open\n"); - //netif_device_detach(dev); - - return 0; -} - -static int ines_gpib_resume(struct pcmcia_device *link) -{ - //struct local_info_t *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - /*if (link->open) { - * ni_gpib_probe(dev); / really? - * //netif_device_attach(dev); - *} - */ - return ines_gpib_config(link); -} - -static struct pcmcia_device_id ines_pcmcia_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x01b4, 0x4730), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, ines_pcmcia_ids); - -static struct pcmcia_driver ines_gpib_cs_driver = { - .owner = THIS_MODULE, - .name = "ines_gpib_cs", - .id_table = ines_pcmcia_ids, - .probe = ines_gpib_probe, - .remove = ines_gpib_remove, - .suspend = ines_gpib_suspend, - .resume = ines_gpib_resume, -}; - -static void ines_pcmcia_cleanup_module(void) -{ - pcmcia_unregister_driver(&ines_gpib_cs_driver); -} - -static struct gpib_interface ines_pcmcia_unaccel_interface = { - .name = "ines_pcmcia_unaccel", - .attach = ines_pcmcia_attach, - .detach = ines_pcmcia_detach, - .read = ines_read, - .write = ines_write, - .command = ines_command, - .take_control = ines_take_control, - .go_to_standby = ines_go_to_standby, - .request_system_control = ines_request_system_control, - .interface_clear = ines_interface_clear, - .remote_enable = ines_remote_enable, - .enable_eos = ines_enable_eos, - .disable_eos = ines_disable_eos, - .parallel_poll = ines_parallel_poll, - .parallel_poll_configure = ines_parallel_poll_configure, - .parallel_poll_response = ines_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ines_line_status, - .update_status = ines_update_status, - .primary_address = ines_primary_address, - .secondary_address = ines_secondary_address, - .serial_poll_response = ines_serial_poll_response, - .serial_poll_status = ines_serial_poll_status, - .t1_delay = ines_t1_delay, - .return_to_local = ines_return_to_local, -}; - -static struct gpib_interface ines_pcmcia_accel_interface = { - .name = "ines_pcmcia_accel", - .attach = ines_pcmcia_accel_attach, - .detach = ines_pcmcia_detach, - .read = ines_accel_read, - .write = ines_accel_write, - .command = ines_command, - .take_control = ines_take_control, - .go_to_standby = ines_go_to_standby, - .request_system_control = ines_request_system_control, - .interface_clear = ines_interface_clear, - .remote_enable = ines_remote_enable, - .enable_eos = ines_enable_eos, - .disable_eos = ines_disable_eos, - .parallel_poll = ines_parallel_poll, - .parallel_poll_configure = ines_parallel_poll_configure, - .parallel_poll_response = ines_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ines_line_status, - .update_status = ines_update_status, - .primary_address = ines_primary_address, - .secondary_address = ines_secondary_address, - .serial_poll_response = ines_serial_poll_response, - .serial_poll_status = ines_serial_poll_status, - .t1_delay = ines_t1_delay, - .return_to_local = ines_return_to_local, -}; - -static struct gpib_interface ines_pcmcia_interface = { - .name = "ines_pcmcia", - .attach = ines_pcmcia_accel_attach, - .detach = ines_pcmcia_detach, - .read = ines_accel_read, - .write = ines_accel_write, - .command = ines_command, - .take_control = ines_take_control, - .go_to_standby = ines_go_to_standby, - .request_system_control = ines_request_system_control, - .interface_clear = ines_interface_clear, - .remote_enable = ines_remote_enable, - .enable_eos = ines_enable_eos, - .disable_eos = ines_disable_eos, - .parallel_poll = ines_parallel_poll, - .parallel_poll_configure = ines_parallel_poll_configure, - .parallel_poll_response = ines_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ines_line_status, - .update_status = ines_update_status, - .primary_address = ines_primary_address, - .secondary_address = ines_secondary_address, - .serial_poll_response = ines_serial_poll_response, - .serial_poll_status = ines_serial_poll_status, - .t1_delay = ines_t1_delay, - .return_to_local = ines_return_to_local, -}; - -static irqreturn_t ines_pcmcia_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - - return ines_interrupt(board); -} - -static int ines_common_pcmcia_attach(struct gpib_board *board) -{ - struct ines_priv *ines_priv; - struct nec7210_priv *nec_priv; - int retval; - - if (!curr_dev) { - dev_err(board->gpib_dev, "no ines pcmcia cards found\n"); - return -1; - } - - retval = ines_generic_attach(board); - if (retval) - return retval; - - ines_priv = board->private_data; - nec_priv = &ines_priv->nec7210_priv; - - if (!request_region(curr_dev->resource[0]->start, - resource_size(curr_dev->resource[0]), DRV_NAME)) { - dev_err(board->gpib_dev, "ioports at 0x%lx already in use\n", - (unsigned long)(curr_dev->resource[0]->start)); - return -1; - } - - nec_priv->iobase = curr_dev->resource[0]->start; - - nec7210_board_reset(nec_priv, board); - - if (request_irq(curr_dev->irq, ines_pcmcia_interrupt, IRQF_SHARED, - "pcmcia-gpib", board)) { - dev_err(board->gpib_dev, "can't request IRQ %d\n", curr_dev->irq); - return -1; - } - ines_priv->irq = curr_dev->irq; - - return 0; -} - -static int ines_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct ines_priv *ines_priv; - int retval; - - retval = ines_common_pcmcia_attach(board); - if (retval < 0) - return retval; - - ines_priv = board->private_data; - ines_online(ines_priv, board, 0); - - return 0; -} - -static int ines_pcmcia_accel_attach(struct gpib_board *board, - const struct gpib_board_config *config) -{ - struct ines_priv *ines_priv; - int retval; - - retval = ines_common_pcmcia_attach(board); - if (retval < 0) - return retval; - - ines_priv = board->private_data; - ines_online(ines_priv, board, 1); - - return 0; -} - -static void ines_pcmcia_detach(struct gpib_board *board) -{ - struct ines_priv *ines_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (ines_priv) { - nec_priv = &ines_priv->nec7210_priv; - if (ines_priv->irq) - free_irq(ines_priv->irq, board); - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - release_region(nec_priv->iobase, ines_pcmcia_iosize); - } - } - ines_free_private(board); -} - -#endif /* CONFIG_GPIB_PCMCIA */ - -static int __init ines_init_module(void) -{ - int ret; - - ret = pci_register_driver(&ines_pci_driver); - if (ret) { - pr_err("pci_register_driver failed: error = %d\n", ret); - return ret; - } - - ret = gpib_register_driver(&ines_pci_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pci; - } - - ret = gpib_register_driver(&ines_pci_unaccel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pci_unaccel; - } - - ret = gpib_register_driver(&ines_pci_accel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pci_accel; - } - - ret = gpib_register_driver(&ines_isa_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_isa; - } - -#ifdef CONFIG_GPIB_PCMCIA - ret = gpib_register_driver(&ines_pcmcia_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pcmcia; - } - - ret = gpib_register_driver(&ines_pcmcia_unaccel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pcmcia_unaccel; - } - - ret = gpib_register_driver(&ines_pcmcia_accel_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pcmcia_accel; - } - - ret = pcmcia_register_driver(&ines_gpib_cs_driver); - if (ret) { - pr_err("pcmcia_register_driver failed: error = %d\n", ret); - goto err_pcmcia_driver; - } -#endif - - return 0; - -#ifdef CONFIG_GPIB_PCMCIA -err_pcmcia_driver: - gpib_unregister_driver(&ines_pcmcia_accel_interface); -err_pcmcia_accel: - gpib_unregister_driver(&ines_pcmcia_unaccel_interface); -err_pcmcia_unaccel: - gpib_unregister_driver(&ines_pcmcia_interface); -err_pcmcia: -#endif - gpib_unregister_driver(&ines_isa_interface); -err_isa: - gpib_unregister_driver(&ines_pci_accel_interface); -err_pci_accel: - gpib_unregister_driver(&ines_pci_unaccel_interface); -err_pci_unaccel: - gpib_unregister_driver(&ines_pci_interface); -err_pci: - pci_unregister_driver(&ines_pci_driver); - - return ret; -} - -static void __exit ines_exit_module(void) -{ - gpib_unregister_driver(&ines_pci_interface); - gpib_unregister_driver(&ines_pci_unaccel_interface); - gpib_unregister_driver(&ines_pci_accel_interface); - gpib_unregister_driver(&ines_isa_interface); -#ifdef CONFIG_GPIB_PCMCIA - gpib_unregister_driver(&ines_pcmcia_interface); - gpib_unregister_driver(&ines_pcmcia_unaccel_interface); - gpib_unregister_driver(&ines_pcmcia_accel_interface); - ines_pcmcia_cleanup_module(); -#endif - - pci_unregister_driver(&ines_pci_driver); -} - -module_init(ines_init_module); -module_exit(ines_exit_module); diff --git a/drivers/staging/gpib/lpvo_usb_gpib/Makefile b/drivers/staging/gpib/lpvo_usb_gpib/Makefile deleted file mode 100644 index 360553488e6d..000000000000 --- a/drivers/staging/gpib/lpvo_usb_gpib/Makefile +++ /dev/null @@ -1,3 +0,0 @@ - -obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib.o - diff --git a/drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c deleted file mode 100644 index dd68c4843490..000000000000 --- a/drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ /dev/null @@ -1,2025 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * This code has been developed at the Department of Physics (University * - * of Florence, Italy) to support in linux-gpib the open usb-gpib adapter * - * implemented at the University of Ljubljana (lpvo.fe.uni-lj.si/gpib) * - * * - * copyright : (C) 2011 Marcello Carla' * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define NAME KBUILD_MODNAME - -/* base module includes */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gpibP.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for LPVO usb devices"); - -/* - * Table of devices that work with this driver. - * - * Currently, only one device is known to be used in the - * lpvo_usb_gpib adapter (FTDI 0403:6001). - * If your adapter uses a different chip, insert a line - * in the following table with proper , . - * - * To have your chip automatically handled by the driver, - * update files "/usr/local/etc/modprobe.d/lpvo_usb_gpib.conf" - * and /usr/local/etc/udev/rules.d/99-lpvo_usb_gpib.rules. - * - */ - -static const struct usb_device_id skel_table[] = { - { USB_DEVICE(0x0403, 0x6001) }, - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, skel_table); - -/* - * *** Diagnostics and Debug *** - * To enable the diagnostic and debug messages either compile with DEBUG set - * or control via the dynamic debug mechanisms. - * The module parameter "debug" controls the sending of debug messages to - * syslog. By default it is set to 0 - * debug = 0: only attach/detach messages are sent - * 1: every action is logged - * 2: extended logging; each single exchanged byte is documented - * (about twice the log volume of [1]) - * To switch debug level: - * At module loading: modprobe lpvo_usb_gpib debug={0,1,2} - * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug - */ - -static int debug; -module_param(debug, int, 0644); - -#define DIA_LOG(level, format, ...) \ - do { if (debug >= (level)) \ - dev_dbg(board->gpib_dev, format, ## __VA_ARGS__); } \ - while (0) - -#define WQT wait_queue_entry_t -#define WQH head -#define WQE entry - -/* standard and extended command sets of the usb-gpib adapter */ - -#define USB_GPIB_ON "\nIB\n" -#define USB_GPIB_OFF "\nIBO\n" -#define USB_GPIB_IBm0 "\nIBm0\n" /* do not assert REN with IFC */ -#define USB_GPIB_IBm1 "\nIBm1\n" /* assert REN with IFC */ -#define USB_GPIB_IBCL "\nIBZ\n" -#define USB_GPIB_STATUS "\nIBS\n" -#define USB_GPIB_READ "\nIB?\n" -#define USB_GPIB_READ_1 "\nIBB\n" -#define USB_GPIB_EOI "\nIBe0\n" -#define USB_GPIB_FTMO "\nIBf0\n" /* disable first byte timeout */ -#define USB_GPIB_TTMOZ "\nIBt0\n" /* disable byte timeout */ - -/* incomplete commands */ - -#define USB_GPIB_BTMO "\nIBt" /* set byte timeout */ -#define USB_GPIB_TTMO "\nIBT" /* set total timeout */ - -#define USB_GPIB_DEBUG_ON "\nIBDE\xAA\n" -#define USB_GPIB_SET_LISTEN "\nIBDT0\n" -#define USB_GPIB_SET_TALK "\nIBDT1\n" -#define USB_GPIB_SET_LINES "\nIBDC.\n" -#define USB_GPIB_SET_DATA "\nIBDM.\n" -#define USB_GPIB_READ_LINES "\nIBD?C\n" -#define USB_GPIB_READ_DATA "\nIBD?M\n" -#define USB_GPIB_READ_BUS "\nIBD??\n" - -/* command sequences */ - -#define USB_GPIB_UNTALK "\nIBC_\n" -#define USB_GPIB_UNLISTEN "\nIBC?\n" - -/* special characters used by the adapter */ - -#define DLE ('\020') -#define STX ('\02') -#define ETX ('\03') -#define ACK ('\06') -#define NODATA ('\03') -#define NODAV ('\011') - -#define IB_BUS_REN 0x01 -#define IB_BUS_IFC 0x02 -#define IB_BUS_NDAC 0x04 -#define IB_BUS_NRFD 0x08 -#define IB_BUS_DAV 0x10 -#define IB_BUS_EOI 0x20 -#define IB_BUS_ATN 0x40 -#define IB_BUS_SRQ 0x80 - -#define INBUF_SIZE 128 - -struct char_buf { /* used by one_char() routine */ - char *inbuf; - int last; - int nchar; -}; - -struct usb_gpib_priv { /* private data to the device */ - u8 eos; /* eos character */ - short eos_flags; /* eos mode */ - int timeout; /* current value for timeout */ - void *dev; /* the usb device private data structure */ -}; - -#define GPIB_DEV (((struct usb_gpib_priv *)board->private_data)->dev) - -static void show_status(struct gpib_board *board) -{ - DIA_LOG(2, "# - buffer_length %d\n", board->buffer_length); - DIA_LOG(2, "# - status %lx\n", board->status); - DIA_LOG(2, "# - use_count %d\n", board->use_count); - DIA_LOG(2, "# - pad %x\n", board->pad); - DIA_LOG(2, "# - sad %x\n", board->sad); - DIA_LOG(2, "# - timeout %d\n", board->usec_timeout); - DIA_LOG(2, "# - ppc %d\n", board->parallel_poll_configuration); - DIA_LOG(2, "# - t1delay %d\n", board->t1_nano_sec); - DIA_LOG(2, "# - online %d\n", board->online); - DIA_LOG(2, "# - autopoll %d\n", board->autospollers); - DIA_LOG(2, "# - autopoll task %p\n", board->autospoll_task); - DIA_LOG(2, "# - minor %d\n", board->minor); - DIA_LOG(2, "# - master %d\n", board->master); - DIA_LOG(2, "# - list %d\n", board->ist); -} - -/* - * GLOBAL VARIABLES: required for - * pairing among gpib minor and usb minor. - * MAX_DEV is the max number of usb-gpib adapters; free - * to change as you like, but no more than 32 - */ - -#define MAX_DEV 8 -static struct usb_interface *lpvo_usb_interfaces[MAX_DEV]; /* registered interfaces */ -static int usb_minors[MAX_DEV]; /* usb minors */ -static int assigned_usb_minors; /* mask of filled slots */ -static struct mutex minors_lock; /* operations on usb_minors are to be protected */ - -/* - * usb-skeleton prototypes - */ - -struct usb_skel; -static ssize_t skel_do_write(struct usb_skel *, const char *, size_t); -static ssize_t skel_do_read(struct usb_skel *, char *, size_t); -static int skel_do_open(struct gpib_board *, int); -static int skel_do_release(struct gpib_board *); - -/* - * usec_diff : take difference in MICROsec between two 'timespec' - * (unix time in sec and NANOsec) - */ - -static inline int usec_diff(struct timespec64 *a, struct timespec64 *b) -{ - return ((a->tv_sec - b->tv_sec) * 1000000 + - (a->tv_nsec - b->tv_nsec) / 1000); -} - -/* - * *** these routines are specific to the usb-gpib adapter *** - */ - -/** - * write_loop() - Send a byte sequence to the adapter - * - * @dev: the private device structure - * @msg: the byte sequence. - * @leng: the byte sequence length. - * - */ - -static int write_loop(void *dev, char *msg, int leng) -{ - return skel_do_write(dev, msg, leng); -} - -/** - * send_command() - Send a byte sequence and return a single byte reply. - * - * @board: the gpib_board_struct data area for this gpib interface - * @msg: the byte sequence. - * @leng: the byte sequence length; can be given as zero and is - * computed automatically, but if 'msg' contains a zero byte, - * it has to be given explicitly. - */ - -static int send_command(struct gpib_board *board, char *msg, int leng) -{ - char buffer[64]; - int nchar; - int retval; - struct timespec64 before, after; - - ktime_get_real_ts64 (&before); - - if (!leng) - leng = strlen(msg); - retval = write_loop(GPIB_DEV, msg, leng); - if (retval < 0) - return retval; - - nchar = skel_do_read(GPIB_DEV, buffer, 64); - - if (nchar < 0) { - dev_err(board->gpib_dev, " return from read: %d\n", nchar); - return nchar; - } else if (nchar != 1) { - dev_err(board->gpib_dev, " Irregular reply to command: %s\n", msg); - return -EIO; - } - ktime_get_real_ts64 (&after); - - DIA_LOG(1, "Sent %d - done %d us.\n", leng, usec_diff(&after, &before)); - - return buffer[0] & 0xff; -} - -/* - * set_control_line() - Set the value of a single gpib control line - * - * @board: the gpib_board_struct data area for this gpib interface - * @line: line mask - * @value: line new value (0/1) - */ - -static int set_control_line(struct gpib_board *board, int line, int value) -{ - char msg[] = USB_GPIB_SET_LINES; - int retval; - int leng = strlen(msg); - - DIA_LOG(1, "setting line %x to %x\n", line, value); - - retval = send_command(board, USB_GPIB_READ_LINES, 0); - - DIA_LOG(1, "old line values: %x\n", retval); - - if (retval == -EIO) - return retval; - - msg[leng - 2] = value ? (retval & ~line) : retval | line; - - retval = send_command(board, msg, 0); - - DIA_LOG(1, "operation result: %x\n", retval); - - return retval; -} - -/* - * one_char() - read one single byte from input buffer - * - * @board: the gpib_board_struct data area for this gpib interface - * @char_buf: the routine private data structure - */ - -static int one_char(struct gpib_board *board, struct char_buf *b) -{ - struct timespec64 before, after; - - if (b->nchar) { - DIA_LOG(2, "-> %x\n", b->inbuf[b->last - b->nchar]); - return b->inbuf[b->last - b->nchar--]; - } - ktime_get_real_ts64 (&before); - b->nchar = skel_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE); - b->last = b->nchar; - ktime_get_real_ts64 (&after); - - DIA_LOG(2, "read %d bytes in %d usec\n", - b->nchar, usec_diff(&after, &before)); - - if (b->nchar > 0) { - DIA_LOG(2, "--> %x\n", b->inbuf[b->last - b->nchar]); - return b->inbuf[b->last - b->nchar--]; - } - return -EIO; -} - -/** - * set_timeout() - set single byte / total timeouts on the adapter - * - * @board: the gpib_board_struct data area for this gpib interface - * - * For sake of speed, the operation is performed only if it - * modifies the current (saved) value. Minimum allowed timeout - * is 30 ms (T30ms -> 8); timeout disable (TNONE -> 0) currently - * not supported. - */ - -static void set_timeout(struct gpib_board *board) -{ - int n, val; - char command[sizeof(USB_GPIB_TTMO) + 6]; - struct usb_gpib_priv *data = board->private_data; - - if (data->timeout == board->usec_timeout) - return; - - n = (board->usec_timeout + 32767) / 32768; - if (n < 2) - n = 2; - - DIA_LOG(1, "Set timeout to %d us -> %d\n", board->usec_timeout, n); - - sprintf(command, "%s%d\n", USB_GPIB_BTMO, n > 255 ? 255 : n); - val = send_command(board, command, 0); - - if (val == ACK) { - if (n > 65535) - n = 65535; - sprintf(command, "%s%d\n", USB_GPIB_TTMO, n); - val = send_command(board, command, 0); - } - - if (val != ACK) - dev_err(board->gpib_dev, "error in timeout set: <%s>\n", command); - else - data->timeout = board->usec_timeout; -} - -/* - * now the standard interface functions - attach and detach - */ - -/** - * usb_gpib_attach() - activate the usb-gpib converter board - * - * @board: the gpib_board_struct data area for this gpib interface - * @config: firmware data, if any (from gpib_config -I ) - * - * The channel name is ttyUSBn, with n=0 by default. Other values for n - * passed with gpib_config -b . - * - * In this routine I trust that when an error code is returned - * detach() will be called. Always. - */ - -static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - int retval, j; - u32 base = config->ibbase; - char *device_path; - int match; - struct usb_device *udev; - - DIA_LOG(0, "Board %p -t %s -m %d -a %p -u %d -l %d -b %d\n", - board, board->interface->name, board->minor, config->device_path, - config->pci_bus, config->pci_slot, base); - - board->private_data = NULL; /* to be sure - we can detach before setting */ - - /* identify device to be attached */ - - mutex_lock(&minors_lock); - - if (config->device_path) { - /* if config->device_path given, try that first */ - for (j = 0 ; j < MAX_DEV ; j++) { - if ((assigned_usb_minors & 1 << j) == 0) - continue; - udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); - device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL); - match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev, - config->device_path); - DIA_LOG(1, "dev. %d: minor %d path: %s --> %d\n", j, - lpvo_usb_interfaces[j]->minor, device_path, match); - kfree(device_path); - if (match) - break; - } - } else if (config->pci_bus != -1 && config->pci_slot != -1) { - /* second: look for bus and slot */ - for (j = 0 ; j < MAX_DEV ; j++) { - if ((assigned_usb_minors & 1 << j) == 0) - continue; - udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); - DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j, - udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot); - if (config->pci_bus == udev->bus->busnum && - config->pci_slot == udev->devnum) - break; - } - } else { /* last chance: usb_minor, given as ibbase */ - for (j = 0 ; j < MAX_DEV ; j++) { - if (usb_minors[j] == base && assigned_usb_minors & 1 << j) - break; - } - } - mutex_unlock(&minors_lock); - - if (j == MAX_DEV) { - dev_err(board->gpib_dev, "Requested device is not registered.\n"); - return -EIO; - } - - board->private_data = kzalloc(sizeof(struct usb_gpib_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - - retval = skel_do_open(board, usb_minors[j]); - - DIA_LOG(1, "Skel open: %d\n", retval); - - if (retval) { - dev_err(board->gpib_dev, "skel open failed.\n"); - kfree(board->private_data); - board->private_data = NULL; - return -ENODEV; - } - - show_status(board); - - retval = send_command(board, USB_GPIB_ON, 0); - DIA_LOG(1, "USB_GPIB_ON returns %x\n", retval); - if (retval != ACK) - return -EIO; - - /* - * We must setup debug mode because we need the extended instruction - * set to cope with the Core (gpib_common) point of view - */ - - retval = send_command(board, USB_GPIB_DEBUG_ON, 0); - DIA_LOG(1, "USB_GPIB_DEBUG_ON returns %x\n", retval); - if (retval != ACK) - return -EIO; - - /* - * We must keep REN off after an IFC because so it is - * assumed by the Core - */ - - retval = send_command(board, USB_GPIB_IBm0, 0); - DIA_LOG(1, "USB_GPIB_IBm0 returns %x\n", retval); - if (retval != ACK) - return -EIO; - - retval = set_control_line(board, IB_BUS_REN, 0); - if (retval != ACK) - return -EIO; - - retval = send_command(board, USB_GPIB_FTMO, 0); - DIA_LOG(1, "USB_GPIB_FTMO returns %x\n", retval); - if (retval != ACK) - return -EIO; - - show_status(board); - DIA_LOG(0, "attached\n"); - return 0; -} - -/** - * usb_gpib_detach() - deactivate the usb-gpib converter board - * - * @board: the gpib_board data area for this gpib interface - * - */ - -static void usb_gpib_detach(struct gpib_board *board) -{ - int retval; - - show_status(board); - - DIA_LOG(0, "detaching\n"); - - if (board->private_data) { - if (GPIB_DEV) { - write_loop(GPIB_DEV, USB_GPIB_OFF, strlen(USB_GPIB_OFF)); - msleep(100); - DIA_LOG(1, "%s", "GPIB off\n"); - retval = skel_do_release(board); - DIA_LOG(1, "skel release -> %d\n", retval); - } - kfree(board->private_data); - board->private_data = NULL; - } - - DIA_LOG(0, "detached\n"); -} - -/* - * Other functions follow in alphabetical order - */ -/* command */ -static int usb_gpib_command(struct gpib_board *board, - u8 *buffer, - size_t length, - size_t *bytes_written) -{ - int i, retval; - char command[6] = "IBc.\n"; - - DIA_LOG(1, "enter %p\n", board); - - set_timeout(board); - - *bytes_written = 0; - for (i = 0 ; i < length ; i++) { - command[3] = buffer[i]; - retval = send_command(board, command, 5); - DIA_LOG(2, "%d ==> %x %x\n", i, buffer[i], retval); - if (retval != 0x06) - return retval; - ++(*bytes_written); - } - return 0; -} - -/** - * usb_gpib_disable_eos() - Disable END on eos byte (END on EOI only) - * - * @board: the gpib_board data area for this gpib interface - * - * With the lpvo adapter eos can only be handled via software. - * Cannot do nothing here, but remember for future use. - */ - -static void usb_gpib_disable_eos(struct gpib_board *board) -{ - ((struct usb_gpib_priv *)board->private_data)->eos_flags &= ~REOS; - DIA_LOG(1, "done: %x\n", - ((struct usb_gpib_priv *)board->private_data)->eos_flags); -} - -/** - * usb_gpib_enable_eos() - Enable END for reads when eos byte is received. - * - * @board: the gpib_board data area for this gpib interface - * @eos_byte: the 'eos' byte - * @compare_8_bits: if zero ignore eigthth bit when comparing - * - */ - -static int usb_gpib_enable_eos(struct gpib_board *board, - u8 eos_byte, - int compare_8_bits) -{ - struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data; - - DIA_LOG(1, "enter with %x\n", eos_byte); - pd->eos = eos_byte; - pd->eos_flags = REOS; - if (compare_8_bits) - pd->eos_flags |= BIN; - return 0; -} - -/** - * usb_gpib_go_to_standby() - De-assert ATN - * - * @board: the gpib_board data area for this gpib interface - */ - -static int usb_gpib_go_to_standby(struct gpib_board *board) -{ - int retval = set_control_line(board, IB_BUS_ATN, 0); - - DIA_LOG(1, "done with %x\n", retval); - - if (retval == ACK) - return 0; - return -EIO; -} - -/** - * usb_gpib_interface_clear() - Assert or de-assert IFC - * - * @board: the gpib_board data area for this gpib interface - * @assert: 1: assert IFC; 0: de-assert IFC - * - * Currently on the assert request we issue the lpvo IBZ - * command that cycles IFC low for 100 usec, then we ignore - * the de-assert request. - */ - -static void usb_gpib_interface_clear(struct gpib_board *board, int assert) -{ - int retval = 0; - - DIA_LOG(1, "enter with %d\n", assert); - - if (assert) { - retval = send_command(board, USB_GPIB_IBCL, 0); - - set_bit(CIC_NUM, &board->status); - } - - DIA_LOG(1, "done with %d %d\n", assert, retval); -} - -/** - * usb_gpib_line_status() - Read the status of the bus lines. - * - * @board: the gpib_board data area for this gpib interface - * - * We can read all lines. - */ -static int usb_gpib_line_status(const struct gpib_board *board) -{ - int buffer; - int line_status = VALID_ALL; /* all lines will be read */ - struct list_head *p, *q; - WQT *item; - unsigned long flags; - int sleep = 0; - - DIA_LOG(1, "%s\n", "request"); - - /* - * if we are on the wait queue (board->wait), do not hurry - * reading status line; instead, pause a little - */ - - spin_lock_irqsave((spinlock_t *)&board->wait.lock, flags); - q = (struct list_head *)&board->wait.WQH; - list_for_each(p, q) { - item = container_of(p, WQT, WQE); - if (item->private == current) { - sleep = 20; - break; - } - /* pid is: ((struct task_struct *) item->private)->pid); */ - } - spin_unlock_irqrestore((spinlock_t *)&board->wait.lock, flags); - if (sleep) { - DIA_LOG(1, "we are on the wait queue - sleep %d ms\n", sleep); - msleep(sleep); - } - - buffer = send_command((struct gpib_board *)board, USB_GPIB_STATUS, 0); - - if (buffer < 0) { - dev_err(board->gpib_dev, "line status read failed with %d\n", buffer); - return -1; - } - - if ((buffer & 0x01) == 0) - line_status |= BUS_REN; - if ((buffer & 0x02) == 0) - line_status |= BUS_IFC; - if ((buffer & 0x04) == 0) - line_status |= BUS_NDAC; - if ((buffer & 0x08) == 0) - line_status |= BUS_NRFD; - if ((buffer & 0x10) == 0) - line_status |= BUS_DAV; - if ((buffer & 0x20) == 0) - line_status |= BUS_EOI; - if ((buffer & 0x40) == 0) - line_status |= BUS_ATN; - if ((buffer & 0x80) == 0) - line_status |= BUS_SRQ; - - DIA_LOG(1, "done with %x %x\n", buffer, line_status); - - return line_status; -} - -/* parallel_poll */ - -static int usb_gpib_parallel_poll(struct gpib_board *board, u8 *result) -{ - /* - * request parallel poll asserting ATN | EOI; - * we suppose ATN already asserted - */ - - int retval; - - DIA_LOG(1, "enter %p\n", board); - - retval = set_control_line(board, IB_BUS_EOI, 1); - if (retval != ACK) - return -EIO; - - *result = send_command(board, USB_GPIB_READ_DATA, 0); - - DIA_LOG(1, "done with %x\n", *result); - - retval = set_control_line(board, IB_BUS_EOI, 0); - if (retval != 0x06) - return -EIO; - - return 0; -} - -/* read */ - -static int usb_gpib_read(struct gpib_board *board, - u8 *buffer, - size_t length, - int *end, - size_t *bytes_read) -{ -#define MAX_READ_EXCESS 16384 - - struct char_buf b = {NULL, 0}; - - int retval; - char c, nc; - int ic; - struct timespec64 before, after; - int read_count = MAX_READ_EXCESS; - struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data; - - DIA_LOG(1, "enter %p -> %zu\n", board, length); - - *bytes_read = 0; /* by default, things go wrong */ - *end = 0; - - set_timeout(board); - - /* single byte read has a special handling */ - - if (length == 1) { - char inbuf[2] = {0, 0}; - - /* read a single character */ - - ktime_get_real_ts64 (&before); - - retval = write_loop(GPIB_DEV, USB_GPIB_READ_1, strlen(USB_GPIB_READ_1)); - if (retval < 0) - return retval; - - retval = skel_do_read(GPIB_DEV, inbuf, 1); - retval += skel_do_read(GPIB_DEV, inbuf + 1, 1); - - ktime_get_real_ts64 (&after); - - DIA_LOG(1, "single read: %x %x %x in %d\n", retval, - inbuf[0], inbuf[1], - usec_diff(&after, &before)); - - /* good char / last char? */ - - if (retval == 2 && inbuf[1] == ACK) { - buffer[0] = inbuf[0]; - *bytes_read = 1; - return 0; - } - if (retval < 2) - return -EIO; - else - return -ETIME; - } - - /* allocate buffer for multibyte read */ - - b.inbuf = kmalloc(INBUF_SIZE, GFP_KERNEL); - if (!b.inbuf) - return -ENOMEM; - - /* send read command and check sequence */ - - retval = write_loop(GPIB_DEV, USB_GPIB_READ, strlen(USB_GPIB_READ)); - if (retval < 0) - goto read_return; - - if (one_char(board, &b) != DLE || one_char(board, &b) != STX) { - dev_err(board->gpib_dev, "wrong sequence\n"); - retval = -EIO; - goto read_return; - } - - /* get data flow */ - - while (1) { - ic = one_char(board, &b); - if (ic == -EIO) { - retval = -EIO; - goto read_return; - } - c = ic; - - if (c == DLE) - nc = one_char(board, &b); - if (c != DLE || nc == DLE) { - /* data byte - store into buffer */ - - if (*bytes_read == length) - break; /* data overflow */ - if (c == DLE) - c = nc; - buffer[(*bytes_read)++] = c; - if (c == pd->eos) { - *end = 1; - break; - } - - } else { - /* we are in the closing sequence */ - c = nc; - if (c == ETX) { - c = one_char(board, &b); - if (c == ACK) { - *end = 1; - retval = 0; - goto read_return; - } else { - dev_err(board->gpib_dev, "wrong end of message %x", c); - retval = -ETIME; - goto read_return; - } - } else { - dev_err(board->gpib_dev, "lone in stream"); - retval = -EIO; - goto read_return; - } - } - } - - /* we had a data overflow - flush excess data */ - - while (read_count--) { - if (one_char(board, &b) != DLE) - continue; - c = one_char(board, &b); - if (c == DLE) - continue; - if (c == ETX) { - c = one_char(board, &b); - if (c == ACK) { - if (MAX_READ_EXCESS - read_count > 1) - dev_dbg(board->gpib_dev, "small buffer - maybe some data lost"); - retval = 0; - goto read_return; - } - break; - } - } - - dev_err(board->gpib_dev, "no input end - board in odd state\n"); - retval = -EIO; - -read_return: - kfree(b.inbuf); - - DIA_LOG(1, "done with byte/status: %d %x %d\n", (int)*bytes_read, retval, *end); - - if (retval == 0 || retval == -ETIME) { - if (send_command(board, USB_GPIB_UNTALK, sizeof(USB_GPIB_UNTALK)) == 0x06) - return retval; - return -EIO; - } - - return retval; -} - -/* remote_enable */ - -static void usb_gpib_remote_enable(struct gpib_board *board, int enable) -{ - int retval; - - retval = set_control_line(board, IB_BUS_REN, enable ? 1 : 0); - if (retval != ACK) - dev_err(board->gpib_dev, "could not set REN line: %x\n", retval); - - DIA_LOG(1, "done with %x\n", retval); -} - -/* request_system_control */ - -static int usb_gpib_request_system_control(struct gpib_board *board, int request_control) -{ - if (!request_control) - return -EINVAL; - - DIA_LOG(1, "done with %d -> %lx\n", request_control, board->status); - return 0; -} - -/* take_control */ -/* beware: the sync flag is ignored; what is its real meaning? */ - -static int usb_gpib_take_control(struct gpib_board *board, int sync) -{ - int retval; - - retval = set_control_line(board, IB_BUS_ATN, 1); - - DIA_LOG(1, "done with %d %x\n", sync, retval); - - if (retval == ACK) - return 0; - return -EIO; -} - -/* update_status */ - -static unsigned int usb_gpib_update_status(struct gpib_board *board, - unsigned int clear_mask) -{ - /* There is nothing we can do here, I guess */ - - board->status &= ~clear_mask; - - DIA_LOG(1, "done with %x %lx\n", clear_mask, board->status); - - return board->status; -} - -/* write */ -/* beware: DLE characters are not escaped - can only send ASCII data */ - -static int usb_gpib_write(struct gpib_board *board, - u8 *buffer, - size_t length, - int send_eoi, - size_t *bytes_written) -{ - int retval; - char *msg; - - DIA_LOG(1, "enter %p -> %zu\n", board, length); - - set_timeout(board); - - msg = kmalloc(length + 8, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - memcpy(msg, "\nIB\020\002", 5); - memcpy(msg + 5, buffer, length); - memcpy(msg + 5 + length, "\020\003\n", 3); - - retval = send_command(board, msg, length + 8); - kfree(msg); - - DIA_LOG(1, "<%.*s> -> %x\n", (int)length, buffer, retval); - - if (retval != ACK) - return -EPIPE; - - *bytes_written = length; - - if (send_command(board, USB_GPIB_UNLISTEN, sizeof(USB_GPIB_UNLISTEN)) != 0x06) - return -EPIPE; - - return length; -} - -/* - * *** following functions not implemented yet *** - */ - -/* parallel_poll configure */ - -static void usb_gpib_parallel_poll_configure(struct gpib_board *board, - u8 configuration) -{ -} - -/* parallel_poll_response */ - -static void usb_gpib_parallel_poll_response(struct gpib_board *board, int ist) -{ -} - -/* primary_address */ - -static int usb_gpib_primary_address(struct gpib_board *board, unsigned int address) -{ - return 0; -} - -/* return_to_local */ - -static void usb_gpib_return_to_local(struct gpib_board *board) -{ -} - -/* secondary_address */ - -static int usb_gpib_secondary_address(struct gpib_board *board, - unsigned int address, - int enable) -{ - return 0; -} - -/* serial_poll_response */ - -static void usb_gpib_serial_poll_response(struct gpib_board *board, u8 status) -{ -} - -/* serial_poll_status */ - -static u8 usb_gpib_serial_poll_status(struct gpib_board *board) -{ - return 0; -} - -/* t1_delay */ - -static int usb_gpib_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - return 0; -} - -/* - * *** module dispatch table and init/exit functions *** - */ - -static struct gpib_interface usb_gpib_interface = { - .name = NAME, - .attach = usb_gpib_attach, - .detach = usb_gpib_detach, - .read = usb_gpib_read, - .write = usb_gpib_write, - .command = usb_gpib_command, - .take_control = usb_gpib_take_control, - .go_to_standby = usb_gpib_go_to_standby, - .request_system_control = usb_gpib_request_system_control, - .interface_clear = usb_gpib_interface_clear, - .remote_enable = usb_gpib_remote_enable, - .enable_eos = usb_gpib_enable_eos, - .disable_eos = usb_gpib_disable_eos, - .parallel_poll = usb_gpib_parallel_poll, - .parallel_poll_configure = usb_gpib_parallel_poll_configure, - .parallel_poll_response = usb_gpib_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = usb_gpib_line_status, - .update_status = usb_gpib_update_status, - .primary_address = usb_gpib_primary_address, - .secondary_address = usb_gpib_secondary_address, - .serial_poll_response = usb_gpib_serial_poll_response, - .serial_poll_status = usb_gpib_serial_poll_status, - .t1_delay = usb_gpib_t1_delay, - .return_to_local = usb_gpib_return_to_local, - .skip_check_for_command_acceptors = 1 -}; - -/* - * usb_gpib_init_module(), usb_gpib_exit_module() - * - * This functions are called every time a new device is detected - * and registered or is removed and unregistered. - * We must take note of created and destroyed usb minors to be used - * when usb_gpib_attach() and usb_gpib_detach() will be called on - * request by gpib_config. - */ - -static int usb_gpib_init_module(struct usb_interface *interface) -{ - int j, mask, rv; - - rv = mutex_lock_interruptible(&minors_lock); - if (rv < 0) - return rv; - - if (!assigned_usb_minors) { - rv = gpib_register_driver(&usb_gpib_interface, THIS_MODULE); - if (rv) { - pr_err("gpib_register_driver failed: error = %d\n", rv); - goto exit; - } - } else { - /* - * check if minor is already registered - maybe useless, but if - * it happens the code is inconsistent somewhere - */ - - for (j = 0 ; j < MAX_DEV ; j++) { - if (usb_minors[j] == interface->minor && assigned_usb_minors & 1 << j) { - pr_err("CODE BUG: USB minor %d registered at %d.\n", - interface->minor, j); - rv = -1; - goto exit; - } - } - } - - /* find a free slot */ - - for (j = 0 ; j < MAX_DEV ; j++) { - mask = 1 << j; - if ((assigned_usb_minors & mask) == 0) { - usb_minors[j] = interface->minor; - lpvo_usb_interfaces[j] = interface; - assigned_usb_minors |= mask; - rv = 0; - goto exit; - } - } - pr_err("No slot available for interface %p minor %d\n", interface, interface->minor); - rv = -1; - -exit: - mutex_unlock(&minors_lock); - return rv; -} - -static void usb_gpib_exit_module(int minor) -{ - int j; - - mutex_lock(&minors_lock); - for (j = 0 ; j < MAX_DEV ; j++) { - if (usb_minors[j] == minor && assigned_usb_minors & 1 << j) { - assigned_usb_minors &= ~(1 << j); - usb_minors[j] = -1; - if (assigned_usb_minors == 0) - gpib_unregister_driver(&usb_gpib_interface); - goto exit; - } - } - pr_err("CODE BUG: USB minor %d not found.\n", minor); - -exit: - mutex_unlock(&minors_lock); -} - -/* - * Default latency time (16 msec) is too long. - * We must use 1 msec (best); anyhow, no more than 5 msec. - * - * Defines and function taken and modified from the kernel tree - * (see ftdi_sio.h and ftdi_sio.c). - */ - -#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ -#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER -#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 -#define WDR_TIMEOUT 5000 /* default urb timeout */ -#define WDR_SHORT_TIMEOUT 1000 /* shorter urb timeout */ - -#define LATENCY_TIMER 1 /* use a small latency timer: 1 ... 5 msec */ -#define LATENCY_CHANNEL 0 /* channel selection in multichannel devices */ -static int write_latency_timer(struct usb_device *udev) -{ - int rv = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - FTDI_SIO_SET_LATENCY_TIMER_REQUEST, - FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, - LATENCY_TIMER, LATENCY_CHANNEL, - NULL, 0, WDR_TIMEOUT); - if (rv < 0) - dev_err(&udev->dev, "Unable to write latency timer: %i\n", rv); - return rv; -} - -/***************************************************************************** - * * - * The following code is a modified version of the USB Skeleton driver * - * written by Greg Kroah-Hartman and available in the kernel tree. * - * * - * Functions skel_open() and skel_release() have been rewritten and named * - * skel_do_open() and skel_do_release() to process the attach and detach * - * requests coming from gpib_config. * - * * - * Functions skel_read() and skel_write() have been split into a * - * skel_do_read() and skel_do_write(), that cover the kernel stuff of read * - * and write operations, and the original skel_read() and skel_write(), * - * that handle communication with user space and call their _do_ companion. * - * * - * Only the _do_ versions are used by the lpvo_usb_gpib driver; other ones * - * can be (optionally) maintained in the compilation to have direct access * - * to a gpib controller for debug and diagnostics. * - * * - * To avoid collisions in names, devices in user space have been renamed * - * lpvo_raw1, lpvo_raw2 .... and the usb driver has been renamed with the * - * gpib module name. * - * * - *****************************************************************************/ - -/* - * USB Skeleton driver - 2.2 - * - * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) - * - * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c - * but has been rewritten to be easier to read and use. - */ - -#include -#include -#include -#include - -/* Get a minor range for your devices from the usb maintainer */ -#define USB_SKEL_MINOR_BASE 192 - -/* private defines */ - -#define MAX_TRANSFER (PAGE_SIZE - 512) -/* - * MAX_TRANSFER is chosen so that the VM is not stressed by - * allocations > PAGE_SIZE and the number of packets in a page - * is an integer 512 is the largest possible packet on EHCI - */ - -#define WRITES_IN_FLIGHT 1 /* we do not want more than one pending write */ -#define USER_DEVICE 1 /* compile for device(s) in user space */ - -/* Structure to hold all of our device specific stuff */ -struct usb_skel { - struct usb_device *udev; /* the usb device for this device */ - struct usb_interface *interface; /* the interface for this device */ - struct semaphore limit_sem; /* limiting the number of writes in progress */ - struct usb_anchor submitted; /* in case need to retract our submissions */ - struct urb *bulk_in_urb; /* the urb to read data with */ - unsigned char *bulk_in_buffer; /* the buffer to receive data */ - size_t bulk_in_size; /* the size of the receive buffer */ - size_t bulk_in_filled; /* number of bytes in the buffer */ - size_t bulk_in_copied; /* already copied to user space */ - __u8 bulk_in_endpoint_addr; /* the address of the bulk in endpoint */ - __u8 bulk_out_endpoint_addr; /* the address of the bulk out endpoint */ - int errors; /* the last request tanked */ - bool ongoing_read; /* a read is going on */ - spinlock_t err_lock; /* lock for errors */ - struct kref kref; - struct mutex io_mutex; /* synchronize I/O with disconnect */ - wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ -}; - -#define to_skel_dev(d) container_of(d, struct usb_skel, kref) - -static struct usb_driver skel_driver; -static void skel_draw_down(struct usb_skel *dev); - -static void skel_delete(struct kref *kref) -{ - struct usb_skel *dev = to_skel_dev(kref); - - usb_free_urb(dev->bulk_in_urb); - usb_put_dev(dev->udev); - kfree(dev->bulk_in_buffer); - kfree(dev); -} - -/* - * skel_do_open() - to be called by usb_gpib_attach - */ - -static int skel_do_open(struct gpib_board *board, int subminor) -{ - struct usb_skel *dev; - struct usb_interface *interface; - int retval = 0; - - interface = usb_find_interface(&skel_driver, subminor); - if (!interface) { - dev_err(board->gpib_dev, "can't find device for minor %d\n", subminor); - retval = -ENODEV; - goto exit; - } - - dev = usb_get_intfdata(interface); - if (!dev) { - retval = -ENODEV; - goto exit; - } - - retval = usb_autopm_get_interface(interface); - if (retval) - goto exit; - - /* increment our usage count for the device */ - kref_get(&dev->kref); - - /* save our object in the file's private structure */ - GPIB_DEV = dev; - -exit: - return retval; -} - -/* - * skel_do_release() - to be called by usb_gpib_detach - */ - -static int skel_do_release(struct gpib_board *board) -{ - struct usb_skel *dev; - - dev = GPIB_DEV; - if (!dev) - return -ENODEV; - - /* allow the device to be autosuspended */ - mutex_lock(&dev->io_mutex); - if (dev->interface) - usb_autopm_put_interface(dev->interface); - mutex_unlock(&dev->io_mutex); - - /* decrement the count on our device */ - kref_put(&dev->kref, skel_delete); - return 0; -} - -/* - * read functions - */ - -static void skel_read_bulk_callback(struct urb *urb) -{ - struct usb_skel *dev; - unsigned long flags; - - dev = urb->context; - - spin_lock_irqsave(&dev->err_lock, flags); - /* sync/async unlink faults aren't errors */ - if (urb->status) { - if (!(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) - dev_err(&dev->interface->dev, "nonzero read bulk status received: %d\n", - urb->status); - - dev->errors = urb->status; - } else { - dev->bulk_in_filled = urb->actual_length; - } - dev->ongoing_read = 0; - spin_unlock_irqrestore(&dev->err_lock, flags); - - wake_up_interruptible(&dev->bulk_in_wait); -} - -static int skel_do_read_io(struct usb_skel *dev, size_t count) -{ - int rv; - - /* prepare a read */ - usb_fill_bulk_urb(dev->bulk_in_urb, - dev->udev, - usb_rcvbulkpipe(dev->udev, - dev->bulk_in_endpoint_addr), - dev->bulk_in_buffer, - min(dev->bulk_in_size, count), - skel_read_bulk_callback, - dev); - /* tell everybody to leave the URB alone */ - spin_lock_irq(&dev->err_lock); - dev->ongoing_read = 1; - spin_unlock_irq(&dev->err_lock); - - /* submit bulk in urb, which means no data to deliver */ - dev->bulk_in_filled = 0; - dev->bulk_in_copied = 0; - - /* do it */ - rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); - if (rv < 0) { - dev_err(&dev->interface->dev, "failed submitting read urb, error %d\n", rv); - rv = (rv == -ENOMEM) ? rv : -EIO; - spin_lock_irq(&dev->err_lock); - dev->ongoing_read = 0; - spin_unlock_irq(&dev->err_lock); - } - - return rv; -} - -/* - * skel_do_read() - read operations from lpvo_usb_gpib - */ - -static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) -{ - int rv; - bool ongoing_io; - - /* if we cannot read at all, return EOF */ - - if (!dev->bulk_in_urb || !count) - return 0; - -restart: /* added to comply with ftdi timeout technique */ - - /* no concurrent readers */ - - rv = mutex_lock_interruptible(&dev->io_mutex); - if (rv < 0) - return rv; - - if (!dev->interface) { /* disconnect() was called */ - rv = -ENODEV; - goto exit; - } - -retry: - /* if IO is under way, we must not touch things */ - spin_lock_irq(&dev->err_lock); - ongoing_io = dev->ongoing_read; - spin_unlock_irq(&dev->err_lock); - - if (ongoing_io) { -// /* nonblocking IO shall not wait */ -// /* no file, no O_NONBLOCK; maybe provide when from user space */ -// if (file->f_flags & O_NONBLOCK) { -// rv = -EAGAIN; -// goto exit; -// } - - /* - * IO may take forever - * hence wait in an interruptible state - */ - rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read)); - if (rv < 0) - goto exit; - } - - /* errors must be reported */ - rv = dev->errors; - if (rv < 0) { - /* any error is reported once */ - dev->errors = 0; - /* to preserve notifications about reset */ - rv = (rv == -EPIPE) ? rv : -EIO; - /* report it */ - goto exit; - } - - /* - * if the buffer is filled we may satisfy the read - * else we need to start IO - */ - - if (dev->bulk_in_filled) { - /* we had read data */ - - size_t available = dev->bulk_in_filled - dev->bulk_in_copied; -// size_t chunk = min(available, count); /* compute chunk later */ - size_t chunk; - - if (!available) { - /* - * all data has been used - * actual IO needs to be done - */ - /* - * it seems that requests for less than dev->bulk_in_size - * are not accepted - */ - rv = skel_do_read_io(dev, dev->bulk_in_size); - if (rv < 0) - goto exit; - else - goto retry; - } - - /* - * data is available - chunk tells us how much shall be copied - */ - - /* - * Condition dev->bulk_in_copied > 0 maybe will never happen. In case, - * signal the event and copy using the original procedure, i.e., copy - * first two bytes also - */ - - if (dev->bulk_in_copied) { - chunk = min(available, count); - memcpy(buffer, dev->bulk_in_buffer + dev->bulk_in_copied, chunk); - rv = chunk; - dev->bulk_in_copied += chunk; - - /* copy discarding first two bytes that contain ftdi chip status */ - - } else { - /* account for two bytes to be discarded */ - chunk = min(available, count + 2); - if (chunk < 2) { - dev_err(&dev->udev->dev, "BAD READ - chunk: %zu\n", chunk); - rv = -EIO; - goto exit; - } - - memcpy(buffer, dev->bulk_in_buffer + 2, chunk - 2); - rv = chunk; - dev->bulk_in_copied += chunk; - } - - /* - * if we are asked for more than we have, - * we start IO but don't wait - * - * No, no read ahead allowed; if the case, more data will be - * asked for by the lpvo_usb_gpib layer. - */ -// if (available < count) -// skel_do_read_io(dev, dev->bulk_in_size); - } else { - /* no data in the buffer */ - rv = skel_do_read_io(dev, dev->bulk_in_size); - if (rv < 0) - goto exit; - else - goto retry; - } -exit: - mutex_unlock(&dev->io_mutex); - if (rv == 2) - goto restart; /* ftdi chip returns two status bytes after a latency anyhow */ - - if (rv > 0) - return rv - 2; /* account for 2 discarded bytes in a valid buffer */ - return rv; -} - -/* - * write functions - */ - -static void skel_write_bulk_callback(struct urb *urb) -{ - struct usb_skel *dev; - unsigned long flags; - - dev = urb->context; - - /* sync/async unlink faults aren't errors */ - if (urb->status) { - if (!(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) - dev_err(&dev->interface->dev, - "nonzero write bulk status received: %d\n", urb->status); - - spin_lock_irqsave(&dev->err_lock, flags); - dev->errors = urb->status; - spin_unlock_irqrestore(&dev->err_lock, flags); - } - - /* free up our allocated buffer */ - usb_free_coherent(urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, urb->transfer_dma); - up(&dev->limit_sem); -} - -/* - * skel_do_write() - write operations from lpvo_usb_gpib - */ - -static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t count) -{ - int retval = 0; - struct urb *urb = NULL; - char *buf = NULL; - size_t writesize = min_t(size_t, count, (size_t)MAX_TRANSFER); - - /* verify that we actually have some data to write */ - if (count == 0) - goto exit; - - /* - * limit the number of URBs in flight to stop a user from using up all - * RAM - */ - /* Only one URB is used, because we can't have a pending write() and go on */ - -// if (!(file->f_flags & O_NONBLOCK)) { /* no NONBLOCK provided */ - if (down_interruptible(&dev->limit_sem)) { - retval = -ERESTARTSYS; - goto exit; - } -// } else { -// if (down_trylock(&dev->limit_sem)) { -// retval = -EAGAIN; -// goto exit; -// } -// } - - spin_lock_irq(&dev->err_lock); - retval = dev->errors; - if (retval < 0) { - /* any error is reported once */ - dev->errors = 0; - /* to preserve notifications about reset */ - retval = (retval == -EPIPE) ? retval : -EIO; - } - spin_unlock_irq(&dev->err_lock); - if (retval < 0) - goto error; - - /* create a urb, and a buffer for it, and copy the data to the urb */ - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - retval = -ENOMEM; - goto error; - } - - buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL, - &urb->transfer_dma); - if (!buf) { - retval = -ENOMEM; - goto error; - } - - memcpy(buf, buffer, count); - - /* this lock makes sure we don't submit URBs to gone devices */ - mutex_lock(&dev->io_mutex); - if (!dev->interface) { /* disconnect() was called */ - mutex_unlock(&dev->io_mutex); - retval = -ENODEV; - goto error; - } - - /* initialize the urb properly */ - usb_fill_bulk_urb(urb, dev->udev, - usb_sndbulkpipe(dev->udev, dev->bulk_out_endpoint_addr), - buf, writesize, skel_write_bulk_callback, dev); - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - usb_anchor_urb(urb, &dev->submitted); - - /* send the data out the bulk port */ - retval = usb_submit_urb(urb, GFP_KERNEL); - mutex_unlock(&dev->io_mutex); - if (retval) { - dev_err(&dev->interface->dev, "failed submitting write urb, error %d\n", retval); - goto error_unanchor; - } - - /* - * release our reference to this urb, the USB core will eventually free - * it entirely - */ - usb_free_urb(urb); - - return writesize; - -error_unanchor: - usb_unanchor_urb(urb); -error: - if (urb) { - usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma); - usb_free_urb(urb); - } - up(&dev->limit_sem); - -exit: - return retval; -} - -/* - * services for the user space devices - */ - -#if USER_DEVICE /* conditional compilation of user space device */ - -static int skel_flush(struct file *file, fl_owner_t id) -{ - struct usb_skel *dev; - int res; - - dev = file->private_data; - if (!dev) - return -ENODEV; - - /* wait for io to stop */ - mutex_lock(&dev->io_mutex); - skel_draw_down(dev); - - /* read out errors, leave subsequent opens a clean slate */ - spin_lock_irq(&dev->err_lock); - res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0; - dev->errors = 0; - spin_unlock_irq(&dev->err_lock); - - mutex_unlock(&dev->io_mutex); - - return res; -} - -static int skel_open(struct inode *inode, struct file *file) -{ - struct usb_skel *dev; - struct usb_interface *interface; - int subminor; - int retval = 0; - - subminor = iminor(inode); - - interface = usb_find_interface(&skel_driver, subminor); - if (!interface) { - pr_err("can't find device for minor %d\n", subminor); - retval = -ENODEV; - goto exit; - } - - dev = usb_get_intfdata(interface); - if (!dev) { - retval = -ENODEV; - goto exit; - } - - retval = usb_autopm_get_interface(interface); - if (retval) - goto exit; - - /* increment our usage count for the device */ - kref_get(&dev->kref); - - /* save our object in the file's private structure */ - file->private_data = dev; - -exit: - return retval; -} - -static int skel_release(struct inode *inode, struct file *file) -{ - struct usb_skel *dev; - - dev = file->private_data; - if (!dev) - return -ENODEV; - - /* allow the device to be autosuspended */ - mutex_lock(&dev->io_mutex); - if (dev->interface) - usb_autopm_put_interface(dev->interface); - mutex_unlock(&dev->io_mutex); - - /* decrement the count on our device */ - kref_put(&dev->kref, skel_delete); - return 0; -} - -/* - * user space access to read function - */ - -static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, - loff_t *ppos) -{ - struct usb_skel *dev; - char *buf; - ssize_t rv; - - dev = file->private_data; - - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - rv = skel_do_read(dev, buf, count); - - if (rv > 0) { - if (copy_to_user(buffer, buf, rv)) { - kfree(buf); - return -EFAULT; - } - } - kfree(buf); - return rv; -} - -/* - * user space access to write function - */ - -static ssize_t skel_write(struct file *file, const char __user *user_buffer, - size_t count, loff_t *ppos) -{ - struct usb_skel *dev; - char *buf; - ssize_t rv; - - dev = file->private_data; - - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, user_buffer, count)) { - kfree(buf); - return -EFAULT; - } - - rv = skel_do_write(dev, buf, count); - kfree(buf); - return rv; -} -#endif - -static const struct file_operations skel_fops = { - .owner = THIS_MODULE, -#if USER_DEVICE - .read = skel_read, - .write = skel_write, - .open = skel_open, - .release = skel_release, - .flush = skel_flush, - .llseek = noop_llseek, -#endif -}; - -/* - * usb class driver info in order to get a minor number from the usb core, - * and to have the device registered with the driver core - */ -#if USER_DEVICE -static struct usb_class_driver skel_class = { - .name = "lpvo_raw%d", - .fops = &skel_fops, - .minor_base = USB_SKEL_MINOR_BASE, -}; -#endif - -static int skel_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_skel *dev; - struct usb_endpoint_descriptor *bulk_in, *bulk_out; - int retval; - char *device_path; - - mutex_init(&minors_lock); /* required for handling minor numbers table */ - - /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - kref_init(&dev->kref); - sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); - mutex_init(&dev->io_mutex); - spin_lock_init(&dev->err_lock); - init_usb_anchor(&dev->submitted); - init_waitqueue_head(&dev->bulk_in_wait); - - dev->udev = usb_get_dev(interface_to_usbdev(interface)); - dev->interface = interface; - - /* set up the endpoint information */ - /* use only the first bulk-in and bulk-out endpoints */ - retval = usb_find_common_endpoints(interface->cur_altsetting, - &bulk_in, &bulk_out, NULL, NULL); - if (retval) { - dev_err(&interface->dev, - "Could not find both bulk-in and bulk-out endpoints\n"); - goto error; - } - - dev->bulk_in_size = usb_endpoint_maxp(bulk_in); - dev->bulk_in_endpoint_addr = bulk_in->bEndpointAddress; - dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL); - if (!dev->bulk_in_buffer) { - retval = -ENOMEM; - goto error; - } - dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->bulk_in_urb) { - retval = -ENOMEM; - goto error; - } - - dev->bulk_out_endpoint_addr = bulk_out->bEndpointAddress; - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - /* let the world know */ - - device_path = kobject_get_path(&dev->udev->dev.kobj, GFP_KERNEL); - dev_dbg(&interface->dev, "New lpvo_usb_device -> bus: %d dev: %d path: %s\n", - dev->udev->bus->busnum, dev->udev->devnum, device_path); - kfree(device_path); - -#if USER_DEVICE - /* we can register the device now, as it is ready */ - retval = usb_register_dev(interface, &skel_class); - if (retval) { - /* something prevented us from registering this driver */ - dev_err(&interface->dev, - "Not able to get a minor for this device.\n"); - usb_set_intfdata(interface, NULL); - goto error; - } -#endif - - write_latency_timer(dev->udev); /* adjust the latency timer */ - - usb_gpib_init_module(interface); /* last, init the lpvo for this minor */ - - return 0; - -error: - /* this frees allocated memory */ - kref_put(&dev->kref, skel_delete); - - return retval; -} - -static void skel_disconnect(struct usb_interface *interface) -{ - struct usb_skel *dev; - int minor = interface->minor; - - usb_gpib_exit_module(minor); /* first, disactivate the lpvo */ - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - -#if USER_DEVICE - /* give back our minor */ - usb_deregister_dev(interface, &skel_class); -#endif - - /* prevent more I/O from starting */ - mutex_lock(&dev->io_mutex); - dev->interface = NULL; - mutex_unlock(&dev->io_mutex); - - usb_kill_anchored_urbs(&dev->submitted); - - /* decrement our usage count */ - kref_put(&dev->kref, skel_delete); -} - -static void skel_draw_down(struct usb_skel *dev) -{ - int time; - - time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); - if (!time) - usb_kill_anchored_urbs(&dev->submitted); - usb_kill_urb(dev->bulk_in_urb); -} - -static int skel_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct usb_skel *dev = usb_get_intfdata(intf); - - if (!dev) - return 0; - skel_draw_down(dev); - return 0; -} - -static int skel_resume(struct usb_interface *intf) -{ - return 0; -} - -static int skel_pre_reset(struct usb_interface *intf) -{ - struct usb_skel *dev = usb_get_intfdata(intf); - - mutex_lock(&dev->io_mutex); - skel_draw_down(dev); - - return 0; -} - -static int skel_post_reset(struct usb_interface *intf) -{ - struct usb_skel *dev = usb_get_intfdata(intf); - - /* we are sure no URBs are active - no locking needed */ - dev->errors = -EPIPE; - mutex_unlock(&dev->io_mutex); - - return 0; -} - -static struct usb_driver skel_driver = { - .name = NAME, - .probe = skel_probe, - .disconnect = skel_disconnect, - .suspend = skel_suspend, - .resume = skel_resume, - .pre_reset = skel_pre_reset, - .post_reset = skel_post_reset, - .id_table = skel_table, - .supports_autosuspend = 1, -}; - -module_usb_driver(skel_driver); diff --git a/drivers/staging/gpib/nec7210/Makefile b/drivers/staging/gpib/nec7210/Makefile deleted file mode 100644 index 64330f2e89d1..000000000000 --- a/drivers/staging/gpib/nec7210/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - -obj-$(CONFIG_GPIB_NEC7210) += nec7210.o - - diff --git a/drivers/staging/gpib/nec7210/board.h b/drivers/staging/gpib/nec7210/board.h deleted file mode 100644 index ac3fe38ade57..000000000000 --- a/drivers/staging/gpib/nec7210/board.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2001, 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _GPIB_PCIIA_BOARD_H -#define _GPIB_PCIIA_BOARD_H - -#include "gpibP.h" -#include -#include -#include -#include - -#include "nec7210.h" - -#endif //_GPIB_PCIIA_BOARD_H - diff --git a/drivers/staging/gpib/nec7210/nec7210.c b/drivers/staging/gpib/nec7210/nec7210.c deleted file mode 100644 index bbf39367f5e4..000000000000 --- a/drivers/staging/gpib/nec7210/nec7210.c +++ /dev/null @@ -1,1121 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2001, 2002 by Frank Mori Hess - ***************************************************************************/ - -#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "board.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB library code for NEC uPD7210"); - -int nec7210_enable_eos(struct gpib_board *board, struct nec7210_priv *priv, u8 eos_byte, - int compare_8_bits) -{ - write_byte(priv, eos_byte, EOSR); - priv->auxa_bits |= HR_REOS; - if (compare_8_bits) - priv->auxa_bits |= HR_BIN; - else - priv->auxa_bits &= ~HR_BIN; - write_byte(priv, priv->auxa_bits, AUXMR); - return 0; -} -EXPORT_SYMBOL(nec7210_enable_eos); - -void nec7210_disable_eos(struct gpib_board *board, struct nec7210_priv *priv) -{ - priv->auxa_bits &= ~HR_REOS; - write_byte(priv, priv->auxa_bits, AUXMR); -} -EXPORT_SYMBOL(nec7210_disable_eos); - -int nec7210_parallel_poll(struct gpib_board *board, struct nec7210_priv *priv, u8 *result) -{ - int ret; - - clear_bit(COMMAND_READY_BN, &priv->state); - - // execute parallel poll - write_byte(priv, AUX_EPP, AUXMR); - // wait for result FIXME: support timeouts - ret = wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state)); - if (ret) { - dev_dbg(board->gpib_dev, "gpib: parallel poll interrupted\n"); - return -ERESTARTSYS; - } - *result = read_byte(priv, CPTR); - - return 0; -} -EXPORT_SYMBOL(nec7210_parallel_poll); - -void nec7210_parallel_poll_configure(struct gpib_board *board, - struct nec7210_priv *priv, unsigned int configuration) -{ - write_byte(priv, PPR | configuration, AUXMR); -} -EXPORT_SYMBOL(nec7210_parallel_poll_configure); - -void nec7210_parallel_poll_response(struct gpib_board *board, struct nec7210_priv *priv, int ist) -{ - if (ist) - write_byte(priv, AUX_SPPF, AUXMR); - else - write_byte(priv, AUX_CPPF, AUXMR); -} -EXPORT_SYMBOL(nec7210_parallel_poll_response); -/* - * This is really only adequate for chips that do a 488.2 style reqt/reqf - * based on bit 6 of the SPMR (see chapter 11.3.3 of 488.2). For simpler chips that simply - * set rsv directly based on bit 6, we either need to do more hardware setup to expose - * the 488.2 capability (for example with NI chips), or we need to implement the - * 488.2 set srv state machine in the driver (if that is even viable). - */ -void nec7210_serial_poll_response(struct gpib_board *board, - struct nec7210_priv *priv, u8 status) -{ - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - if (status & request_service_bit) { - priv->srq_pending = 1; - clear_bit(SPOLL_NUM, &board->status); - - } else { - priv->srq_pending = 0; - } - write_byte(priv, status, SPMR); - spin_unlock_irqrestore(&board->spinlock, flags); -} -EXPORT_SYMBOL(nec7210_serial_poll_response); - -u8 nec7210_serial_poll_status(struct gpib_board *board, struct nec7210_priv *priv) -{ - return read_byte(priv, SPSR); -} -EXPORT_SYMBOL(nec7210_serial_poll_status); - -int nec7210_primary_address(const struct gpib_board *board, struct nec7210_priv *priv, - unsigned int address) -{ - // put primary address in address0 - write_byte(priv, address & ADDRESS_MASK, ADR); - return 0; -} -EXPORT_SYMBOL(nec7210_primary_address); - -int nec7210_secondary_address(const struct gpib_board *board, struct nec7210_priv *priv, - unsigned int address, int enable) -{ - if (enable) { - // put secondary address in address1 - write_byte(priv, HR_ARS | (address & ADDRESS_MASK), ADR); - // go to address mode 2 - priv->reg_bits[ADMR] &= ~HR_ADM0; - priv->reg_bits[ADMR] |= HR_ADM1; - } else { - // disable address1 register - write_byte(priv, HR_ARS | HR_DT | HR_DL, ADR); - // go to address mode 1 - priv->reg_bits[ADMR] |= HR_ADM0; - priv->reg_bits[ADMR] &= ~HR_ADM1; - } - write_byte(priv, priv->reg_bits[ADMR], ADMR); - return 0; -} -EXPORT_SYMBOL(nec7210_secondary_address); - -static void update_talker_state(struct nec7210_priv *priv, unsigned int address_status_bits) -{ - if ((address_status_bits & HR_TA)) { - if ((address_status_bits & HR_NATN)) { - if (address_status_bits & HR_SPMS) - priv->talker_state = serial_poll_active; - else - priv->talker_state = talker_active; - } else { - priv->talker_state = talker_addressed; - } - } else { - priv->talker_state = talker_idle; - } -} - -static void update_listener_state(struct nec7210_priv *priv, unsigned int address_status_bits) -{ - if (address_status_bits & HR_LA) { - if ((address_status_bits & HR_NATN)) - priv->listener_state = listener_active; - else - priv->listener_state = listener_addressed; - } else { - priv->listener_state = listener_idle; - } -} - -unsigned int nec7210_update_status_nolock(struct gpib_board *board, struct nec7210_priv *priv) -{ - int address_status_bits; - u8 spoll_status; - - if (!priv) - return 0; - - address_status_bits = read_byte(priv, ADSR); - if (address_status_bits & HR_CIC) - set_bit(CIC_NUM, &board->status); - else - clear_bit(CIC_NUM, &board->status); - // check for talker/listener addressed - update_talker_state(priv, address_status_bits); - if (priv->talker_state == talker_active || priv->talker_state == talker_addressed) - set_bit(TACS_NUM, &board->status); - else - clear_bit(TACS_NUM, &board->status); - update_listener_state(priv, address_status_bits); - if (priv->listener_state == listener_active || - priv->listener_state == listener_addressed) - set_bit(LACS_NUM, &board->status); - else - clear_bit(LACS_NUM, &board->status); - if (address_status_bits & HR_NATN) - clear_bit(ATN_NUM, &board->status); - else - set_bit(ATN_NUM, &board->status); - spoll_status = nec7210_serial_poll_status(board, priv); - if (priv->srq_pending && (spoll_status & request_service_bit) == 0) { - priv->srq_pending = 0; - set_bit(SPOLL_NUM, &board->status); - } - - /* - * we rely on the interrupt handler to set the - * rest of the status bits - */ - - return board->status; -} -EXPORT_SYMBOL(nec7210_update_status_nolock); - -unsigned int nec7210_update_status(struct gpib_board *board, struct nec7210_priv *priv, - unsigned int clear_mask) -{ - unsigned long flags; - unsigned int retval; - - spin_lock_irqsave(&board->spinlock, flags); - board->status &= ~clear_mask; - retval = nec7210_update_status_nolock(board, priv); - spin_unlock_irqrestore(&board->spinlock, flags); - - return retval; -} -EXPORT_SYMBOL(nec7210_update_status); - -unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg, - unsigned int mask, unsigned int bits) -{ - priv->reg_bits[reg] &= ~mask; - priv->reg_bits[reg] |= mask & bits; - write_byte(priv, priv->reg_bits[reg], reg); - return priv->reg_bits[reg]; -} -EXPORT_SYMBOL(nec7210_set_reg_bits); - -void nec7210_set_handshake_mode(struct gpib_board *board, struct nec7210_priv *priv, int mode) -{ - unsigned long flags; - - mode &= HR_HANDSHAKE_MASK; - - spin_lock_irqsave(&board->spinlock, flags); - if ((priv->auxa_bits & HR_HANDSHAKE_MASK) != mode) { - priv->auxa_bits &= ~HR_HANDSHAKE_MASK; - priv->auxa_bits |= mode; - write_byte(priv, priv->auxa_bits, AUXMR); - } - spin_unlock_irqrestore(&board->spinlock, flags); -} -EXPORT_SYMBOL(nec7210_set_handshake_mode); - -u8 nec7210_read_data_in(struct gpib_board *board, struct nec7210_priv *priv, int *end) -{ - unsigned long flags; - u8 data; - - spin_lock_irqsave(&board->spinlock, flags); - data = read_byte(priv, DIR); - clear_bit(READ_READY_BN, &priv->state); - if (test_and_clear_bit(RECEIVED_END_BN, &priv->state)) - *end = 1; - else - *end = 0; - spin_unlock_irqrestore(&board->spinlock, flags); - - return data; -} -EXPORT_SYMBOL(nec7210_read_data_in); - -int nec7210_take_control(struct gpib_board *board, struct nec7210_priv *priv, int syncronous) -{ - int i; - const int timeout = 100; - int retval = 0; - unsigned int adsr_bits = 0; - - if (syncronous) - write_byte(priv, AUX_TCS, AUXMR); - else - write_byte(priv, AUX_TCA, AUXMR); - // busy wait until ATN is asserted - for (i = 0; i < timeout; i++) { - adsr_bits = read_byte(priv, ADSR); - if ((adsr_bits & HR_NATN) == 0) - break; - udelay(1); - } - if (i == timeout) - return -ETIMEDOUT; - - clear_bit(WRITE_READY_BN, &priv->state); - - return retval; -} -EXPORT_SYMBOL(nec7210_take_control); - -int nec7210_go_to_standby(struct gpib_board *board, struct nec7210_priv *priv) -{ - int i; - const int timeout = 1000; - unsigned int adsr_bits = 0; - int retval = 0; - - write_byte(priv, AUX_GTS, AUXMR); - // busy wait until ATN is released - for (i = 0; i < timeout; i++) { - adsr_bits = read_byte(priv, ADSR); - if (adsr_bits & HR_NATN) - break; - udelay(1); - } - // if busy wait has failed, try sleeping - if (i == timeout) { - for (i = 0; i < HZ; i++) { - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(1)) - return -ERESTARTSYS; - adsr_bits = read_byte(priv, ADSR); - if (adsr_bits & HR_NATN) - break; - } - if (i == HZ) - return -ETIMEDOUT; - } - - clear_bit(COMMAND_READY_BN, &priv->state); - return retval; -} -EXPORT_SYMBOL(nec7210_go_to_standby); - -int nec7210_request_system_control(struct gpib_board *board, struct nec7210_priv *priv, - int request_control) -{ - if (request_control == 0) { - write_byte(priv, AUX_CREN, AUXMR); - write_byte(priv, AUX_CIFC, AUXMR); - write_byte(priv, AUX_DSC, AUXMR); - } - return 0; -} -EXPORT_SYMBOL(nec7210_request_system_control); - -void nec7210_interface_clear(struct gpib_board *board, struct nec7210_priv *priv, int assert) -{ - if (assert) - write_byte(priv, AUX_SIFC, AUXMR); - else - write_byte(priv, AUX_CIFC, AUXMR); -} -EXPORT_SYMBOL(nec7210_interface_clear); - -void nec7210_remote_enable(struct gpib_board *board, struct nec7210_priv *priv, int enable) -{ - if (enable) - write_byte(priv, AUX_SREN, AUXMR); - else - write_byte(priv, AUX_CREN, AUXMR); -} -EXPORT_SYMBOL(nec7210_remote_enable); - -void nec7210_release_rfd_holdoff(struct gpib_board *board, struct nec7210_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - if (test_bit(RFD_HOLDOFF_BN, &priv->state) && - test_bit(READ_READY_BN, &priv->state) == 0) { - write_byte(priv, AUX_FH, AUXMR); - clear_bit(RFD_HOLDOFF_BN, &priv->state); - } - spin_unlock_irqrestore(&board->spinlock, flags); -} -EXPORT_SYMBOL(nec7210_release_rfd_holdoff); - -int nec7210_t1_delay(struct gpib_board *board, struct nec7210_priv *priv, - unsigned int nano_sec) -{ - unsigned int retval; - - if (nano_sec <= 500) { - priv->auxb_bits |= HR_TRI; - retval = 500; - } else { - priv->auxb_bits &= ~HR_TRI; - retval = 2000; - } - write_byte(priv, priv->auxb_bits, AUXMR); - - return retval; -} -EXPORT_SYMBOL(nec7210_t1_delay); - -void nec7210_return_to_local(const struct gpib_board *board, struct nec7210_priv *priv) -{ - write_byte(priv, AUX_RTL, AUXMR); -} -EXPORT_SYMBOL(nec7210_return_to_local); - -static inline short nec7210_atn_has_changed(struct gpib_board *board, struct nec7210_priv *priv) -{ - short address_status_bits = read_byte(priv, ADSR); - - if (address_status_bits & HR_NATN) { - if (test_bit(ATN_NUM, &board->status)) - return 1; - else - return 0; - } else { - if (test_bit(ATN_NUM, &board->status)) - return 0; - else - return 1; - } - return -1; -} - -int nec7210_command(struct gpib_board *board, struct nec7210_priv *priv, u8 - *buffer, size_t length, size_t *bytes_written) -{ - int retval = 0; - unsigned long flags; - - *bytes_written = 0; - - clear_bit(BUS_ERROR_BN, &priv->state); - - while (*bytes_written < length) { - if (wait_event_interruptible(board->wait, - test_bit(COMMAND_READY_BN, &priv->state) || - test_bit(BUS_ERROR_BN, &priv->state) || - test_bit(TIMO_NUM, &board->status))) { - dev_dbg(board->gpib_dev, "command wait interrupted\n"); - retval = -ERESTARTSYS; - break; - } - if (test_bit(TIMO_NUM, &board->status)) - break; - if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) - break; - spin_lock_irqsave(&board->spinlock, flags); - clear_bit(COMMAND_READY_BN, &priv->state); - write_byte(priv, buffer[*bytes_written], CDOR); - spin_unlock_irqrestore(&board->spinlock, flags); - - ++(*bytes_written); - - if (need_resched()) - schedule(); - } - // wait for last byte to get sent - if (wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state) || - test_bit(BUS_ERROR_BN, &priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - - if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) - retval = -EIO; - - return retval; -} -EXPORT_SYMBOL(nec7210_command); - -static int pio_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - ssize_t retval = 0; - - *bytes_read = 0; - *end = 0; - - while (*bytes_read < length) { - if (wait_event_interruptible(board->wait, - test_bit(READ_READY_BN, &priv->state) || - test_bit(DEV_CLEAR_BN, &priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - break; - } - if (test_bit(READ_READY_BN, &priv->state)) { - if (*bytes_read == 0) { - /* - * We set the handshake mode here because we know - * no new bytes will arrive (it has already arrived - * and is awaiting being read out of the chip) while we are changing - * modes. This ensures we can reliably keep track - * of the holdoff state. - */ - nec7210_set_handshake_mode(board, priv, HR_HLDA); - } - buffer[(*bytes_read)++] = nec7210_read_data_in(board, priv, end); - if (*end) - break; - } - if (test_bit(TIMO_NUM, &board->status)) { - retval = -ETIMEDOUT; - break; - } - if (test_bit(DEV_CLEAR_BN, &priv->state)) { - retval = -EINTR; - break; - } - - if (*bytes_read < length) - nec7210_release_rfd_holdoff(board, priv); - - if (need_resched()) - schedule(); - } - return retval; -} - -#ifdef NEC_DMA -static ssize_t __dma_read(struct gpib_board *board, struct nec7210_priv *priv, size_t length) -{ - ssize_t retval = 0; - size_t count = 0; - unsigned long flags, dma_irq_flags; - - if (length == 0) - return 0; - - spin_lock_irqsave(&board->spinlock, flags); - - dma_irq_flags = claim_dma_lock(); - disable_dma(priv->dma_channel); - /* program dma controller */ - clear_dma_ff(priv->dma_channel); - set_dma_count(priv->dma_channel, length); - set_dma_addr(priv->dma_channel, priv->dma_buffer_addr); - set_dma_mode(priv->dma_channel, DMA_MODE_READ); - release_dma_lock(dma_irq_flags); - - enable_dma(priv->dma_channel); - - set_bit(DMA_READ_IN_PROGRESS_BN, &priv->state); - clear_bit(READ_READY_BN, &priv->state); - - // enable nec7210 dma - nec7210_set_reg_bits(priv, IMR2, HR_DMAI, HR_DMAI); - - spin_unlock_irqrestore(&board->spinlock, flags); - - // wait for data to transfer - if (wait_event_interruptible(board->wait, - test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state) == 0 || - test_bit(DEV_CLEAR_BN, &priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &priv->state)) - retval = -EINTR; - - // disable nec7210 dma - nec7210_set_reg_bits(priv, IMR2, HR_DMAI, 0); - - // record how many bytes we transferred - flags = claim_dma_lock(); - clear_dma_ff(priv->dma_channel); - disable_dma(priv->dma_channel); - count += length - get_dma_residue(priv->dma_channel); - release_dma_lock(flags); - - return retval ? retval : count; -} - -static ssize_t dma_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length) -{ - size_t remain = length; - size_t transfer_size; - ssize_t retval = 0; - - while (remain > 0) { - transfer_size = (priv->dma_buffer_length < remain) ? - priv->dma_buffer_length : remain; - retval = __dma_read(board, priv, transfer_size); - if (retval < 0) - break; - memcpy(buffer, priv->dma_buffer, transfer_size); - remain -= retval; - buffer += retval; - if (test_bit(RECEIVED_END_BN, &priv->state)) - break; - } - - if (retval < 0) - return retval; - - return length - remain; -} -#endif - -int nec7210_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - ssize_t retval = 0; - - *end = 0; - *bytes_read = 0; - - if (length == 0) - return 0; - - clear_bit(DEV_CLEAR_BN, &priv->state); // XXX wrong - - nec7210_release_rfd_holdoff(board, priv); - - retval = pio_read(board, priv, buffer, length, end, bytes_read); - - return retval; -} -EXPORT_SYMBOL(nec7210_read); - -static int pio_write_wait(struct gpib_board *board, struct nec7210_priv *priv, - short wake_on_lacs, short wake_on_atn, short wake_on_bus_error) -{ - // wait until byte is ready to be sent - if (wait_event_interruptible(board->wait, - (test_bit(TACS_NUM, &board->status) && - test_bit(WRITE_READY_BN, &priv->state)) || - test_bit(DEV_CLEAR_BN, &priv->state) || - (wake_on_bus_error && test_bit(BUS_ERROR_BN, &priv->state)) || - (wake_on_lacs && test_bit(LACS_NUM, &board->status)) || - (wake_on_atn && test_bit(ATN_NUM, &board->status)) || - test_bit(TIMO_NUM, &board->status))) - return -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - - if (test_bit(DEV_CLEAR_BN, &priv->state)) - return -EINTR; - - if (wake_on_bus_error && test_and_clear_bit(BUS_ERROR_BN, &priv->state)) - return -EIO; - - return 0; -} - -static int pio_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length, size_t *bytes_written) -{ - size_t last_count = 0; - ssize_t retval = 0; - unsigned long flags; - const int max_bus_errors = (length > 1000) ? length : 1000; - int bus_error_count = 0; - *bytes_written = 0; - - clear_bit(BUS_ERROR_BN, &priv->state); - - while (*bytes_written < length) { - if (need_resched()) - schedule(); - - retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210); - if (retval == -EIO) { - /* resend last byte on bus error */ - *bytes_written = last_count; - /* - * we can get unrecoverable bus errors, - * so give up after a while - */ - bus_error_count++; - if (bus_error_count > max_bus_errors) - return retval; - continue; - } else { - if (retval < 0) - return retval; - } - spin_lock_irqsave(&board->spinlock, flags); - clear_bit(BUS_ERROR_BN, &priv->state); - clear_bit(WRITE_READY_BN, &priv->state); - last_count = *bytes_written; - write_byte(priv, buffer[(*bytes_written)++], CDOR); - spin_unlock_irqrestore(&board->spinlock, flags); - } - retval = pio_write_wait(board, priv, 1, 1, priv->type == NEC7210); - return retval; -} - -#ifdef NEC_DMA -static ssize_t __dma_write(struct gpib_board *board, struct nec7210_priv *priv, dma_addr_t address, - size_t length) -{ - unsigned long flags, dma_irq_flags; - int residue = 0; - int retval = 0; - - spin_lock_irqsave(&board->spinlock, flags); - - /* program dma controller */ - dma_irq_flags = claim_dma_lock(); - disable_dma(priv->dma_channel); - clear_dma_ff(priv->dma_channel); - set_dma_count(priv->dma_channel, length); - set_dma_addr(priv->dma_channel, address); - set_dma_mode(priv->dma_channel, DMA_MODE_WRITE); - enable_dma(priv->dma_channel); - release_dma_lock(dma_irq_flags); - - // enable board's dma for output - nec7210_set_reg_bits(priv, IMR2, HR_DMAO, HR_DMAO); - - clear_bit(WRITE_READY_BN, &priv->state); - set_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state); - - spin_unlock_irqrestore(&board->spinlock, flags); - - // suspend until message is sent - if (wait_event_interruptible(board->wait, - test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0 || - test_bit(BUS_ERROR_BN, &priv->state) || - test_bit(DEV_CLEAR_BN, &priv->state) || - test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_and_clear_bit(DEV_CLEAR_BN, &priv->state)) - retval = -EINTR; - if (test_and_clear_bit(BUS_ERROR_BN, &priv->state)) - retval = -EIO; - - // disable board's dma - nec7210_set_reg_bits(priv, IMR2, HR_DMAO, 0); - - dma_irq_flags = claim_dma_lock(); - clear_dma_ff(priv->dma_channel); - disable_dma(priv->dma_channel); - residue = get_dma_residue(priv->dma_channel); - release_dma_lock(dma_irq_flags); - - if (residue) - retval = -EPIPE; - - return retval ? retval : length; -} - -static ssize_t dma_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer, - size_t length) -{ - size_t remain = length; - size_t transfer_size; - ssize_t retval = 0; - - while (remain > 0) { - transfer_size = (priv->dma_buffer_length < remain) ? - priv->dma_buffer_length : remain; - memcpy(priv->dma_buffer, buffer, transfer_size); - retval = __dma_write(board, priv, priv->dma_buffer_addr, transfer_size); - if (retval < 0) - break; - remain -= retval; - buffer += retval; - } - - if (retval < 0) - return retval; - - return length - remain; -} -#endif -int nec7210_write(struct gpib_board *board, struct nec7210_priv *priv, - u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written) -{ - int retval = 0; - - *bytes_written = 0; - - clear_bit(DEV_CLEAR_BN, &priv->state); // XXX - - if (send_eoi) - length-- ; // save the last byte for sending EOI - - if (length > 0) { - // isa dma transfer - if (0 /*priv->dma_channel*/) { -/* - * dma writes are unreliable since they can't recover from bus errors - * (which happen when ATN is asserted in the middle of a write) - */ -#ifdef NEC_DMA - retval = dma_write(board, priv, buffer, length); - if (retval < 0) - return retval; - count += retval; -#endif - } else { // PIO transfer - size_t num_bytes; - - retval = pio_write(board, priv, buffer, length, &num_bytes); - - *bytes_written += num_bytes; - if (retval < 0) - return retval; - } - } - if (send_eoi) { - size_t num_bytes; - - /* - * We need to wait to make sure we will immediately be able to write the data byte - * into the chip before sending the associated AUX_SEOI command. This is really - * only needed for length==1 since otherwise the earlier calls to pio_write - * will have dont the wait already. - */ - retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210); - if (retval < 0) - return retval; - /*send EOI */ - write_byte(priv, AUX_SEOI, AUXMR); - - retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - } - - return retval; -} -EXPORT_SYMBOL(nec7210_write); - -/* - * interrupt service routine - */ -irqreturn_t nec7210_interrupt(struct gpib_board *board, struct nec7210_priv *priv) -{ - int status1, status2; - - // read interrupt status (also clears status) - status1 = read_byte(priv, ISR1); - status2 = read_byte(priv, ISR2); - - return nec7210_interrupt_have_status(board, priv, status1, status2); -} -EXPORT_SYMBOL(nec7210_interrupt); - -irqreturn_t nec7210_interrupt_have_status(struct gpib_board *board, - struct nec7210_priv *priv, int status1, int status2) -{ -#ifdef NEC_DMA - unsigned long dma_flags; -#endif - int retval = IRQ_NONE; - - // record service request in status - if (status2 & HR_SRQI) - set_bit(SRQI_NUM, &board->status); - - // change in lockout status - if (status2 & HR_LOKC) { - if (status2 & HR_LOK) - set_bit(LOK_NUM, &board->status); - else - clear_bit(LOK_NUM, &board->status); - } - - // change in remote status - if (status2 & HR_REMC) { - if (status2 & HR_REM) - set_bit(REM_NUM, &board->status); - else - clear_bit(REM_NUM, &board->status); - } - - // record reception of END - if (status1 & HR_END) { - set_bit(RECEIVED_END_BN, &priv->state); - if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE) - set_bit(RFD_HOLDOFF_BN, &priv->state); - } - - // get incoming data in PIO mode - if ((status1 & HR_DI)) { - set_bit(READ_READY_BN, &priv->state); - if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDA) - set_bit(RFD_HOLDOFF_BN, &priv->state); - } -#ifdef NEC_DMA - // check for dma read transfer complete - if (test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state)) { - dma_flags = claim_dma_lock(); - disable_dma(priv->dma_channel); - clear_dma_ff(priv->dma_channel); - if ((status1 & HR_END) || get_dma_residue(priv->dma_channel) == 0) - clear_bit(DMA_READ_IN_PROGRESS_BN, &priv->state); - else - enable_dma(priv->dma_channel); - release_dma_lock(dma_flags); - } -#endif - if ((status1 & HR_DO)) { - if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0) - set_bit(WRITE_READY_BN, &priv->state); -#ifdef NEC_DMA - if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state)) { // write data, isa dma mode - // check if dma transfer is complete - dma_flags = claim_dma_lock(); - disable_dma(priv->dma_channel); - clear_dma_ff(priv->dma_channel); - if (get_dma_residue(priv->dma_channel) == 0) { - clear_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state); - // XXX race? byte may still be in CDOR reg - } else { - clear_bit(WRITE_READY_BN, &priv->state); - enable_dma(priv->dma_channel); - } - release_dma_lock(dma_flags); - } -#endif - } - - // outgoing command can be sent - if (status2 & HR_CO) - set_bit(COMMAND_READY_BN, &priv->state); - - // command pass through received - if (status1 & HR_CPT) - write_byte(priv, AUX_NVAL, AUXMR); - - if (status1 & HR_ERR) - set_bit(BUS_ERROR_BN, &priv->state); - - if (status1 & HR_DEC) { - unsigned short address_status_bits = read_byte(priv, ADSR); - - // ignore device clear events if we are controller in charge - if ((address_status_bits & HR_CIC) == 0) { - push_gpib_event(board, EVENT_DEV_CLR); - set_bit(DEV_CLEAR_BN, &priv->state); - } - } - - if (status1 & HR_DET) - push_gpib_event(board, EVENT_DEV_TRG); - - // Addressing status has changed - if (status2 & HR_ADSC) - set_bit(ADR_CHANGE_BN, &priv->state); - - if ((status1 & priv->reg_bits[IMR1]) || - (status2 & (priv->reg_bits[IMR2] & IMR2_ENABLE_INTR_MASK)) || - nec7210_atn_has_changed(board, priv)) { - nec7210_update_status_nolock(board, priv); - dev_dbg(board->gpib_dev, "minor %i, stat %lx, isr1 0x%x, imr1 0x%x, isr2 0x%x, imr2 0x%x\n", - board->minor, board->status, status1, priv->reg_bits[IMR1], status2, - priv->reg_bits[IMR2]); - wake_up_interruptible(&board->wait); /* wake up sleeping process */ - retval = IRQ_HANDLED; - } - - return retval; -} -EXPORT_SYMBOL(nec7210_interrupt_have_status); - -void nec7210_board_reset(struct nec7210_priv *priv, const struct gpib_board *board) -{ - /* 7210 chip reset */ - write_byte(priv, AUX_CR, AUXMR); - - /* disable all interrupts */ - priv->reg_bits[IMR1] = 0; - write_byte(priv, priv->reg_bits[IMR1], IMR1); - priv->reg_bits[IMR2] = 0; - write_byte(priv, priv->reg_bits[IMR2], IMR2); - write_byte(priv, 0, SPMR); - - /* clear registers by reading */ - read_byte(priv, CPTR); - read_byte(priv, ISR1); - read_byte(priv, ISR2); - - /* parallel poll unconfigure */ - write_byte(priv, PPR | HR_PPU, AUXMR); - - priv->reg_bits[ADMR] = HR_TRM0 | HR_TRM1; - - priv->auxa_bits = AUXRA | HR_HLDA; - write_byte(priv, priv->auxa_bits, AUXMR); - - write_byte(priv, AUXRE | 0, AUXMR); - - /* set INT pin to active high, enable command pass through of unknown commands */ - priv->auxb_bits = AUXRB | HR_CPTE; - write_byte(priv, priv->auxb_bits, AUXMR); - write_byte(priv, AUXRE, AUXMR); -} -EXPORT_SYMBOL(nec7210_board_reset); - -void nec7210_board_online(struct nec7210_priv *priv, const struct gpib_board *board) -{ - /* set GPIB address */ - nec7210_primary_address(board, priv, board->pad); - nec7210_secondary_address(board, priv, board->sad, board->sad >= 0); - - /* enable interrupts */ - priv->reg_bits[IMR1] = HR_ERRIE | HR_DECIE | HR_ENDIE | - HR_DETIE | HR_CPTIE | HR_DOIE | HR_DIIE; - priv->reg_bits[IMR2] = IMR2_ENABLE_INTR_MASK; - write_byte(priv, priv->reg_bits[IMR1], IMR1); - write_byte(priv, priv->reg_bits[IMR2], IMR2); - - write_byte(priv, AUX_PON, AUXMR); -} -EXPORT_SYMBOL(nec7210_board_online); - -#ifdef CONFIG_HAS_IOPORT -/* wrappers for io */ -u8 nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num) -{ - return inb(priv->iobase + register_num * priv->offset); -} -EXPORT_SYMBOL(nec7210_ioport_read_byte); - -void nec7210_ioport_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num) -{ - if (register_num == AUXMR) - /* - * locking makes absolutely sure noone accesses the - * AUXMR register faster than once per microsecond - */ - nec7210_locking_ioport_write_byte(priv, data, register_num); - else - outb(data, priv->iobase + register_num * priv->offset); -} -EXPORT_SYMBOL(nec7210_ioport_write_byte); - -/* locking variants of io wrappers, for chips that page-in registers */ -u8 nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num) -{ - u8 retval; - unsigned long flags; - - spin_lock_irqsave(&priv->register_page_lock, flags); - retval = inb(priv->iobase + register_num * priv->offset); - spin_unlock_irqrestore(&priv->register_page_lock, flags); - return retval; -} -EXPORT_SYMBOL(nec7210_locking_ioport_read_byte); - -void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, u8 data, - unsigned int register_num) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->register_page_lock, flags); - if (register_num == AUXMR) - udelay(1); - outb(data, priv->iobase + register_num * priv->offset); - spin_unlock_irqrestore(&priv->register_page_lock, flags); -} -EXPORT_SYMBOL(nec7210_locking_ioport_write_byte); -#endif - -u8 nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num) -{ - return readb(priv->mmiobase + register_num * priv->offset); -} -EXPORT_SYMBOL(nec7210_iomem_read_byte); - -void nec7210_iomem_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num) -{ - if (register_num == AUXMR) - /* - * locking makes absolutely sure noone accesses the - * AUXMR register faster than once per microsecond - */ - nec7210_locking_iomem_write_byte(priv, data, register_num); - else - writeb(data, priv->mmiobase + register_num * priv->offset); -} -EXPORT_SYMBOL(nec7210_iomem_write_byte); - -u8 nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num) -{ - u8 retval; - unsigned long flags; - - spin_lock_irqsave(&priv->register_page_lock, flags); - retval = readb(priv->mmiobase + register_num * priv->offset); - spin_unlock_irqrestore(&priv->register_page_lock, flags); - return retval; -} -EXPORT_SYMBOL(nec7210_locking_iomem_read_byte); - -void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data, - unsigned int register_num) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->register_page_lock, flags); - if (register_num == AUXMR) - udelay(1); - writeb(data, priv->mmiobase + register_num * priv->offset); - spin_unlock_irqrestore(&priv->register_page_lock, flags); -} -EXPORT_SYMBOL(nec7210_locking_iomem_write_byte); - -static int __init nec7210_init_module(void) -{ - return 0; -} - -static void __exit nec7210_exit_module(void) -{ -} - -module_init(nec7210_init_module); -module_exit(nec7210_exit_module); diff --git a/drivers/staging/gpib/ni_usb/Makefile b/drivers/staging/gpib/ni_usb/Makefile deleted file mode 100644 index 469c5d16add3..000000000000 --- a/drivers/staging/gpib/ni_usb/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - -obj-$(CONFIG_GPIB_NI_USB) += ni_usb_gpib.o - - diff --git a/drivers/staging/gpib/ni_usb/ni_usb_gpib.c b/drivers/staging/gpib/ni_usb/ni_usb_gpib.c deleted file mode 100644 index 1f8412de9fa3..000000000000 --- a/drivers/staging/gpib/ni_usb/ni_usb_gpib.c +++ /dev/null @@ -1,2678 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * driver for National Instruments usb to gpib adapters - * copyright : (C) 2004 by Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include -#include -#include -#include "ni_usb_gpib.h" -#include "gpibP.h" -#include "nec7210.h" -#include "tnt4882_registers.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for National Instruments USB devices"); - -#define MAX_NUM_NI_USB_INTERFACES 128 -static struct usb_interface *ni_usb_driver_interfaces[MAX_NUM_NI_USB_INTERFACES]; - -static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status); -static int ni_usb_set_interrupt_monitor(struct gpib_board *board, unsigned int monitored_bits); -static void ni_usb_stop(struct ni_usb_priv *ni_priv); - -static DEFINE_MUTEX(ni_usb_hotplug_lock); - -// calculates a reasonable timeout in that can be passed to usb functions -static inline unsigned long ni_usb_timeout_msecs(unsigned int usec) -{ - if (usec == 0) - return 0; - return 2000 + usec / 500; -}; - -// returns timeout code byte for use in ni-usb-b instructions -static unsigned short ni_usb_timeout_code(unsigned int usec) -{ - if (usec == 0) - return 0xf0; - else if (usec <= 10) - return 0xf1; - else if (usec <= 30) - return 0xf2; - else if (usec <= 100) - return 0xf3; - else if (usec <= 300) - return 0xf4; - else if (usec <= 1000) - return 0xf5; - else if (usec <= 3000) - return 0xf6; - else if (usec <= 10000) - return 0xf7; - else if (usec <= 30000) - return 0xf8; - else if (usec <= 100000) - return 0xf9; - else if (usec <= 300000) - return 0xfa; - else if (usec <= 1000000) - return 0xfb; - else if (usec <= 3000000) - return 0xfc; - else if (usec <= 10000000) - return 0xfd; - else if (usec <= 30000000) - return 0xfe; - else if (usec <= 100000000) - return 0xff; - else if (usec <= 300000000) - return 0x01; - /* - * NI driver actually uses 0xff for timeout T1000s, which is a bug in their code. - * I've verified on a usb-b that a code of 0x2 is correct for a 1000 sec timeout - */ - else if (usec <= 1000000000) - return 0x02; - pr_err("bug? usec is greater than 1e9\n"); - return 0xf0; -} - -static void ni_usb_bulk_complete(struct urb *urb) -{ - struct ni_usb_urb_ctx *context = urb->context; - - complete(&context->complete); -} - -static void ni_usb_timeout_handler(struct timer_list *t) -{ - struct ni_usb_priv *ni_priv = timer_container_of(ni_priv, t, - bulk_timer); - struct ni_usb_urb_ctx *context = &ni_priv->context; - - context->timed_out = 1; - complete(&context->complete); -}; - -// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases -static int ni_usb_nonblocking_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data, - int data_length, int *actual_data_length, - int timeout_msecs) -{ - struct usb_device *usb_dev; - int retval; - unsigned int out_pipe; - struct ni_usb_urb_ctx *context = &ni_priv->context; - - *actual_data_length = 0; - mutex_lock(&ni_priv->bulk_transfer_lock); - if (!ni_priv->bus_interface) { - mutex_unlock(&ni_priv->bulk_transfer_lock); - return -ENODEV; - } - if (ni_priv->bulk_urb) { - mutex_unlock(&ni_priv->bulk_transfer_lock); - return -EAGAIN; - } - ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ni_priv->bulk_urb) { - mutex_unlock(&ni_priv->bulk_transfer_lock); - return -ENOMEM; - } - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_pipe = usb_sndbulkpipe(usb_dev, ni_priv->bulk_out_endpoint); - init_completion(&context->complete); - context->timed_out = 0; - usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, out_pipe, data, data_length, - &ni_usb_bulk_complete, context); - - if (timeout_msecs) - mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); - - retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL); - if (retval) { - timer_delete_sync(&ni_priv->bulk_timer); - usb_free_urb(ni_priv->bulk_urb); - ni_priv->bulk_urb = NULL; - dev_err(&usb_dev->dev, "failed to submit bulk out urb, retval=%i\n", - retval); - mutex_unlock(&ni_priv->bulk_transfer_lock); - return retval; - } - mutex_unlock(&ni_priv->bulk_transfer_lock); - wait_for_completion(&context->complete); // wait for ni_usb_bulk_complete - if (context->timed_out) { - usb_kill_urb(ni_priv->bulk_urb); - dev_err(&usb_dev->dev, "killed urb due to timeout\n"); - retval = -ETIMEDOUT; - } else { - retval = ni_priv->bulk_urb->status; - } - - timer_delete_sync(&ni_priv->bulk_timer); - *actual_data_length = ni_priv->bulk_urb->actual_length; - mutex_lock(&ni_priv->bulk_transfer_lock); - usb_free_urb(ni_priv->bulk_urb); - ni_priv->bulk_urb = NULL; - mutex_unlock(&ni_priv->bulk_transfer_lock); - return retval; -} - -static int ni_usb_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data, int data_length, - int *actual_data_length, int timeout_msecs) -{ - int retval; - int timeout_msecs_remaining = timeout_msecs; - - retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length, actual_data_length, - timeout_msecs_remaining); - while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) { - usleep_range(1000, 1500); - retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length, - actual_data_length, - timeout_msecs_remaining); - if (timeout_msecs != 0) - --timeout_msecs_remaining; - } - if (timeout_msecs != 0 && timeout_msecs_remaining <= 0) - return -ETIMEDOUT; - return retval; -} - -// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases -static int ni_usb_nonblocking_receive_bulk_msg(struct ni_usb_priv *ni_priv, - void *data, int data_length, - int *actual_data_length, int timeout_msecs, - int interruptible) -{ - struct usb_device *usb_dev; - int retval; - unsigned int in_pipe; - struct ni_usb_urb_ctx *context = &ni_priv->context; - - *actual_data_length = 0; - mutex_lock(&ni_priv->bulk_transfer_lock); - if (!ni_priv->bus_interface) { - mutex_unlock(&ni_priv->bulk_transfer_lock); - return -ENODEV; - } - if (ni_priv->bulk_urb) { - mutex_unlock(&ni_priv->bulk_transfer_lock); - return -EAGAIN; - } - ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ni_priv->bulk_urb) { - mutex_unlock(&ni_priv->bulk_transfer_lock); - return -ENOMEM; - } - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - in_pipe = usb_rcvbulkpipe(usb_dev, ni_priv->bulk_in_endpoint); - init_completion(&context->complete); - context->timed_out = 0; - usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, in_pipe, data, data_length, - &ni_usb_bulk_complete, context); - - if (timeout_msecs) - mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs)); - - retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL); - if (retval) { - timer_delete_sync(&ni_priv->bulk_timer); - usb_free_urb(ni_priv->bulk_urb); - ni_priv->bulk_urb = NULL; - dev_err(&usb_dev->dev, "failed to submit bulk in urb, retval=%i\n", retval); - mutex_unlock(&ni_priv->bulk_transfer_lock); - return retval; - } - mutex_unlock(&ni_priv->bulk_transfer_lock); - if (interruptible) { - if (wait_for_completion_interruptible(&context->complete)) { - /* - * If we got interrupted by a signal while - * waiting for the usb gpib to respond, we - * should send a stop command so it will - * finish up with whatever it was doing and - * send its response now. - */ - ni_usb_stop(ni_priv); - retval = -ERESTARTSYS; - /* - * now do an uninterruptible wait, it shouldn't take long - * for the board to respond now. - */ - wait_for_completion(&context->complete); - } - } else { - wait_for_completion(&context->complete); - } - if (context->timed_out) { - usb_kill_urb(ni_priv->bulk_urb); - dev_err(&usb_dev->dev, "killed urb due to timeout\n"); - retval = -ETIMEDOUT; - } else { - if (ni_priv->bulk_urb->status) - retval = ni_priv->bulk_urb->status; - } - timer_delete_sync(&ni_priv->bulk_timer); - *actual_data_length = ni_priv->bulk_urb->actual_length; - mutex_lock(&ni_priv->bulk_transfer_lock); - usb_free_urb(ni_priv->bulk_urb); - ni_priv->bulk_urb = NULL; - mutex_unlock(&ni_priv->bulk_transfer_lock); - return retval; -} - -static int ni_usb_receive_bulk_msg(struct ni_usb_priv *ni_priv, void *data, - int data_length, int *actual_data_length, int timeout_msecs, - int interruptible) -{ - int retval; - int timeout_msecs_remaining = timeout_msecs; - - retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length, - actual_data_length, timeout_msecs_remaining, - interruptible); - while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) { - usleep_range(1000, 1500); - retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length, - actual_data_length, - timeout_msecs_remaining, - interruptible); - if (timeout_msecs != 0) - --timeout_msecs_remaining; - } - if (timeout_msecs && timeout_msecs_remaining <= 0) - return -ETIMEDOUT; - return retval; -} - -static int ni_usb_receive_control_msg(struct ni_usb_priv *ni_priv, __u8 request, - __u8 requesttype, __u16 value, __u16 index, - void *data, __u16 size, int timeout_msecs) -{ - struct usb_device *usb_dev; - int retval; - unsigned int in_pipe; - - mutex_lock(&ni_priv->control_transfer_lock); - if (!ni_priv->bus_interface) { - mutex_unlock(&ni_priv->control_transfer_lock); - return -ENODEV; - } - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - in_pipe = usb_rcvctrlpipe(usb_dev, 0); - retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data, - size, timeout_msecs); - mutex_unlock(&ni_priv->control_transfer_lock); - return retval; -} - -static void ni_usb_soft_update_status(struct gpib_board *board, unsigned int ni_usb_ibsta, - unsigned int clear_mask) -{ - static const unsigned int ni_usb_ibsta_mask = SRQI | ATN | CIC | REM | LACS | TACS | LOK; - - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - unsigned int need_monitoring_bits = ni_usb_ibsta_monitor_mask; - unsigned long flags; - - board->status &= ~clear_mask; - board->status &= ~ni_usb_ibsta_mask; - board->status |= ni_usb_ibsta & ni_usb_ibsta_mask; - if (ni_usb_ibsta & DCAS) - push_gpib_event(board, EVENT_DEV_CLR); - if (ni_usb_ibsta & DTAS) - push_gpib_event(board, EVENT_DEV_TRG); - - spin_lock_irqsave(&board->spinlock, flags); -/* remove set status bits from monitored set why ?***/ - ni_priv->monitored_ibsta_bits &= ~ni_usb_ibsta; - need_monitoring_bits &= ~ni_priv->monitored_ibsta_bits; /* mm - monitored set */ - spin_unlock_irqrestore(&board->spinlock, flags); - dev_dbg(&usb_dev->dev, "need_monitoring_bits=0x%x\n", need_monitoring_bits); - - if (need_monitoring_bits & ~ni_usb_ibsta) - ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); - else if (need_monitoring_bits & ni_usb_ibsta) - wake_up_interruptible(&board->wait); - - dev_dbg(&usb_dev->dev, "ibsta=0x%x\n", ni_usb_ibsta); -} - -static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status) -{ - u16 count; - - status->id = buffer[0]; - status->ibsta = (buffer[1] << 8) | buffer[2]; - status->error_code = buffer[3]; - count = buffer[4] | (buffer[5] << 8); - count = ~count; - count++; - status->count = count; - return 8; -}; - -static void ni_usb_dump_raw_block(const u8 *raw_data, int length) -{ - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true); -} - -static int ni_usb_parse_register_read_block(const u8 *raw_data, unsigned int *results, - int num_results) -{ - int i = 0; - int j; - int unexpected = 0; - static const int results_per_chunk = 3; - - for (j = 0; j < num_results;) { - int k; - - if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_START_ID) { - pr_err("parse error: wrong start id\n"); - unexpected = 1; - } - for (k = 0; k < results_per_chunk && j < num_results; ++k) - results[j++] = raw_data[i++]; - } - while (i % 4) - i++; - if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_END_ID) { - pr_err("parse error: wrong end id\n"); - unexpected = 1; - } - if (raw_data[i++] % results_per_chunk != num_results % results_per_chunk) { - pr_err("parse error: wrong count=%i for NIUSB_REGISTER_READ_DATA_END\n", - (int)raw_data[i - 1]); - unexpected = 1; - } - while (i % 4) { - if (raw_data[i++] != 0) { - pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", - i - 1, (int)raw_data[i - 1]); - unexpected = 1; - } - } - if (unexpected) - ni_usb_dump_raw_block(raw_data, i); - return i; -} - -static int ni_usb_parse_termination_block(const u8 *buffer) -{ - int i = 0; - - if (buffer[i++] != NIUSB_TERM_ID || - buffer[i++] != 0x0 || - buffer[i++] != 0x0 || - buffer[i++] != 0x0) { - pr_err("received unexpected termination block\n"); - pr_err(" expected: 0x%x 0x%x 0x%x 0x%x\n", NIUSB_TERM_ID, 0x0, 0x0, 0x0); - pr_err(" received: 0x%x 0x%x 0x%x 0x%x\n", - buffer[i - 4], buffer[i - 3], buffer[i - 2], buffer[i - 1]); - } - return i; -}; - -static int parse_board_ibrd_readback(const u8 *raw_data, struct ni_usb_status_block *status, - u8 *parsed_data, int parsed_data_length, - int *actual_bytes_read) -{ - static const int ibrd_data_block_length = 0xf; - static const int ibrd_extended_data_block_length = 0x1e; - int data_block_length = 0; - int i = 0; - int j = 0; - int k; - int num_data_blocks = 0; - struct ni_usb_status_block register_write_status; - int unexpected = 0; - - while (raw_data[i] == NIUSB_IBRD_DATA_ID || raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) { - if (raw_data[i] == NIUSB_IBRD_DATA_ID) { - data_block_length = ibrd_data_block_length; - } else if (raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) { - data_block_length = ibrd_extended_data_block_length; - if (raw_data[++i] != 0) { - pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", - i, (int)raw_data[i]); - unexpected = 1; - } - } else { - pr_err("Unexpected NIUSB_IBRD ID\n"); - return -EINVAL; - } - ++i; - for (k = 0; k < data_block_length; k++) { - if (j < parsed_data_length) - parsed_data[j++] = raw_data[i++]; - else - ++i; - } - ++num_data_blocks; - } - i += ni_usb_parse_status_block(&raw_data[i], status); - if (status->id != NIUSB_IBRD_STATUS_ID) { - pr_err("bug: status->id=%i, != ibrd_status_id\n", status->id); - return -EIO; - } - i++; - if (num_data_blocks) { - *actual_bytes_read = (num_data_blocks - 1) * data_block_length + raw_data[i++]; - } else { - ++i; - *actual_bytes_read = 0; - } - if (*actual_bytes_read > j) - pr_err("bug: discarded data. actual_bytes_read=%i, j=%i\n", *actual_bytes_read, j); - for (k = 0; k < 2; k++) - if (raw_data[i++] != 0) { - pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", - i - 1, (int)raw_data[i - 1]); - unexpected = 1; - } - i += ni_usb_parse_status_block(&raw_data[i], ®ister_write_status); - if (register_write_status.id != NIUSB_REG_WRITE_ID) { - pr_err("unexpected data: register write status id=0x%x, expected 0x%x\n", - register_write_status.id, NIUSB_REG_WRITE_ID); - unexpected = 1; - } - if (raw_data[i++] != 2) { - pr_err("unexpected data: register write count=%i, expected 2\n", - (int)raw_data[i - 1]); - unexpected = 1; - } - for (k = 0; k < 3; k++) - if (raw_data[i++] != 0) { - pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n", - i - 1, (int)raw_data[i - 1]); - unexpected = 1; - } - i += ni_usb_parse_termination_block(&raw_data[i]); - if (unexpected) - ni_usb_dump_raw_block(raw_data, i); - return i; -} - -static int ni_usb_parse_reg_write_status_block(const u8 *raw_data, - struct ni_usb_status_block *status, - int *writes_completed) -{ - int i = 0; - - i += ni_usb_parse_status_block(raw_data, status); - *writes_completed = raw_data[i++]; - while (i % 4) - i++; - return i; -} - -static int ni_usb_write_registers(struct ni_usb_priv *ni_priv, - const struct ni_usb_register *writes, int num_writes, - unsigned int *ibsta) -{ - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - int retval; - u8 *out_data, *in_data; - int out_data_length; - static const int in_data_length = 0x20; - int bytes_written = 0, bytes_read = 0; - int i = 0; - int j; - struct ni_usb_status_block status; - static const int bytes_per_write = 3; - int reg_writes_completed; - - out_data_length = num_writes * bytes_per_write + 0x10; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - i += ni_usb_bulk_register_write_header(&out_data[i], num_writes); - for (j = 0; j < num_writes; j++) - i += ni_usb_bulk_register_write(&out_data[i], writes[j]); - while (i % 4) - out_data[i++] = 0x00; - i += ni_usb_bulk_termination(&out_data[i]); - - mutex_lock(&ni_priv->addressed_transfer_lock); - - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); - kfree(out_data); - if (retval) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - return retval; - } - - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - return -ENOMEM; - } - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); - if (retval || bytes_read != 16) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - ni_usb_dump_raw_block(in_data, bytes_read); - kfree(in_data); - return retval; - } - - mutex_unlock(&ni_priv->addressed_transfer_lock); - - ni_usb_parse_reg_write_status_block(in_data, &status, ®_writes_completed); - // FIXME parse extra 09 status bits and termination - kfree(in_data); - if (status.id != NIUSB_REG_WRITE_ID) { - dev_err(&usb_dev->dev, "parse error, id=0x%x != NIUSB_REG_WRITE_ID\n", status.id); - return -EIO; - } - if (status.error_code) { - dev_err(&usb_dev->dev, "nonzero error code 0x%x\n", status.error_code); - return -EIO; - } - if (reg_writes_completed != num_writes) { - dev_err(&usb_dev->dev, "reg_writes_completed=%i, num_writes=%i\n", - reg_writes_completed, num_writes); - return -EIO; - } - if (ibsta) - *ibsta = status.ibsta; - return 0; -} - -// interface functions -static int ni_usb_read(struct gpib_board *board, u8 *buffer, size_t length, - int *end, size_t *bytes_read) -{ - int retval, parse_retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - static const int out_data_length = 0x20; - int in_data_length; - int usb_bytes_written = 0, usb_bytes_read = 0; - int i = 0; - int complement_count; - int actual_length; - struct ni_usb_status_block status; - static const int max_read_length = 0xffff; - struct ni_usb_register reg; - - *bytes_read = 0; - if (!ni_priv->bus_interface) - return -ENODEV; - if (length > max_read_length) - return -EINVAL; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - out_data[i++] = 0x0a; - out_data[i++] = ni_priv->eos_mode >> 8; - out_data[i++] = ni_priv->eos_char; - out_data[i++] = ni_usb_timeout_code(board->usec_timeout); - complement_count = length - 1; - complement_count = ~complement_count; - out_data[i++] = complement_count & 0xff; - out_data[i++] = (complement_count >> 8) & 0xff; - out_data[i++] = 0x0; - out_data[i++] = 0x0; - i += ni_usb_bulk_register_write_header(&out_data[i], 2); - reg.device = NIUSB_SUBDEV_TNT4882; - reg.address = nec7210_to_tnt4882_offset(AUXMR); - reg.value = AUX_HLDI; - i += ni_usb_bulk_register_write(&out_data[i], reg); - reg.value = AUX_CLEAR_END; - i += ni_usb_bulk_register_write(&out_data[i], reg); - while (i % 4) // pad with zeros to 4-byte boundary - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - - mutex_lock(&ni_priv->addressed_transfer_lock); - - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written, 1000); - kfree(out_data); - if (retval || usb_bytes_written != i) { - if (retval == 0) - retval = -EIO; - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n", - retval, usb_bytes_written, i); - mutex_unlock(&ni_priv->addressed_transfer_lock); - return retval; - } - - in_data_length = (length / 30 + 1) * 0x20 + 0x20; - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - return -ENOMEM; - } - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read, - ni_usb_timeout_msecs(board->usec_timeout), 1); - - mutex_unlock(&ni_priv->addressed_transfer_lock); - - if (retval == -ERESTARTSYS) { - } else if (retval) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, usb_bytes_read=%i\n", - retval, usb_bytes_read); - kfree(in_data); - return retval; - } - parse_retval = parse_board_ibrd_readback(in_data, &status, buffer, length, &actual_length); - if (parse_retval != usb_bytes_read) { - if (parse_retval >= 0) - parse_retval = -EIO; - dev_err(&usb_dev->dev, "retval=%i usb_bytes_read=%i\n", - parse_retval, usb_bytes_read); - kfree(in_data); - return parse_retval; - } - if (actual_length != length - status.count) { - dev_err(&usb_dev->dev, "actual_length=%i expected=%li\n", - actual_length, (long)(length - status.count)); - ni_usb_dump_raw_block(in_data, usb_bytes_read); - } - kfree(in_data); - switch (status.error_code) { - case NIUSB_NO_ERROR: - retval = 0; - break; - case NIUSB_ABORTED_ERROR: - /* - * this is expected if ni_usb_receive_bulk_msg got - * interrupted by a signal and returned -ERESTARTSYS - */ - break; - case NIUSB_ATN_STATE_ERROR: - if (status.ibsta & DCAS) { - retval = -EINTR; - } else { - retval = -EIO; - dev_dbg(&usb_dev->dev, "read when ATN set stat: 0x%06x\n", status.ibsta); - } - break; - case NIUSB_ADDRESSING_ERROR: - retval = -EIO; - break; - case NIUSB_TIMEOUT_ERROR: - retval = -ETIMEDOUT; - break; - case NIUSB_EOSMODE_ERROR: - dev_err(&usb_dev->dev, "driver bug, we should have been able to avoid NIUSB_EOSMODE_ERROR.\n"); - retval = -EINVAL; - break; - default: - dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code); - retval = -EIO; - break; - } - ni_usb_soft_update_status(board, status.ibsta, 0); - if (status.ibsta & END) - *end = 1; - else - *end = 0; - *bytes_read = actual_length; - return retval; -} - -static int ni_usb_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, size_t *bytes_written) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - int out_data_length; - static const int in_data_length = 0x10; - int usb_bytes_written = 0, usb_bytes_read = 0; - int i = 0, j; - int complement_count; - struct ni_usb_status_block status; - static const int max_write_length = 0xffff; - - if (!ni_priv->bus_interface) - return -ENODEV; - if (length > max_write_length) - return -EINVAL; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_data_length = length + 0x10; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - out_data[i++] = 0x0d; - complement_count = length - 1; - complement_count = ~complement_count; - out_data[i++] = complement_count & 0xff; - out_data[i++] = (complement_count >> 8) & 0xff; - out_data[i++] = ni_usb_timeout_code(board->usec_timeout); - out_data[i++] = 0x0; - out_data[i++] = 0x0; - if (send_eoi) - out_data[i++] = 0x8; - else - out_data[i++] = 0x0; - out_data[i++] = 0x0; - for (j = 0; j < length; j++) - out_data[i++] = buffer[j]; - while (i % 4) // pad with zeros to 4-byte boundary - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - - mutex_lock(&ni_priv->addressed_transfer_lock); - - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written, - ni_usb_timeout_msecs(board->usec_timeout)); - kfree(out_data); - if (retval || usb_bytes_written != i) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n", - retval, usb_bytes_written, i); - return retval; - } - - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - return -ENOMEM; - } - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read, - ni_usb_timeout_msecs(board->usec_timeout), 1); - - mutex_unlock(&ni_priv->addressed_transfer_lock); - - if ((retval && retval != -ERESTARTSYS) || usb_bytes_read != 12) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, usb_bytes_read=%i\n", - retval, usb_bytes_read); - kfree(in_data); - return retval; - } - ni_usb_parse_status_block(in_data, &status); - kfree(in_data); - switch (status.error_code) { - case NIUSB_NO_ERROR: - retval = 0; - break; - case NIUSB_ABORTED_ERROR: - /* - * this is expected if ni_usb_receive_bulk_msg got - * interrupted by a signal and returned -ERESTARTSYS - */ - break; - case NIUSB_ADDRESSING_ERROR: - dev_err(&usb_dev->dev, "Addressing error retval %d error code=%i\n", - retval, status.error_code); - retval = -ENXIO; - break; - case NIUSB_NO_LISTENER_ERROR: - retval = -ECOMM; - break; - case NIUSB_TIMEOUT_ERROR: - retval = -ETIMEDOUT; - break; - default: - dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code); - retval = -EPIPE; - break; - } - ni_usb_soft_update_status(board, status.ibsta, 0); - *bytes_written = length - status.count; - return retval; -} - -static int ni_usb_command_chunk(struct gpib_board *board, u8 *buffer, size_t length, - size_t *command_bytes_written) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - int out_data_length; - static const int in_data_length = 0x10; - int bytes_written = 0, bytes_read = 0; - int i = 0, j; - unsigned int complement_count; - struct ni_usb_status_block status; - // usb-b gives error 4 if you try to send more than 16 command bytes at once - static const int max_command_length = 0x10; - - *command_bytes_written = 0; - if (!ni_priv->bus_interface) - return -ENODEV; - if (length > max_command_length) - length = max_command_length; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_data_length = length + 0x10; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - out_data[i++] = 0x0c; - complement_count = length - 1; - complement_count = ~complement_count; - out_data[i++] = complement_count; - out_data[i++] = 0x0; - out_data[i++] = ni_usb_timeout_code(board->usec_timeout); - for (j = 0; j < length; j++) - out_data[i++] = buffer[j]; - while (i % 4) // pad with zeros to 4-byte boundary - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - - mutex_lock(&ni_priv->addressed_transfer_lock); - - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, - ni_usb_timeout_msecs(board->usec_timeout)); - kfree(out_data); - if (retval || bytes_written != i) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - return retval; - } - - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - return -ENOMEM; - } - - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, - ni_usb_timeout_msecs(board->usec_timeout), 1); - - mutex_unlock(&ni_priv->addressed_transfer_lock); - - if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - kfree(in_data); - return retval; - } - ni_usb_parse_status_block(in_data, &status); - kfree(in_data); - *command_bytes_written = length - status.count; - switch (status.error_code) { - case NIUSB_NO_ERROR: - break; - case NIUSB_ABORTED_ERROR: - /* - * this is expected if ni_usb_receive_bulk_msg got - * interrupted by a signal and returned -ERESTARTSYS - */ - break; - case NIUSB_NO_BUS_ERROR: - return -ENOTCONN; - case NIUSB_EOSMODE_ERROR: - dev_err(&usb_dev->dev, "got eosmode error. Driver bug?\n"); - return -EIO; - case NIUSB_TIMEOUT_ERROR: - return -ETIMEDOUT; - default: - dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code); - return -EIO; - } - ni_usb_soft_update_status(board, status.ibsta, 0); - return 0; -} - -static int ni_usb_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - size_t count; - int retval; - - *bytes_written = 0; - while (*bytes_written < length) { - retval = ni_usb_command_chunk(board, buffer + *bytes_written, - length - *bytes_written, &count); - *bytes_written += count; - if (retval < 0) - return retval; - } - return 0; -} - -static int ni_usb_take_control(struct gpib_board *board, int synchronous) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - static const int out_data_length = 0x10; - static const int in_data_length = 0x10; - int bytes_written = 0, bytes_read = 0; - int i = 0; - struct ni_usb_status_block status; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - out_data[i++] = NIUSB_IBCAC_ID; - if (synchronous) - out_data[i++] = 0x1; - else - out_data[i++] = 0x0; - out_data[i++] = 0x0; - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - - mutex_lock(&ni_priv->addressed_transfer_lock); - - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); - kfree(out_data); - if (retval || bytes_written != i) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - return retval; - } - - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - return -ENOMEM; - } - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 1); - - mutex_unlock(&ni_priv->addressed_transfer_lock); - - if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) { - if (retval == 0) - retval = -EIO; - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - kfree(in_data); - return retval; - } - ni_usb_parse_status_block(in_data, &status); - kfree(in_data); - ni_usb_soft_update_status(board, status.ibsta, 0); - return retval; -} - -static int ni_usb_go_to_standby(struct gpib_board *board) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - static const int out_data_length = 0x10; - static const int in_data_length = 0x20; - int bytes_written = 0, bytes_read = 0; - int i = 0; - struct ni_usb_status_block status; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - - out_data[i++] = NIUSB_IBGTS_ID; - out_data[i++] = 0x0; - out_data[i++] = 0x0; - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - - mutex_lock(&ni_priv->addressed_transfer_lock); - - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); - kfree(out_data); - if (retval || bytes_written != i) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - return retval; - } - - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - return -ENOMEM; - } - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); - - mutex_unlock(&ni_priv->addressed_transfer_lock); - - if (retval || bytes_read != 12) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - kfree(in_data); - return retval; - } - ni_usb_parse_status_block(in_data, &status); - kfree(in_data); - if (status.id != NIUSB_IBGTS_ID) - dev_err(&usb_dev->dev, "bug: status.id 0x%x != INUSB_IBGTS_ID\n", status.id); - ni_usb_soft_update_status(board, status.ibsta, 0); - return 0; -} - -static int ni_usb_request_system_control(struct gpib_board *board, int request_control) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int i = 0; - struct ni_usb_register writes[4]; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - if (request_control) { - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = CMDR; - writes[i].value = SETSC; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_CIFC; - i++; - } else { - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_CREN; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_CIFC; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_DSC; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = CMDR; - writes[i].value = CLRSC; - i++; - } - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return retval; - } - if (!request_control) - ni_priv->ren_state = 0; - ni_usb_soft_update_status(board, ibsta, 0); - return 0; -} - -// FIXME maybe the interface should have a "pulse interface clear" function that can return an error? -static void ni_usb_interface_clear(struct gpib_board *board, int assert) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - static const int out_data_length = 0x10; - static const int in_data_length = 0x10; - int bytes_written = 0, bytes_read = 0; - int i = 0; - struct ni_usb_status_block status; - - if (!ni_priv->bus_interface) - return; // -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); -// FIXME: we are going to pulse when assert is true, and ignore otherwise - if (assert == 0) - return; - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return; - out_data[i++] = NIUSB_IBSIC_ID; - out_data[i++] = 0x0; - out_data[i++] = 0x0; - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); - kfree(out_data); - if (retval || bytes_written != i) { - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - return; - } - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) - return; - - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); - if (retval || bytes_read != 12) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - kfree(in_data); - return; - } - ni_usb_parse_status_block(in_data, &status); - kfree(in_data); - ni_usb_soft_update_status(board, status.ibsta, 0); -} - -static void ni_usb_remote_enable(struct gpib_board *board, int enable) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - struct ni_usb_register reg; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return; // -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - reg.device = NIUSB_SUBDEV_TNT4882; - reg.address = nec7210_to_tnt4882_offset(AUXMR); - if (enable) - reg.value = AUX_SREN; - else - reg.value = AUX_CREN; - retval = ni_usb_write_registers(ni_priv, ®, 1, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return; //retval; - } - ni_priv->ren_state = enable; - ni_usb_soft_update_status(board, ibsta, 0); - return;// 0; -} - -static int ni_usb_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct ni_usb_priv *ni_priv = board->private_data; - - ni_priv->eos_char = eos_byte; - ni_priv->eos_mode |= REOS; - if (compare_8_bits) - ni_priv->eos_mode |= BIN; - else - ni_priv->eos_mode &= ~BIN; - return 0; -} - -static void ni_usb_disable_eos(struct gpib_board *board) -{ - struct ni_usb_priv *ni_priv = board->private_data; - /* - * adapter gets unhappy if you don't zero all the bits - * for the eos mode and eos char (returns error 4 on reads). - */ - ni_priv->eos_mode = 0; - ni_priv->eos_char = 0; -} - -static unsigned int ni_usb_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - static const int buffer_length = 8; - u8 *buffer; - struct ni_usb_status_block status; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - buffer = kmalloc(buffer_length, GFP_KERNEL); - if (!buffer) - return board->status; - - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN | - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x200, 0x0, buffer, buffer_length, 1000); - if (retval != buffer_length) { - dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval); - kfree(buffer); - return board->status; - } - ni_usb_parse_status_block(buffer, &status); - kfree(buffer); - ni_usb_soft_update_status(board, status.ibsta, clear_mask); - return board->status; -} - -// tells ni-usb to immediately stop an ongoing i/o operation -static void ni_usb_stop(struct ni_usb_priv *ni_priv) -{ - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - int retval; - static const int buffer_length = 8; - u8 *buffer; - struct ni_usb_status_block status; - - buffer = kmalloc(buffer_length, GFP_KERNEL); - if (!buffer) - return; - - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_STOP_REQUEST, USB_DIR_IN | - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, 0x0, buffer, buffer_length, 1000); - if (retval != buffer_length) { - dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval); - kfree(buffer); - return; - } - ni_usb_parse_status_block(buffer, &status); - kfree(buffer); -} - -static int ni_usb_primary_address(struct gpib_board *board, unsigned int address) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int i = 0; - struct ni_usb_register writes[2]; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(ADR); - writes[i].value = address; - i++; - writes[i].device = NIUSB_SUBDEV_UNKNOWN2; - writes[i].address = 0x0; - writes[i].value = address; - i++; - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return retval; - } - ni_usb_soft_update_status(board, ibsta, 0); - return 0; -} - -static int ni_usb_write_sad(struct ni_usb_register *writes, int address, int enable) -{ - unsigned int adr_bits, admr_bits; - int i = 0; - - adr_bits = HR_ARS; - admr_bits = HR_TRM0 | HR_TRM1; - if (enable) { - adr_bits |= address; - admr_bits |= HR_ADM1; - } else { - adr_bits |= HR_DT | HR_DL; - admr_bits |= HR_ADM0; - } - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(ADR); - writes[i].value = adr_bits; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(ADMR); - writes[i].value = admr_bits; - i++; - writes[i].device = NIUSB_SUBDEV_UNKNOWN2; - writes[i].address = 0x1; - writes[i].value = enable ? MSA(address) : 0x0; - i++; - return i; -} - -static int ni_usb_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int i = 0; - struct ni_usb_register writes[3]; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - i += ni_usb_write_sad(writes, address, enable); - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return retval; - } - ni_usb_soft_update_status(board, ibsta, 0); - return 0; -} - -static int ni_usb_parallel_poll(struct gpib_board *board, u8 *result) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - static const int out_data_length = 0x10; - static const int in_data_length = 0x20; - int bytes_written = 0, bytes_read = 0; - int i = 0; - int j = 0; - struct ni_usb_status_block status; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - - out_data[i++] = NIUSB_IBRPP_ID; - out_data[i++] = 0xf0; // FIXME: this should be the parallel poll timeout code - out_data[i++] = 0x0; - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - /*FIXME: 1000 should use parallel poll timeout (not supported yet)*/ - retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); - - kfree(out_data); - if (retval || bytes_written != i) { - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - return retval; - } - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) - return -ENOMEM; - - /*FIXME: should use parallel poll timeout (not supported yet)*/ - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, - &bytes_read, 1000, 1); - - if (retval && retval != -ERESTARTSYS) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - kfree(in_data); - return retval; - } - j += ni_usb_parse_status_block(in_data, &status); - *result = in_data[j++]; - kfree(in_data); - ni_usb_soft_update_status(board, status.ibsta, 0); - return retval; -} - -static void ni_usb_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int i = 0; - struct ni_usb_register writes[1]; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return; // -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = PPR | config; - i++; - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return;// retval; - } - ni_usb_soft_update_status(board, ibsta, 0); - return;// 0; -} - -static void ni_usb_parallel_poll_response(struct gpib_board *board, int ist) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int i = 0; - struct ni_usb_register writes[1]; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return; // -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - if (ist) - writes[i].value = AUX_SPPF; - else - writes[i].value = AUX_CPPF; - i++; - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return;// retval; - } - ni_usb_soft_update_status(board, ibsta, 0); - return;// 0; -} - -static void ni_usb_serial_poll_response(struct gpib_board *board, u8 status) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int i = 0; - struct ni_usb_register writes[1]; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return; // -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(SPMR); - writes[i].value = status; - i++; - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return;// retval; - } - ni_usb_soft_update_status(board, ibsta, 0); - return;// 0; -} - -static u8 ni_usb_serial_poll_status(struct gpib_board *board) -{ - return 0; -} - -static void ni_usb_return_to_local(struct gpib_board *board) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int i = 0; - struct ni_usb_register writes[1]; - unsigned int ibsta; - - if (!ni_priv->bus_interface) - return; // -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_RTL; - i++; - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return;// retval; - } - ni_usb_soft_update_status(board, ibsta, 0); - return;// 0; -} - -static int ni_usb_line_status(const struct gpib_board *board) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - u8 *out_data, *in_data; - static const int out_data_length = 0x20; - static const int in_data_length = 0x20; - int bytes_written = 0, bytes_read = 0; - int i = 0; - unsigned int bsr_bits; - int line_status = VALID_ALL; - // NI windows driver reads 0xd(HSSEL), 0xc (ARD0), 0x1f (BSR) - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) - return -ENOMEM; - - /* line status gets called during ibwait */ - retval = mutex_trylock(&ni_priv->addressed_transfer_lock); - - if (retval == 0) { - kfree(out_data); - return -EBUSY; - } - i += ni_usb_bulk_register_read_header(&out_data[i], 1); - i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_TNT4882, BSR); - while (i % 4) - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000); - kfree(out_data); - if (retval || bytes_written != i) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - if (retval != -EAGAIN) - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n", - retval, bytes_written, i); - return retval; - } - - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) { - mutex_unlock(&ni_priv->addressed_transfer_lock); - return -ENOMEM; - } - retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, in_data, in_data_length, - &bytes_read, 1000, 0); - - mutex_unlock(&ni_priv->addressed_transfer_lock); - - if (retval) { - if (retval != -EAGAIN) - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - kfree(in_data); - return retval; - } - - ni_usb_parse_register_read_block(in_data, &bsr_bits, 1); - kfree(in_data); - if (bsr_bits & BCSR_REN_BIT) - line_status |= BUS_REN; - if (bsr_bits & BCSR_IFC_BIT) - line_status |= BUS_IFC; - if (bsr_bits & BCSR_SRQ_BIT) - line_status |= BUS_SRQ; - if (bsr_bits & BCSR_EOI_BIT) - line_status |= BUS_EOI; - if (bsr_bits & BCSR_NRFD_BIT) - line_status |= BUS_NRFD; - if (bsr_bits & BCSR_NDAC_BIT) - line_status |= BUS_NDAC; - if (bsr_bits & BCSR_DAV_BIT) - line_status |= BUS_DAV; - if (bsr_bits & BCSR_ATN_BIT) - line_status |= BUS_ATN; - return line_status; -} - -static int ni_usb_setup_t1_delay(struct ni_usb_register *reg, unsigned int nano_sec, - unsigned int *actual_ns) -{ - int i = 0; - - *actual_ns = 2000; - - reg[i].device = NIUSB_SUBDEV_TNT4882; - reg[i].address = nec7210_to_tnt4882_offset(AUXMR); - if (nano_sec <= 1100) { - reg[i].value = AUXRI | USTD | SISB; - *actual_ns = 1100; - } else { - reg[i].value = AUXRI | SISB; - } - i++; - reg[i].device = NIUSB_SUBDEV_TNT4882; - reg[i].address = nec7210_to_tnt4882_offset(AUXMR); - if (nano_sec <= 500) { - reg[i].value = AUXRB | HR_TRI; - *actual_ns = 500; - } else { - reg[i].value = AUXRB; - } - i++; - reg[i].device = NIUSB_SUBDEV_TNT4882; - reg[i].address = KEYREG; - if (nano_sec <= 350) { - reg[i].value = MSTD; - *actual_ns = 350; - } else { - reg[i].value = 0x0; - } - i++; - return i; -} - -static int ni_usb_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - struct ni_usb_register writes[3]; - unsigned int ibsta; - unsigned int actual_ns; - int i; - - if (!ni_priv->bus_interface) - return -ENODEV; - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - i = ni_usb_setup_t1_delay(writes, nano_sec, &actual_ns); - retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta); - if (retval < 0) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return retval; - } - board->t1_nano_sec = actual_ns; - ni_usb_soft_update_status(board, ibsta, 0); - return actual_ns; -} - -static int ni_usb_allocate_private(struct gpib_board *board) -{ - struct ni_usb_priv *ni_priv; - - board->private_data = kmalloc(sizeof(struct ni_usb_priv), GFP_KERNEL); - if (!board->private_data) - return -ENOMEM; - ni_priv = board->private_data; - memset(ni_priv, 0, sizeof(struct ni_usb_priv)); - mutex_init(&ni_priv->bulk_transfer_lock); - mutex_init(&ni_priv->control_transfer_lock); - mutex_init(&ni_priv->interrupt_transfer_lock); - mutex_init(&ni_priv->addressed_transfer_lock); - return 0; -} - -static void ni_usb_free_private(struct ni_usb_priv *ni_priv) -{ - usb_free_urb(ni_priv->interrupt_urb); - kfree(ni_priv); -} - -#define NUM_INIT_WRITES 26 -static int ni_usb_setup_init(struct gpib_board *board, struct ni_usb_register *writes) -{ - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - unsigned int mask, actual_ns; - int i = 0; - - writes[i].device = NIUSB_SUBDEV_UNKNOWN3; - writes[i].address = 0x10; - writes[i].value = 0x0; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = CMDR; - writes[i].value = SOFT_RESET; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - mask = AUXRA | HR_HLDA; - if (ni_priv->eos_mode & BIN) - mask |= HR_BIN; - writes[i].value = mask; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = AUXCR; - writes[i].value = mask; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = HSSEL; - writes[i].value = TNT_ONE_CHIP_BIT; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_CR; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = IMR0; - writes[i].value = TNT_IMR0_ALWAYS_BITS; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(IMR1); - writes[i].value = 0x0; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(IMR2); - writes[i].value = 0x0; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = IMR3; - writes[i].value = 0x0; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_HLDI; - i++; - - i += ni_usb_setup_t1_delay(&writes[i], board->t1_nano_sec, &actual_ns); - - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUXRG | NTNL_BIT; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = CMDR; - if (board->master) - mask = SETSC; // set system controller - else - mask = CLRSC; // clear system controller - writes[i].value = mask; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_CIFC; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(ADR); - writes[i].value = board->pad; - i++; - writes[i].device = NIUSB_SUBDEV_UNKNOWN2; - writes[i].address = 0x0; - writes[i].value = board->pad; - i++; - - i += ni_usb_write_sad(&writes[i], board->sad, board->sad >= 0); - - writes[i].device = NIUSB_SUBDEV_UNKNOWN2; - writes[i].address = 0x2; // could this be a timeout ? - writes[i].value = 0xfd; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = 0xf; // undocumented address - writes[i].value = 0x11; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_PON; - i++; - writes[i].device = NIUSB_SUBDEV_TNT4882; - writes[i].address = nec7210_to_tnt4882_offset(AUXMR); - writes[i].value = AUX_CPPF; - i++; - if (i > NUM_INIT_WRITES) { - dev_err(&usb_dev->dev, "bug!, buffer overrun, i=%i\n", i); - return 0; - } - return i; -} - -static int ni_usb_init(struct gpib_board *board) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - struct ni_usb_register *writes; - unsigned int ibsta; - int writes_len; - - writes = kmalloc_array(NUM_INIT_WRITES, sizeof(*writes), GFP_KERNEL); - if (!writes) - return -ENOMEM; - - writes_len = ni_usb_setup_init(board, writes); - if (writes_len) - retval = ni_usb_write_registers(ni_priv, writes, writes_len, &ibsta); - else - return -EFAULT; - kfree(writes); - if (retval) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return retval; - } - ni_usb_soft_update_status(board, ibsta, 0); - return 0; -} - -static void ni_usb_interrupt_complete(struct urb *urb) -{ - struct gpib_board *board = urb->context; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - int retval; - struct ni_usb_status_block status; - unsigned long flags; - - switch (urb->status) { - /* success */ - case 0: - break; - /* unlinked, don't resubmit */ - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* other error, resubmit */ - retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC); - if (retval) - dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); - return; - } - - ni_usb_parse_status_block(urb->transfer_buffer, &status); - - spin_lock_irqsave(&board->spinlock, flags); - ni_priv->monitored_ibsta_bits &= ~status.ibsta; - spin_unlock_irqrestore(&board->spinlock, flags); - - wake_up_interruptible(&board->wait); - - retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC); - if (retval) - dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n"); -} - -static int ni_usb_set_interrupt_monitor(struct gpib_board *board, unsigned int monitored_bits) -{ - int retval; - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - static const int buffer_length = 8; - u8 *buffer; - struct ni_usb_status_block status; - unsigned long flags; - - buffer = kmalloc(buffer_length, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - spin_lock_irqsave(&board->spinlock, flags); - ni_priv->monitored_ibsta_bits = ni_usb_ibsta_monitor_mask & monitored_bits; - spin_unlock_irqrestore(&board->spinlock, flags); - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN | - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x300, ni_usb_ibsta_monitor_mask & monitored_bits, - buffer, buffer_length, 1000); - if (retval != buffer_length) { - dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval); - kfree(buffer); - return -1; - } - ni_usb_parse_status_block(buffer, &status); - kfree(buffer); - return 0; -} - -static int ni_usb_setup_urbs(struct gpib_board *board) -{ - struct ni_usb_priv *ni_priv = board->private_data; - struct usb_device *usb_dev; - int int_pipe; - int retval; - - if (ni_priv->interrupt_in_endpoint < 0) - return 0; - - mutex_lock(&ni_priv->interrupt_transfer_lock); - if (!ni_priv->bus_interface) { - mutex_unlock(&ni_priv->interrupt_transfer_lock); - return -ENODEV; - } - ni_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ni_priv->interrupt_urb) { - mutex_unlock(&ni_priv->interrupt_transfer_lock); - return -ENOMEM; - } - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - int_pipe = usb_rcvintpipe(usb_dev, ni_priv->interrupt_in_endpoint); - usb_fill_int_urb(ni_priv->interrupt_urb, usb_dev, int_pipe, ni_priv->interrupt_buffer, - sizeof(ni_priv->interrupt_buffer), &ni_usb_interrupt_complete, board, 1); - retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL); - mutex_unlock(&ni_priv->interrupt_transfer_lock); - if (retval) { - dev_err(&usb_dev->dev, "failed to submit first interrupt urb, retval=%i\n", retval); - return retval; - } - return 0; -} - -static void ni_usb_cleanup_urbs(struct ni_usb_priv *ni_priv) -{ - if (ni_priv && ni_priv->bus_interface) { - if (ni_priv->interrupt_urb) - usb_kill_urb(ni_priv->interrupt_urb); - if (ni_priv->bulk_urb) - usb_kill_urb(ni_priv->bulk_urb); - } -} - -static int ni_usb_b_read_serial_number(struct ni_usb_priv *ni_priv) -{ - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - int retval; - u8 *out_data; - u8 *in_data; - static const int out_data_length = 0x20; - static const int in_data_length = 0x20; - int bytes_written = 0, bytes_read = 0; - int i = 0; - static const int num_reads = 4; - unsigned int results[4]; - int j; - unsigned int serial_number; - - in_data = kmalloc(in_data_length, GFP_KERNEL); - if (!in_data) - return -ENOMEM; - - out_data = kmalloc(out_data_length, GFP_KERNEL); - if (!out_data) { - kfree(in_data); - return -ENOMEM; - } - i += ni_usb_bulk_register_read_header(&out_data[i], num_reads); - i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_1_REG); - i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_2_REG); - i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_3_REG); - i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_4_REG); - while (i % 4) - out_data[i++] = 0x0; - i += ni_usb_bulk_termination(&out_data[i]); - retval = ni_usb_send_bulk_msg(ni_priv, out_data, out_data_length, &bytes_written, 1000); - if (retval) { - dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%li\n", - retval, bytes_written, (long)out_data_length); - goto serial_out; - } - retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0); - if (retval) { - dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n", - retval, bytes_read); - ni_usb_dump_raw_block(in_data, bytes_read); - goto serial_out; - } - if (ARRAY_SIZE(results) < num_reads) { - dev_err(&usb_dev->dev, "serial number eetup bug\n"); - retval = -EINVAL; - goto serial_out; - } - ni_usb_parse_register_read_block(in_data, results, num_reads); - serial_number = 0; - for (j = 0; j < num_reads; ++j) - serial_number |= (results[j] & 0xff) << (8 * j); - dev_dbg(&usb_dev->dev, "board serial number is 0x%x\n", serial_number); - retval = 0; -serial_out: - kfree(in_data); - kfree(out_data); - return retval; -} - -static int ni_usb_hs_wait_for_ready(struct ni_usb_priv *ni_priv) -{ - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - static const int buffer_size = 0x10; - static const int timeout = 50; - static const int msec_sleep_duration = 100; - int i; int retval; - int j; - int unexpected = 0; - unsigned int serial_number; - u8 *buffer; - - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_SERIAL_NUMBER_REQUEST, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, 0x0, buffer, buffer_size, 1000); - if (retval < 0) { - dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", - NI_USB_SERIAL_NUMBER_REQUEST, retval); - goto ready_out; - } - j = 0; - if (buffer[j] != NI_USB_SERIAL_NUMBER_REQUEST) { - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n", - j, (int)buffer[j], NI_USB_SERIAL_NUMBER_REQUEST); - unexpected = 1; - } - if (unexpected) - ni_usb_dump_raw_block(buffer, retval); - // NI-USB-HS+ pads the serial with 0x0 to make 16 bytes - if (retval != 5 && retval != 16) { - dev_err(&usb_dev->dev, "received unexpected number of bytes = %i, expected 5 or 16\n", - retval); - ni_usb_dump_raw_block(buffer, retval); - } - serial_number = 0; - serial_number |= buffer[++j]; - serial_number |= (buffer[++j] << 8); - serial_number |= (buffer[++j] << 16); - serial_number |= (buffer[++j] << 24); - dev_dbg(&usb_dev->dev, "board serial number is 0x%x\n", serial_number); - for (i = 0; i < timeout; ++i) { - int ready = 0; - - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_POLL_READY_REQUEST, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, 0x0, buffer, buffer_size, 100); - if (retval < 0) { - dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", - NI_USB_POLL_READY_REQUEST, retval); - goto ready_out; - } - j = 0; - unexpected = 0; - if (buffer[j] != NI_USB_POLL_READY_REQUEST) { // [0] - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n", - j, (int)buffer[j], NI_USB_POLL_READY_REQUEST); - unexpected = 1; - } - ++j; - if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [1] HS+ sends 0x0 - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n", - j, (int)buffer[j]); - unexpected = 1; - } - if (buffer[++j] != 0x0) { // [2] - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n", - j, (int)buffer[j], 0x0); - unexpected = 1; - } - ++j; - /* - * MC usb-488 (and sometimes NI-USB-HS?) sends 0x8 here; MC usb-488A sends 0x7 here - * NI-USB-HS+ sends 0x0 - */ - if (buffer[j] != 0x1 && buffer[j] != 0x8 && buffer[j] != 0x7 && buffer[j] != 0x0) { - // [3] - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0, 0x1, 0x7 or 0x8\n", - j, (int)buffer[j]); - unexpected = 1; - } - ++j; - // NI-USB-HS+ sends 0 here - if (buffer[j] != 0x30 && buffer[j] != 0x0) { // [4] - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x30\n", - j, (int)buffer[j]); - unexpected = 1; - } - ++j; - // MC usb-488 (and sometimes NI-USB-HS?) and NI-USB-HS+ sends 0x0 here - if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [5] - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n", - j, (int)buffer[j]); - unexpected = 1; - } - if (buffer[++j] != 0x0) { // [6] - ready = 1; - // NI-USB-HS+ sends 0xf or 0x19 here - if (buffer[j] != 0x2 && buffer[j] != 0xe && buffer[j] != 0xf && - buffer[j] != 0x16 && buffer[j] != 0x19) { - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x2, 0xe, 0xf, 0x16 or 0x19\n", - j, (int)buffer[j]); - unexpected = 1; - } - } - if (buffer[++j] != 0x0) { // [7] - ready = 1; - // MC usb-488 sends 0x5 here; MC usb-488A sends 0x6 here - if (buffer[j] != 0x3 && buffer[j] != 0x5 && buffer[j] != 0x6 && - buffer[j] != 0x8) { - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x3 or 0x5, 0x6 or 0x08\n", - j, (int)buffer[j]); - unexpected = 1; - } - } - ++j; - if (buffer[j] != 0x0 && buffer[j] != 0x2) { // [8] MC usb-488 sends 0x2 here - dev_err(&usb_dev->dev, " unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x2\n", - j, (int)buffer[j]); - unexpected = 1; - } - ++j; - // MC usb-488A and NI-USB-HS sends 0x3 here; NI-USB-HS+ sends 0x30 here - if (buffer[j] != 0x0 && buffer[j] != 0x3 && buffer[j] != 0x30) { // [9] - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0, 0x3 or 0x30\n", - j, (int)buffer[j]); - unexpected = 1; - } - if (buffer[++j] != 0x0) { // [10] MC usb-488 sends 0x7 here, new HS+ sends 0x59 - ready = 1; - if (buffer[j] != 0x96 && buffer[j] != 0x7 && buffer[j] != 0x6e && - buffer[j] != 0x59) { - dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x96, 0x07, 0x6e or 0x59\n", - j, (int)buffer[j]); - unexpected = 1; - } - } - if (unexpected) - ni_usb_dump_raw_block(buffer, retval); - if (ready) - break; - retval = msleep_interruptible(msec_sleep_duration); - if (retval) { - retval = -ERESTARTSYS; - goto ready_out; - } - } - retval = 0; - -ready_out: - kfree(buffer); - dev_dbg(&usb_dev->dev, "exit retval=%d\n", retval); - return retval; -} - -/* - * This does some extra init for HS+ models, as observed on Windows. One of the - * control requests causes the LED to stop blinking. - * I'm not sure what the other 2 requests do. None of these requests are actually required - * for the adapter to work, maybe they do some init for the analyzer interface - * (which we don't use). - */ -static int ni_usb_hs_plus_extra_init(struct ni_usb_priv *ni_priv) -{ - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - int retval; - u8 *buffer; - static const int buffer_size = 16; - int transfer_size; - - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - do { - transfer_size = 16; - - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0x48_REQUEST, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, 0x0, buffer, transfer_size, 1000); - if (retval < 0) { - dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", - NI_USB_HS_PLUS_0x48_REQUEST, retval); - break; - } - // expected response data: 48 f3 30 00 00 00 00 00 00 00 00 00 00 00 00 00 - if (buffer[0] != NI_USB_HS_PLUS_0x48_REQUEST) - dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n", - (int)buffer[0], NI_USB_HS_PLUS_0x48_REQUEST); - - transfer_size = 2; - - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_LED_REQUEST, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x1, 0x0, buffer, transfer_size, 1000); - if (retval < 0) { - dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", - NI_USB_HS_PLUS_LED_REQUEST, retval); - break; - } - // expected response data: 4b 00 - if (buffer[0] != NI_USB_HS_PLUS_LED_REQUEST) - dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n", - (int)buffer[0], NI_USB_HS_PLUS_LED_REQUEST); - - transfer_size = 9; - - retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0xf8_REQUEST, - USB_DIR_IN | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, - 0x0, 0x1, buffer, transfer_size, 1000); - if (retval < 0) { - dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n", - NI_USB_HS_PLUS_0xf8_REQUEST, retval); - break; - } - // expected response data: f8 01 00 00 00 01 00 00 00 - if (buffer[0] != NI_USB_HS_PLUS_0xf8_REQUEST) - dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n", - (int)buffer[0], NI_USB_HS_PLUS_0xf8_REQUEST); - } while (0); - - // cleanup - kfree(buffer); - return retval; -} - -static inline int ni_usb_device_match(struct usb_interface *interface, - const struct gpib_board_config *config) -{ - if (gpib_match_device_path(&interface->dev, config->device_path) == 0) - return 0; - return 1; -} - -static int ni_usb_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - int retval; - int i, index; - struct ni_usb_priv *ni_priv; - int product_id; - struct usb_device *usb_dev; - - mutex_lock(&ni_usb_hotplug_lock); - retval = ni_usb_allocate_private(board); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - ni_priv = board->private_data; - for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { - if (ni_usb_driver_interfaces[i] && - !usb_get_intfdata(ni_usb_driver_interfaces[i]) && - ni_usb_device_match(ni_usb_driver_interfaces[i], config)) { - ni_priv->bus_interface = ni_usb_driver_interfaces[i]; - usb_set_intfdata(ni_usb_driver_interfaces[i], board); - usb_dev = interface_to_usbdev(ni_priv->bus_interface); - index = i; - break; - } - } - if (i == MAX_NUM_NI_USB_INTERFACES) { - mutex_unlock(&ni_usb_hotplug_lock); - dev_err(board->gpib_dev, "No supported adapters found, have you loaded its firmware?\n"); - return -ENODEV; - } - if (usb_reset_configuration(interface_to_usbdev(ni_priv->bus_interface))) - dev_err(&usb_dev->dev, "usb_reset_configuration() failed.\n"); - - product_id = le16_to_cpu(usb_dev->descriptor.idProduct); - ni_priv->product_id = product_id; - - timer_setup(&ni_priv->bulk_timer, ni_usb_timeout_handler, 0); - - switch (product_id) { - case USB_DEVICE_ID_NI_USB_B: - ni_priv->bulk_out_endpoint = NIUSB_B_BULK_OUT_ENDPOINT; - ni_priv->bulk_in_endpoint = NIUSB_B_BULK_IN_ENDPOINT; - ni_priv->interrupt_in_endpoint = NIUSB_B_INTERRUPT_IN_ENDPOINT; - ni_usb_b_read_serial_number(ni_priv); - break; - case USB_DEVICE_ID_NI_USB_HS: - case USB_DEVICE_ID_MC_USB_488: - case USB_DEVICE_ID_KUSB_488A: - ni_priv->bulk_out_endpoint = NIUSB_HS_BULK_OUT_ENDPOINT; - ni_priv->bulk_in_endpoint = NIUSB_HS_BULK_IN_ENDPOINT; - ni_priv->interrupt_in_endpoint = NIUSB_HS_INTERRUPT_IN_ENDPOINT; - retval = ni_usb_hs_wait_for_ready(ni_priv); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - break; - case USB_DEVICE_ID_NI_USB_HS_PLUS: - ni_priv->bulk_out_endpoint = NIUSB_HS_PLUS_BULK_OUT_ENDPOINT; - ni_priv->bulk_in_endpoint = NIUSB_HS_PLUS_BULK_IN_ENDPOINT; - ni_priv->interrupt_in_endpoint = NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT; - retval = ni_usb_hs_wait_for_ready(ni_priv); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - retval = ni_usb_hs_plus_extra_init(ni_priv); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - break; - default: - mutex_unlock(&ni_usb_hotplug_lock); - dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id %x\n", - product_id); - return -EINVAL; - } - - retval = ni_usb_setup_urbs(board); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - retval = ni_usb_set_interrupt_monitor(board, 0); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - - board->t1_nano_sec = 500; - - retval = ni_usb_init(board); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - - mutex_unlock(&ni_usb_hotplug_lock); - dev_info(&usb_dev->dev, - "bus %d dev num %d attached to gpib%d, intf %i\n", - usb_dev->bus->busnum, usb_dev->devnum, board->minor, index); - return retval; -} - -static int ni_usb_shutdown_hardware(struct ni_usb_priv *ni_priv) -{ - struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface); - int retval; - struct ni_usb_register writes[2]; - static const int writes_length = ARRAY_SIZE(writes); - unsigned int ibsta; - - writes[0].device = NIUSB_SUBDEV_TNT4882; - writes[0].address = nec7210_to_tnt4882_offset(AUXMR); - writes[0].value = AUX_CR; - writes[1].device = NIUSB_SUBDEV_UNKNOWN3; - writes[1].address = 0x10; - writes[1].value = 0x0; - retval = ni_usb_write_registers(ni_priv, writes, writes_length, &ibsta); - if (retval) { - dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval); - return retval; - } - return 0; -} - -static void ni_usb_detach(struct gpib_board *board) -{ - struct ni_usb_priv *ni_priv; - - mutex_lock(&ni_usb_hotplug_lock); - /* - * under windows, software unplug does chip_reset nec7210 aux command, - * then writes 0x0 to address 0x10 of device 3 - */ - ni_priv = board->private_data; - if (ni_priv) { - if (ni_priv->bus_interface) { - ni_usb_set_interrupt_monitor(board, 0); - ni_usb_shutdown_hardware(ni_priv); - usb_set_intfdata(ni_priv->bus_interface, NULL); - } - mutex_lock(&ni_priv->bulk_transfer_lock); - mutex_lock(&ni_priv->control_transfer_lock); - mutex_lock(&ni_priv->interrupt_transfer_lock); - ni_usb_cleanup_urbs(ni_priv); - ni_usb_free_private(ni_priv); - } - mutex_unlock(&ni_usb_hotplug_lock); -} - -static struct gpib_interface ni_usb_gpib_interface = { - .name = "ni_usb_b", - .attach = ni_usb_attach, - .detach = ni_usb_detach, - .read = ni_usb_read, - .write = ni_usb_write, - .command = ni_usb_command, - .take_control = ni_usb_take_control, - .go_to_standby = ni_usb_go_to_standby, - .request_system_control = ni_usb_request_system_control, - .interface_clear = ni_usb_interface_clear, - .remote_enable = ni_usb_remote_enable, - .enable_eos = ni_usb_enable_eos, - .disable_eos = ni_usb_disable_eos, - .parallel_poll = ni_usb_parallel_poll, - .parallel_poll_configure = ni_usb_parallel_poll_configure, - .parallel_poll_response = ni_usb_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = ni_usb_line_status, - .update_status = ni_usb_update_status, - .primary_address = ni_usb_primary_address, - .secondary_address = ni_usb_secondary_address, - .serial_poll_response = ni_usb_serial_poll_response, - .serial_poll_status = ni_usb_serial_poll_status, - .t1_delay = ni_usb_t1_delay, - .return_to_local = ni_usb_return_to_local, - .skip_check_for_command_acceptors = 1 -}; - -// Table with the USB-devices: just now only testing IDs -static struct usb_device_id ni_usb_driver_device_table[] = { - {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_B)}, - {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS)}, - // gpib-usb-hs+ has a second interface for the analyzer, which we ignore - {USB_DEVICE_INTERFACE_NUMBER(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS_PLUS, 0)}, - {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_KUSB_488A)}, - {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_MC_USB_488)}, - {} /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, ni_usb_driver_device_table); - -static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb_device_id *id) -{ - struct usb_device *usb_dev = interface_to_usbdev(interface); - int i; - char *path; - static const int path_length = 1024; - - mutex_lock(&ni_usb_hotplug_lock); - usb_get_dev(usb_dev); - for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { - if (!ni_usb_driver_interfaces[i]) { - ni_usb_driver_interfaces[i] = interface; - usb_set_intfdata(interface, NULL); - break; - } - } - if (i == MAX_NUM_NI_USB_INTERFACES) { - usb_put_dev(usb_dev); - mutex_unlock(&ni_usb_hotplug_lock); - dev_err(&usb_dev->dev, "ni_usb_driver_interfaces[] full\n"); - return -1; - } - path = kmalloc(path_length, GFP_KERNEL); - if (!path) { - usb_put_dev(usb_dev); - mutex_unlock(&ni_usb_hotplug_lock); - return -ENOMEM; - } - usb_make_path(usb_dev, path, path_length); - dev_info(&usb_dev->dev, "probe succeeded for path: %s\n", path); - kfree(path); - mutex_unlock(&ni_usb_hotplug_lock); - return 0; -} - -static void ni_usb_driver_disconnect(struct usb_interface *interface) -{ - struct usb_device *usb_dev = interface_to_usbdev(interface); - int i; - - mutex_lock(&ni_usb_hotplug_lock); - for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { - if (ni_usb_driver_interfaces[i] == interface) { - struct gpib_board *board = usb_get_intfdata(interface); - - if (board) { - struct ni_usb_priv *ni_priv = board->private_data; - - if (ni_priv) { - mutex_lock(&ni_priv->bulk_transfer_lock); - mutex_lock(&ni_priv->control_transfer_lock); - mutex_lock(&ni_priv->interrupt_transfer_lock); - ni_usb_cleanup_urbs(ni_priv); - ni_priv->bus_interface = NULL; - mutex_unlock(&ni_priv->interrupt_transfer_lock); - mutex_unlock(&ni_priv->control_transfer_lock); - mutex_unlock(&ni_priv->bulk_transfer_lock); - } - } - ni_usb_driver_interfaces[i] = NULL; - break; - } - } - if (i == MAX_NUM_NI_USB_INTERFACES) - dev_err(&usb_dev->dev, "unable to find interface bug?\n"); - usb_put_dev(usb_dev); - mutex_unlock(&ni_usb_hotplug_lock); -} - -static int ni_usb_driver_suspend(struct usb_interface *interface, pm_message_t message) -{ - struct usb_device *usb_dev = interface_to_usbdev(interface); - struct gpib_board *board; - int i, retval; - - mutex_lock(&ni_usb_hotplug_lock); - - for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { - if (ni_usb_driver_interfaces[i] == interface) { - board = usb_get_intfdata(interface); - if (board) - break; - } - } - if (i == MAX_NUM_NI_USB_INTERFACES) { - mutex_unlock(&ni_usb_hotplug_lock); - return 0; - } - - struct ni_usb_priv *ni_priv = board->private_data; - - if (ni_priv) { - ni_usb_set_interrupt_monitor(board, 0); - retval = ni_usb_shutdown_hardware(ni_priv); - if (retval) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - if (ni_priv->interrupt_urb) { - mutex_lock(&ni_priv->interrupt_transfer_lock); - ni_usb_cleanup_urbs(ni_priv); - mutex_unlock(&ni_priv->interrupt_transfer_lock); - } - dev_dbg(&usb_dev->dev, - "bus %d dev num %d gpib%d, interface %i suspended\n", - usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); - } - - mutex_unlock(&ni_usb_hotplug_lock); - return 0; -} - -static int ni_usb_driver_resume(struct usb_interface *interface) -{ - struct usb_device *usb_dev = interface_to_usbdev(interface); - - struct gpib_board *board; - int i, retval; - - mutex_lock(&ni_usb_hotplug_lock); - - for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { - if (ni_usb_driver_interfaces[i] == interface) { - board = usb_get_intfdata(interface); - if (board) - break; - } - } - if (i == MAX_NUM_NI_USB_INTERFACES) { - mutex_unlock(&ni_usb_hotplug_lock); - return 0; - } - - struct ni_usb_priv *ni_priv = board->private_data; - - if (ni_priv) { - if (ni_priv->interrupt_urb) { - mutex_lock(&ni_priv->interrupt_transfer_lock); - retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL); - if (retval) { - dev_err(&usb_dev->dev, "resume failed to resubmit interrupt urb, retval=%i\n", - retval); - mutex_unlock(&ni_priv->interrupt_transfer_lock); - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - mutex_unlock(&ni_priv->interrupt_transfer_lock); - } else { - dev_err(&usb_dev->dev, "bug! resume int urb not set up\n"); - mutex_unlock(&ni_usb_hotplug_lock); - return -EINVAL; - } - - switch (ni_priv->product_id) { - case USB_DEVICE_ID_NI_USB_B: - ni_usb_b_read_serial_number(ni_priv); - break; - case USB_DEVICE_ID_NI_USB_HS: - case USB_DEVICE_ID_MC_USB_488: - case USB_DEVICE_ID_KUSB_488A: - retval = ni_usb_hs_wait_for_ready(ni_priv); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - break; - case USB_DEVICE_ID_NI_USB_HS_PLUS: - retval = ni_usb_hs_wait_for_ready(ni_priv); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - retval = ni_usb_hs_plus_extra_init(ni_priv); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - break; - default: - mutex_unlock(&ni_usb_hotplug_lock); - dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id\n"); - return -EINVAL; - } - - retval = ni_usb_set_interrupt_monitor(board, 0); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - - retval = ni_usb_init(board); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask); - if (retval < 0) { - mutex_unlock(&ni_usb_hotplug_lock); - return retval; - } - if (board->master) - ni_usb_interface_clear(board, 1); // this is a pulsed action - if (ni_priv->ren_state) - ni_usb_remote_enable(board, 1); - - dev_dbg(&usb_dev->dev, - "bus %d dev num %d gpib%d, interface %i resumed\n", - usb_dev->bus->busnum, usb_dev->devnum, board->minor, i); - } - - mutex_unlock(&ni_usb_hotplug_lock); - return 0; -} - -static struct usb_driver ni_usb_bus_driver = { - .name = DRV_NAME, - .probe = ni_usb_driver_probe, - .disconnect = ni_usb_driver_disconnect, - .suspend = ni_usb_driver_suspend, - .resume = ni_usb_driver_resume, - .id_table = ni_usb_driver_device_table, -}; - -static int __init ni_usb_init_module(void) -{ - int i; - int ret; - - for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) - ni_usb_driver_interfaces[i] = NULL; - - ret = usb_register(&ni_usb_bus_driver); - if (ret) { - pr_err("usb_register failed: error = %d\n", ret); - return ret; - } - - ret = gpib_register_driver(&ni_usb_gpib_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - return ret; - } - - return 0; -} - -static void __exit ni_usb_exit_module(void) -{ - gpib_unregister_driver(&ni_usb_gpib_interface); - usb_deregister(&ni_usb_bus_driver); -} - -module_init(ni_usb_init_module); -module_exit(ni_usb_exit_module); diff --git a/drivers/staging/gpib/ni_usb/ni_usb_gpib.h b/drivers/staging/gpib/ni_usb/ni_usb_gpib.h deleted file mode 100644 index 688f5e08792f..000000000000 --- a/drivers/staging/gpib/ni_usb/ni_usb_gpib.h +++ /dev/null @@ -1,226 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/*************************************************************************** - * copyright : (C) 2004 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _NI_USB_GPIB_H -#define _NI_USB_GPIB_H - -#include -#include -#include -#include -#include "gpibP.h" - -enum { - USB_VENDOR_ID_NI = 0x3923 -}; - -enum { - USB_DEVICE_ID_NI_USB_B = 0x702a, - USB_DEVICE_ID_NI_USB_B_PREINIT = 0x702b, // device id before firmware is loaded - USB_DEVICE_ID_NI_USB_HS = 0x709b, - USB_DEVICE_ID_NI_USB_HS_PLUS = 0x7618, - USB_DEVICE_ID_KUSB_488A = 0x725c, - USB_DEVICE_ID_MC_USB_488 = 0x725d -}; - -enum ni_usb_device { - NIUSB_SUBDEV_TNT4882 = 1, - NIUSB_SUBDEV_UNKNOWN2 = 2, - NIUSB_SUBDEV_UNKNOWN3 = 3, -}; - -enum endpoint_addresses { - NIUSB_B_BULK_OUT_ENDPOINT = 0x2, - NIUSB_B_BULK_IN_ENDPOINT = 0x2, - NIUSB_B_BULK_IN_ALT_ENDPOINT = 0x6, - NIUSB_B_INTERRUPT_IN_ENDPOINT = 0x4, -}; - -enum hs_enpoint_addresses { - NIUSB_HS_BULK_OUT_ENDPOINT = 0x2, - NIUSB_HS_BULK_OUT_ALT_ENDPOINT = 0x6, - NIUSB_HS_BULK_IN_ENDPOINT = 0x4, - NIUSB_HS_BULK_IN_ALT_ENDPOINT = 0x8, - NIUSB_HS_INTERRUPT_IN_ENDPOINT = 0x1, -}; - -enum hs_plus_endpoint_addresses { - NIUSB_HS_PLUS_BULK_OUT_ENDPOINT = 0x1, - NIUSB_HS_PLUS_BULK_OUT_ALT_ENDPOINT = 0x4, - NIUSB_HS_PLUS_BULK_IN_ENDPOINT = 0x2, - NIUSB_HS_PLUS_BULK_IN_ALT_ENDPOINT = 0x5, - NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT = 0x3, -}; - -struct ni_usb_urb_ctx { - struct completion complete; - unsigned timed_out : 1; -}; - -// struct which defines private_data for ni_usb devices -struct ni_usb_priv { - struct usb_interface *bus_interface; - int bulk_out_endpoint; - int bulk_in_endpoint; - int interrupt_in_endpoint; - u8 eos_char; - unsigned short eos_mode; - unsigned int monitored_ibsta_bits; - struct urb *bulk_urb; - struct urb *interrupt_urb; - u8 interrupt_buffer[0x11]; - struct mutex addressed_transfer_lock; // protect transfer lock - struct mutex bulk_transfer_lock; // protect bulk message sends - struct mutex control_transfer_lock; // protect control messages - struct mutex interrupt_transfer_lock; // protect interrupt messages - struct timer_list bulk_timer; - struct ni_usb_urb_ctx context; - int product_id; - unsigned short ren_state; -}; - -struct ni_usb_status_block { - short id; - unsigned short ibsta; - short error_code; - unsigned short count; -}; - -struct ni_usb_register { - enum ni_usb_device device; - short address; - unsigned short value; -}; - -enum ni_usb_bulk_ids { - NIUSB_IBCAC_ID = 0x1, - NIUSB_UNKNOWN3_ID = 0x3, // device level function id? - NIUSB_TERM_ID = 0x4, - NIUSB_IBGTS_ID = 0x6, - NIUSB_IBRPP_ID = 0x7, - NIUSB_REG_READ_ID = 0x8, - NIUSB_REG_WRITE_ID = 0x9, - NIUSB_IBSIC_ID = 0xf, - NIUSB_REGISTER_READ_DATA_START_ID = 0x34, - NIUSB_REGISTER_READ_DATA_END_ID = 0x35, - NIUSB_IBRD_DATA_ID = 0x36, - NIUSB_IBRD_EXTENDED_DATA_ID = 0x37, - NIUSB_IBRD_STATUS_ID = 0x38 -}; - -enum ni_usb_error_codes { - NIUSB_NO_ERROR = 0, - /* - * NIUSB_ABORTED_ERROR occurs when I/O is interrupted early by - * doing a NI_USB_STOP_REQUEST on the control endpoint. - */ - NIUSB_ABORTED_ERROR = 1, - /* - * NIUSB_READ_ATN_ERROR occurs when you do a board read while - * ATN is set - */ - NIUSB_ATN_STATE_ERROR = 2, - /* - * NIUSB_ADDRESSING_ERROR occurs when you do a board - * read/write as CIC but are not in LACS/TACS - */ - NIUSB_ADDRESSING_ERROR = 3, - /* - * NIUSB_EOSMODE_ERROR occurs on reads if any eos mode or char - * bits are set when REOS is not set. - * Have also seen error 4 if you try to send more than 16 - * command bytes at once on a usb-b. - */ - NIUSB_EOSMODE_ERROR = 4, - /* - * NIUSB_NO_BUS_ERROR occurs when you try to write a command - * byte but there are no devices connected to the gpib bus - */ - NIUSB_NO_BUS_ERROR = 5, - /* - * NIUSB_NO_LISTENER_ERROR occurs when you do a board write as - * CIC with no listener - */ - NIUSB_NO_LISTENER_ERROR = 8, - /* get NIUSB_TIMEOUT_ERROR on board read/write timeout */ - NIUSB_TIMEOUT_ERROR = 10, -}; - -enum ni_usb_control_requests { - NI_USB_STOP_REQUEST = 0x20, - NI_USB_WAIT_REQUEST = 0x21, - NI_USB_POLL_READY_REQUEST = 0x40, - NI_USB_SERIAL_NUMBER_REQUEST = 0x41, - NI_USB_HS_PLUS_0x48_REQUEST = 0x48, - NI_USB_HS_PLUS_LED_REQUEST = 0x4b, - NI_USB_HS_PLUS_0xf8_REQUEST = 0xf8 -}; - -static const unsigned int ni_usb_ibsta_monitor_mask = - SRQI | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS; - -static inline int nec7210_to_tnt4882_offset(int offset) -{ - return 2 * offset; -}; - -static inline int ni_usb_bulk_termination(u8 *buffer) -{ - int i = 0; - - buffer[i++] = NIUSB_TERM_ID; - buffer[i++] = 0x0; - buffer[i++] = 0x0; - buffer[i++] = 0x0; - return i; -} - -enum ni_usb_unknown3_register { - SERIAL_NUMBER_4_REG = 0x8, - SERIAL_NUMBER_3_REG = 0x9, - SERIAL_NUMBER_2_REG = 0xa, - SERIAL_NUMBER_1_REG = 0xb, -}; - -static inline int ni_usb_bulk_register_write_header(u8 *buffer, int num_writes) -{ - int i = 0; - - buffer[i++] = NIUSB_REG_WRITE_ID; - buffer[i++] = num_writes; - buffer[i++] = 0x0; - return i; -} - -static inline int ni_usb_bulk_register_write(u8 *buffer, struct ni_usb_register reg) -{ - int i = 0; - - buffer[i++] = reg.device; - buffer[i++] = reg.address; - buffer[i++] = reg.value; - return i; -} - -static inline int ni_usb_bulk_register_read_header(u8 *buffer, int num_reads) -{ - int i = 0; - - buffer[i++] = NIUSB_REG_READ_ID; - buffer[i++] = num_reads; - return i; -} - -static inline int ni_usb_bulk_register_read(u8 *buffer, int device, int address) -{ - int i = 0; - - buffer[i++] = device; - buffer[i++] = address; - return i; -} - -#endif // _NI_USB_GPIB_H diff --git a/drivers/staging/gpib/pc2/Makefile b/drivers/staging/gpib/pc2/Makefile deleted file mode 100644 index 481ee4296e1b..000000000000 --- a/drivers/staging/gpib/pc2/Makefile +++ /dev/null @@ -1,5 +0,0 @@ - -obj-$(CONFIG_GPIB_PC2) += pc2_gpib.o - - - diff --git a/drivers/staging/gpib/pc2/pc2_gpib.c b/drivers/staging/gpib/pc2/pc2_gpib.c deleted file mode 100644 index 9f3943d1df66..000000000000 --- a/drivers/staging/gpib/pc2/pc2_gpib.c +++ /dev/null @@ -1,684 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2001, 2002 by Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "nec7210.h" -#include "gpibP.h" - -// struct which defines private_data for pc2 driver -struct pc2_priv { - struct nec7210_priv nec7210_priv; - unsigned int irq; - // io address that clears interrupt for pc2a (0x2f0 + irq) - unsigned int clear_intr_addr; -}; - -// pc2 uses 8 consecutive io addresses -static const int pc2_iosize = 8; -static const int pc2a_iosize = 8; -static const int pc2_2a_iosize = 16; - -// offset between io addresses of successive nec7210 registers -static const int pc2a_reg_offset = 0x400; -static const int pc2_reg_offset = 1; - -// interrupt service routine -static irqreturn_t pc2_interrupt(int irq, void *arg); -static irqreturn_t pc2a_interrupt(int irq, void *arg); - -// pc2 specific registers and bits - -// interrupt clear register address -static const int pc2a_clear_intr_iobase = 0x2f0; -static inline unsigned int CLEAR_INTR_REG(unsigned int irq) -{ - return pc2a_clear_intr_iobase + irq; -} - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for PC2/PC2a and compatible devices"); - -/* - * GPIB interrupt service routines - */ - -irqreturn_t pc2_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - struct pc2_priv *priv = board->private_data; - unsigned long flags; - irqreturn_t retval; - - spin_lock_irqsave(&board->spinlock, flags); - retval = nec7210_interrupt(board, &priv->nec7210_priv); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -irqreturn_t pc2a_interrupt(int irq, void *arg) -{ - struct gpib_board *board = arg; - struct pc2_priv *priv = board->private_data; - int status1, status2; - unsigned long flags; - irqreturn_t retval; - - spin_lock_irqsave(&board->spinlock, flags); - // read interrupt status (also clears status) - status1 = read_byte(&priv->nec7210_priv, ISR1); - status2 = read_byte(&priv->nec7210_priv, ISR2); - /* clear interrupt circuit */ - if (priv->irq) - outb(0xff, CLEAR_INTR_REG(priv->irq)); - retval = nec7210_interrupt_have_status(board, &priv->nec7210_priv, status1, status2); - spin_unlock_irqrestore(&board->spinlock, flags); - return retval; -} - -// wrappers for interface functions -static int pc2_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); -} - -static int pc2_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); -} - -static int pc2_command(struct gpib_board *board, u8 *buffer, - size_t length, size_t *bytes_written) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); -} - -static int pc2_take_control(struct gpib_board *board, int synchronous) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_take_control(board, &priv->nec7210_priv, synchronous); -} - -static int pc2_go_to_standby(struct gpib_board *board) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_go_to_standby(board, &priv->nec7210_priv); -} - -static int pc2_request_system_control(struct gpib_board *board, int request_control) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_request_system_control(board, &priv->nec7210_priv, request_control); -} - -static void pc2_interface_clear(struct gpib_board *board, int assert) -{ - struct pc2_priv *priv = board->private_data; - - nec7210_interface_clear(board, &priv->nec7210_priv, assert); -} - -static void pc2_remote_enable(struct gpib_board *board, int enable) -{ - struct pc2_priv *priv = board->private_data; - - nec7210_remote_enable(board, &priv->nec7210_priv, enable); -} - -static int pc2_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); -} - -static void pc2_disable_eos(struct gpib_board *board) -{ - struct pc2_priv *priv = board->private_data; - - nec7210_disable_eos(board, &priv->nec7210_priv); -} - -static unsigned int pc2_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_update_status(board, &priv->nec7210_priv, clear_mask); -} - -static int pc2_primary_address(struct gpib_board *board, unsigned int address) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_primary_address(board, &priv->nec7210_priv, address); -} - -static int pc2_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); -} - -static int pc2_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_parallel_poll(board, &priv->nec7210_priv, result); -} - -static void pc2_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - struct pc2_priv *priv = board->private_data; - - nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); -} - -static void pc2_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct pc2_priv *priv = board->private_data; - - nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); -} - -static void pc2_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct pc2_priv *priv = board->private_data; - - nec7210_serial_poll_response(board, &priv->nec7210_priv, status); -} - -static u8 pc2_serial_poll_status(struct gpib_board *board) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_serial_poll_status(board, &priv->nec7210_priv); -} - -static int pc2_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct pc2_priv *priv = board->private_data; - - return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec); -} - -static void pc2_return_to_local(struct gpib_board *board) -{ - struct pc2_priv *priv = board->private_data; - - nec7210_return_to_local(board, &priv->nec7210_priv); -} - -static int allocate_private(struct gpib_board *board) -{ - struct pc2_priv *priv; - - board->private_data = kmalloc(sizeof(struct pc2_priv), GFP_KERNEL); - if (!board->private_data) - return -1; - priv = board->private_data; - memset(priv, 0, sizeof(struct pc2_priv)); - init_nec7210_private(&priv->nec7210_priv); - return 0; -} - -static void free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static int pc2_generic_attach(struct gpib_board *board, const struct gpib_board_config *config, - enum nec7210_chipset chipset) -{ - struct pc2_priv *pc2_priv; - struct nec7210_priv *nec_priv; - - board->status = 0; - if (allocate_private(board)) - return -ENOMEM; - pc2_priv = board->private_data; - nec_priv = &pc2_priv->nec7210_priv; - nec_priv->read_byte = nec7210_ioport_read_byte; - nec_priv->write_byte = nec7210_ioport_write_byte; - nec_priv->type = chipset; - -#ifndef PC2_DMA - /* - * board->dev hasn't been initialized, so forget about DMA until this driver - * is adapted to use isa_register_driver. - */ - if (config->ibdma) - // driver needs to be adapted to use isa_register_driver to get a struct device* - dev_err(board->gpib_dev, "DMA disabled for pc2 gpib"); -#else - if (config->ibdma) { - nec_priv->dma_buffer_length = 0x1000; - nec_priv->dma_buffer = dma_alloc_coherent(board->dev, - nec_priv->dma_buffer_length, & - nec_priv->dma_buffer_addr, GFP_ATOMIC); - if (!nec_priv->dma_buffer) - return -ENOMEM; - - // request isa dma channel - if (request_dma(config->ibdma, "pc2")) { - dev_err(board->gpib_dev, "can't request DMA %d\n", config->ibdma); - return -1; - } - nec_priv->dma_channel = config->ibdma; - } -#endif - - return 0; -} - -static int pc2_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - int isr_flags = 0; - struct pc2_priv *pc2_priv; - struct nec7210_priv *nec_priv; - int retval; - - retval = pc2_generic_attach(board, config, NEC7210); - if (retval) - return retval; - - pc2_priv = board->private_data; - nec_priv = &pc2_priv->nec7210_priv; - nec_priv->offset = pc2_reg_offset; - - if (!request_region(config->ibbase, pc2_iosize, "pc2")) { - dev_err(board->gpib_dev, "ioports are already in use\n"); - return -EBUSY; - } - nec_priv->iobase = config->ibbase; - - nec7210_board_reset(nec_priv, board); - - // install interrupt handler - if (config->ibirq) { - if (request_irq(config->ibirq, pc2_interrupt, isr_flags, "pc2", board)) { - dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq); - return -EBUSY; - } - } - pc2_priv->irq = config->ibirq; - /* poll so we can detect assertion of ATN */ - if (gpib_request_pseudo_irq(board, pc2_interrupt)) { - dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n"); - return -1; - } - /* set internal counter register for 8 MHz input clock */ - write_byte(nec_priv, ICR | 8, AUXMR); - - nec7210_board_online(nec_priv, board); - - return 0; -} - -static void pc2_detach(struct gpib_board *board) -{ - struct pc2_priv *pc2_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (pc2_priv) { - nec_priv = &pc2_priv->nec7210_priv; -#ifdef PC2_DMA - if (nec_priv->dma_channel) - free_dma(nec_priv->dma_channel); -#endif - gpib_free_pseudo_irq(board); - if (pc2_priv->irq) - free_irq(pc2_priv->irq, board); - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - release_region(nec_priv->iobase, pc2_iosize); - } - if (nec_priv->dma_buffer) { - dma_free_coherent(board->dev, nec_priv->dma_buffer_length, - nec_priv->dma_buffer, nec_priv->dma_buffer_addr); - nec_priv->dma_buffer = NULL; - } - } - free_private(board); -} - -static int pc2a_common_attach(struct gpib_board *board, const struct gpib_board_config *config, - unsigned int num_registers, enum nec7210_chipset chipset) -{ - unsigned int i, j; - struct pc2_priv *pc2_priv; - struct nec7210_priv *nec_priv; - int retval; - - retval = pc2_generic_attach(board, config, chipset); - if (retval) - return retval; - - pc2_priv = board->private_data; - nec_priv = &pc2_priv->nec7210_priv; - nec_priv->offset = pc2a_reg_offset; - - switch (config->ibbase) { - case 0x02e1: - case 0x22e1: - case 0x42e1: - case 0x62e1: - break; - default: - dev_err(board->gpib_dev, "PCIIa base range invalid, must be one of 0x[0246]2e1, but is 0x%x\n", - config->ibbase); - return -1; - } - - if (config->ibirq) { - if (config->ibirq < 2 || config->ibirq > 7) { - dev_err(board->gpib_dev, "illegal interrupt level %i\n", - config->ibirq); - return -1; - } - } else { - dev_err(board->gpib_dev, "interrupt disabled, using polling mode (slow)\n"); - } -#ifdef CHECK_IOPORTS - unsigned int err = 0; - - for (i = 0; i < num_registers; i++) { - if (check_region(config->ibbase + i * pc2a_reg_offset, 1)) - err++; - } - if (config->ibirq && check_region(pc2a_clear_intr_iobase + config->ibirq, 1)) - err++; - if (err) { - dev_err(board->gpib_dev, "ioports are already in use"); - return -EBUSY; - } -#endif - for (i = 0; i < num_registers; i++) { - if (!request_region(config->ibbase + - i * pc2a_reg_offset, 1, "pc2a")) { - dev_err(board->gpib_dev, "ioports are already in use"); - for (j = 0; j < i; j++) - release_region(config->ibbase + - j * pc2a_reg_offset, 1); - return -EBUSY; - } - } - nec_priv->iobase = config->ibbase; - if (config->ibirq) { - if (!request_region(pc2a_clear_intr_iobase + config->ibirq, 1, "pc2a")) { - dev_err(board->gpib_dev, "ioports are already in use"); - return -1; - } - pc2_priv->clear_intr_addr = pc2a_clear_intr_iobase + config->ibirq; - if (request_irq(config->ibirq, pc2a_interrupt, 0, "pc2a", board)) { - dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq); - return -EBUSY; - } - } - pc2_priv->irq = config->ibirq; - /* poll so we can detect assertion of ATN */ - if (gpib_request_pseudo_irq(board, pc2_interrupt)) { - dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n"); - return -1; - } - - // make sure interrupt is clear - if (pc2_priv->irq) - outb(0xff, CLEAR_INTR_REG(pc2_priv->irq)); - - nec7210_board_reset(nec_priv, board); - - /* set internal counter register for 8 MHz input clock */ - write_byte(nec_priv, ICR | 8, AUXMR); - - nec7210_board_online(nec_priv, board); - - return 0; -} - -static int pc2a_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - return pc2a_common_attach(board, config, pc2a_iosize, NEC7210); -} - -static int pc2a_cb7210_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - return pc2a_common_attach(board, config, pc2a_iosize, CB7210); -} - -static int pc2_2a_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - return pc2a_common_attach(board, config, pc2_2a_iosize, NAT4882); -} - -static void pc2a_common_detach(struct gpib_board *board, unsigned int num_registers) -{ - int i; - struct pc2_priv *pc2_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (pc2_priv) { - nec_priv = &pc2_priv->nec7210_priv; -#ifdef PC2_DMA - if (nec_priv->dma_channel) - free_dma(nec_priv->dma_channel); -#endif - gpib_free_pseudo_irq(board); - if (pc2_priv->irq) - free_irq(pc2_priv->irq, board); - if (nec_priv->iobase) { - nec7210_board_reset(nec_priv, board); - for (i = 0; i < num_registers; i++) - release_region(nec_priv->iobase + - i * pc2a_reg_offset, 1); - } - if (pc2_priv->clear_intr_addr) - release_region(pc2_priv->clear_intr_addr, 1); - if (nec_priv->dma_buffer) { - dma_free_coherent(board->dev, nec_priv->dma_buffer_length, - nec_priv->dma_buffer, - nec_priv->dma_buffer_addr); - nec_priv->dma_buffer = NULL; - } - } - free_private(board); -} - -static void pc2a_detach(struct gpib_board *board) -{ - pc2a_common_detach(board, pc2a_iosize); -} - -static void pc2_2a_detach(struct gpib_board *board) -{ - pc2a_common_detach(board, pc2_2a_iosize); -} - -static struct gpib_interface pc2_interface = { - .name = "pcII", - .attach = pc2_attach, - .detach = pc2_detach, - .read = pc2_read, - .write = pc2_write, - .command = pc2_command, - .take_control = pc2_take_control, - .go_to_standby = pc2_go_to_standby, - .request_system_control = pc2_request_system_control, - .interface_clear = pc2_interface_clear, - .remote_enable = pc2_remote_enable, - .enable_eos = pc2_enable_eos, - .disable_eos = pc2_disable_eos, - .parallel_poll = pc2_parallel_poll, - .parallel_poll_configure = pc2_parallel_poll_configure, - .parallel_poll_response = pc2_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = NULL, - .update_status = pc2_update_status, - .primary_address = pc2_primary_address, - .secondary_address = pc2_secondary_address, - .serial_poll_response = pc2_serial_poll_response, - .serial_poll_status = pc2_serial_poll_status, - .t1_delay = pc2_t1_delay, - .return_to_local = pc2_return_to_local, -}; - -static struct gpib_interface pc2a_interface = { - .name = "pcIIa", - .attach = pc2a_attach, - .detach = pc2a_detach, - .read = pc2_read, - .write = pc2_write, - .command = pc2_command, - .take_control = pc2_take_control, - .go_to_standby = pc2_go_to_standby, - .request_system_control = pc2_request_system_control, - .interface_clear = pc2_interface_clear, - .remote_enable = pc2_remote_enable, - .enable_eos = pc2_enable_eos, - .disable_eos = pc2_disable_eos, - .parallel_poll = pc2_parallel_poll, - .parallel_poll_configure = pc2_parallel_poll_configure, - .parallel_poll_response = pc2_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = NULL, - .update_status = pc2_update_status, - .primary_address = pc2_primary_address, - .secondary_address = pc2_secondary_address, - .serial_poll_response = pc2_serial_poll_response, - .serial_poll_status = pc2_serial_poll_status, - .t1_delay = pc2_t1_delay, - .return_to_local = pc2_return_to_local, -}; - -static struct gpib_interface pc2a_cb7210_interface = { - .name = "pcIIa_cb7210", - .attach = pc2a_cb7210_attach, - .detach = pc2a_detach, - .read = pc2_read, - .write = pc2_write, - .command = pc2_command, - .take_control = pc2_take_control, - .go_to_standby = pc2_go_to_standby, - .request_system_control = pc2_request_system_control, - .interface_clear = pc2_interface_clear, - .remote_enable = pc2_remote_enable, - .enable_eos = pc2_enable_eos, - .disable_eos = pc2_disable_eos, - .parallel_poll = pc2_parallel_poll, - .parallel_poll_configure = pc2_parallel_poll_configure, - .parallel_poll_response = pc2_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = NULL, // XXX - .update_status = pc2_update_status, - .primary_address = pc2_primary_address, - .secondary_address = pc2_secondary_address, - .serial_poll_response = pc2_serial_poll_response, - .serial_poll_status = pc2_serial_poll_status, - .t1_delay = pc2_t1_delay, - .return_to_local = pc2_return_to_local, -}; - -static struct gpib_interface pc2_2a_interface = { - .name = "pcII_IIa", - .attach = pc2_2a_attach, - .detach = pc2_2a_detach, - .read = pc2_read, - .write = pc2_write, - .command = pc2_command, - .take_control = pc2_take_control, - .go_to_standby = pc2_go_to_standby, - .request_system_control = pc2_request_system_control, - .interface_clear = pc2_interface_clear, - .remote_enable = pc2_remote_enable, - .enable_eos = pc2_enable_eos, - .disable_eos = pc2_disable_eos, - .parallel_poll = pc2_parallel_poll, - .parallel_poll_configure = pc2_parallel_poll_configure, - .parallel_poll_response = pc2_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = NULL, - .update_status = pc2_update_status, - .primary_address = pc2_primary_address, - .secondary_address = pc2_secondary_address, - .serial_poll_response = pc2_serial_poll_response, - .serial_poll_status = pc2_serial_poll_status, - .t1_delay = pc2_t1_delay, - .return_to_local = pc2_return_to_local, -}; - -static int __init pc2_init_module(void) -{ - int ret; - - ret = gpib_register_driver(&pc2_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - return ret; - } - - ret = gpib_register_driver(&pc2a_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pc2a; - } - - ret = gpib_register_driver(&pc2a_cb7210_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_cb7210; - } - - ret = gpib_register_driver(&pc2_2a_interface, THIS_MODULE); - if (ret) { - pr_err("gpib_register_driver failed: error = %d\n", ret); - goto err_pc2_2a; - } - - return 0; - -err_pc2_2a: - gpib_unregister_driver(&pc2a_cb7210_interface); -err_cb7210: - gpib_unregister_driver(&pc2a_interface); -err_pc2a: - gpib_unregister_driver(&pc2_interface); - - return ret; -} - -static void __exit pc2_exit_module(void) -{ - gpib_unregister_driver(&pc2_interface); - gpib_unregister_driver(&pc2a_interface); - gpib_unregister_driver(&pc2a_cb7210_interface); - gpib_unregister_driver(&pc2_2a_interface); -} - -module_init(pc2_init_module); -module_exit(pc2_exit_module); - diff --git a/drivers/staging/gpib/tms9914/Makefile b/drivers/staging/gpib/tms9914/Makefile deleted file mode 100644 index 4705ab07f413..000000000000 --- a/drivers/staging/gpib/tms9914/Makefile +++ /dev/null @@ -1,6 +0,0 @@ - -obj-$(CONFIG_GPIB_TMS9914) += tms9914.o - - - - diff --git a/drivers/staging/gpib/tms9914/tms9914.c b/drivers/staging/gpib/tms9914/tms9914.c deleted file mode 100644 index 72a11596a35e..000000000000 --- a/drivers/staging/gpib/tms9914/tms9914.c +++ /dev/null @@ -1,914 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * copyright : (C) 2001, 2002 by Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gpibP.h" -#include "tms9914.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB library for tms9914"); - -static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv); - -int tms9914_take_control(struct gpib_board *board, struct tms9914_priv *priv, int synchronous) -{ - int i; - const int timeout = 100; - - if (synchronous) - write_byte(priv, AUX_TCS, AUXCR); - else - write_byte(priv, AUX_TCA, AUXCR); - // busy wait until ATN is asserted - for (i = 0; i < timeout; i++) { - if ((read_byte(priv, ADSR) & HR_ATN)) - break; - udelay(1); - } - if (i == timeout) - return -ETIMEDOUT; - - clear_bit(WRITE_READY_BN, &priv->state); - - return 0; -} -EXPORT_SYMBOL_GPL(tms9914_take_control); - -/* - * The agilent 82350B has a buggy implementation of tcs which interferes with the - * operation of tca. It appears to be based on the controller state machine - * described in the TI 9900 TMS9914A data manual published in 1982. This - * manual describes tcs as putting the controller into a CWAS - * state where it waits indefinitely for ANRS and ignores tca. Since a - * functioning tca is far more important than tcs, we work around the - * problem by never issuing tcs. - * - * I don't know if this problem exists in the real tms9914a or just in the fpga - * of the 82350B. For now, only the agilent_82350b uses this workaround. - * The rest of the tms9914 based drivers still use tms9914_take_control - * directly (which does issue tcs). - */ -int tms9914_take_control_workaround(struct gpib_board *board, - struct tms9914_priv *priv, int synchronous) -{ - if (synchronous) - return -ETIMEDOUT; - return tms9914_take_control(board, priv, synchronous); -} -EXPORT_SYMBOL_GPL(tms9914_take_control_workaround); - -int tms9914_go_to_standby(struct gpib_board *board, struct tms9914_priv *priv) -{ - int i; - const int timeout = 1000; - - write_byte(priv, AUX_GTS, AUXCR); - // busy wait until ATN is released - for (i = 0; i < timeout; i++) { - if ((read_byte(priv, ADSR) & HR_ATN) == 0) - break; - udelay(1); - } - if (i == timeout) - return -ETIMEDOUT; - - clear_bit(COMMAND_READY_BN, &priv->state); - - return 0; -} -EXPORT_SYMBOL_GPL(tms9914_go_to_standby); - -void tms9914_interface_clear(struct gpib_board *board, struct tms9914_priv *priv, int assert) -{ - if (assert) { - write_byte(priv, AUX_SIC | AUX_CS, AUXCR); - - set_bit(CIC_NUM, &board->status); - } else { - write_byte(priv, AUX_SIC, AUXCR); - } -} -EXPORT_SYMBOL_GPL(tms9914_interface_clear); - -void tms9914_remote_enable(struct gpib_board *board, struct tms9914_priv *priv, int enable) -{ - if (enable) - write_byte(priv, AUX_SRE | AUX_CS, AUXCR); - else - write_byte(priv, AUX_SRE, AUXCR); -} -EXPORT_SYMBOL_GPL(tms9914_remote_enable); - -int tms9914_request_system_control(struct gpib_board *board, struct tms9914_priv *priv, - int request_control) -{ - if (request_control) { - write_byte(priv, AUX_RQC, AUXCR); - } else { - clear_bit(CIC_NUM, &board->status); - write_byte(priv, AUX_RLC, AUXCR); - } - return 0; -} -EXPORT_SYMBOL_GPL(tms9914_request_system_control); - -unsigned int tms9914_t1_delay(struct gpib_board *board, struct tms9914_priv *priv, - unsigned int nano_sec) -{ - static const int clock_period = 200; // assuming 5Mhz input clock - int num_cycles; - - num_cycles = 12; - - if (nano_sec <= 8 * clock_period) { - write_byte(priv, AUX_STDL | AUX_CS, AUXCR); - num_cycles = 8; - } else { - write_byte(priv, AUX_STDL, AUXCR); - } - - if (nano_sec <= 4 * clock_period) { - write_byte(priv, AUX_VSTDL | AUX_CS, AUXCR); - num_cycles = 4; - } else { - write_byte(priv, AUX_VSTDL, AUXCR); - } - - return num_cycles * clock_period; -} -EXPORT_SYMBOL_GPL(tms9914_t1_delay); - -void tms9914_return_to_local(const struct gpib_board *board, struct tms9914_priv *priv) -{ - write_byte(priv, AUX_RTL, AUXCR); -} -EXPORT_SYMBOL_GPL(tms9914_return_to_local); - -void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode) -{ - switch (mode) { - case TMS9914_HOLDOFF_NONE: - write_byte(priv, AUX_HLDE, AUXCR); - write_byte(priv, AUX_HLDA, AUXCR); - break; - case TMS9914_HOLDOFF_EOI: - write_byte(priv, AUX_HLDE | AUX_CS, AUXCR); - write_byte(priv, AUX_HLDA, AUXCR); - break; - case TMS9914_HOLDOFF_ALL: - write_byte(priv, AUX_HLDE, AUXCR); - write_byte(priv, AUX_HLDA | AUX_CS, AUXCR); - break; - default: - pr_err("bug! bad holdoff mode %i\n", mode); - break; - } - priv->holdoff_mode = mode; -} -EXPORT_SYMBOL_GPL(tms9914_set_holdoff_mode); - -void tms9914_release_holdoff(struct tms9914_priv *priv) -{ - if (priv->holdoff_active) { - write_byte(priv, AUX_RHDF, AUXCR); - priv->holdoff_active = 0; - } -} -EXPORT_SYMBOL_GPL(tms9914_release_holdoff); - -int tms9914_enable_eos(struct gpib_board *board, struct tms9914_priv *priv, u8 eos_byte, - int compare_8_bits) -{ - priv->eos = eos_byte; - priv->eos_flags = REOS; - if (compare_8_bits) - priv->eos_flags |= BIN; - return 0; -} -EXPORT_SYMBOL(tms9914_enable_eos); - -void tms9914_disable_eos(struct gpib_board *board, struct tms9914_priv *priv) -{ - priv->eos_flags &= ~REOS; -} -EXPORT_SYMBOL(tms9914_disable_eos); - -int tms9914_parallel_poll(struct gpib_board *board, struct tms9914_priv *priv, u8 *result) -{ - // execute parallel poll - write_byte(priv, AUX_CS | AUX_RPP, AUXCR); - udelay(2); - *result = read_byte(priv, CPTR); - // clear parallel poll state - write_byte(priv, AUX_RPP, AUXCR); - return 0; -} -EXPORT_SYMBOL(tms9914_parallel_poll); - -static void set_ppoll_reg(struct tms9914_priv *priv, int enable, - unsigned int dio_line, int sense, int ist) -{ - u8 dio_byte; - - if (enable && ((sense && ist) || (!sense && !ist))) { - dio_byte = 1 << (dio_line - 1); - write_byte(priv, dio_byte, PPR); - } else { - write_byte(priv, 0, PPR); - } -} - -void tms9914_parallel_poll_configure(struct gpib_board *board, - struct tms9914_priv *priv, u8 config) -{ - priv->ppoll_enable = (config & PPC_DISABLE) == 0; - priv->ppoll_line = (config & PPC_DIO_MASK) + 1; - priv->ppoll_sense = (config & PPC_SENSE) != 0; - set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, board->ist); -} -EXPORT_SYMBOL(tms9914_parallel_poll_configure); - -void tms9914_parallel_poll_response(struct gpib_board *board, - struct tms9914_priv *priv, int ist) -{ - set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, ist); -} -EXPORT_SYMBOL(tms9914_parallel_poll_response); - -void tms9914_serial_poll_response(struct gpib_board *board, - struct tms9914_priv *priv, u8 status) -{ - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - write_byte(priv, status, SPMR); - priv->spoll_status = status; - if (status & request_service_bit) - write_byte(priv, AUX_RSV2 | AUX_CS, AUXCR); - else - write_byte(priv, AUX_RSV2, AUXCR); - spin_unlock_irqrestore(&board->spinlock, flags); -} -EXPORT_SYMBOL(tms9914_serial_poll_response); - -u8 tms9914_serial_poll_status(struct gpib_board *board, struct tms9914_priv *priv) -{ - u8 status; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - status = priv->spoll_status; - spin_unlock_irqrestore(&board->spinlock, flags); - - return status; -} -EXPORT_SYMBOL(tms9914_serial_poll_status); - -int tms9914_primary_address(struct gpib_board *board, - struct tms9914_priv *priv, unsigned int address) -{ - // put primary address in address0 - write_byte(priv, address & ADDRESS_MASK, ADR); - return 0; -} -EXPORT_SYMBOL(tms9914_primary_address); - -int tms9914_secondary_address(struct gpib_board *board, struct tms9914_priv *priv, - unsigned int address, int enable) -{ - if (enable) - priv->imr1_bits |= HR_APTIE; - else - priv->imr1_bits &= ~HR_APTIE; - - write_byte(priv, priv->imr1_bits, IMR1); - return 0; -} -EXPORT_SYMBOL(tms9914_secondary_address); - -unsigned int tms9914_update_status(struct gpib_board *board, struct tms9914_priv *priv, - unsigned int clear_mask) -{ - unsigned long flags; - unsigned int retval; - - spin_lock_irqsave(&board->spinlock, flags); - retval = update_status_nolock(board, priv); - board->status &= ~clear_mask; - spin_unlock_irqrestore(&board->spinlock, flags); - - return retval; -} -EXPORT_SYMBOL(tms9914_update_status); - -static void update_talker_state(struct tms9914_priv *priv, unsigned int address_status_bits) -{ - if (address_status_bits & HR_TA) { - if (address_status_bits & HR_ATN) - priv->talker_state = talker_addressed; - else - /* - * this could also be serial_poll_active, but the tms9914 provides no - * way to distinguish, so we'll assume talker_active - */ - priv->talker_state = talker_active; - } else { - priv->talker_state = talker_idle; - } -} - -static void update_listener_state(struct tms9914_priv *priv, unsigned int address_status_bits) -{ - if (address_status_bits & HR_LA) { - if (address_status_bits & HR_ATN) - priv->listener_state = listener_addressed; - else - priv->listener_state = listener_active; - } else { - priv->listener_state = listener_idle; - } -} - -static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv) -{ - int address_status; - int bsr_bits; - - address_status = read_byte(priv, ADSR); - - // check for remote/local - if (address_status & HR_REM) - set_bit(REM_NUM, &board->status); - else - clear_bit(REM_NUM, &board->status); - // check for lockout - if (address_status & HR_LLO) - set_bit(LOK_NUM, &board->status); - else - clear_bit(LOK_NUM, &board->status); - // check for ATN - if (address_status & HR_ATN) - set_bit(ATN_NUM, &board->status); - else - clear_bit(ATN_NUM, &board->status); - // check for talker/listener addressed - update_talker_state(priv, address_status); - if (priv->talker_state == talker_active || priv->talker_state == talker_addressed) - set_bit(TACS_NUM, &board->status); - else - clear_bit(TACS_NUM, &board->status); - - update_listener_state(priv, address_status); - if (priv->listener_state == listener_active || priv->listener_state == listener_addressed) - set_bit(LACS_NUM, &board->status); - else - clear_bit(LACS_NUM, &board->status); - // Check for SRQI - not reset elsewhere except in autospoll - if (board->status & SRQI) { - bsr_bits = read_byte(priv, BSR); - if (!(bsr_bits & BSR_SRQ_BIT)) - clear_bit(SRQI_NUM, &board->status); - } - - dev_dbg(board->gpib_dev, "status 0x%lx, state 0x%lx\n", board->status, priv->state); - - return board->status; -} - -int tms9914_line_status(const struct gpib_board *board, struct tms9914_priv *priv) -{ - int bsr_bits; - int status = VALID_ALL; - - bsr_bits = read_byte(priv, BSR); - - if (bsr_bits & BSR_REN_BIT) - status |= BUS_REN; - if (bsr_bits & BSR_IFC_BIT) - status |= BUS_IFC; - if (bsr_bits & BSR_SRQ_BIT) - status |= BUS_SRQ; - if (bsr_bits & BSR_EOI_BIT) - status |= BUS_EOI; - if (bsr_bits & BSR_NRFD_BIT) - status |= BUS_NRFD; - if (bsr_bits & BSR_NDAC_BIT) - status |= BUS_NDAC; - if (bsr_bits & BSR_DAV_BIT) - status |= BUS_DAV; - if (bsr_bits & BSR_ATN_BIT) - status |= BUS_ATN; - - return status; -} -EXPORT_SYMBOL(tms9914_line_status); - -static int check_for_eos(struct tms9914_priv *priv, u8 byte) -{ - static const u8 seven_bit_compare_mask = 0x7f; - - if ((priv->eos_flags & REOS) == 0) - return 0; - - if (priv->eos_flags & BIN) { - if (priv->eos == byte) - return 1; - } else { - if ((priv->eos & seven_bit_compare_mask) == (byte & seven_bit_compare_mask)) - return 1; - } - return 0; -} - -static int wait_for_read_byte(struct gpib_board *board, struct tms9914_priv *priv) -{ - if (wait_event_interruptible(board->wait, - test_bit(READ_READY_BN, &priv->state) || - test_bit(DEV_CLEAR_BN, &priv->state) || - test_bit(TIMO_NUM, &board->status))) - return -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - - if (test_bit(DEV_CLEAR_BN, &priv->state)) - return -EINTR; - return 0; -} - -static inline u8 tms9914_read_data_in(struct gpib_board *board, - struct tms9914_priv *priv, int *end) -{ - unsigned long flags; - u8 data; - - spin_lock_irqsave(&board->spinlock, flags); - clear_bit(READ_READY_BN, &priv->state); - data = read_byte(priv, DIR); - if (test_and_clear_bit(RECEIVED_END_BN, &priv->state)) - *end = 1; - else - *end = 0; - switch (priv->holdoff_mode) { - case TMS9914_HOLDOFF_EOI: - if (*end) - priv->holdoff_active = 1; - break; - case TMS9914_HOLDOFF_ALL: - priv->holdoff_active = 1; - break; - case TMS9914_HOLDOFF_NONE: - break; - default: - dev_err(board->gpib_dev, "bug! bad holdoff mode %i\n", priv->holdoff_mode); - break; - } - spin_unlock_irqrestore(&board->spinlock, flags); - - return data; -} - -static int pio_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - ssize_t retval = 0; - - *bytes_read = 0; - *end = 0; - while (*bytes_read < length && *end == 0) { - tms9914_release_holdoff(priv); - retval = wait_for_read_byte(board, priv); - if (retval < 0) - return retval; - buffer[(*bytes_read)++] = tms9914_read_data_in(board, priv, end); - - if (check_for_eos(priv, buffer[*bytes_read - 1])) - *end = 1; - } - - return retval; -} - -int tms9914_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, - size_t length, int *end, size_t *bytes_read) -{ - ssize_t retval = 0; - size_t num_bytes; - - *end = 0; - *bytes_read = 0; - if (length == 0) - return 0; - - clear_bit(DEV_CLEAR_BN, &priv->state); - - // transfer data (except for last byte) - if (length > 1) { - if (priv->eos_flags & REOS) - tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); - else - tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_EOI); - // PIO transfer - retval = pio_read(board, priv, buffer, length - 1, end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0) - return retval; - buffer += num_bytes; - length -= num_bytes; - } - // read last bytes if we haven't received an END yet - if (*end == 0) { - // make sure we holdoff after last byte read - tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); - retval = pio_read(board, priv, buffer, length, end, &num_bytes); - *bytes_read += num_bytes; - if (retval < 0) - return retval; - } - return 0; -} -EXPORT_SYMBOL(tms9914_read); - -static int pio_write_wait(struct gpib_board *board, struct tms9914_priv *priv) -{ - // wait until next byte is ready to be sent - if (wait_event_interruptible(board->wait, - test_bit(WRITE_READY_BN, &priv->state) || - test_bit(BUS_ERROR_BN, &priv->state) || - test_bit(DEV_CLEAR_BN, &priv->state) || - test_bit(TIMO_NUM, &board->status))) - return -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - if (test_bit(BUS_ERROR_BN, &priv->state)) - return -EIO; - if (test_bit(DEV_CLEAR_BN, &priv->state)) - return -EINTR; - - return 0; -} - -static int pio_write(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, - size_t length, size_t *bytes_written) -{ - ssize_t retval = 0; - unsigned long flags; - - *bytes_written = 0; - while (*bytes_written < length) { - retval = pio_write_wait(board, priv); - if (retval < 0) - break; - - spin_lock_irqsave(&board->spinlock, flags); - clear_bit(WRITE_READY_BN, &priv->state); - write_byte(priv, buffer[(*bytes_written)++], CDOR); - spin_unlock_irqrestore(&board->spinlock, flags); - } - retval = pio_write_wait(board, priv); - if (retval < 0) - return retval; - - return length; -} - -int tms9914_write(struct gpib_board *board, struct tms9914_priv *priv, - u8 *buffer, size_t length, int send_eoi, size_t *bytes_written) -{ - ssize_t retval = 0; - - *bytes_written = 0; - if (length == 0) - return 0; - - clear_bit(BUS_ERROR_BN, &priv->state); - clear_bit(DEV_CLEAR_BN, &priv->state); - - if (send_eoi) - length-- ; /* save the last byte for sending EOI */ - - if (length > 0) { - size_t num_bytes; - // PIO transfer - retval = pio_write(board, priv, buffer, length, &num_bytes); - *bytes_written += num_bytes; - if (retval < 0) - return retval; - } - if (send_eoi) { - size_t num_bytes; - /*send EOI */ - write_byte(priv, AUX_SEOI, AUXCR); - - retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes); - *bytes_written += num_bytes; - } - return retval; -} -EXPORT_SYMBOL(tms9914_write); - -static void check_my_address_state(struct gpib_board *board, - struct tms9914_priv *priv, int cmd_byte) -{ - if (cmd_byte == MLA(board->pad)) { - priv->primary_listen_addressed = 1; - // become active listener - if (board->sad < 0) - write_byte(priv, AUX_LON | AUX_CS, AUXCR); - } else if (board->sad >= 0 && priv->primary_listen_addressed && - cmd_byte == MSA(board->sad)) { - // become active listener - write_byte(priv, AUX_LON | AUX_CS, AUXCR); - } else if (cmd_byte != MLA(board->pad) && (cmd_byte & 0xe0) == LAD) { - priv->primary_listen_addressed = 0; - } else if (cmd_byte == UNL) { - priv->primary_listen_addressed = 0; - write_byte(priv, AUX_LON, AUXCR); - } else if (cmd_byte == MTA(board->pad)) { - priv->primary_talk_addressed = 1; - if (board->sad < 0) - // make active talker - write_byte(priv, AUX_TON | AUX_CS, AUXCR); - } else if (board->sad >= 0 && priv->primary_talk_addressed && - cmd_byte == MSA(board->sad)) { - // become active talker - write_byte(priv, AUX_TON | AUX_CS, AUXCR); - } else if (cmd_byte != MTA(board->pad) && (cmd_byte & 0xe0) == TAD) { - // Other Talk Address - priv->primary_talk_addressed = 0; - write_byte(priv, AUX_TON, AUXCR); - } else if (cmd_byte == UNT) { - priv->primary_talk_addressed = 0; - write_byte(priv, AUX_TON, AUXCR); - } -} - -int tms9914_command(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, - size_t length, size_t *bytes_written) -{ - int retval = 0; - unsigned long flags; - - *bytes_written = 0; - while (*bytes_written < length) { - if (wait_event_interruptible(board->wait, - test_bit(COMMAND_READY_BN, - &priv->state) || - test_bit(TIMO_NUM, &board->status))) - break; - if (test_bit(TIMO_NUM, &board->status)) - break; - - spin_lock_irqsave(&board->spinlock, flags); - clear_bit(COMMAND_READY_BN, &priv->state); - write_byte(priv, buffer[*bytes_written], CDOR); - spin_unlock_irqrestore(&board->spinlock, flags); - - check_my_address_state(board, priv, buffer[*bytes_written]); - - ++(*bytes_written); - } - // wait until last command byte is written - if (wait_event_interruptible(board->wait, - test_bit(COMMAND_READY_BN, - &priv->state) || test_bit(TIMO_NUM, &board->status))) - retval = -ERESTARTSYS; - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - - return retval; -} -EXPORT_SYMBOL(tms9914_command); - -irqreturn_t tms9914_interrupt(struct gpib_board *board, struct tms9914_priv *priv) -{ - int status0, status1; - - // read interrupt status (also clears status) - status0 = read_byte(priv, ISR0); - status1 = read_byte(priv, ISR1); - return tms9914_interrupt_have_status(board, priv, status0, status1); -} -EXPORT_SYMBOL(tms9914_interrupt); - -irqreturn_t tms9914_interrupt_have_status(struct gpib_board *board, struct tms9914_priv *priv, - int status0, int status1) -{ - // record reception of END - if (status0 & HR_END) - set_bit(RECEIVED_END_BN, &priv->state); - // get incoming data in PIO mode - if ((status0 & HR_BI)) - set_bit(READ_READY_BN, &priv->state); - if ((status0 & HR_BO)) { - if (read_byte(priv, ADSR) & HR_ATN) - set_bit(COMMAND_READY_BN, &priv->state); - else - set_bit(WRITE_READY_BN, &priv->state); - } - - if (status0 & HR_SPAS) { - priv->spoll_status &= ~request_service_bit; - write_byte(priv, priv->spoll_status, SPMR); - // FIXME: set SPOLL status bit - } - // record service request in status - if (status1 & HR_SRQ) - set_bit(SRQI_NUM, &board->status); - // have been addressed (with secondary addressing disabled) - if (status1 & HR_MA) - // clear dac holdoff - write_byte(priv, AUX_VAL, AUXCR); - // unrecognized command received - if (status1 & HR_UNC) { - unsigned short command_byte = read_byte(priv, CPTR) & gpib_command_mask; - - switch (command_byte) { - case PP_CONFIG: - priv->ppoll_configure_state = 1; - /* - * AUX_PTS generates another UNC interrupt on the next command byte - * if it is in the secondary address group (such as PPE and PPD). - */ - write_byte(priv, AUX_PTS, AUXCR); - write_byte(priv, AUX_VAL, AUXCR); - break; - case PPU: - tms9914_parallel_poll_configure(board, priv, command_byte); - write_byte(priv, AUX_VAL, AUXCR); - break; - default: - if (is_PPE(command_byte) || is_PPD(command_byte)) { - if (priv->ppoll_configure_state) { - tms9914_parallel_poll_configure(board, priv, command_byte); - write_byte(priv, AUX_VAL, AUXCR); - } else {// bad parallel poll configure byte - // clear dac holdoff - write_byte(priv, AUX_INVAL, AUXCR); - } - } else { - // clear dac holdoff - write_byte(priv, AUX_INVAL, AUXCR); - } - break; - } - - if (in_primary_command_group(command_byte) && command_byte != PP_CONFIG) - priv->ppoll_configure_state = 0; - } - - if (status1 & HR_ERR) { - dev_dbg(board->gpib_dev, "gpib bus error\n"); - set_bit(BUS_ERROR_BN, &priv->state); - } - - if (status1 & HR_IFC) { - push_gpib_event(board, EVENT_IFC); - clear_bit(CIC_NUM, &board->status); - } - - if (status1 & HR_GET) { - push_gpib_event(board, EVENT_DEV_TRG); - // clear dac holdoff - write_byte(priv, AUX_VAL, AUXCR); - } - - if (status1 & HR_DCAS) { - push_gpib_event(board, EVENT_DEV_CLR); - // clear dac holdoff - write_byte(priv, AUX_VAL, AUXCR); - set_bit(DEV_CLEAR_BN, &priv->state); - } - - // check for being addressed with secondary addressing - if (status1 & HR_APT) { - if (board->sad < 0) - dev_err(board->gpib_dev, "bug, APT interrupt without secondary addressing?\n"); - if ((read_byte(priv, CPTR) & gpib_command_mask) == MSA(board->sad)) - write_byte(priv, AUX_VAL, AUXCR); - else - write_byte(priv, AUX_INVAL, AUXCR); - } - - if ((status0 & priv->imr0_bits) || (status1 & priv->imr1_bits)) { - dev_dbg(board->gpib_dev, "isr0 0x%x, imr0 0x%x, isr1 0x%x, imr1 0x%x\n", - status0, priv->imr0_bits, status1, priv->imr1_bits); - update_status_nolock(board, priv); - wake_up_interruptible(&board->wait); - } - return IRQ_HANDLED; -} -EXPORT_SYMBOL(tms9914_interrupt_have_status); - -void tms9914_board_reset(struct tms9914_priv *priv) -{ - /* chip reset */ - write_byte(priv, AUX_CHIP_RESET | AUX_CS, AUXCR); - - /* disable all interrupts */ - priv->imr0_bits = 0; - write_byte(priv, priv->imr0_bits, IMR0); - priv->imr1_bits = 0; - write_byte(priv, priv->imr1_bits, IMR1); - write_byte(priv, AUX_DAI | AUX_CS, AUXCR); - - /* clear registers by reading */ - read_byte(priv, CPTR); - read_byte(priv, ISR0); - read_byte(priv, ISR1); - - write_byte(priv, 0, SPMR); - - /* parallel poll unconfigure */ - write_byte(priv, 0, PPR); - /* request for data holdoff */ - tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); -} -EXPORT_SYMBOL_GPL(tms9914_board_reset); - -void tms9914_online(struct gpib_board *board, struct tms9914_priv *priv) -{ - /* set GPIB address */ - tms9914_primary_address(board, priv, board->pad); - tms9914_secondary_address(board, priv, board->sad, board->sad >= 0); - - /* enable tms9914 interrupts */ - priv->imr0_bits |= HR_MACIE | HR_RLCIE | HR_ENDIE | HR_BOIE | HR_BIIE | - HR_SPASIE; - priv->imr1_bits |= HR_MAIE | HR_SRQIE | HR_UNCIE | HR_ERRIE | HR_IFCIE | - HR_GETIE | HR_DCASIE; - write_byte(priv, priv->imr0_bits, IMR0); - write_byte(priv, priv->imr1_bits, IMR1); - write_byte(priv, AUX_DAI, AUXCR); - - /* turn off reset state */ - write_byte(priv, AUX_CHIP_RESET, AUXCR); -} -EXPORT_SYMBOL_GPL(tms9914_online); - -#ifdef CONFIG_HAS_IOPORT -// wrapper for inb -u8 tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num) -{ - return inb(priv->iobase + register_num * priv->offset); -} -EXPORT_SYMBOL_GPL(tms9914_ioport_read_byte); - -// wrapper for outb -void tms9914_ioport_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) -{ - outb(data, priv->iobase + register_num * priv->offset); - if (register_num == AUXCR) - udelay(1); -} -EXPORT_SYMBOL_GPL(tms9914_ioport_write_byte); -#endif - -// wrapper for readb -u8 tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num) -{ - return readb(priv->mmiobase + register_num * priv->offset); -} -EXPORT_SYMBOL_GPL(tms9914_iomem_read_byte); - -// wrapper for writeb -void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) -{ - writeb(data, priv->mmiobase + register_num * priv->offset); - if (register_num == AUXCR) - udelay(1); -} -EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte); - -static int __init tms9914_init_module(void) -{ - return 0; -} - -static void __exit tms9914_exit_module(void) -{ -} - -module_init(tms9914_init_module); -module_exit(tms9914_exit_module); - diff --git a/drivers/staging/gpib/tnt4882/Makefile b/drivers/staging/gpib/tnt4882/Makefile deleted file mode 100644 index fa1687ad0d1b..000000000000 --- a/drivers/staging/gpib/tnt4882/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882.o - -tnt4882-objs := tnt4882_gpib.o mite.o - - - diff --git a/drivers/staging/gpib/tnt4882/mite.c b/drivers/staging/gpib/tnt4882/mite.c deleted file mode 100644 index 847b96f411bd..000000000000 --- a/drivers/staging/gpib/tnt4882/mite.c +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -/* - * Hardware driver for NI Mite PCI interface chip, - * adapted from COMEDI - * - * Copyright (C) 1997-8 David A. Schleef - * Copyright (C) 2002 Frank Mori Hess - * - * The PCI-MIO E series driver was originally written by - * Tomasz Motylewski <...>, and ported to comedi by ds. - * - * References for specifications: - * - * 321747b.pdf Register Level Programmer Manual (obsolete) - * 321747c.pdf Register Level Programmer Manual (new) - * DAQ-STC reference manual - * - * Other possibly relevant info: - * - * 320517c.pdf User manual (obsolete) - * 320517f.pdf User manual (new) - * 320889a.pdf delete - * 320906c.pdf maximum signal ratings - * 321066a.pdf about 16x - * 321791a.pdf discontinuation of at-mio-16e-10 rev. c - * 321808a.pdf about at-mio-16e-10 rev P - * 321837a.pdf discontinuation of at-mio-16de-10 rev d - * 321838a.pdf about at-mio-16de-10 rev N - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mite.h" - -#define PCI_MITE_SIZE 4096 -#define PCI_DAQ_SIZE 4096 - -struct mite_struct *mite_devices; - -#define TOP_OF_PAGE(x) ((x) | (~(PAGE_MASK))) - -void mite_init(void) -{ - struct pci_dev *pcidev; - struct mite_struct *mite; - - for (pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, NULL); - pcidev; - pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, pcidev)) { - mite = kzalloc(sizeof(*mite), GFP_KERNEL); - if (!mite) - return; - - mite->pcidev = pcidev; - pci_dev_get(mite->pcidev); - mite->next = mite_devices; - mite_devices = mite; - } -} - -int mite_setup(struct mite_struct *mite) -{ - u32 addr; - - if (pci_enable_device(mite->pcidev)) { - pr_err("mite: error enabling mite.\n"); - return -EIO; - } - pci_set_master(mite->pcidev); - if (pci_request_regions(mite->pcidev, "mite")) { - pr_err("mite: failed to request mite io regions.\n"); - return -EIO; - } - addr = pci_resource_start(mite->pcidev, 0); - mite->mite_phys_addr = addr; - mite->mite_io_addr = ioremap(addr, pci_resource_len(mite->pcidev, 0)); - if (!mite->mite_io_addr) { - pr_err("mite: failed to remap mite io memory address.\n"); - return -ENOMEM; - } - addr = pci_resource_start(mite->pcidev, 1); - mite->daq_phys_addr = addr; - mite->daq_io_addr = ioremap(mite->daq_phys_addr, pci_resource_len(mite->pcidev, 1)); - if (!mite->daq_io_addr) { - pr_err("mite: failed to remap daq io memory address.\n"); - return -ENOMEM; - } - writel(mite->daq_phys_addr | WENAB, mite->mite_io_addr + MITE_IODWBSR); - mite->used = 1; - return 0; -} - -void mite_cleanup(void) -{ - struct mite_struct *mite, *next; - - for (mite = mite_devices; mite; mite = next) { - next = mite->next; - if (mite->pcidev) - pci_dev_put(mite->pcidev); - kfree(mite); - } -} - -void mite_unsetup(struct mite_struct *mite) -{ - if (!mite) - return; - if (mite->mite_io_addr) { - iounmap(mite->mite_io_addr); - mite->mite_io_addr = NULL; - } - if (mite->daq_io_addr) { - iounmap(mite->daq_io_addr); - mite->daq_io_addr = NULL; - } - if (mite->mite_phys_addr) { - pci_release_regions(mite->pcidev); - pci_disable_device(mite->pcidev); - mite->mite_phys_addr = 0; - } - mite->used = 0; -} diff --git a/drivers/staging/gpib/tnt4882/mite.h b/drivers/staging/gpib/tnt4882/mite.h deleted file mode 100644 index a1fdba9672a0..000000000000 --- a/drivers/staging/gpib/tnt4882/mite.h +++ /dev/null @@ -1,234 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -/* - * Hardware driver for NI Mite PCI interface chip - * - * Copyright (C) 1999 David A. Schleef - */ - -#ifndef _MITE_H_ -#define _MITE_H_ - -#include - -#define PCI_VENDOR_ID_NATINST 0x1093 - -//#define DEBUG_MITE - -#ifdef DEBUG_MITE -#define MDPRINTK(format, args...) pr_debug(format, ## args) -#else -#define MDPRINTK(args...) -#endif - -#define MITE_RING_SIZE 3000 -struct mite_dma_chain { - u32 count; - u32 addr; - u32 next; -}; - -struct mite_struct { - struct mite_struct *next; - int used; - - struct pci_dev *pcidev; - unsigned long mite_phys_addr; - void __iomem *mite_io_addr; - unsigned long daq_phys_addr; - void __iomem *daq_io_addr; - - int DMA_CheckNearEnd; - - struct mite_dma_chain ring[MITE_RING_SIZE]; -}; - -extern struct mite_struct *mite_devices; - -extern inline unsigned int mite_irq(struct mite_struct *mite) -{ - return mite->pcidev->irq; -}; - -extern inline unsigned int mite_device_id(struct mite_struct *mite) -{ - return mite->pcidev->device; -}; - -void mite_init(void); -void mite_cleanup(void); -int mite_setup(struct mite_struct *mite); -void mite_unsetup(struct mite_struct *mite); -void mite_list_devices(void); - -#define CHAN_OFFSET(x) (0x100 * (x)) - -/* DMA base for chan 0 is 0x500, chan 1 is 0x600 */ - -#define MITE_CHOR 0x500 -#define CHOR_DMARESET BIT(31) -#define CHOR_SET_SEND_TC BIT(11) -#define CHOR_CLR_SEND_TC BIT(10) -#define CHOR_SET_LPAUSE BIT(9) -#define CHOR_CLR_LPAUSE BIT(8) -#define CHOR_CLRDONE BIT(7) -#define CHOR_CLRRB BIT(6) -#define CHOR_CLRLC BIT(5) -#define CHOR_FRESET BIT(4) -#define CHOR_ABORT BIT(3) -#define CHOR_STOP BIT(2) -#define CHOR_CONT BIT(1) -#define CHOR_START BIT(0) -#define CHOR_PON (CHOR_CLR_SEND_TC | CHOR_CLR_LPAUSE) - -#define MITE_CHCR 0x504 -#define CHCR_SET_DMA_IE BIT(31) -#define CHCR_CLR_DMA_IE BIT(30) -#define CHCR_SET_LINKP_IE BIT(29) -#define CHCR_CLR_LINKP_IE BIT(28) -#define CHCR_SET_SAR_IE BIT(27) -#define CHCR_CLR_SAR_IE BIT(26) -#define CHCR_SET_DONE_IE BIT(25) -#define CHCR_CLR_DONE_IE BIT(24) -#define CHCR_SET_MRDY_IE BIT(23) -#define CHCR_CLR_MRDY_IE BIT(22) -#define CHCR_SET_DRDY_IE BIT(21) -#define CHCR_CLR_DRDY_IE BIT(20) -#define CHCR_SET_LC_IE BIT(19) -#define CHCR_CLR_LC_IE BIT(18) -#define CHCR_SET_CONT_RB_IE BIT(17) -#define CHCR_CLR_CONT_RB_IE BIT(16) -#define CHCR_FIFODIS BIT(15) -#define CHCR_FIFO_ON 0 -#define CHCR_BURSTEN BIT(14) -#define CHCR_NO_BURSTEN 0 -#define CHCR_NFTP(x) ((x) << 11) -#define CHCR_NFTP0 CHCR_NFTP(0) -#define CHCR_NFTP1 CHCR_NFTP(1) -#define CHCR_NFTP2 CHCR_NFTP(2) -#define CHCR_NFTP4 CHCR_NFTP(3) -#define CHCR_NFTP8 CHCR_NFTP(4) -#define CHCR_NFTP16 CHCR_NFTP(5) -#define CHCR_NETP(x) ((x) << 11) -#define CHCR_NETP0 CHCR_NETP(0) -#define CHCR_NETP1 CHCR_NETP(1) -#define CHCR_NETP2 CHCR_NETP(2) -#define CHCR_NETP4 CHCR_NETP(3) -#define CHCR_NETP8 CHCR_NETP(4) -#define CHCR_CHEND1 BIT(5) -#define CHCR_CHEND0 BIT(4) -#define CHCR_DIR BIT(3) -#define CHCR_DEV_TO_MEM CHCR_DIR -#define CHCR_MEM_TO_DEV 0 -#define CHCR_NORMAL ((0) << 0) -#define CHCR_CONTINUE ((1) << 0) -#define CHCR_RINGBUFF ((2) << 0) -#define CHCR_LINKSHORT ((4) << 0) -#define CHCR_LINKLONG ((5) << 0) -#define CHCRPON (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE | \ - CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE | \ - CHCR_CLR_LC_IE | CHCR_CLR_CONT_IE) - -#define MITE_TCR 0x508 - -/* CR bits */ -#define CR_RL(x) ((x) << 21) -#define CR_RL0 CR_RL(0) -#define CR_RL1 CR_RL(1) -#define CR_RL2 CR_RL(2) -#define CR_RL4 CR_RL(3) -#define CR_RL8 CR_RL(4) -#define CR_RL16 CR_RL(5) -#define CR_RL32 CR_RL(6) -#define CR_RL64 CR_RL(7) -#define CR_RD(x) ((x) << 19) -#define CR_RD0 CR_RD(0) -#define CR_RD32 CR_RD(1) -#define CR_RD512 CR_RD(2) -#define CR_RD8192 CR_RD(3) -#define CR_REQS(x) ((x) << 16) -#define CR_REQSDRQ0 CR_REQS(4) -#define CR_REQSDRQ1 CR_REQS(5) -#define CR_REQSDRQ2 CR_REQS(6) -#define CR_REQSDRQ3 CR_REQS(7) -#define CR_ASEQX(x) ((x) << 10) -#define CR_ASEQX0 CR_ASEQX(0) -#define CR_ASEQDONT CR_ASEQX0 -#define CR_ASEQXP1 CR_ASEQX(1) -#define CR_ASEQUP CR_ASEQXP1 -#define CR_ASEQXP2 CR_ASEQX(2) -#define CR_ASEQDOWN CR_ASEQXP2 -#define CR_ASEQXP4 CR_ASEQX(3) -#define CR_ASEQXP8 CR_ASEQX(4) -#define CR_ASEQXP16 CR_ASEQX(5) -#define CR_ASEQXP32 CR_ASEQX(6) -#define CR_ASEQXP64 CR_ASEQX(7) -#define CR_ASEQXM1 CR_ASEQX(9) -#define CR_ASEQXM2 CR_ASEQX(10) -#define CR_ASEQXM4 CR_ASEQX(11) -#define CR_ASEQXM8 CR_ASEQX(12) -#define CR_ASEQXM16 CR_ASEQX(13) -#define CR_ASEQXM32 CR_ASEQX(14) -#define CR_ASEQXM64 CR_ASEQX(15) -#define CR_PSIZEBYTE BIT(8) -#define CR_PSIZEHALF (2 << 8) -#define CR_PSIZEWORD (3 << 8) -#define CR_PORTCPU (0 << 6) -#define CR_PORTIO BIT(6) -#define CR_PORTVXI (2 << 6) -#define CR_PORTMXI (3 << 6) -#define CR_AMDEVICE BIT(0) - -#define CHSR_INT 0x80000000 -#define CHSR_DONE 0x02000000 -#define CHSR_LINKC 0x00080000 - -#define MITE_MCR 0x50c -#define MCRPON 0 - -#define MITE_MAR 0x510 - -#define MITE_DCR 0x514 -#define DCR_NORMAL BIT(29) -#define DCRPON 0 - -#define MITE_DAR 0x518 - -#define MITE_LKCR 0x51c - -#define MITE_LKAR 0x520 -#define MITE_LLKAR 0x524 -#define MITE_BAR 0x528 -#define MITE_BCR 0x52c -#define MITE_SAR 0x530 -#define MITE_WSCR 0x534 -#define MITE_WSER 0x538 -#define MITE_CHSR 0x53c -#define MITE_FCR 0x540 - -#define MITE_FIFO 0x80 -#define MITE_FIFOEND 0xff - -#define MITE_AMRAM 0x00 -#define MITE_AMDEVICE 0x01 -#define MITE_AMHOST_A32_SINGLE 0x09 -#define MITE_AMHOST_A24_SINGLE 0x39 -#define MITE_AMHOST_A16_SINGLE 0x29 -#define MITE_AMHOST_A32_BLOCK 0x0b -#define MITE_AMHOST_A32D64_BLOCK 0x08 -#define MITE_AMHOST_A24_BLOCK 0x3b - -enum mite_registers { - MITE_IODWBSR = 0xc0, // IO Device Window Base Size Register - MITE_CSIGR = 0x460, // chip signature - MITE_IODWBSR_1 = 0xc4, // IO Device Window Base Size Register 1 (used by 6602 boards) - MITE_IODWCR_1 = 0xf4 -}; - -enum MITE_IODWBSR_bits { - WENAB = 0x80, // window enable - WENAB_6602 = 0x8c // window enable for 6602 boards -}; - -#endif - diff --git a/drivers/staging/gpib/tnt4882/tnt4882_gpib.c b/drivers/staging/gpib/tnt4882/tnt4882_gpib.c deleted file mode 100644 index c03a976b7380..000000000000 --- a/drivers/staging/gpib/tnt4882/tnt4882_gpib.c +++ /dev/null @@ -1,1838 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/*************************************************************************** - * National Instruments boards using tnt4882 or compatible chips (at-gpib, etc). - * copyright : (C) 2001, 2002 by Frank Mori Hess - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define dev_fmt pr_fmt -#define DRV_NAME KBUILD_MODNAME - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nec7210.h" -#include "gpibP.h" -#include "mite.h" -#include "tnt4882_registers.h" - -static const int ISAPNP_VENDOR_ID_NI = ISAPNP_VENDOR('N', 'I', 'C'); -static const int ISAPNP_ID_NI_ATGPIB_TNT = 0xc601; -enum { - PCI_DEVICE_ID_NI_GPIB = 0xc801, - PCI_DEVICE_ID_NI_GPIB_PLUS = 0xc811, - PCI_DEVICE_ID_NI_GPIB_PLUS2 = 0x71ad, - PCI_DEVICE_ID_NI_PXIGPIB = 0xc821, - PCI_DEVICE_ID_NI_PMCGPIB = 0xc831, - PCI_DEVICE_ID_NI_PCIEGPIB = 0x70cf, - PCI_DEVICE_ID_NI_PCIE2GPIB = 0x710e, -// Measurement Computing PCI-488 same design as PCI-GPIB with TNT5004 - PCI_DEVICE_ID_MC_PCI488 = 0x7259, - PCI_DEVICE_ID_CEC_NI_GPIB = 0x7258 -}; - -// struct which defines private_data for tnt4882 devices -struct tnt4882_priv { - struct nec7210_priv nec7210_priv; - struct mite_struct *mite; - struct pnp_dev *pnp_dev; - unsigned int irq; - unsigned short imr0_bits; - unsigned short imr3_bits; - unsigned short auxg_bits; // bits written to auxiliary register G -}; - -static irqreturn_t tnt4882_internal_interrupt(struct gpib_board *board); - -// register offset for nec7210 compatible registers -static const int atgpib_reg_offset = 2; - -// number of ioports used -static const int atgpib_iosize = 32; - -/* paged io */ -static inline unsigned int tnt_paged_readb(struct tnt4882_priv *priv, unsigned long offset) -{ - iowrite8(AUX_PAGEIN, priv->nec7210_priv.mmiobase + AUXMR * priv->nec7210_priv.offset); - udelay(1); - return ioread8(priv->nec7210_priv.mmiobase + offset); -} - -static inline void tnt_paged_writeb(struct tnt4882_priv *priv, unsigned int value, - unsigned long offset) -{ - iowrite8(AUX_PAGEIN, priv->nec7210_priv.mmiobase + AUXMR * priv->nec7210_priv.offset); - udelay(1); - iowrite8(value, priv->nec7210_priv.mmiobase + offset); -} - -/* readb/writeb wrappers */ -static inline unsigned short tnt_readb(struct tnt4882_priv *priv, unsigned long offset) -{ - void __iomem *address = priv->nec7210_priv.mmiobase + offset; - unsigned long flags; - unsigned short retval; - spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock; - - spin_lock_irqsave(register_lock, flags); - switch (offset) { - case CSR: - case SASR: - case ISR0: - case BSR: - switch (priv->nec7210_priv.type) { - case TNT4882: - case TNT5004: - retval = ioread8(address); - break; - case NAT4882: - retval = tnt_paged_readb(priv, offset - tnt_pagein_offset); - break; - case NEC7210: - retval = 0; - break; - default: - retval = 0; - break; - } - break; - default: - retval = ioread8(address); - break; - } - spin_unlock_irqrestore(register_lock, flags); - return retval; -} - -static inline void tnt_writeb(struct tnt4882_priv *priv, unsigned short value, unsigned long offset) -{ - void __iomem *address = priv->nec7210_priv.mmiobase + offset; - unsigned long flags; - spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock; - - spin_lock_irqsave(register_lock, flags); - switch (offset) { - case KEYREG: - case IMR0: - case BCR: - switch (priv->nec7210_priv.type) { - case TNT4882: - case TNT5004: - iowrite8(value, address); - break; - case NAT4882: - tnt_paged_writeb(priv, value, offset - tnt_pagein_offset); - break; - case NEC7210: - break; - default: - break; - } - break; - default: - iowrite8(value, address); - break; - } - spin_unlock_irqrestore(register_lock, flags); -} - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPIB driver for National Instruments boards using tnt4882 or compatible chips"); - -static int tnt4882_line_status(const struct gpib_board *board) -{ - int status = VALID_ALL; - int bcsr_bits; - struct tnt4882_priv *tnt_priv; - - tnt_priv = board->private_data; - - bcsr_bits = tnt_readb(tnt_priv, BSR); - - if (bcsr_bits & BCSR_REN_BIT) - status |= BUS_REN; - if (bcsr_bits & BCSR_IFC_BIT) - status |= BUS_IFC; - if (bcsr_bits & BCSR_SRQ_BIT) - status |= BUS_SRQ; - if (bcsr_bits & BCSR_EOI_BIT) - status |= BUS_EOI; - if (bcsr_bits & BCSR_NRFD_BIT) - status |= BUS_NRFD; - if (bcsr_bits & BCSR_NDAC_BIT) - status |= BUS_NDAC; - if (bcsr_bits & BCSR_DAV_BIT) - status |= BUS_DAV; - if (bcsr_bits & BCSR_ATN_BIT) - status |= BUS_ATN; - - return status; -} - -static int tnt4882_t1_delay(struct gpib_board *board, unsigned int nano_sec) -{ - struct tnt4882_priv *tnt_priv = board->private_data; - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - unsigned int retval; - - retval = nec7210_t1_delay(board, nec_priv, nano_sec); - if (nec_priv->type == NEC7210) - return retval; - - if (nano_sec <= 350) { - tnt_writeb(tnt_priv, MSTD, KEYREG); - retval = 350; - } else { - tnt_writeb(tnt_priv, 0, KEYREG); - } - if (nano_sec > 500 && nano_sec <= 1100) { - write_byte(nec_priv, AUXRI | USTD, AUXMR); - retval = 1100; - } else { - write_byte(nec_priv, AUXRI, AUXMR); - } - return retval; -} - -static int fifo_word_available(struct tnt4882_priv *tnt_priv) -{ - int status2; - int retval; - - status2 = tnt_readb(tnt_priv, STS2); - retval = (status2 & AEFN) && (status2 & BEFN); - - return retval; -} - -static int fifo_byte_available(struct tnt4882_priv *tnt_priv) -{ - int status2; - int retval; - - status2 = tnt_readb(tnt_priv, STS2); - retval = (status2 & AEFN) || (status2 & BEFN); - - return retval; -} - -static int fifo_xfer_done(struct tnt4882_priv *tnt_priv) -{ - int status1; - int retval; - - status1 = tnt_readb(tnt_priv, STS1); - retval = status1 & (S_DONE | S_HALT); - - return retval; -} - -static int drain_fifo_words(struct tnt4882_priv *tnt_priv, u8 *buffer, int num_bytes) -{ - int count = 0; - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - - while (fifo_word_available(tnt_priv) && count + 2 <= num_bytes) { - short word; - - word = ioread16(nec_priv->mmiobase + FIFOB); - buffer[count++] = word & 0xff; - buffer[count++] = (word >> 8) & 0xff; - } - return count; -} - -static void tnt4882_release_holdoff(struct gpib_board *board, struct tnt4882_priv *tnt_priv) -{ - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - unsigned short sasr_bits; - - sasr_bits = tnt_readb(tnt_priv, SASR); - - /* - * tnt4882 not in one-chip mode won't always release holdoff unless we - * are in the right mode when release handshake command is given - */ - if (sasr_bits & AEHS_BIT) /* holding off due to holdoff on end mode*/ { - nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); - write_byte(nec_priv, AUX_FH, AUXMR); - } else if (sasr_bits & ANHS1_BIT) { /* held off due to holdoff on all data mode*/ - nec7210_set_handshake_mode(board, nec_priv, HR_HLDA); - write_byte(nec_priv, AUX_FH, AUXMR); - nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); - } else { /* held off due to holdoff immediately command */ - nec7210_set_handshake_mode(board, nec_priv, HR_HLDE); - write_byte(nec_priv, AUX_FH, AUXMR); - } -} - -static int tnt4882_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read) -{ - size_t count = 0; - ssize_t retval = 0; - struct tnt4882_priv *tnt_priv = board->private_data; - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - unsigned int bits; - s32 hw_count; - unsigned long flags; - - *bytes_read = 0; - // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race - clear_bit(DEV_CLEAR_BN, &nec_priv->state); - clear_bit(ADR_CHANGE_BN, &nec_priv->state); - - nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, HR_ENDIE); - if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI); - else - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - tnt_writeb(tnt_priv, nec_priv->auxa_bits | HR_HLDA, CCR); - bits = TNT_B_16BIT | TNT_IN | TNT_CCEN; - tnt_writeb(tnt_priv, bits, CFG); - tnt_writeb(tnt_priv, RESET_FIFO, CMDR); - udelay(1); - // load 2's complement of count into hardware counters - hw_count = -length; - tnt_writeb(tnt_priv, hw_count & 0xff, CNT0); - tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1); - tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2); - tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3); - - tnt4882_release_holdoff(board, tnt_priv); - - tnt_writeb(tnt_priv, GO, CMDR); - udelay(1); - - spin_lock_irqsave(&board->spinlock, flags); - tnt_priv->imr3_bits |= HR_DONE | HR_NEF; - tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); - spin_unlock_irqrestore(&board->spinlock, flags); - - while (count + 2 <= length && - test_bit(RECEIVED_END_BN, &nec_priv->state) == 0 && - fifo_xfer_done(tnt_priv) == 0) { - // wait until a word is ready - if (wait_event_interruptible(board->wait, - fifo_word_available(tnt_priv) || - fifo_xfer_done(tnt_priv) || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(ADR_CHANGE_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - break; - } - if (test_bit(TIMO_NUM, &board->status)) { - retval = -ETIMEDOUT; - break; - } - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { - retval = -EINTR; - break; - } - if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) { - retval = -EINTR; - break; - } - - spin_lock_irqsave(&board->spinlock, flags); - count += drain_fifo_words(tnt_priv, &buffer[count], length - count); - tnt_priv->imr3_bits |= HR_NEF; - tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); - spin_unlock_irqrestore(&board->spinlock, flags); - - if (need_resched()) - schedule(); - } - // wait for last byte - if (count < length) { - spin_lock_irqsave(&board->spinlock, flags); - tnt_priv->imr3_bits |= HR_DONE | HR_NEF; - tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); - spin_unlock_irqrestore(&board->spinlock, flags); - - if (wait_event_interruptible(board->wait, - fifo_xfer_done(tnt_priv) || - test_bit(RECEIVED_END_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(ADR_CHANGE_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) { - retval = -ERESTARTSYS; - } - if (test_bit(TIMO_NUM, &board->status)) - retval = -ETIMEDOUT; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - retval = -EINTR; - if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) - retval = -EINTR; - count += drain_fifo_words(tnt_priv, &buffer[count], length - count); - if (fifo_byte_available(tnt_priv) && count < length) - buffer[count++] = tnt_readb(tnt_priv, FIFOB); - } - if (count < length) - tnt_writeb(tnt_priv, STOP, CMDR); - udelay(1); - - nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, 0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0); - /* - * force handling of any pending interrupts (seems to be needed - * to keep interrupts from getting hosed, plus for syncing - * with RECEIVED_END below) - */ - tnt4882_internal_interrupt(board); - /* RECEIVED_END should be in sync now */ - if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) - *end = 1; - if (retval < 0) { - // force immediate holdoff - write_byte(nec_priv, AUX_HLDI, AUXMR); - - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - } - *bytes_read = count; - - return retval; -} - -static int fifo_space_available(struct tnt4882_priv *tnt_priv) -{ - int status2; - int retval; - - status2 = tnt_readb(tnt_priv, STS2); - retval = (status2 & AFFN) && (status2 & BFFN); - - return retval; -} - -static unsigned int tnt_transfer_count(struct tnt4882_priv *tnt_priv) -{ - unsigned int count = 0; - - count |= tnt_readb(tnt_priv, CNT0) & 0xff; - count |= (tnt_readb(tnt_priv, CNT1) << 8) & 0xff00; - count |= (tnt_readb(tnt_priv, CNT2) << 16) & 0xff0000; - count |= (tnt_readb(tnt_priv, CNT3) << 24) & 0xff000000; - // return two's complement - return -count; -}; - -static int write_wait(struct gpib_board *board, struct tnt4882_priv *tnt_priv, - int wait_for_done, int send_commands) -{ - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - - if (wait_event_interruptible(board->wait, - (!wait_for_done && fifo_space_available(tnt_priv)) || - fifo_xfer_done(tnt_priv) || - test_bit(BUS_ERROR_BN, &nec_priv->state) || - test_bit(DEV_CLEAR_BN, &nec_priv->state) || - test_bit(TIMO_NUM, &board->status))) - return -ERESTARTSYS; - - if (test_bit(TIMO_NUM, &board->status)) - return -ETIMEDOUT; - if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) - return (send_commands) ? -ENOTCONN : -ECOMM; - if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) - return -EINTR; - return 0; -} - -static int generic_write(struct gpib_board *board, u8 *buffer, size_t length, - int send_eoi, int send_commands, size_t *bytes_written) -{ - size_t count = 0; - ssize_t retval = 0; - struct tnt4882_priv *tnt_priv = board->private_data; - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - unsigned int bits; - s32 hw_count; - unsigned long flags; - - *bytes_written = 0; - // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race - clear_bit(DEV_CLEAR_BN, &nec_priv->state); - - nec7210_set_reg_bits(nec_priv, IMR1, HR_ERRIE, HR_ERRIE); - - if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO); - else - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0); - - tnt_writeb(tnt_priv, RESET_FIFO, CMDR); - udelay(1); - - bits = TNT_B_16BIT; - if (send_eoi) { - bits |= TNT_CCEN; - if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004) - tnt_writeb(tnt_priv, AUX_SEOI, CCR); - } - if (send_commands) - bits |= TNT_COMMAND; - tnt_writeb(tnt_priv, bits, CFG); - - // load 2's complement of count into hardware counters - hw_count = -length; - tnt_writeb(tnt_priv, hw_count & 0xff, CNT0); - tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1); - tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2); - tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3); - - tnt_writeb(tnt_priv, GO, CMDR); - udelay(1); - - spin_lock_irqsave(&board->spinlock, flags); - tnt_priv->imr3_bits |= HR_DONE; - tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); - spin_unlock_irqrestore(&board->spinlock, flags); - - while (count < length) { - // wait until byte is ready to be sent - retval = write_wait(board, tnt_priv, 0, send_commands); - if (retval < 0) - break; - if (fifo_xfer_done(tnt_priv)) - break; - spin_lock_irqsave(&board->spinlock, flags); - while (fifo_space_available(tnt_priv) && count < length) { - u16 word; - - word = buffer[count++] & 0xff; - if (count < length) - word |= (buffer[count++] << 8) & 0xff00; - iowrite16(word, nec_priv->mmiobase + FIFOB); - } -// avoid unnecessary HR_NFF interrupts -// tnt_priv->imr3_bits |= HR_NFF; -// tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); - spin_unlock_irqrestore(&board->spinlock, flags); - - if (need_resched()) - schedule(); - } - // wait last byte has been sent - if (retval == 0) - retval = write_wait(board, tnt_priv, 1, send_commands); - - tnt_writeb(tnt_priv, STOP, CMDR); - udelay(1); - - nec7210_set_reg_bits(nec_priv, IMR1, HR_ERR, 0x0); - nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0x0); - /* - * force handling of any interrupts that happened - * while they were masked (this appears to be needed) - */ - tnt4882_internal_interrupt(board); - *bytes_written = length - tnt_transfer_count(tnt_priv); - return retval; -} - -static int tnt4882_accel_write(struct gpib_board *board, u8 *buffer, - size_t length, int send_eoi, size_t *bytes_written) -{ - return generic_write(board, buffer, length, send_eoi, 0, bytes_written); -} - -static int tnt4882_command(struct gpib_board *board, u8 *buffer, size_t length, - size_t *bytes_written) -{ - return generic_write(board, buffer, length, 0, 1, bytes_written); -} - -static irqreturn_t tnt4882_internal_interrupt(struct gpib_board *board) -{ - struct tnt4882_priv *priv = board->private_data; - int isr0_bits, isr3_bits, imr3_bits; - unsigned long flags; - - spin_lock_irqsave(&board->spinlock, flags); - - nec7210_interrupt(board, &priv->nec7210_priv); - - isr0_bits = tnt_readb(priv, ISR0); - isr3_bits = tnt_readb(priv, ISR3); - imr3_bits = priv->imr3_bits; - - if (isr0_bits & TNT_IFCI_BIT) - push_gpib_event(board, EVENT_IFC); - // XXX don't need this wakeup, one below should do? -// wake_up_interruptible(&board->wait); - - if (isr3_bits & HR_NFF) - priv->imr3_bits &= ~HR_NFF; - if (isr3_bits & HR_NEF) - priv->imr3_bits &= ~HR_NEF; - if (isr3_bits & HR_DONE) - priv->imr3_bits &= ~HR_DONE; - if (isr3_bits & (HR_INTR | HR_TLCI)) { - dev_dbg(board->gpib_dev, "minor %i isr0 0x%x imr0 0x%x isr3 0x%x imr3 0x%x\n", - board->minor, isr0_bits, priv->imr0_bits, isr3_bits, imr3_bits); - tnt_writeb(priv, priv->imr3_bits, IMR3); - wake_up_interruptible(&board->wait); - } - spin_unlock_irqrestore(&board->spinlock, flags); - return IRQ_HANDLED; -} - -static irqreturn_t tnt4882_interrupt(int irq, void *arg) -{ - return tnt4882_internal_interrupt(arg); -} - -// wrappers for interface functions -static int tnt4882_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, - size_t *bytes_read) -{ - struct tnt4882_priv *priv = board->private_data; - struct nec7210_priv *nec_priv = &priv->nec7210_priv; - int retval; - int dummy; - - retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read); - - if (retval < 0) { // force immediate holdoff - write_byte(nec_priv, AUX_HLDI, AUXMR); - - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - - nec7210_read_data_in(board, nec_priv, &dummy); - } - return retval; -} - -static int tnt4882_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, - size_t *bytes_written) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written); -} - -static int tnt4882_command_unaccel(struct gpib_board *board, u8 *buffer, - size_t length, size_t *bytes_written) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written); -} - -static int tnt4882_take_control(struct gpib_board *board, int synchronous) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_take_control(board, &priv->nec7210_priv, synchronous); -} - -static int tnt4882_go_to_standby(struct gpib_board *board) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_go_to_standby(board, &priv->nec7210_priv); -} - -static int tnt4882_request_system_control(struct gpib_board *board, int request_control) -{ - struct tnt4882_priv *priv = board->private_data; - int retval; - - if (request_control) { - tnt_writeb(priv, SETSC, CMDR); - udelay(1); - } - retval = nec7210_request_system_control(board, &priv->nec7210_priv, request_control); - if (!request_control) { - tnt_writeb(priv, CLRSC, CMDR); - udelay(1); - } - return retval; -} - -static void tnt4882_interface_clear(struct gpib_board *board, int assert) -{ - struct tnt4882_priv *priv = board->private_data; - - nec7210_interface_clear(board, &priv->nec7210_priv, assert); -} - -static void tnt4882_remote_enable(struct gpib_board *board, int enable) -{ - struct tnt4882_priv *priv = board->private_data; - - nec7210_remote_enable(board, &priv->nec7210_priv, enable); -} - -static int tnt4882_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits); -} - -static void tnt4882_disable_eos(struct gpib_board *board) -{ - struct tnt4882_priv *priv = board->private_data; - - nec7210_disable_eos(board, &priv->nec7210_priv); -} - -static unsigned int tnt4882_update_status(struct gpib_board *board, unsigned int clear_mask) -{ - unsigned long flags; - u8 line_status; - struct tnt4882_priv *priv = board->private_data; - - spin_lock_irqsave(&board->spinlock, flags); - board->status &= ~clear_mask; - nec7210_update_status_nolock(board, &priv->nec7210_priv); - /* set / clear SRQ state since it is not cleared by interrupt */ - line_status = tnt_readb(priv, BSR); - if (line_status & BCSR_SRQ_BIT) - set_bit(SRQI_NUM, &board->status); - else - clear_bit(SRQI_NUM, &board->status); - spin_unlock_irqrestore(&board->spinlock, flags); - return board->status; -} - -static int tnt4882_primary_address(struct gpib_board *board, unsigned int address) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_primary_address(board, &priv->nec7210_priv, address); -} - -static int tnt4882_secondary_address(struct gpib_board *board, unsigned int address, int enable) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); -} - -static int tnt4882_parallel_poll(struct gpib_board *board, u8 *result) -{ - struct tnt4882_priv *tnt_priv = board->private_data; - - if (tnt_priv->nec7210_priv.type != NEC7210) { - tnt_priv->auxg_bits |= RPP2_BIT; - write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); - udelay(2); // FIXME use parallel poll timeout - *result = read_byte(&tnt_priv->nec7210_priv, CPTR); - tnt_priv->auxg_bits &= ~RPP2_BIT; - write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); - return 0; - } else { - return nec7210_parallel_poll(board, &tnt_priv->nec7210_priv, result); - } -} - -static void tnt4882_parallel_poll_configure(struct gpib_board *board, u8 config) -{ - struct tnt4882_priv *priv = board->private_data; - - if (priv->nec7210_priv.type == TNT5004) { - /* configure locally */ - write_byte(&priv->nec7210_priv, AUXRI | 0x4, AUXMR); - if (config) - /* set response + clear sense */ - write_byte(&priv->nec7210_priv, PPR | config, AUXMR); - else - /* disable ppoll */ - write_byte(&priv->nec7210_priv, PPR | 0x10, AUXMR); - } else { - nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config); - } -} - -static void tnt4882_parallel_poll_response(struct gpib_board *board, int ist) -{ - struct tnt4882_priv *priv = board->private_data; - - nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist); -} - -/* - * this is just used by the old nec7210 isa interfaces, the newer - * boards use tnt4882_serial_poll_response2 - */ -static void tnt4882_serial_poll_response(struct gpib_board *board, u8 status) -{ - struct tnt4882_priv *priv = board->private_data; - - nec7210_serial_poll_response(board, &priv->nec7210_priv, status); -} - -static void tnt4882_serial_poll_response2(struct gpib_board *board, u8 status, - int new_reason_for_service) -{ - struct tnt4882_priv *priv = board->private_data; - unsigned long flags; - const int MSS = status & request_service_bit; - const int reqt = MSS && new_reason_for_service; - const int reqf = MSS == 0; - - spin_lock_irqsave(&board->spinlock, flags); - if (reqt) { - priv->nec7210_priv.srq_pending = 1; - clear_bit(SPOLL_NUM, &board->status); - } else { - if (reqf) - priv->nec7210_priv.srq_pending = 0; - } - if (reqt) - /* - * It may seem like a race to issue reqt before updating - * the status byte, but it is not. The chip does not - * issue the reqt until the SPMR is written to at - * a later time. - */ - write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR); - else if (reqf) - write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR); - /* - * We need to always zero bit 6 of the status byte before writing it to - * the SPMR to insure we are using - * serial poll mode SP1, and not accidentally triggering mode SP3. - */ - write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR); - spin_unlock_irqrestore(&board->spinlock, flags); -} - -static u8 tnt4882_serial_poll_status(struct gpib_board *board) -{ - struct tnt4882_priv *priv = board->private_data; - - return nec7210_serial_poll_status(board, &priv->nec7210_priv); -} - -static void tnt4882_return_to_local(struct gpib_board *board) -{ - struct tnt4882_priv *priv = board->private_data; - - nec7210_return_to_local(board, &priv->nec7210_priv); -} - -static void tnt4882_board_reset(struct tnt4882_priv *tnt_priv, struct gpib_board *board) -{ - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - - tnt_priv->imr0_bits = 0; - tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0); - tnt_priv->imr3_bits = 0; - tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); - tnt_readb(tnt_priv, IMR0); - tnt_readb(tnt_priv, IMR3); - nec7210_board_reset(nec_priv, board); -} - -static int tnt4882_allocate_private(struct gpib_board *board) -{ - struct tnt4882_priv *tnt_priv; - - board->private_data = kmalloc(sizeof(struct tnt4882_priv), GFP_KERNEL); - if (!board->private_data) - return -1; - tnt_priv = board->private_data; - memset(tnt_priv, 0, sizeof(struct tnt4882_priv)); - init_nec7210_private(&tnt_priv->nec7210_priv); - return 0; -} - -static void tnt4882_free_private(struct gpib_board *board) -{ - kfree(board->private_data); - board->private_data = NULL; -} - -static void tnt4882_init(struct tnt4882_priv *tnt_priv, const struct gpib_board *board) -{ - struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv; - - /* Turbo488 software reset */ - tnt_writeb(tnt_priv, SOFT_RESET, CMDR); - udelay(1); - - // turn off one-chip mode - tnt_writeb(tnt_priv, NODMA, HSSEL); - tnt_writeb(tnt_priv, 0, ACCWR); - // make sure we are in 7210 mode - tnt_writeb(tnt_priv, AUX_7210, AUXCR); - udelay(1); - // registers might be swapped, so write it to the swapped address too - tnt_writeb(tnt_priv, AUX_7210, SWAPPED_AUXCR); - udelay(1); - // turn on one-chip mode - if (nec_priv->type == TNT4882 || nec_priv->type == TNT5004) - tnt_writeb(tnt_priv, NODMA | TNT_ONE_CHIP_BIT, HSSEL); - else - tnt_writeb(tnt_priv, NODMA, HSSEL); - - nec7210_board_reset(nec_priv, board); - // read-clear isr0 - tnt_readb(tnt_priv, ISR0); - - // enable passing of nat4882 interrupts - tnt_priv->imr3_bits = HR_TLCI; - tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3); - - // enable interrupt - tnt_writeb(tnt_priv, 0x1, INTRT); - - // force immediate holdoff - write_byte(&tnt_priv->nec7210_priv, AUX_HLDI, AUXMR); - - set_bit(RFD_HOLDOFF_BN, &nec_priv->state); - - tnt_priv->auxg_bits = AUXRG | NTNL_BIT; - write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR); - - nec7210_board_online(nec_priv, board); - // enable interface clear interrupt for event queue - tnt_priv->imr0_bits = TNT_IMR0_ALWAYS_BITS | TNT_ATNI_BIT | TNT_IFCIE_BIT; - tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0); -} - -static int ni_pci_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct tnt4882_priv *tnt_priv; - struct nec7210_priv *nec_priv; - int isr_flags = IRQF_SHARED; - int retval; - struct mite_struct *mite; - - board->status = 0; - - if (tnt4882_allocate_private(board)) - return -ENOMEM; - tnt_priv = board->private_data; - nec_priv = &tnt_priv->nec7210_priv; - nec_priv->type = TNT4882; - nec_priv->read_byte = nec7210_locking_iomem_read_byte; - nec_priv->write_byte = nec7210_locking_iomem_write_byte; - nec_priv->offset = atgpib_reg_offset; - - if (!mite_devices) - return -ENODEV; - - for (mite = mite_devices; mite; mite = mite->next) { - short found_board; - - if (mite->used) - continue; - if (config->pci_bus >= 0 && config->pci_bus != mite->pcidev->bus->number) - continue; - if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(mite->pcidev->devfn)) - continue; - switch (mite_device_id(mite)) { - case PCI_DEVICE_ID_NI_GPIB: - case PCI_DEVICE_ID_NI_GPIB_PLUS: - case PCI_DEVICE_ID_NI_GPIB_PLUS2: - case PCI_DEVICE_ID_NI_PXIGPIB: - case PCI_DEVICE_ID_NI_PMCGPIB: - case PCI_DEVICE_ID_NI_PCIEGPIB: - case PCI_DEVICE_ID_NI_PCIE2GPIB: -// support for Measurement Computing PCI-488 - case PCI_DEVICE_ID_MC_PCI488: - case PCI_DEVICE_ID_CEC_NI_GPIB: - found_board = 1; - break; - default: - found_board = 0; - break; - } - if (found_board) - break; - } - if (!mite) - return -ENODEV; - - tnt_priv->mite = mite; - retval = mite_setup(tnt_priv->mite); - if (retval < 0) - return retval; - - nec_priv->mmiobase = tnt_priv->mite->daq_io_addr; - - // get irq - retval = request_irq(mite_irq(tnt_priv->mite), tnt4882_interrupt, isr_flags, "ni-pci-gpib", - board); - if (retval) { - dev_err(board->gpib_dev, "failed to obtain pci irq %d\n", mite_irq(tnt_priv->mite)); - return retval; - } - tnt_priv->irq = mite_irq(tnt_priv->mite); - - // TNT5004 detection - switch (tnt_readb(tnt_priv, CSR) & 0xf0) { - case 0x30: - nec_priv->type = TNT4882; - break; - case 0x40: - nec_priv->type = TNT5004; - break; - } - tnt4882_init(tnt_priv, board); - - return 0; -} - -static void ni_pci_detach(struct gpib_board *board) -{ - struct tnt4882_priv *tnt_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (tnt_priv) { - nec_priv = &tnt_priv->nec7210_priv; - - if (nec_priv->mmiobase) - tnt4882_board_reset(tnt_priv, board); - if (tnt_priv->irq) - free_irq(tnt_priv->irq, board); - if (tnt_priv->mite) - mite_unsetup(tnt_priv->mite); - } - tnt4882_free_private(board); -} - -static int ni_isapnp_find(struct pnp_dev **dev) -{ - *dev = pnp_find_dev(NULL, ISAPNP_VENDOR_ID_NI, - ISAPNP_FUNCTION(ISAPNP_ID_NI_ATGPIB_TNT), NULL); - if (!*dev || !(*dev)->card) - return -ENODEV; - if (pnp_device_attach(*dev) < 0) - return -EBUSY; - if (pnp_activate_dev(*dev) < 0) { - pnp_device_detach(*dev); - return -EAGAIN; - } - if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { - pnp_device_detach(*dev); - return -EINVAL; - } - return 0; -} - -static int ni_isa_attach_common(struct gpib_board *board, const struct gpib_board_config *config, - enum nec7210_chipset chipset) -{ - struct tnt4882_priv *tnt_priv; - struct nec7210_priv *nec_priv; - int isr_flags = 0; - u32 iobase; - int irq; - int retval; - - board->status = 0; - - if (tnt4882_allocate_private(board)) - return -ENOMEM; - tnt_priv = board->private_data; - nec_priv = &tnt_priv->nec7210_priv; - nec_priv->type = chipset; - nec_priv->read_byte = nec7210_locking_ioport_read_byte; - nec_priv->write_byte = nec7210_locking_ioport_write_byte; - nec_priv->offset = atgpib_reg_offset; - - // look for plug-n-play board - if (config->ibbase == 0) { - struct pnp_dev *dev; - - retval = ni_isapnp_find(&dev); - if (retval < 0) - return retval; - tnt_priv->pnp_dev = dev; - iobase = pnp_port_start(dev, 0); - irq = pnp_irq(dev, 0); - } else { - iobase = config->ibbase; - irq = config->ibirq; - } - // allocate ioports - if (!request_region(iobase, atgpib_iosize, "atgpib")) - return -EBUSY; - - nec_priv->mmiobase = ioport_map(iobase, atgpib_iosize); - if (!nec_priv->mmiobase) - return -EBUSY; - - // get irq - retval = request_irq(irq, tnt4882_interrupt, isr_flags, "atgpib", board); - if (retval) { - dev_err(board->gpib_dev, "failed to request ISA irq %d\n", irq); - return retval; - } - tnt_priv->irq = irq; - - tnt4882_init(tnt_priv, board); - - return 0; -} - -static int ni_tnt_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - return ni_isa_attach_common(board, config, TNT4882); -} - -static int ni_nat4882_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - return ni_isa_attach_common(board, config, NAT4882); -} - -static int ni_nec_isa_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - return ni_isa_attach_common(board, config, NEC7210); -} - -static void ni_isa_detach(struct gpib_board *board) -{ - struct tnt4882_priv *tnt_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (tnt_priv) { - nec_priv = &tnt_priv->nec7210_priv; - if (nec_priv->iobase) - tnt4882_board_reset(tnt_priv, board); - if (tnt_priv->irq) - free_irq(tnt_priv->irq, board); - if (nec_priv->mmiobase) - ioport_unmap(nec_priv->mmiobase); - if (nec_priv->iobase) - release_region(nec_priv->iobase, atgpib_iosize); - if (tnt_priv->pnp_dev) - pnp_device_detach(tnt_priv->pnp_dev); - } - tnt4882_free_private(board); -} - -static int tnt4882_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - return 0; -} - -static struct gpib_interface ni_pci_interface = { - .name = "ni_pci", - .attach = ni_pci_attach, - .detach = ni_pci_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response2 = tnt4882_serial_poll_response2, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_pci_accel_interface = { - .name = "ni_pci_accel", - .attach = ni_pci_attach, - .detach = ni_pci_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response2 = tnt4882_serial_poll_response2, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_isa_interface = { - .name = "ni_isa", - .attach = ni_tnt_isa_attach, - .detach = ni_isa_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response2 = tnt4882_serial_poll_response2, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_nat4882_isa_interface = { - .name = "ni_nat4882_isa", - .attach = ni_nat4882_isa_attach, - .detach = ni_isa_detach, - .read = tnt4882_read, - .write = tnt4882_write, - .command = tnt4882_command_unaccel, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response2 = tnt4882_serial_poll_response2, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_nec_isa_interface = { - .name = "ni_nec_isa", - .attach = ni_nec_isa_attach, - .detach = ni_isa_detach, - .read = tnt4882_read, - .write = tnt4882_write, - .command = tnt4882_command_unaccel, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = NULL, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response = tnt4882_serial_poll_response, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_isa_accel_interface = { - .name = "ni_isa_accel", - .attach = ni_tnt_isa_attach, - .detach = ni_isa_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response2 = tnt4882_serial_poll_response2, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_nat4882_isa_accel_interface = { - .name = "ni_nat4882_isa_accel", - .attach = ni_nat4882_isa_attach, - .detach = ni_isa_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command_unaccel, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response2 = tnt4882_serial_poll_response2, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_nec_isa_accel_interface = { - .name = "ni_nec_isa_accel", - .attach = ni_nec_isa_attach, - .detach = ni_isa_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command_unaccel, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = NULL, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response = tnt4882_serial_poll_response, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static const struct pci_device_id tnt4882_pci_table[] = { - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB)}, - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS)}, - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS2)}, - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PXIGPIB)}, - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PMCGPIB)}, - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIEGPIB)}, - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIE2GPIB)}, - // support for Measurement Computing PCI-488 - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_MC_PCI488)}, - {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_CEC_NI_GPIB)}, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, tnt4882_pci_table); - -static struct pci_driver tnt4882_pci_driver = { - .name = DRV_NAME, - .id_table = tnt4882_pci_table, - .probe = &tnt4882_pci_probe -}; - -#if 0 -/* unused, will be needed when the driver is turned into a pnp_driver */ -static const struct pnp_device_id tnt4882_pnp_table[] = { - {.id = "NICC601"}, - {.id = ""} -}; -MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table); -#endif - -#ifdef CONFIG_GPIB_PCMCIA -static struct gpib_interface ni_pcmcia_interface; -static struct gpib_interface ni_pcmcia_accel_interface; -static int __init init_ni_gpib_cs(void); -static void __exit exit_ni_gpib_cs(void); -#endif - -static int __init tnt4882_init_module(void) -{ - int result; - - result = pci_register_driver(&tnt4882_pci_driver); - if (result) { - pr_err("pci_register_driver failed: error = %d\n", result); - return result; - } - - result = gpib_register_driver(&ni_isa_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_isa; - } - - result = gpib_register_driver(&ni_isa_accel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_isa_accel; - } - - result = gpib_register_driver(&ni_nat4882_isa_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_nat4882_isa; - } - - result = gpib_register_driver(&ni_nat4882_isa_accel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_nat4882_isa_accel; - } - - result = gpib_register_driver(&ni_nec_isa_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_nec_isa; - } - - result = gpib_register_driver(&ni_nec_isa_accel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_nec_isa_accel; - } - - result = gpib_register_driver(&ni_pci_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_pci; - } - - result = gpib_register_driver(&ni_pci_accel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_pci_accel; - } - -#ifdef CONFIG_GPIB_PCMCIA - result = gpib_register_driver(&ni_pcmcia_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_pcmcia; - } - - result = gpib_register_driver(&ni_pcmcia_accel_interface, THIS_MODULE); - if (result) { - pr_err("gpib_register_driver failed: error = %d\n", result); - goto err_pcmcia_accel; - } - - result = init_ni_gpib_cs(); - if (result) { - pr_err("pcmcia_register_driver failed: error = %d\n", result); - goto err_pcmcia_driver; - } -#endif - - mite_init(); - - return 0; - -#ifdef CONFIG_GPIB_PCMCIA -err_pcmcia_driver: - gpib_unregister_driver(&ni_pcmcia_accel_interface); -err_pcmcia_accel: - gpib_unregister_driver(&ni_pcmcia_interface); -err_pcmcia: -#endif - gpib_unregister_driver(&ni_pci_accel_interface); -err_pci_accel: - gpib_unregister_driver(&ni_pci_interface); -err_pci: - gpib_unregister_driver(&ni_nec_isa_accel_interface); -err_nec_isa_accel: - gpib_unregister_driver(&ni_nec_isa_interface); -err_nec_isa: - gpib_unregister_driver(&ni_nat4882_isa_accel_interface); -err_nat4882_isa_accel: - gpib_unregister_driver(&ni_nat4882_isa_interface); -err_nat4882_isa: - gpib_unregister_driver(&ni_isa_accel_interface); -err_isa_accel: - gpib_unregister_driver(&ni_isa_interface); -err_isa: - pci_unregister_driver(&tnt4882_pci_driver); - - return result; -} - -static void __exit tnt4882_exit_module(void) -{ - gpib_unregister_driver(&ni_isa_interface); - gpib_unregister_driver(&ni_isa_accel_interface); - gpib_unregister_driver(&ni_nat4882_isa_interface); - gpib_unregister_driver(&ni_nat4882_isa_accel_interface); - gpib_unregister_driver(&ni_nec_isa_interface); - gpib_unregister_driver(&ni_nec_isa_accel_interface); - gpib_unregister_driver(&ni_pci_interface); - gpib_unregister_driver(&ni_pci_accel_interface); -#ifdef CONFIG_GPIB_PCMCIA - gpib_unregister_driver(&ni_pcmcia_interface); - gpib_unregister_driver(&ni_pcmcia_accel_interface); - exit_ni_gpib_cs(); -#endif - - mite_cleanup(); - - pci_unregister_driver(&tnt4882_pci_driver); -} - -#ifdef CONFIG_GPIB_PCMCIA - -#include -#include -#include -#include -#include - -#include -#include -#include - -static int ni_gpib_config(struct pcmcia_device *link); -static void ni_gpib_release(struct pcmcia_device *link); -static void ni_pcmcia_detach(struct gpib_board *board); - -/* - * A linked list of "instances" of the dummy device. Each actual - * PCMCIA card corresponds to one device instance, and is described - * by one dev_link_t structure (defined in ds.h). - * - * You may not want to use a linked list for this -- for example, the - * memory card driver uses an array of dev_link_t pointers, where minor - * device numbers are used to derive the corresponding array index. - * - * I think this dev_list is obsolete but the pointer is needed to keep - * the module instance for the ni_pcmcia_attach function. - */ - -static struct pcmcia_device *curr_dev; - -struct local_info_t { - struct pcmcia_device *p_dev; - struct gpib_board *dev; - int stop; - struct bus_operations *bus; -}; - -/* - * ni_gpib_probe() creates an "instance" of the driver, allocating - * local data structures for one device. The device is registered - * with Card Services. - */ - -static int ni_gpib_probe(struct pcmcia_device *link) -{ - struct local_info_t *info; - //struct struct gpib_board *dev; - - /* Allocate space for private device-specific data */ - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->p_dev = link; - link->priv = info; - - /* - * General socket configuration defaults can go here. In this - * client, we assume very little, and rely on the CIS for almost - * everything. In most clients, many details (i.e., number, sizes, - * and attributes of IO windows) are fixed by the nature of the - * device, and can be hard-wired here. - */ - link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - /* Register with Card Services */ - curr_dev = link; - return ni_gpib_config(link); -} - -/* - * This deletes a driver "instance". The device is de-registered - * with Card Services. If it has been released, all local data - * structures are freed. Otherwise, the structures will be freed - * when the device is released. - */ -static void ni_gpib_remove(struct pcmcia_device *link) -{ - struct local_info_t *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - if (info->dev) - ni_pcmcia_detach(info->dev); - ni_gpib_release(link); - - //free_netdev(dev); - kfree(info); -} - -static int ni_gpib_config_iteration(struct pcmcia_device *link, void *priv_data) -{ - int retval; - - retval = pcmcia_request_io(link); - if (retval != 0) - return retval; - - return 0; -} - -/* - * ni_gpib_config() is scheduled to run after a CARD_INSERTION event - * is received, to configure the PCMCIA socket, and to make the - * device available to the system. - */ -static int ni_gpib_config(struct pcmcia_device *link) -{ - //struct local_info_t *info = link->priv; - //struct gpib_board *dev = info->dev; - int last_ret; - - last_ret = pcmcia_loop_config(link, &ni_gpib_config_iteration, NULL); - if (last_ret) { - dev_warn(&link->dev, "no configuration found\n"); - ni_gpib_release(link); - return last_ret; - } - - last_ret = pcmcia_enable_device(link); - if (last_ret) { - ni_gpib_release(link); - return last_ret; - } - return 0; -} /* ni_gpib_config */ - -/* - * After a card is removed, ni_gpib_release() will unregister the - * device, and release the PCMCIA configuration. If the device is - * still open, this will be postponed until it is closed. - */ -static void ni_gpib_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} /* ni_gpib_release */ - -static int ni_gpib_suspend(struct pcmcia_device *link) -{ - //struct local_info_t *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - if (link->open) - dev_warn(&link->dev, "Device still open\n"); - //netif_device_detach(dev); - - return 0; -} - -static int ni_gpib_resume(struct pcmcia_device *link) -{ - //struct local_info_t *info = link->priv; - //struct struct gpib_board *dev = info->dev; - - /*if (link->open) { - * ni_gpib_probe(dev); / really? - * //netif_device_attach(dev); - *} - */ - return ni_gpib_config(link); -} - -static struct pcmcia_device_id ni_pcmcia_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4882), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0c71), // NI PCMCIA-GPIB+ - PCMCIA_DEVICE_NULL -}; - -MODULE_DEVICE_TABLE(pcmcia, ni_pcmcia_ids); - -static struct pcmcia_driver ni_gpib_cs_driver = { - .name = "ni_gpib_cs", - .owner = THIS_MODULE, - .drv = { .name = "ni_gpib_cs", }, - .id_table = ni_pcmcia_ids, - .probe = ni_gpib_probe, - .remove = ni_gpib_remove, - .suspend = ni_gpib_suspend, - .resume = ni_gpib_resume, -}; - -static int __init init_ni_gpib_cs(void) -{ - return pcmcia_register_driver(&ni_gpib_cs_driver); -} - -static void __exit exit_ni_gpib_cs(void) -{ - pcmcia_unregister_driver(&ni_gpib_cs_driver); -} - -static const int pcmcia_gpib_iosize = 32; - -static int ni_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config) -{ - struct local_info_t *info; - struct tnt4882_priv *tnt_priv; - struct nec7210_priv *nec_priv; - int isr_flags = IRQF_SHARED; - int retval; - - if (!curr_dev) - return -ENODEV; - - info = curr_dev->priv; - info->dev = board; - - board->status = 0; - - if (tnt4882_allocate_private(board)) - return -ENOMEM; - - tnt_priv = board->private_data; - nec_priv = &tnt_priv->nec7210_priv; - nec_priv->type = TNT4882; - nec_priv->read_byte = nec7210_locking_ioport_read_byte; - nec_priv->write_byte = nec7210_locking_ioport_write_byte; - nec_priv->offset = atgpib_reg_offset; - - if (!request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]), - DRV_NAME)) - return -ENOMEM; - - nec_priv->mmiobase = ioport_map(curr_dev->resource[0]->start, - resource_size(curr_dev->resource[0])); - if (!nec_priv->mmiobase) - return -ENOMEM; - - // get irq - retval = request_irq(curr_dev->irq, tnt4882_interrupt, isr_flags, DRV_NAME, board); - if (retval) { - dev_err(board->gpib_dev, "failed to obtain PCMCIA irq %d\n", curr_dev->irq); - return retval; - } - tnt_priv->irq = curr_dev->irq; - - tnt4882_init(tnt_priv, board); - - return 0; -} - -static void ni_pcmcia_detach(struct gpib_board *board) -{ - struct tnt4882_priv *tnt_priv = board->private_data; - struct nec7210_priv *nec_priv; - - if (tnt_priv) { - nec_priv = &tnt_priv->nec7210_priv; - if (tnt_priv->irq) - free_irq(tnt_priv->irq, board); - if (nec_priv->mmiobase) - ioport_unmap(nec_priv->mmiobase); - if (nec_priv->iobase) { - tnt4882_board_reset(tnt_priv, board); - release_region(nec_priv->iobase, pcmcia_gpib_iosize); - } - } - tnt4882_free_private(board); -} - -static struct gpib_interface ni_pcmcia_interface = { - .name = "ni_pcmcia", - .attach = ni_pcmcia_attach, - .detach = ni_pcmcia_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response = tnt4882_serial_poll_response, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -static struct gpib_interface ni_pcmcia_accel_interface = { - .name = "ni_pcmcia_accel", - .attach = ni_pcmcia_attach, - .detach = ni_pcmcia_detach, - .read = tnt4882_accel_read, - .write = tnt4882_accel_write, - .command = tnt4882_command, - .take_control = tnt4882_take_control, - .go_to_standby = tnt4882_go_to_standby, - .request_system_control = tnt4882_request_system_control, - .interface_clear = tnt4882_interface_clear, - .remote_enable = tnt4882_remote_enable, - .enable_eos = tnt4882_enable_eos, - .disable_eos = tnt4882_disable_eos, - .parallel_poll = tnt4882_parallel_poll, - .parallel_poll_configure = tnt4882_parallel_poll_configure, - .parallel_poll_response = tnt4882_parallel_poll_response, - .local_parallel_poll_mode = NULL, // XXX - .line_status = tnt4882_line_status, - .update_status = tnt4882_update_status, - .primary_address = tnt4882_primary_address, - .secondary_address = tnt4882_secondary_address, - .serial_poll_response = tnt4882_serial_poll_response, - .serial_poll_status = tnt4882_serial_poll_status, - .t1_delay = tnt4882_t1_delay, - .return_to_local = tnt4882_return_to_local, -}; - -#endif // CONFIG_GPIB_PCMCIA - -module_init(tnt4882_init_module); -module_exit(tnt4882_exit_module); diff --git a/drivers/staging/gpib/uapi/gpib.h b/drivers/staging/gpib/uapi/gpib.h deleted file mode 100644 index 2a7f5eeb9777..000000000000 --- a/drivers/staging/gpib/uapi/gpib.h +++ /dev/null @@ -1,104 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _GPIB_H -#define _GPIB_H - -#define GPIB_MAX_NUM_BOARDS 16 -#define GPIB_MAX_NUM_DESCRIPTORS 0x1000 - -enum ibsta_bit_numbers { - DCAS_NUM = 0, - DTAS_NUM = 1, - LACS_NUM = 2, - TACS_NUM = 3, - ATN_NUM = 4, - CIC_NUM = 5, - REM_NUM = 6, - LOK_NUM = 7, - CMPL_NUM = 8, - EVENT_NUM = 9, - SPOLL_NUM = 10, - RQS_NUM = 11, - SRQI_NUM = 12, - END_NUM = 13, - TIMO_NUM = 14, - ERR_NUM = 15 -}; - -/* IBSTA status bits (returned by all functions) */ -enum ibsta_bits { - DCAS = (1 << DCAS_NUM), /* device clear state */ - DTAS = (1 << DTAS_NUM), /* device trigger state */ - LACS = (1 << LACS_NUM), /* GPIB interface is addressed as Listener */ - TACS = (1 << TACS_NUM), /* GPIB interface is addressed as Talker */ - ATN = (1 << ATN_NUM), /* Attention is asserted */ - CIC = (1 << CIC_NUM), /* GPIB interface is Controller-in-Charge */ - REM = (1 << REM_NUM), /* remote state */ - LOK = (1 << LOK_NUM), /* lockout state */ - CMPL = (1 << CMPL_NUM), /* I/O is complete */ - EVENT = (1 << EVENT_NUM), /* DCAS, DTAS, or IFC has occurred */ - SPOLL = (1 << SPOLL_NUM), /* board serial polled by busmaster */ - RQS = (1 << RQS_NUM), /* Device requesting service */ - SRQI = (1 << SRQI_NUM), /* SRQ is asserted */ - END = (1 << END_NUM), /* EOI or EOS encountered */ - TIMO = (1 << TIMO_NUM), /* Time limit on I/O or wait function exceeded */ - ERR = (1 << ERR_NUM), /* Function call terminated on error */ - - device_status_mask = ERR | TIMO | END | CMPL | RQS, - board_status_mask = ERR | TIMO | END | CMPL | SPOLL | - EVENT | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS | SRQI, -}; - -/* End-of-string (EOS) modes for use with ibeos */ - -enum eos_flags { - EOS_MASK = 0x1c00, - REOS = 0x0400, /* Terminate reads on EOS */ - XEOS = 0x800, /* assert EOI when EOS char is sent */ - BIN = 0x1000 /* Do 8-bit compare on EOS */ -}; - -/* GPIB Bus Control Lines bit vector */ -enum bus_control_line { - VALID_DAV = 0x01, - VALID_NDAC = 0x02, - VALID_NRFD = 0x04, - VALID_IFC = 0x08, - VALID_REN = 0x10, - VALID_SRQ = 0x20, - VALID_ATN = 0x40, - VALID_EOI = 0x80, - VALID_ALL = 0xff, - BUS_DAV = 0x0100, /* DAV line status bit */ - BUS_NDAC = 0x0200, /* NDAC line status bit */ - BUS_NRFD = 0x0400, /* NRFD line status bit */ - BUS_IFC = 0x0800, /* IFC line status bit */ - BUS_REN = 0x1000, /* REN line status bit */ - BUS_SRQ = 0x2000, /* SRQ line status bit */ - BUS_ATN = 0x4000, /* ATN line status bit */ - BUS_EOI = 0x8000 /* EOI line status bit */ -}; - -enum ppe_bits { - PPC_DISABLE = 0x10, - PPC_SENSE = 0x8, /* parallel poll sense bit */ - PPC_DIO_MASK = 0x7 -}; - -enum { - request_service_bit = 0x40, -}; - -enum gpib_events { - EVENT_NONE = 0, - EVENT_DEV_TRG = 1, - EVENT_DEV_CLR = 2, - EVENT_IFC = 3 -}; - -#endif /* _GPIB_H */ - diff --git a/drivers/staging/gpib/uapi/gpib_ioctl.h b/drivers/staging/gpib/uapi/gpib_ioctl.h deleted file mode 100644 index d544d8e4362c..000000000000 --- a/drivers/staging/gpib/uapi/gpib_ioctl.h +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ - -/*************************************************************************** - * copyright : (C) 2002 by Frank Mori Hess - ***************************************************************************/ - -#ifndef _GPIB_IOCTL_H -#define _GPIB_IOCTL_H - -#include -#include - -#define GPIB_CODE 160 - -struct gpib_board_type_ioctl { - char name[100]; -}; - -/* argument for read/write/command ioctls */ -struct gpib_read_write_ioctl { - __u64 buffer_ptr; - __u32 requested_transfer_count; - __u32 completed_transfer_count; - __s32 end; /* end flag return for reads, end io suppression request for cmd*/ - __s32 handle; -}; - -struct gpib_open_dev_ioctl { - __u32 handle; - __u32 pad; - __s32 sad; - __u32 is_board; -}; - -struct gpib_close_dev_ioctl { - __u32 handle; -}; - -struct gpib_serial_poll_ioctl { - __u32 pad; - __s32 sad; - __u8 status_byte; - __u8 padding[3]; /* align to 32 bit boundary */ -}; - -struct gpib_eos_ioctl { - __s32 eos; - __s32 eos_flags; -}; - -struct gpib_wait_ioctl { - __s32 handle; - __s32 wait_mask; - __s32 clear_mask; - __s32 set_mask; - __s32 ibsta; - __s32 pad; - __s32 sad; - __u32 usec_timeout; -}; - -struct gpib_online_ioctl { - __u64 init_data_ptr; - __s32 init_data_length; - __s32 online; -}; - -struct gpib_spoll_bytes_ioctl { - __u32 num_bytes; - __u32 pad; - __s32 sad; -}; - -struct gpib_board_info_ioctl { - __u32 pad; - __s32 sad; - __s32 parallel_poll_configuration; - __s32 autopolling; - __s32 is_system_controller; - __u32 t1_delay; - unsigned ist : 1; - unsigned no_7_bit_eos : 1; - unsigned padding :30; /* align to 32 bit boundary */ -}; - -struct gpib_select_pci_ioctl { - __s32 pci_bus; - __s32 pci_slot; -}; - -struct gpib_ppoll_config_ioctl { - __u8 config; - unsigned set_ist : 1; - unsigned clear_ist : 1; - unsigned padding :22; /* align to 32 bit boundary */ -}; - -struct gpib_pad_ioctl { - __u32 handle; - __u32 pad; -}; - -struct gpib_sad_ioctl { - __u32 handle; - __s32 sad; -}; - -/* select a piece of hardware to attach by its sysfs device path */ -struct gpib_select_device_path_ioctl { - char device_path[0x1000]; -}; - -/* update status byte and request service */ -struct gpib_request_service2 { - __u8 status_byte; - __u8 padding[3]; /* align to 32 bit boundary */ - __s32 new_reason_for_service; -}; - -/* Standard functions. */ -enum gpib_ioctl { - IBRD = _IOWR(GPIB_CODE, 100, struct gpib_read_write_ioctl), - IBWRT = _IOWR(GPIB_CODE, 101, struct gpib_read_write_ioctl), - IBCMD = _IOWR(GPIB_CODE, 102, struct gpib_read_write_ioctl), - IBOPENDEV = _IOWR(GPIB_CODE, 3, struct gpib_open_dev_ioctl), - IBCLOSEDEV = _IOW(GPIB_CODE, 4, struct gpib_close_dev_ioctl), - IBWAIT = _IOWR(GPIB_CODE, 5, struct gpib_wait_ioctl), - IBRPP = _IOWR(GPIB_CODE, 6, __u8), - - IBSIC = _IOW(GPIB_CODE, 9, __u32), - IBSRE = _IOW(GPIB_CODE, 10, __s32), - IBGTS = _IO(GPIB_CODE, 11), - IBCAC = _IOW(GPIB_CODE, 12, __s32), - IBLINES = _IOR(GPIB_CODE, 14, __s16), - IBPAD = _IOW(GPIB_CODE, 15, struct gpib_pad_ioctl), - IBSAD = _IOW(GPIB_CODE, 16, struct gpib_sad_ioctl), - IBTMO = _IOW(GPIB_CODE, 17, __u32), - IBRSP = _IOWR(GPIB_CODE, 18, struct gpib_serial_poll_ioctl), - IBEOS = _IOW(GPIB_CODE, 19, struct gpib_eos_ioctl), - IBRSV = _IOW(GPIB_CODE, 20, __u8), - CFCBASE = _IOW(GPIB_CODE, 21, __u64), - CFCIRQ = _IOW(GPIB_CODE, 22, __u32), - CFCDMA = _IOW(GPIB_CODE, 23, __u32), - CFCBOARDTYPE = _IOW(GPIB_CODE, 24, struct gpib_board_type_ioctl), - - IBMUTEX = _IOW(GPIB_CODE, 26, __s32), - IBSPOLL_BYTES = _IOWR(GPIB_CODE, 27, struct gpib_spoll_bytes_ioctl), - IBPPC = _IOW(GPIB_CODE, 28, struct gpib_ppoll_config_ioctl), - IBBOARD_INFO = _IOR(GPIB_CODE, 29, struct gpib_board_info_ioctl), - - IBQUERY_BOARD_RSV = _IOR(GPIB_CODE, 31, __s32), - IBSELECT_PCI = _IOWR(GPIB_CODE, 32, struct gpib_select_pci_ioctl), - IBEVENT = _IOR(GPIB_CODE, 33, __s16), - IBRSC = _IOW(GPIB_CODE, 34, __s32), - IB_T1_DELAY = _IOW(GPIB_CODE, 35, __u32), - IBLOC = _IO(GPIB_CODE, 36), - - IBAUTOSPOLL = _IOW(GPIB_CODE, 38, __s16), - IBONL = _IOW(GPIB_CODE, 39, struct gpib_online_ioctl), - IBPP2_SET = _IOW(GPIB_CODE, 40, __s16), - IBPP2_GET = _IOR(GPIB_CODE, 41, __s16), - IBSELECT_DEVICE_PATH = _IOW(GPIB_CODE, 43, struct gpib_select_device_path_ioctl), - /* 44 was IBSELECT_SERIAL_NUMBER */ - IBRSV2 = _IOW(GPIB_CODE, 45, struct gpib_request_service2) -}; - -#endif /* _GPIB_IOCTL_H */ diff --git a/include/uapi/linux/gpib.h b/include/uapi/linux/gpib.h new file mode 100644 index 000000000000..2a7f5eeb9777 --- /dev/null +++ b/include/uapi/linux/gpib.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_H +#define _GPIB_H + +#define GPIB_MAX_NUM_BOARDS 16 +#define GPIB_MAX_NUM_DESCRIPTORS 0x1000 + +enum ibsta_bit_numbers { + DCAS_NUM = 0, + DTAS_NUM = 1, + LACS_NUM = 2, + TACS_NUM = 3, + ATN_NUM = 4, + CIC_NUM = 5, + REM_NUM = 6, + LOK_NUM = 7, + CMPL_NUM = 8, + EVENT_NUM = 9, + SPOLL_NUM = 10, + RQS_NUM = 11, + SRQI_NUM = 12, + END_NUM = 13, + TIMO_NUM = 14, + ERR_NUM = 15 +}; + +/* IBSTA status bits (returned by all functions) */ +enum ibsta_bits { + DCAS = (1 << DCAS_NUM), /* device clear state */ + DTAS = (1 << DTAS_NUM), /* device trigger state */ + LACS = (1 << LACS_NUM), /* GPIB interface is addressed as Listener */ + TACS = (1 << TACS_NUM), /* GPIB interface is addressed as Talker */ + ATN = (1 << ATN_NUM), /* Attention is asserted */ + CIC = (1 << CIC_NUM), /* GPIB interface is Controller-in-Charge */ + REM = (1 << REM_NUM), /* remote state */ + LOK = (1 << LOK_NUM), /* lockout state */ + CMPL = (1 << CMPL_NUM), /* I/O is complete */ + EVENT = (1 << EVENT_NUM), /* DCAS, DTAS, or IFC has occurred */ + SPOLL = (1 << SPOLL_NUM), /* board serial polled by busmaster */ + RQS = (1 << RQS_NUM), /* Device requesting service */ + SRQI = (1 << SRQI_NUM), /* SRQ is asserted */ + END = (1 << END_NUM), /* EOI or EOS encountered */ + TIMO = (1 << TIMO_NUM), /* Time limit on I/O or wait function exceeded */ + ERR = (1 << ERR_NUM), /* Function call terminated on error */ + + device_status_mask = ERR | TIMO | END | CMPL | RQS, + board_status_mask = ERR | TIMO | END | CMPL | SPOLL | + EVENT | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS | SRQI, +}; + +/* End-of-string (EOS) modes for use with ibeos */ + +enum eos_flags { + EOS_MASK = 0x1c00, + REOS = 0x0400, /* Terminate reads on EOS */ + XEOS = 0x800, /* assert EOI when EOS char is sent */ + BIN = 0x1000 /* Do 8-bit compare on EOS */ +}; + +/* GPIB Bus Control Lines bit vector */ +enum bus_control_line { + VALID_DAV = 0x01, + VALID_NDAC = 0x02, + VALID_NRFD = 0x04, + VALID_IFC = 0x08, + VALID_REN = 0x10, + VALID_SRQ = 0x20, + VALID_ATN = 0x40, + VALID_EOI = 0x80, + VALID_ALL = 0xff, + BUS_DAV = 0x0100, /* DAV line status bit */ + BUS_NDAC = 0x0200, /* NDAC line status bit */ + BUS_NRFD = 0x0400, /* NRFD line status bit */ + BUS_IFC = 0x0800, /* IFC line status bit */ + BUS_REN = 0x1000, /* REN line status bit */ + BUS_SRQ = 0x2000, /* SRQ line status bit */ + BUS_ATN = 0x4000, /* ATN line status bit */ + BUS_EOI = 0x8000 /* EOI line status bit */ +}; + +enum ppe_bits { + PPC_DISABLE = 0x10, + PPC_SENSE = 0x8, /* parallel poll sense bit */ + PPC_DIO_MASK = 0x7 +}; + +enum { + request_service_bit = 0x40, +}; + +enum gpib_events { + EVENT_NONE = 0, + EVENT_DEV_TRG = 1, + EVENT_DEV_CLR = 2, + EVENT_IFC = 3 +}; + +#endif /* _GPIB_H */ + diff --git a/include/uapi/linux/gpib_ioctl.h b/include/uapi/linux/gpib_ioctl.h new file mode 100644 index 000000000000..d544d8e4362c --- /dev/null +++ b/include/uapi/linux/gpib_ioctl.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +/*************************************************************************** + * copyright : (C) 2002 by Frank Mori Hess + ***************************************************************************/ + +#ifndef _GPIB_IOCTL_H +#define _GPIB_IOCTL_H + +#include +#include + +#define GPIB_CODE 160 + +struct gpib_board_type_ioctl { + char name[100]; +}; + +/* argument for read/write/command ioctls */ +struct gpib_read_write_ioctl { + __u64 buffer_ptr; + __u32 requested_transfer_count; + __u32 completed_transfer_count; + __s32 end; /* end flag return for reads, end io suppression request for cmd*/ + __s32 handle; +}; + +struct gpib_open_dev_ioctl { + __u32 handle; + __u32 pad; + __s32 sad; + __u32 is_board; +}; + +struct gpib_close_dev_ioctl { + __u32 handle; +}; + +struct gpib_serial_poll_ioctl { + __u32 pad; + __s32 sad; + __u8 status_byte; + __u8 padding[3]; /* align to 32 bit boundary */ +}; + +struct gpib_eos_ioctl { + __s32 eos; + __s32 eos_flags; +}; + +struct gpib_wait_ioctl { + __s32 handle; + __s32 wait_mask; + __s32 clear_mask; + __s32 set_mask; + __s32 ibsta; + __s32 pad; + __s32 sad; + __u32 usec_timeout; +}; + +struct gpib_online_ioctl { + __u64 init_data_ptr; + __s32 init_data_length; + __s32 online; +}; + +struct gpib_spoll_bytes_ioctl { + __u32 num_bytes; + __u32 pad; + __s32 sad; +}; + +struct gpib_board_info_ioctl { + __u32 pad; + __s32 sad; + __s32 parallel_poll_configuration; + __s32 autopolling; + __s32 is_system_controller; + __u32 t1_delay; + unsigned ist : 1; + unsigned no_7_bit_eos : 1; + unsigned padding :30; /* align to 32 bit boundary */ +}; + +struct gpib_select_pci_ioctl { + __s32 pci_bus; + __s32 pci_slot; +}; + +struct gpib_ppoll_config_ioctl { + __u8 config; + unsigned set_ist : 1; + unsigned clear_ist : 1; + unsigned padding :22; /* align to 32 bit boundary */ +}; + +struct gpib_pad_ioctl { + __u32 handle; + __u32 pad; +}; + +struct gpib_sad_ioctl { + __u32 handle; + __s32 sad; +}; + +/* select a piece of hardware to attach by its sysfs device path */ +struct gpib_select_device_path_ioctl { + char device_path[0x1000]; +}; + +/* update status byte and request service */ +struct gpib_request_service2 { + __u8 status_byte; + __u8 padding[3]; /* align to 32 bit boundary */ + __s32 new_reason_for_service; +}; + +/* Standard functions. */ +enum gpib_ioctl { + IBRD = _IOWR(GPIB_CODE, 100, struct gpib_read_write_ioctl), + IBWRT = _IOWR(GPIB_CODE, 101, struct gpib_read_write_ioctl), + IBCMD = _IOWR(GPIB_CODE, 102, struct gpib_read_write_ioctl), + IBOPENDEV = _IOWR(GPIB_CODE, 3, struct gpib_open_dev_ioctl), + IBCLOSEDEV = _IOW(GPIB_CODE, 4, struct gpib_close_dev_ioctl), + IBWAIT = _IOWR(GPIB_CODE, 5, struct gpib_wait_ioctl), + IBRPP = _IOWR(GPIB_CODE, 6, __u8), + + IBSIC = _IOW(GPIB_CODE, 9, __u32), + IBSRE = _IOW(GPIB_CODE, 10, __s32), + IBGTS = _IO(GPIB_CODE, 11), + IBCAC = _IOW(GPIB_CODE, 12, __s32), + IBLINES = _IOR(GPIB_CODE, 14, __s16), + IBPAD = _IOW(GPIB_CODE, 15, struct gpib_pad_ioctl), + IBSAD = _IOW(GPIB_CODE, 16, struct gpib_sad_ioctl), + IBTMO = _IOW(GPIB_CODE, 17, __u32), + IBRSP = _IOWR(GPIB_CODE, 18, struct gpib_serial_poll_ioctl), + IBEOS = _IOW(GPIB_CODE, 19, struct gpib_eos_ioctl), + IBRSV = _IOW(GPIB_CODE, 20, __u8), + CFCBASE = _IOW(GPIB_CODE, 21, __u64), + CFCIRQ = _IOW(GPIB_CODE, 22, __u32), + CFCDMA = _IOW(GPIB_CODE, 23, __u32), + CFCBOARDTYPE = _IOW(GPIB_CODE, 24, struct gpib_board_type_ioctl), + + IBMUTEX = _IOW(GPIB_CODE, 26, __s32), + IBSPOLL_BYTES = _IOWR(GPIB_CODE, 27, struct gpib_spoll_bytes_ioctl), + IBPPC = _IOW(GPIB_CODE, 28, struct gpib_ppoll_config_ioctl), + IBBOARD_INFO = _IOR(GPIB_CODE, 29, struct gpib_board_info_ioctl), + + IBQUERY_BOARD_RSV = _IOR(GPIB_CODE, 31, __s32), + IBSELECT_PCI = _IOWR(GPIB_CODE, 32, struct gpib_select_pci_ioctl), + IBEVENT = _IOR(GPIB_CODE, 33, __s16), + IBRSC = _IOW(GPIB_CODE, 34, __s32), + IB_T1_DELAY = _IOW(GPIB_CODE, 35, __u32), + IBLOC = _IO(GPIB_CODE, 36), + + IBAUTOSPOLL = _IOW(GPIB_CODE, 38, __s16), + IBONL = _IOW(GPIB_CODE, 39, struct gpib_online_ioctl), + IBPP2_SET = _IOW(GPIB_CODE, 40, __s16), + IBPP2_GET = _IOR(GPIB_CODE, 41, __s16), + IBSELECT_DEVICE_PATH = _IOW(GPIB_CODE, 43, struct gpib_select_device_path_ioctl), + /* 44 was IBSELECT_SERIAL_NUMBER */ + IBRSV2 = _IOW(GPIB_CODE, 45, struct gpib_request_service2) +}; + +#endif /* _GPIB_IOCTL_H */ -- cgit v1.2.3 From f0fdaa4ad55b7c6e46a5ccb9102bc9a96cad360f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 27 Oct 2025 21:04:09 -0700 Subject: virt: acrn: split acrn_mmio_dev_res out of acrn_mmiodev Add struct acrn_mmio_dev_res before struct acrn_mmio_dev. The former is used in the latter and breaking them up provides better kernel-doc documentation for the struct members. Suggested-by: Fei Li Signed-off-by: Randy Dunlap Acked-by: Fei Li Link: https://patch.msgid.link/20251028040409.868254-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/acrn.h | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/acrn.h b/include/uapi/linux/acrn.h index 7b714c1902eb..79e7855a8c42 100644 --- a/include/uapi/linux/acrn.h +++ b/include/uapi/linux/acrn.h @@ -418,26 +418,32 @@ struct acrn_pcidev { }; /** - * struct acrn_mmiodev - Info for assigning or de-assigning a MMIO device - * @name: Name of the MMIO device. - * @res[].user_vm_pa: Physical address of User VM of the MMIO region - * for the MMIO device. - * @res[].service_vm_pa: Physical address of Service VM of the MMIO - * region for the MMIO device. - * @res[].size: Size of the MMIO region for the MMIO device. - * @res[].mem_type: Memory type of the MMIO region for the MMIO - * device. + * struct acrn_mmio_dev_res - MMIO device resource description + * @user_vm_pa: Physical address of User VM of the MMIO region + * for the MMIO device. + * @service_vm_pa: Physical address of Service VM of the MMIO + * region for the MMIO device. + * @size: Size of the MMIO region for the MMIO device. + * @mem_type: Memory type of the MMIO region for the MMIO + * device. + */ +struct acrn_mmio_dev_res { + __u64 user_vm_pa; + __u64 service_vm_pa; + __u64 size; + __u64 mem_type; +}; + +/** + * struct acrn_mmiodev - Info for assigning or de-assigning an MMIO device + * @name: Name of the MMIO device. + * @res: Array of MMIO device descriptions * * This structure will be passed to hypervisor directly. */ struct acrn_mmiodev { __u8 name[8]; - struct { - __u64 user_vm_pa; - __u64 service_vm_pa; - __u64 size; - __u64 mem_type; - } res[ACRN_MMIODEV_RES_NUM]; + struct acrn_mmio_dev_res res[ACRN_MMIODEV_RES_NUM]; }; /** -- cgit v1.2.3 From 81c45c62dc3eefd83af8eb8df10e45705e8e3a47 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 3 Nov 2025 09:27:55 -0800 Subject: iommu/arm-smmu-v3-iommufd: Allow attaching nested domain for GBPA cases A vDEVICE has been a hard requirement for attaching a nested domain to the device. This makes sense when installing a guest STE, since a vSID must be present and given to the kernel during the vDEVICE allocation. But, when CR0.SMMUEN is disabled, VM doesn't really need a vSID to program the vSMMU behavior as GBPA will take effect, in which case the vSTE in the nested domain could have carried the bypass or abort configuration in GBPA register. Thus, having such a hard requirement doesn't work well for GBPA. Skip vmaster allocation in arm_smmu_attach_prepare_vmaster() for an abort or bypass vSTE. Note that device on this attachment won't report vevents. Update the uAPI doc accordingly. Link: https://patch.msgid.link/r/20251103172755.2026145-1-nicolinc@nvidia.com Tested-by: Shameer Kolothum Signed-off-by: Nicolin Chen Reviewed-by: Jason Gunthorpe Reviewed-by: Pranjal Shrivastava Tested-by: Shuai Xue Signed-off-by: Jason Gunthorpe --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 13 ++++++++++++- include/uapi/linux/iommufd.h | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c index 8cd8929bbfdf..e5fbbdbdea24 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c @@ -99,6 +99,8 @@ static void arm_smmu_make_nested_domain_ste( int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state, struct arm_smmu_nested_domain *nested_domain) { + unsigned int cfg = + FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(nested_domain->ste[0])); struct arm_smmu_vmaster *vmaster; unsigned long vsid; int ret; @@ -107,8 +109,17 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state, ret = iommufd_viommu_get_vdev_id(&nested_domain->vsmmu->core, state->master->dev, &vsid); - if (ret) + /* + * Attaching to a translate nested domain must allocate a vDEVICE prior, + * as CD/ATS invalidations and vevents require a vSID to work properly. + * A abort/bypass domain is allowed to attach w/o vmaster for GBPA case. + */ + if (ret) { + if (cfg == STRTAB_STE_0_CFG_ABORT || + cfg == STRTAB_STE_0_CFG_BYPASS) + return 0; return ret; + } vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL); if (!vmaster) diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index c218c89e0e2e..2c41920b641d 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -450,6 +450,16 @@ struct iommu_hwpt_vtd_s1 { * nested domain will translate the same as the nesting parent. The S1 will * install a Context Descriptor Table pointing at userspace memory translated * by the nesting parent. + * + * It's suggested to allocate a vDEVICE object carrying vSID and then re-attach + * the nested domain, as soon as the vSID is available in the VMM level: + * + * - when Cfg=translate, a vDEVICE must be allocated prior to attaching to the + * allocated nested domain, as CD/ATS invalidations and vevents need a vSID. + * - when Cfg=bypass/abort, a vDEVICE is not enforced during the nested domain + * attachment, to support a GBPA case where VM sets CR0.SMMUEN=0. However, if + * VM sets CR0.SMMUEN=1 while missing a vDEVICE object, kernel would fail to + * report events to the VM. E.g. F_TRANSLATION when guest STE.Cfg=abort. */ struct iommu_hwpt_arm_smmuv3 { __aligned_le64 ste[2]; -- cgit v1.2.3 From 9e2fd062fa1713a33380cc97ef324d086dd45ba5 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Tue, 25 Nov 2025 11:58:31 -0500 Subject: liveupdate: luo_core: Live Update Orchestrator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "Live Update Orchestrator", v8. This series introduces the Live Update Orchestrator, a kernel subsystem designed to facilitate live kernel updates using a kexec-based reboot. This capability is critical for cloud environments, allowing hypervisors to be updated with minimal downtime for running virtual machines. LUO achieves this by preserving the state of selected resources, such as memory, devices and their dependencies, across the kernel transition. As a key feature, this series includes support for preserving memfd file descriptors, which allows critical in-memory data, such as guest RAM or any other large memory region, to be maintained in RAM across the kexec reboot. The other series that use LUO, are VFIO [1], IOMMU [2], and PCI [3] preservations. Github repo of this series [4]. The core of LUO is a framework for managing the lifecycle of preserved resources through a userspace-driven interface. Key features include: - Session Management Userspace agent (i.e. luod [5]) creates named sessions, each represented by a file descriptor (via centralized agent that controls /dev/liveupdate). The lifecycle of all preserved resources within a session is tied to this FD, ensuring automatic kernel cleanup if the controlling userspace agent crashes or exits unexpectedly. - File Preservation A handler-based framework allows specific file types (demonstrated here with memfd) to be preserved. Handlers manage the serialization, restoration, and lifecycle of their specific file types. - File-Lifecycle-Bound State A new mechanism for managing shared global state whose lifecycle is tied to the preservation of one or more files. This is crucial for subsystems like IOMMU or HugeTLB, where multiple file descriptors may depend on a single, shared underlying resource that must be preserved only once. - KHO Integration LUO drives the Kexec Handover framework programmatically to pass its serialized metadata to the next kernel. The LUO state is finalized and added to the kexec image just before the reboot is triggered. In the future this step will also be removed once stateless KHO is merged [6]. - Userspace Interface Control is provided via ioctl commands on /dev/liveupdate for creating and retrieving sessions, as well as on session file descriptors for managing individual files. - Testing The series includes a set of selftests, including userspace API validation, kexec-based lifecycle tests for various session and file scenarios, and a new in-kernel test module to validate the FLB logic. Introduce LUO, a mechanism intended to facilitate kernel updates while keeping designated devices operational across the transition (e.g., via kexec). The primary use case is updating hypervisors with minimal disruption to running virtual machines. For userspace side of hypervisor update we have copyless migration. LUO is for updating the kernel. This initial patch lays the groundwork for the LUO subsystem. Further functionality, including the implementation of state transition logic, integration with KHO, and hooks for subsystems and file descriptors, will be added in subsequent patches. Create a character device at /dev/liveupdate. A new uAPI header, , will define the necessary structures. The magic number for IOCTL is registered in Documentation/userspace-api/ioctl/ioctl-number.rst. Link: https://lkml.kernel.org/r/20251125165850.3389713-1-pasha.tatashin@soleen.com Link: https://lkml.kernel.org/r/20251125165850.3389713-2-pasha.tatashin@soleen.com Link: https://lore.kernel.org/all/20251018000713.677779-1-vipinsh@google.com/ [1] Link: https://lore.kernel.org/linux-iommu/20250928190624.3735830-1-skhawaja@google.com [2] Link: https://lore.kernel.org/linux-pci/20250916-luo-pci-v2-0-c494053c3c08@kernel.org [3] Link: https://github.com/googleprodkernel/linux-liveupdate/tree/luo/v8 [4] Link: https://tinyurl.com/luoddesign [5] Link: https://lore.kernel.org/all/20251020100306.2709352-1-jasonmiu@google.com [6] Link: https://lore.kernel.org/all/20251115233409.768044-1-pasha.tatashin@soleen.com [7] Link: https://github.com/soleen/linux/blob/luo/v8b03/diff.v7.v8 [8] Signed-off-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Reviewed-by: Mike Rapoport (Microsoft) Tested-by: David Matlack Cc: Aleksander Lobakin Cc: Alexander Graf Cc: Alice Ryhl Cc: Andriy Shevchenko Cc: anish kumar Cc: Anna Schumaker Cc: Bartosz Golaszewski Cc: Bjorn Helgaas Cc: Borislav Betkov Cc: Chanwoo Choi Cc: Chen Ridong Cc: Chris Li Cc: Christian Brauner Cc: Daniel Wagner Cc: Danilo Krummrich Cc: Dan Williams Cc: David Hildenbrand Cc: David Jeffery Cc: David Rientjes Cc: Greg Kroah-Hartman Cc: Guixin Liu Cc: "H. Peter Anvin" Cc: Hugh Dickins Cc: Ilpo Järvinen Cc: Ingo Molnar Cc: Ira Weiny Cc: Jann Horn Cc: Jason Gunthorpe Cc: Jens Axboe Cc: Joanthan Cameron Cc: Joel Granados Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Lennart Poettering Cc: Leon Romanovsky Cc: Leon Romanovsky Cc: Lukas Wunner Cc: Marc Rutland Cc: Masahiro Yamada Cc: Matthew Maurer Cc: Miguel Ojeda Cc: Myugnjoo Ham Cc: Parav Pandit Cc: Randy Dunlap Cc: Roman Gushchin Cc: Saeed Mahameed Cc: Samiullah Khawaja Cc: Song Liu Cc: Steven Rostedt Cc: Stuart Hayes Cc: Tejun Heo Cc: Thomas Gleinxer Cc: Thomas Weißschuh Cc: Vincent Guittot Cc: William Tu Cc: Yoann Congal Cc: Zijun Hu Cc: Pratyush Yadav Cc: Zhu Yanjun Signed-off-by: Andrew Morton --- Documentation/userspace-api/ioctl/ioctl-number.rst | 2 + include/linux/liveupdate.h | 35 +++++++ include/uapi/linux/liveupdate.h | 46 +++++++++ kernel/liveupdate/Kconfig | 21 ++++ kernel/liveupdate/Makefile | 5 + kernel/liveupdate/luo_core.c | 111 +++++++++++++++++++++ 6 files changed, 220 insertions(+) create mode 100644 include/linux/liveupdate.h create mode 100644 include/uapi/linux/liveupdate.h create mode 100644 kernel/liveupdate/luo_core.c (limited to 'include/uapi/linux') diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 7c527a01d1cf..7232b3544cec 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -385,6 +385,8 @@ Code Seq# Include File Comments 0xB8 01-02 uapi/misc/mrvl_cn10k_dpi.h Marvell CN10K DPI driver 0xB8 all uapi/linux/mshv.h Microsoft Hyper-V /dev/mshv driver +0xBA 00-0F uapi/linux/liveupdate.h Pasha Tatashin + 0xC0 00-0F linux/usb/iowarrior.h 0xCA 00-0F uapi/misc/cxl.h Dead since 6.15 0xCA 10-2F uapi/misc/ocxl.h diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h new file mode 100644 index 000000000000..c6a1d6bd90cb --- /dev/null +++ b/include/linux/liveupdate.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ +#ifndef _LINUX_LIVEUPDATE_H +#define _LINUX_LIVEUPDATE_H + +#include +#include +#include + +#ifdef CONFIG_LIVEUPDATE + +/* Return true if live update orchestrator is enabled */ +bool liveupdate_enabled(void); + +/* Called during kexec to tell LUO that entered into reboot */ +int liveupdate_reboot(void); + +#else /* CONFIG_LIVEUPDATE */ + +static inline bool liveupdate_enabled(void) +{ + return false; +} + +static inline int liveupdate_reboot(void) +{ + return 0; +} + +#endif /* CONFIG_LIVEUPDATE */ +#endif /* _LINUX_LIVEUPDATE_H */ diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h new file mode 100644 index 000000000000..df34c1642c4d --- /dev/null +++ b/include/uapi/linux/liveupdate.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +/* + * Userspace interface for /dev/liveupdate + * Live Update Orchestrator + * + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +#ifndef _UAPI_LIVEUPDATE_H +#define _UAPI_LIVEUPDATE_H + +#include +#include + +/** + * DOC: General ioctl format + * + * The ioctl interface follows a general format to allow for extensibility. Each + * ioctl is passed in a structure pointer as the argument providing the size of + * the structure in the first u32. The kernel checks that any structure space + * beyond what it understands is 0. This allows userspace to use the backward + * compatible portion while consistently using the newer, larger, structures. + * + * ioctls use a standard meaning for common errnos: + * + * - ENOTTY: The IOCTL number itself is not supported at all + * - E2BIG: The IOCTL number is supported, but the provided structure has + * non-zero in a part the kernel does not understand. + * - EOPNOTSUPP: The IOCTL number is supported, and the structure is + * understood, however a known field has a value the kernel does not + * understand or support. + * - EINVAL: Everything about the IOCTL was understood, but a field is not + * correct. + * - ENOENT: A provided token does not exist. + * - ENOMEM: Out of memory. + * - EOVERFLOW: Mathematics overflowed. + * + * As well as additional errnos, within specific ioctls. + */ + +/* The ioctl type, documented in ioctl-number.rst */ +#define LIVEUPDATE_IOCTL_TYPE 0xBA + +#endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig index a973a54447de..9b2515f31afb 100644 --- a/kernel/liveupdate/Kconfig +++ b/kernel/liveupdate/Kconfig @@ -51,4 +51,25 @@ config KEXEC_HANDOVER_ENABLE_DEFAULT The default behavior can still be overridden at boot time by passing 'kho=off'. +config LIVEUPDATE + bool "Live Update Orchestrator" + depends on KEXEC_HANDOVER + help + Enable the Live Update Orchestrator. Live Update is a mechanism, + typically based on kexec, that allows the kernel to be updated + while keeping selected devices operational across the transition. + These devices are intended to be reclaimed by the new kernel and + re-attached to their original workload without requiring a device + reset. + + Ability to handover a device from current to the next kernel depends + on specific support within device drivers and related kernel + subsystems. + + This feature primarily targets virtual machine hosts to quickly update + the kernel hypervisor with minimal disruption to the running virtual + machines. + + If unsure, say N. + endmenu diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index f52ce1ebcf86..08954c1770c4 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -1,5 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 +luo-y := \ + luo_core.o + obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o obj-$(CONFIG_KEXEC_HANDOVER_DEBUGFS) += kexec_handover_debugfs.o + +obj-$(CONFIG_LIVEUPDATE) += luo.o diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c new file mode 100644 index 000000000000..30ad8836360b --- /dev/null +++ b/kernel/liveupdate/luo_core.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: Live Update Orchestrator (LUO) + * + * Live Update is a specialized, kexec-based reboot process that allows a + * running kernel to be updated from one version to another while preserving + * the state of selected resources and keeping designated hardware devices + * operational. For these devices, DMA activity may continue throughout the + * kernel transition. + * + * While the primary use case driving this work is supporting live updates of + * the Linux kernel when it is used as a hypervisor in cloud environments, the + * LUO framework itself is designed to be workload-agnostic. Live Update + * facilitates a full kernel version upgrade for any type of system. + * + * For example, a non-hypervisor system running an in-memory cache like + * memcached with many gigabytes of data can use LUO. The userspace service + * can place its cache into a memfd, have its state preserved by LUO, and + * restore it immediately after the kernel kexec. + * + * Whether the system is running virtual machines, containers, a + * high-performance database, or networking services, LUO's primary goal is to + * enable a full kernel update by preserving critical userspace state and + * keeping essential devices operational. + * + * The core of LUO is a mechanism that tracks the progress of a live update, + * along with a callback API that allows other kernel subsystems to participate + * in the process. Example subsystems that can hook into LUO include: kvm, + * iommu, interrupts, vfio, participating filesystems, and memory management. + * + * LUO uses Kexec Handover to transfer memory state from the current kernel to + * the next kernel. For more details see + * Documentation/core-api/kho/concepts.rst. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +static struct { + bool enabled; +} luo_global; + +static int __init early_liveupdate_param(char *buf) +{ + return kstrtobool(buf, &luo_global.enabled); +} +early_param("liveupdate", early_liveupdate_param); + +/* Public Functions */ + +/** + * liveupdate_reboot() - Kernel reboot notifier for live update final + * serialization. + * + * This function is invoked directly from the reboot() syscall pathway + * if kexec is in progress. + * + * If any callback fails, this function aborts KHO, undoes the freeze() + * callbacks, and returns an error. + */ +int liveupdate_reboot(void) +{ + return 0; +} + +/** + * liveupdate_enabled - Check if the live update feature is enabled. + * + * This function returns the state of the live update feature flag, which + * can be controlled via the ``liveupdate`` kernel command-line parameter. + * + * @return true if live update is enabled, false otherwise. + */ +bool liveupdate_enabled(void) +{ + return luo_global.enabled; +} + +struct luo_device_state { + struct miscdevice miscdev; +}; + +static const struct file_operations luo_fops = { + .owner = THIS_MODULE, +}; + +static struct luo_device_state luo_dev = { + .miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "liveupdate", + .fops = &luo_fops, + }, +}; + +static int __init liveupdate_ioctl_init(void) +{ + if (!liveupdate_enabled()) + return 0; + + return misc_register(&luo_dev.miscdev); +} +late_initcall(liveupdate_ioctl_init); -- cgit v1.2.3 From 0153094d03df5a2e834a19c59b255649a258ae46 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Tue, 25 Nov 2025 11:58:34 -0500 Subject: liveupdate: luo_session: add sessions support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce concept of "Live Update Sessions" within the LUO framework. LUO sessions provide a mechanism to group and manage `struct file *` instances (representing file descriptors) that need to be preserved across a kexec-based live update. Each session is identified by a unique name and acts as a container for file objects whose state is critical to a userspace workload, such as a virtual machine or a high-performance database, aiming to maintain their functionality across a kernel transition. This groundwork establishes the framework for preserving file-backed state across kernel updates, with the actual file data preservation mechanisms to be implemented in subsequent patches. [dan.carpenter@linaro.org: fix use after free in luo_session_deserialize()] Link: https://lkml.kernel.org/r/c5dd637d7eed3a3be48c5e9fedb881596a3b1f5a.1764163896.git.dan.carpenter@linaro.org Link: https://lkml.kernel.org/r/20251125165850.3389713-5-pasha.tatashin@soleen.com Signed-off-by: Pasha Tatashin Signed-off-by: Dan Carpenter Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav Tested-by: David Matlack Cc: Aleksander Lobakin Cc: Alexander Graf Cc: Alice Ryhl Cc: Andriy Shevchenko Cc: anish kumar Cc: Anna Schumaker Cc: Bartosz Golaszewski Cc: Bjorn Helgaas Cc: Borislav Betkov Cc: Chanwoo Choi Cc: Chen Ridong Cc: Chris Li Cc: Christian Brauner Cc: Daniel Wagner Cc: Danilo Krummrich Cc: Dan Williams Cc: David Hildenbrand Cc: David Jeffery Cc: David Rientjes Cc: Greg Kroah-Hartman Cc: Guixin Liu Cc: "H. Peter Anvin" Cc: Hugh Dickins Cc: Ilpo Järvinen Cc: Ingo Molnar Cc: Ira Weiny Cc: Jann Horn Cc: Jason Gunthorpe Cc: Jens Axboe Cc: Joanthan Cameron Cc: Joel Granados Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Lennart Poettering Cc: Leon Romanovsky Cc: Leon Romanovsky Cc: Lukas Wunner Cc: Marc Rutland Cc: Masahiro Yamada Cc: Matthew Maurer Cc: Miguel Ojeda Cc: Myugnjoo Ham Cc: Parav Pandit Cc: Pratyush Yadav Cc: Randy Dunlap Cc: Roman Gushchin Cc: Saeed Mahameed Cc: Samiullah Khawaja Cc: Song Liu Cc: Steven Rostedt Cc: Stuart Hayes Cc: Tejun Heo Cc: Thomas Gleinxer Cc: Thomas Weißschuh Cc: Vincent Guittot Cc: William Tu Cc: Yoann Congal Cc: Zhu Yanjun Cc: Zijun Hu Signed-off-by: Andrew Morton --- include/linux/kho/abi/luo.h | 71 ++++++ include/uapi/linux/liveupdate.h | 3 + kernel/liveupdate/Makefile | 3 +- kernel/liveupdate/luo_core.c | 9 + kernel/liveupdate/luo_internal.h | 29 +++ kernel/liveupdate/luo_session.c | 463 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 kernel/liveupdate/luo_session.c (limited to 'include/uapi/linux') diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h index 2099b51929e5..bf1ab2910959 100644 --- a/include/linux/kho/abi/luo.h +++ b/include/linux/kho/abi/luo.h @@ -32,6 +32,11 @@ * / { * compatible = "luo-v1"; * liveupdate-number = <...>; + * + * luo-session { + * compatible = "luo-session-v1"; + * luo-session-header = ; + * }; * }; * * Main LUO Node (/): @@ -40,11 +45,37 @@ * Identifies the overall LUO ABI version. * - liveupdate-number: u64 * A counter tracking the number of successful live updates performed. + * + * Session Node (luo-session): + * This node describes all preserved user-space sessions. + * + * - compatible: "luo-session-v1" + * Identifies the session ABI version. + * - luo-session-header: u64 + * The physical address of a `struct luo_session_header_ser`. This structure + * is the header for a contiguous block of memory containing an array of + * `struct luo_session_ser`, one for each preserved session. + * + * Serialization Structures: + * The FDT properties point to memory regions containing arrays of simple, + * `__packed` structures. These structures contain the actual preserved state. + * + * - struct luo_session_header_ser: + * Header for the session array. Contains the total page count of the + * preserved memory block and the number of `struct luo_session_ser` + * entries that follow. + * + * - struct luo_session_ser: + * Metadata for a single session, including its name and a physical pointer + * to another preserved memory block containing an array of + * `struct luo_file_ser` for all files in that session. */ #ifndef _LINUX_KHO_ABI_LUO_H #define _LINUX_KHO_ABI_LUO_H +#include + /* * The LUO FDT hooks all LUO state for sessions, fds, etc. * In the root it also carries "liveupdate-number" 64-bit property that @@ -55,4 +86,44 @@ #define LUO_FDT_COMPATIBLE "luo-v1" #define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number" +/* + * LUO FDT session node + * LUO_FDT_SESSION_HEADER: is a u64 physical address of struct + * luo_session_header_ser + */ +#define LUO_FDT_SESSION_NODE_NAME "luo-session" +#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v1" +#define LUO_FDT_SESSION_HEADER "luo-session-header" + +/** + * struct luo_session_header_ser - Header for the serialized session data block. + * @count: The number of `struct luo_session_ser` entries that immediately + * follow this header in the memory block. + * + * This structure is located at the beginning of a contiguous block of + * physical memory preserved across the kexec. It provides the necessary + * metadata to interpret the array of session entries that follow. + * + * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated. + */ +struct luo_session_header_ser { + u64 count; +} __packed; + +/** + * struct luo_session_ser - Represents the serialized metadata for a LUO session. + * @name: The unique name of the session, provided by the userspace at + * the time of session creation. + * + * This structure is used to package session-specific metadata for transfer + * between kernels via Kexec Handover. An array of these structures (one per + * session) is created and passed to the new kernel, allowing it to reconstruct + * the session context. + * + * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated. + */ +struct luo_session_ser { + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; +} __packed; + #endif /* _LINUX_KHO_ABI_LUO_H */ diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h index df34c1642c4d..40578ae19668 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -43,4 +43,7 @@ /* The ioctl type, documented in ioctl-number.rst */ #define LIVEUPDATE_IOCTL_TYPE 0xBA +/* The maximum length of session name including null termination */ +#define LIVEUPDATE_SESSION_NAME_LENGTH 64 + #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index 08954c1770c4..6af93caa58cf 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 luo-y := \ - luo_core.o + luo_core.o \ + luo_session.o obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 9f9fe9a81b29..a0f7788cd003 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -118,6 +118,10 @@ static int __init luo_early_startup(void) pr_info("Retrieved live update data, liveupdate number: %lld\n", luo_global.liveupdate_num); + err = luo_session_setup_incoming(luo_global.fdt_in); + if (err) + return err; + return 0; } @@ -154,6 +158,7 @@ static int __init luo_fdt_setup(void) err |= fdt_begin_node(fdt_out, ""); err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE); err |= fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln)); + err |= luo_session_setup_outgoing(fdt_out); err |= fdt_end_node(fdt_out); err |= fdt_finish(fdt_out); if (err) @@ -211,6 +216,10 @@ int liveupdate_reboot(void) if (!liveupdate_enabled()) return 0; + err = luo_session_serialize(); + if (err) + return err; + err = kho_finalize(); if (err) { pr_err("kho_finalize failed %d\n", err); diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index 8612687b2000..05ae91695ec6 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -19,4 +19,33 @@ */ #define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__) +/** + * struct luo_session - Represents an active or incoming Live Update session. + * @name: A unique name for this session, used for identification and + * retrieval. + * @ser: Pointer to the serialized data for this session. + * @list: A list_head member used to link this session into a global list + * of either outgoing (to be preserved) or incoming (restored from + * previous kernel) sessions. + * @retrieved: A boolean flag indicating whether this session has been + * retrieved by a consumer in the new kernel. + * @mutex: protects fields in the luo_session. + */ +struct luo_session { + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; + struct luo_session_ser *ser; + struct list_head list; + bool retrieved; + struct mutex mutex; +}; + +int luo_session_create(const char *name, struct file **filep); +int luo_session_retrieve(const char *name, struct file **filep); +int __init luo_session_setup_outgoing(void *fdt); +int __init luo_session_setup_incoming(void *fdt); +int luo_session_serialize(void); +int luo_session_deserialize(void); +bool luo_session_quiesce(void); +void luo_session_resume(void); + #endif /* _LINUX_LUO_INTERNAL_H */ diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c new file mode 100644 index 000000000000..3a031446d3a4 --- /dev/null +++ b/kernel/liveupdate/luo_session.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: LUO Sessions + * + * LUO Sessions provide the core mechanism for grouping and managing `struct + * file *` instances that need to be preserved across a kexec-based live + * update. Each session acts as a named container for a set of file objects, + * allowing a userspace agent to manage the lifecycle of resources critical to a + * workload. + * + * Core Concepts: + * + * - Named Containers: Sessions are identified by a unique, user-provided name, + * which is used for both creation in the current kernel and retrieval in the + * next kernel. + * + * - Userspace Interface: Session management is driven from userspace via + * ioctls on /dev/liveupdate. + * + * - Serialization: Session metadata is preserved using the KHO framework. When + * a live update is triggered via kexec, an array of `struct luo_session_ser` + * is populated and placed in a preserved memory region. An FDT node is also + * created, containing the count of sessions and the physical address of this + * array. + * + * Session Lifecycle: + * + * 1. Creation: A userspace agent calls `luo_session_create()` to create a + * new, empty session and receives a file descriptor for it. + * + * 2. Serialization: When the `reboot(LINUX_REBOOT_CMD_KEXEC)` syscall is + * made, `luo_session_serialize()` is called. It iterates through all + * active sessions and writes their metadata into a memory area preserved + * by KHO. + * + * 3. Deserialization (in new kernel): After kexec, `luo_session_deserialize()` + * runs, reading the serialized data and creating a list of `struct + * luo_session` objects representing the preserved sessions. + * + * 4. Retrieval: A userspace agent in the new kernel can then call + * `luo_session_retrieve()` with a session name to get a new file + * descriptor and access the preserved state. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "luo_internal.h" + +/* 16 4K pages, give space for 744 sessions */ +#define LUO_SESSION_PGCNT 16ul +#define LUO_SESSION_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \ + sizeof(struct luo_session_header_ser)) / \ + sizeof(struct luo_session_ser)) + +/** + * struct luo_session_header - Header struct for managing LUO sessions. + * @count: The number of sessions currently tracked in the @list. + * @list: The head of the linked list of `struct luo_session` instances. + * @rwsem: A read-write semaphore providing synchronized access to the + * session list and other fields in this structure. + * @header_ser: The header data of serialization array. + * @ser: The serialized session data (an array of + * `struct luo_session_ser`). + * @active: Set to true when first initialized. If previous kernel did not + * send session data, active stays false for incoming. + */ +struct luo_session_header { + long count; + struct list_head list; + struct rw_semaphore rwsem; + struct luo_session_header_ser *header_ser; + struct luo_session_ser *ser; + bool active; +}; + +/** + * struct luo_session_global - Global container for managing LUO sessions. + * @incoming: The sessions passed from the previous kernel. + * @outgoing: The sessions that are going to be passed to the next kernel. + */ +struct luo_session_global { + struct luo_session_header incoming; + struct luo_session_header outgoing; +}; + +static struct luo_session_global luo_session_global = { + .incoming = { + .list = LIST_HEAD_INIT(luo_session_global.incoming.list), + .rwsem = __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem), + }, + .outgoing = { + .list = LIST_HEAD_INIT(luo_session_global.outgoing.list), + .rwsem = __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem), + }, +}; + +static struct luo_session *luo_session_alloc(const char *name) +{ + struct luo_session *session = kzalloc(sizeof(*session), GFP_KERNEL); + + if (!session) + return ERR_PTR(-ENOMEM); + + strscpy(session->name, name, sizeof(session->name)); + INIT_LIST_HEAD(&session->list); + mutex_init(&session->mutex); + + return session; +} + +static void luo_session_free(struct luo_session *session) +{ + mutex_destroy(&session->mutex); + kfree(session); +} + +static int luo_session_insert(struct luo_session_header *sh, + struct luo_session *session) +{ + struct luo_session *it; + + guard(rwsem_write)(&sh->rwsem); + + /* + * For outgoing we should make sure there is room in serialization array + * for new session. + */ + if (sh == &luo_session_global.outgoing) { + if (sh->count == LUO_SESSION_MAX) + return -ENOMEM; + } + + /* + * For small number of sessions this loop won't hurt performance + * but if we ever start using a lot of sessions, this might + * become a bottle neck during deserialization time, as it would + * cause O(n*n) complexity. + */ + list_for_each_entry(it, &sh->list, list) { + if (!strncmp(it->name, session->name, sizeof(it->name))) + return -EEXIST; + } + list_add_tail(&session->list, &sh->list); + sh->count++; + + return 0; +} + +static void luo_session_remove(struct luo_session_header *sh, + struct luo_session *session) +{ + guard(rwsem_write)(&sh->rwsem); + list_del(&session->list); + sh->count--; +} + +static int luo_session_release(struct inode *inodep, struct file *filep) +{ + struct luo_session *session = filep->private_data; + struct luo_session_header *sh; + + /* If retrieved is set, it means this session is from incoming list */ + if (session->retrieved) + sh = &luo_session_global.incoming; + else + sh = &luo_session_global.outgoing; + + luo_session_remove(sh, session); + luo_session_free(session); + + return 0; +} + +static const struct file_operations luo_session_fops = { + .owner = THIS_MODULE, + .release = luo_session_release, +}; + +/* Create a "struct file" for session */ +static int luo_session_getfile(struct luo_session *session, struct file **filep) +{ + char name_buf[128]; + struct file *file; + + lockdep_assert_held(&session->mutex); + snprintf(name_buf, sizeof(name_buf), "[luo_session] %s", session->name); + file = anon_inode_getfile(name_buf, &luo_session_fops, session, O_RDWR); + if (IS_ERR(file)) + return PTR_ERR(file); + + *filep = file; + + return 0; +} + +int luo_session_create(const char *name, struct file **filep) +{ + struct luo_session *session; + int err; + + session = luo_session_alloc(name); + if (IS_ERR(session)) + return PTR_ERR(session); + + err = luo_session_insert(&luo_session_global.outgoing, session); + if (err) + goto err_free; + + scoped_guard(mutex, &session->mutex) + err = luo_session_getfile(session, filep); + if (err) + goto err_remove; + + return 0; + +err_remove: + luo_session_remove(&luo_session_global.outgoing, session); +err_free: + luo_session_free(session); + + return err; +} + +int luo_session_retrieve(const char *name, struct file **filep) +{ + struct luo_session_header *sh = &luo_session_global.incoming; + struct luo_session *session = NULL; + struct luo_session *it; + int err; + + scoped_guard(rwsem_read, &sh->rwsem) { + list_for_each_entry(it, &sh->list, list) { + if (!strncmp(it->name, name, sizeof(it->name))) { + session = it; + break; + } + } + } + + if (!session) + return -ENOENT; + + guard(mutex)(&session->mutex); + if (session->retrieved) + return -EINVAL; + + err = luo_session_getfile(session, filep); + if (!err) + session->retrieved = true; + + return err; +} + +int __init luo_session_setup_outgoing(void *fdt_out) +{ + struct luo_session_header_ser *header_ser; + u64 header_ser_pa; + int err; + + header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); + if (IS_ERR(header_ser)) + return PTR_ERR(header_ser); + header_ser_pa = virt_to_phys(header_ser); + + err = fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME); + err |= fdt_property_string(fdt_out, "compatible", + LUO_FDT_SESSION_COMPATIBLE); + err |= fdt_property(fdt_out, LUO_FDT_SESSION_HEADER, &header_ser_pa, + sizeof(header_ser_pa)); + err |= fdt_end_node(fdt_out); + + if (err) + goto err_unpreserve; + + luo_session_global.outgoing.header_ser = header_ser; + luo_session_global.outgoing.ser = (void *)(header_ser + 1); + luo_session_global.outgoing.active = true; + + return 0; + +err_unpreserve: + kho_unpreserve_free(header_ser); + return err; +} + +int __init luo_session_setup_incoming(void *fdt_in) +{ + struct luo_session_header_ser *header_ser; + int err, header_size, offset; + u64 header_ser_pa; + const void *ptr; + + offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_SESSION_NODE_NAME); + if (offset < 0) { + pr_err("Unable to get session node: [%s]\n", + LUO_FDT_SESSION_NODE_NAME); + return -EINVAL; + } + + err = fdt_node_check_compatible(fdt_in, offset, + LUO_FDT_SESSION_COMPATIBLE); + if (err) { + pr_err("Session node incompatible [%s]\n", + LUO_FDT_SESSION_COMPATIBLE); + return -EINVAL; + } + + header_size = 0; + ptr = fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEADER, &header_size); + if (!ptr || header_size != sizeof(u64)) { + pr_err("Unable to get session header '%s' [%d]\n", + LUO_FDT_SESSION_HEADER, header_size); + return -EINVAL; + } + + header_ser_pa = get_unaligned((u64 *)ptr); + header_ser = phys_to_virt(header_ser_pa); + + luo_session_global.incoming.header_ser = header_ser; + luo_session_global.incoming.ser = (void *)(header_ser + 1); + luo_session_global.incoming.active = true; + + return 0; +} + +int luo_session_deserialize(void) +{ + struct luo_session_header *sh = &luo_session_global.incoming; + static bool is_deserialized; + static int err; + + /* If has been deserialized, always return the same error code */ + if (is_deserialized) + return err; + + is_deserialized = true; + if (!sh->active) + return 0; + + /* + * Note on error handling: + * + * If deserialization fails (e.g., allocation failure or corrupt data), + * we intentionally skip cleanup of sessions that were already restored. + * + * A partial failure leaves the preserved state inconsistent. + * Implementing a safe "undo" to unwind complex dependencies (sessions, + * files, hardware state) is error-prone and provides little value, as + * the system is effectively in a broken state. + * + * We treat these resources as leaked. The expected recovery path is for + * userspace to detect the failure and trigger a reboot, which will + * reliably reset devices and reclaim memory. + */ + for (int i = 0; i < sh->header_ser->count; i++) { + struct luo_session *session; + + session = luo_session_alloc(sh->ser[i].name); + if (IS_ERR(session)) { + pr_warn("Failed to allocate session [%s] during deserialization %pe\n", + sh->ser[i].name, session); + return PTR_ERR(session); + } + + err = luo_session_insert(sh, session); + if (err) { + pr_warn("Failed to insert session [%s] %pe\n", + session->name, ERR_PTR(err)); + luo_session_free(session); + return err; + } + } + + kho_restore_free(sh->header_ser); + sh->header_ser = NULL; + sh->ser = NULL; + + return 0; +} + +int luo_session_serialize(void) +{ + struct luo_session_header *sh = &luo_session_global.outgoing; + struct luo_session *session; + int i = 0; + + guard(rwsem_write)(&sh->rwsem); + list_for_each_entry(session, &sh->list, list) { + strscpy(sh->ser[i].name, session->name, + sizeof(sh->ser[i].name)); + i++; + } + sh->header_ser->count = sh->count; + + return 0; +} + +/** + * luo_session_quiesce - Ensure no active sessions exist and lock session lists. + * + * Acquires exclusive write locks on both incoming and outgoing session lists. + * It then validates no sessions exist in either list. + * + * This mechanism is used during file handler un/registration to ensure that no + * sessions are currently using the handler, and no new sessions can be created + * while un/registration is in progress. + * + * This prevents registering new handlers while sessions are active or + * while deserialization is in progress. + * + * Return: + * true - System is quiescent (0 sessions) and locked. + * false - Active sessions exist. The locks are released internally. + */ +bool luo_session_quiesce(void) +{ + down_write(&luo_session_global.incoming.rwsem); + down_write(&luo_session_global.outgoing.rwsem); + + if (luo_session_global.incoming.count || + luo_session_global.outgoing.count) { + up_write(&luo_session_global.outgoing.rwsem); + up_write(&luo_session_global.incoming.rwsem); + return false; + } + + return true; +} + +/** + * luo_session_resume - Unlock session lists and resume normal activity. + * + * Releases the exclusive locks acquired by a successful call to + * luo_session_quiesce(). + */ +void luo_session_resume(void) +{ + up_write(&luo_session_global.outgoing.rwsem); + up_write(&luo_session_global.incoming.rwsem); +} -- cgit v1.2.3 From 81cd25d263a182b3dcdc8af3b92e4b8e4db336de Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Tue, 25 Nov 2025 11:58:35 -0500 Subject: liveupdate: luo_core: add user interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the user-space interface for the Live Update Orchestrator via ioctl commands, enabling external control over the live update process and management of preserved resources. The idea is that there is going to be a single userspace agent driving the live update, therefore, only a single process can ever hold this device opened at a time. The following ioctl commands are introduced: LIVEUPDATE_IOCTL_CREATE_SESSION Provides a way for userspace to create a named session for grouping file descriptors that need to be preserved. It returns a new file descriptor representing the session. LIVEUPDATE_IOCTL_RETRIEVE_SESSION Allows the userspace agent in the new kernel to reclaim a preserved session by its name, receiving a new file descriptor to manage the restored resources. Link: https://lkml.kernel.org/r/20251125165850.3389713-6-pasha.tatashin@soleen.com Signed-off-by: Pasha Tatashin Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav Tested-by: David Matlack Cc: Aleksander Lobakin Cc: Alexander Graf Cc: Alice Ryhl Cc: Andriy Shevchenko Cc: anish kumar Cc: Anna Schumaker Cc: Bartosz Golaszewski Cc: Bjorn Helgaas Cc: Borislav Betkov Cc: Chanwoo Choi Cc: Chen Ridong Cc: Chris Li Cc: Christian Brauner Cc: Daniel Wagner Cc: Danilo Krummrich Cc: Dan Williams Cc: David Hildenbrand Cc: David Jeffery Cc: David Rientjes Cc: Greg Kroah-Hartman Cc: Guixin Liu Cc: "H. Peter Anvin" Cc: Hugh Dickins Cc: Ilpo Järvinen Cc: Ingo Molnar Cc: Ira Weiny Cc: Jann Horn Cc: Jason Gunthorpe Cc: Jens Axboe Cc: Joanthan Cameron Cc: Joel Granados Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Lennart Poettering Cc: Leon Romanovsky Cc: Leon Romanovsky Cc: Lukas Wunner Cc: Marc Rutland Cc: Masahiro Yamada Cc: Matthew Maurer Cc: Miguel Ojeda Cc: Myugnjoo Ham Cc: Parav Pandit Cc: Pratyush Yadav Cc: Randy Dunlap Cc: Roman Gushchin Cc: Saeed Mahameed Cc: Samiullah Khawaja Cc: Song Liu Cc: Steven Rostedt Cc: Stuart Hayes Cc: Tejun Heo Cc: Thomas Gleinxer Cc: Thomas Weißschuh Cc: Vincent Guittot Cc: William Tu Cc: Yoann Congal Cc: Zhu Yanjun Cc: Zijun Hu Signed-off-by: Andrew Morton --- include/uapi/linux/liveupdate.h | 64 ++++++++++++++ kernel/liveupdate/luo_core.c | 178 +++++++++++++++++++++++++++++++++++++++ kernel/liveupdate/luo_internal.h | 21 +++++ 3 files changed, 263 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h index 40578ae19668..1183cf984b5f 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -46,4 +46,68 @@ /* The maximum length of session name including null termination */ #define LIVEUPDATE_SESSION_NAME_LENGTH 64 +/* The /dev/liveupdate ioctl commands */ +enum { + LIVEUPDATE_CMD_BASE = 0x00, + LIVEUPDATE_CMD_CREATE_SESSION = LIVEUPDATE_CMD_BASE, + LIVEUPDATE_CMD_RETRIEVE_SESSION = 0x01, +}; + +/** + * struct liveupdate_ioctl_create_session - ioctl(LIVEUPDATE_IOCTL_CREATE_SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_create_session) + * @fd: Output; The new file descriptor for the created session. + * @name: Input; A null-terminated string for the session name, max + * length %LIVEUPDATE_SESSION_NAME_LENGTH including termination + * character. + * + * Creates a new live update session for managing preserved resources. + * This ioctl can only be called on the main /dev/liveupdate device. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_ioctl_create_session { + __u32 size; + __s32 fd; + __u8 name[LIVEUPDATE_SESSION_NAME_LENGTH]; +}; + +#define LIVEUPDATE_IOCTL_CREATE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_CREATE_SESSION) + +/** + * struct liveupdate_ioctl_retrieve_session - ioctl(LIVEUPDATE_IOCTL_RETRIEVE_SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_retrieve_session) + * @fd: Output; The new file descriptor for the retrieved session. + * @name: Input; A null-terminated string identifying the session to retrieve. + * The name must exactly match the name used when the session was + * created in the previous kernel. + * + * Retrieves a handle (a new file descriptor) for a preserved session by its + * name. This is the primary mechanism for a userspace agent to regain control + * of its preserved resources after a live update. + * + * The userspace application provides the null-terminated `name` of a session + * it created before the live update. If a preserved session with a matching + * name is found, the kernel instantiates it and returns a new file descriptor + * in the `fd` field. This new session FD can then be used for all file-specific + * operations, such as restoring individual file descriptors with + * LIVEUPDATE_SESSION_RETRIEVE_FD. + * + * It is the responsibility of the userspace application to know the names of + * the sessions it needs to retrieve. If no session with the given name is + * found, the ioctl will fail with -ENOENT. + * + * This ioctl can only be called on the main /dev/liveupdate device when the + * system is in the LIVEUPDATE_STATE_UPDATED state. + */ +struct liveupdate_ioctl_retrieve_session { + __u32 size; + __s32 fd; + __u8 name[LIVEUPDATE_SESSION_NAME_LENGTH]; +}; + +#define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION) + #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index a0f7788cd003..f7ecaf7740d1 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -41,7 +41,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include #include +#include #include #include #include @@ -246,12 +252,183 @@ bool liveupdate_enabled(void) return luo_global.enabled; } +/** + * DOC: LUO ioctl Interface + * + * The IOCTL user-space control interface for the LUO subsystem. + * It registers a character device, typically found at ``/dev/liveupdate``, + * which allows a userspace agent to manage the LUO state machine and its + * associated resources, such as preservable file descriptors. + * + * To ensure that the state machine is controlled by a single entity, access + * to this device is exclusive: only one process is permitted to have + * ``/dev/liveupdate`` open at any given time. Subsequent open attempts will + * fail with -EBUSY until the first process closes its file descriptor. + * This singleton model simplifies state management by preventing conflicting + * commands from multiple userspace agents. + */ + struct luo_device_state { struct miscdevice miscdev; + atomic_t in_use; }; +static int luo_ioctl_create_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_create_session *argp = ucmd->cmd; + struct file *file; + int err; + + argp->fd = get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + err = luo_session_create(argp->name, &file); + if (err) + goto err_put_fd; + + err = luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + goto err_put_file; + + fd_install(argp->fd, file); + + return 0; + +err_put_file: + fput(file); +err_put_fd: + put_unused_fd(argp->fd); + + return err; +} + +static int luo_ioctl_retrieve_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_retrieve_session *argp = ucmd->cmd; + struct file *file; + int err; + + argp->fd = get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + err = luo_session_retrieve(argp->name, &file); + if (err < 0) + goto err_put_fd; + + err = luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + goto err_put_file; + + fd_install(argp->fd, file); + + return 0; + +err_put_file: + fput(file); +err_put_fd: + put_unused_fd(argp->fd); + + return err; +} + +static int luo_open(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev = container_of(filep->private_data, + struct luo_device_state, + miscdev); + + if (atomic_cmpxchg(&ldev->in_use, 0, 1)) + return -EBUSY; + + /* Always return -EIO to user if deserialization fail */ + if (luo_session_deserialize()) { + atomic_set(&ldev->in_use, 0); + return -EIO; + } + + return 0; +} + +static int luo_release(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev = container_of(filep->private_data, + struct luo_device_state, + miscdev); + atomic_set(&ldev->in_use, 0); + + return 0; +} + +union ucmd_buffer { + struct liveupdate_ioctl_create_session create; + struct liveupdate_ioctl_retrieve_session retrieve; +}; + +struct luo_ioctl_op { + unsigned int size; + unsigned int min_size; + unsigned int ioctl_num; + int (*execute)(struct luo_ucmd *ucmd); +}; + +#define IOCTL_OP(_ioctl, _fn, _struct, _last) \ + [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_BASE] = { \ + .size = sizeof(_struct) + \ + BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ + sizeof(_struct)), \ + .min_size = offsetofend(_struct, _last), \ + .ioctl_num = _ioctl, \ + .execute = _fn, \ + } + +static const struct luo_ioctl_op luo_ioctl_ops[] = { + IOCTL_OP(LIVEUPDATE_IOCTL_CREATE_SESSION, luo_ioctl_create_session, + struct liveupdate_ioctl_create_session, name), + IOCTL_OP(LIVEUPDATE_IOCTL_RETRIEVE_SESSION, luo_ioctl_retrieve_session, + struct liveupdate_ioctl_retrieve_session, name), +}; + +static long luo_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + const struct luo_ioctl_op *op; + struct luo_ucmd ucmd = {}; + union ucmd_buffer buf; + unsigned int nr; + int err; + + nr = _IOC_NR(cmd); + if (nr < LIVEUPDATE_CMD_BASE || + (nr - LIVEUPDATE_CMD_BASE) >= ARRAY_SIZE(luo_ioctl_ops)) { + return -EINVAL; + } + + ucmd.ubuffer = (void __user *)arg; + err = get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer); + if (err) + return err; + + op = &luo_ioctl_ops[nr - LIVEUPDATE_CMD_BASE]; + if (op->ioctl_num != cmd) + return -ENOIOCTLCMD; + if (ucmd.user_size < op->min_size) + return -EINVAL; + + ucmd.cmd = &buf; + err = copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer, + ucmd.user_size); + if (err) + return err; + + return op->execute(&ucmd); +} + static const struct file_operations luo_fops = { .owner = THIS_MODULE, + .open = luo_open, + .release = luo_release, + .unlocked_ioctl = luo_ioctl, }; static struct luo_device_state luo_dev = { @@ -260,6 +437,7 @@ static struct luo_device_state luo_dev = { .name = "liveupdate", .fops = &luo_fops, }, + .in_use = ATOMIC_INIT(0), }; static int __init liveupdate_ioctl_init(void) diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index 05ae91695ec6..1292ac47eef8 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -9,6 +9,27 @@ #define _LINUX_LUO_INTERNAL_H #include +#include + +struct luo_ucmd { + void __user *ubuffer; + u32 user_size; + void *cmd; +}; + +static inline int luo_ucmd_respond(struct luo_ucmd *ucmd, + size_t kernel_cmd_size) +{ + /* + * Copy the minimum of what the user provided and what we actually + * have. + */ + if (copy_to_user(ucmd->ubuffer, ucmd->cmd, + min_t(size_t, ucmd->user_size, kernel_cmd_size))) { + return -EFAULT; + } + return 0; +} /* * Handles a deserialization failure: devices and memory is in unpredictable -- cgit v1.2.3 From 16cec0d265219f14a7fcebcc43aeb69205adba56 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Tue, 25 Nov 2025 11:58:37 -0500 Subject: liveupdate: luo_session: add ioctls for file preservation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introducing the userspace interface and internal logic required to manage the lifecycle of file descriptors within a session. Previously, a session was merely a container; this change makes it a functional management unit. The following capabilities are added: A new set of ioctl commands are added, which operate on the file descriptor returned by CREATE_SESSION. This allows userspace to: - LIVEUPDATE_SESSION_PRESERVE_FD: Add a file descriptor to a session to be preserved across the live update. - LIVEUPDATE_SESSION_RETRIEVE_FD: Retrieve a preserved file in the new kernel using its unique token. - LIVEUPDATE_SESSION_FINISH: finish session The session's .release handler is enhanced to be state-aware. When a session's file descriptor is closed, it correctly unpreserves the session based on its current state before freeing all associated file resources. Link: https://lkml.kernel.org/r/20251125165850.3389713-8-pasha.tatashin@soleen.com Signed-off-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Reviewed-by: Mike Rapoport (Microsoft) Tested-by: David Matlack Cc: Aleksander Lobakin Cc: Alexander Graf Cc: Alice Ryhl Cc: Andriy Shevchenko Cc: anish kumar Cc: Anna Schumaker Cc: Bartosz Golaszewski Cc: Bjorn Helgaas Cc: Borislav Betkov Cc: Chanwoo Choi Cc: Chen Ridong Cc: Chris Li Cc: Christian Brauner Cc: Daniel Wagner Cc: Danilo Krummrich Cc: Dan Williams Cc: David Hildenbrand Cc: David Jeffery Cc: David Rientjes Cc: Greg Kroah-Hartman Cc: Guixin Liu Cc: "H. Peter Anvin" Cc: Hugh Dickins Cc: Ilpo Järvinen Cc: Ingo Molnar Cc: Ira Weiny Cc: Jann Horn Cc: Jason Gunthorpe Cc: Jens Axboe Cc: Joanthan Cameron Cc: Joel Granados Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Lennart Poettering Cc: Leon Romanovsky Cc: Leon Romanovsky Cc: Lukas Wunner Cc: Marc Rutland Cc: Masahiro Yamada Cc: Matthew Maurer Cc: Miguel Ojeda Cc: Myugnjoo Ham Cc: Parav Pandit Cc: Pratyush Yadav Cc: Randy Dunlap Cc: Roman Gushchin Cc: Saeed Mahameed Cc: Samiullah Khawaja Cc: Song Liu Cc: Steven Rostedt Cc: Stuart Hayes Cc: Tejun Heo Cc: Thomas Gleinxer Cc: Thomas Weißschuh Cc: Vincent Guittot Cc: William Tu Cc: Yoann Congal Cc: Zhu Yanjun Cc: Zijun Hu Signed-off-by: Andrew Morton --- include/uapi/linux/liveupdate.h | 103 ++++++++++++++++++++++ kernel/liveupdate/luo_session.c | 187 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 288 insertions(+), 2 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h index 1183cf984b5f..30bc66ee9436 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -53,6 +53,14 @@ enum { LIVEUPDATE_CMD_RETRIEVE_SESSION = 0x01, }; +/* ioctl commands for session file descriptors */ +enum { + LIVEUPDATE_CMD_SESSION_BASE = 0x40, + LIVEUPDATE_CMD_SESSION_PRESERVE_FD = LIVEUPDATE_CMD_SESSION_BASE, + LIVEUPDATE_CMD_SESSION_RETRIEVE_FD = 0x41, + LIVEUPDATE_CMD_SESSION_FINISH = 0x42, +}; + /** * struct liveupdate_ioctl_create_session - ioctl(LIVEUPDATE_IOCTL_CREATE_SESSION) * @size: Input; sizeof(struct liveupdate_ioctl_create_session) @@ -110,4 +118,99 @@ struct liveupdate_ioctl_retrieve_session { #define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \ _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION) +/* Session specific IOCTLs */ + +/** + * struct liveupdate_session_preserve_fd - ioctl(LIVEUPDATE_SESSION_PRESERVE_FD) + * @size: Input; sizeof(struct liveupdate_session_preserve_fd) + * @fd: Input; The user-space file descriptor to be preserved. + * @token: Input; An opaque, unique token for preserved resource. + * + * Holds parameters for preserving a file descriptor. + * + * User sets the @fd field identifying the file descriptor to preserve + * (e.g., memfd, kvm, iommufd, VFIO). The kernel validates if this FD type + * and its dependencies are supported for preservation. If validation passes, + * the kernel marks the FD internally and *initiates the process* of preparing + * its state for saving. The actual snapshotting of the state typically occurs + * during the subsequent %LIVEUPDATE_IOCTL_PREPARE execution phase, though + * some finalization might occur during freeze. + * On successful validation and initiation, the kernel uses the @token + * field with an opaque identifier representing the resource being preserved. + * This token confirms the FD is targeted for preservation and is required for + * the subsequent %LIVEUPDATE_SESSION_RETRIEVE_FD call after the live update. + * + * Return: 0 on success (validation passed, preservation initiated), negative + * error code on failure (e.g., unsupported FD type, dependency issue, + * validation failed). + */ +struct liveupdate_session_preserve_fd { + __u32 size; + __s32 fd; + __aligned_u64 token; +}; + +#define LIVEUPDATE_SESSION_PRESERVE_FD \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_PRESERVE_FD) + +/** + * struct liveupdate_session_retrieve_fd - ioctl(LIVEUPDATE_SESSION_RETRIEVE_FD) + * @size: Input; sizeof(struct liveupdate_session_retrieve_fd) + * @fd: Output; The new file descriptor representing the fully restored + * kernel resource. + * @token: Input; An opaque, token that was used to preserve the resource. + * + * Retrieve a previously preserved file descriptor. + * + * User sets the @token field to the value obtained from a successful + * %LIVEUPDATE_IOCTL_FD_PRESERVE call before the live update. On success, + * the kernel restores the state (saved during the PREPARE/FREEZE phases) + * associated with the token and populates the @fd field with a new file + * descriptor referencing the restored resource in the current (new) kernel. + * This operation must be performed *before* signaling completion via + * %LIVEUPDATE_IOCTL_FINISH. + * + * Return: 0 on success, negative error code on failure (e.g., invalid token). + */ +struct liveupdate_session_retrieve_fd { + __u32 size; + __s32 fd; + __aligned_u64 token; +}; + +#define LIVEUPDATE_SESSION_RETRIEVE_FD \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_RETRIEVE_FD) + +/** + * struct liveupdate_session_finish - ioctl(LIVEUPDATE_SESSION_FINISH) + * @size: Input; sizeof(struct liveupdate_session_finish) + * @reserved: Input; Must be zero. Reserved for future use. + * + * Signals the completion of the restoration process for a retrieved session. + * This is the final operation that should be performed on a session file + * descriptor after a live update. + * + * This ioctl must be called once all required file descriptors for the session + * have been successfully retrieved (using %LIVEUPDATE_SESSION_RETRIEVE_FD) and + * are fully restored from the userspace and kernel perspective. + * + * Upon success, the kernel releases its ownership of the preserved resources + * associated with this session. This allows internal resources to be freed, + * typically by decrementing reference counts on the underlying preserved + * objects. + * + * If this operation fails, the resources remain preserved in memory. Userspace + * may attempt to call finish again. The resources will otherwise be reset + * during the next live update cycle. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_session_finish { + __u32 size; + __u32 reserved; +}; + +#define LIVEUPDATE_SESSION_FINISH \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_FINISH) + #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 3a031446d3a4..dbdbc3bd7929 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -125,6 +125,8 @@ static struct luo_session *luo_session_alloc(const char *name) return ERR_PTR(-ENOMEM); strscpy(session->name, name, sizeof(session->name)); + INIT_LIST_HEAD(&session->file_set.files_list); + luo_file_set_init(&session->file_set); INIT_LIST_HEAD(&session->list); mutex_init(&session->mutex); @@ -133,6 +135,7 @@ static struct luo_session *luo_session_alloc(const char *name) static void luo_session_free(struct luo_session *session) { + luo_file_set_destroy(&session->file_set); mutex_destroy(&session->mutex); kfree(session); } @@ -177,16 +180,46 @@ static void luo_session_remove(struct luo_session_header *sh, sh->count--; } +static int luo_session_finish_one(struct luo_session *session) +{ + guard(mutex)(&session->mutex); + return luo_file_finish(&session->file_set); +} + +static void luo_session_unfreeze_one(struct luo_session *session, + struct luo_session_ser *ser) +{ + guard(mutex)(&session->mutex); + luo_file_unfreeze(&session->file_set, &ser->file_set_ser); +} + +static int luo_session_freeze_one(struct luo_session *session, + struct luo_session_ser *ser) +{ + guard(mutex)(&session->mutex); + return luo_file_freeze(&session->file_set, &ser->file_set_ser); +} + static int luo_session_release(struct inode *inodep, struct file *filep) { struct luo_session *session = filep->private_data; struct luo_session_header *sh; /* If retrieved is set, it means this session is from incoming list */ - if (session->retrieved) + if (session->retrieved) { + int err = luo_session_finish_one(session); + + if (err) { + pr_warn("Unable to finish session [%s] on release\n", + session->name); + return err; + } sh = &luo_session_global.incoming; - else + } else { + scoped_guard(mutex, &session->mutex) + luo_file_unpreserve_files(&session->file_set); sh = &luo_session_global.outgoing; + } luo_session_remove(sh, session); luo_session_free(session); @@ -194,9 +227,140 @@ static int luo_session_release(struct inode *inodep, struct file *filep) return 0; } +static int luo_session_preserve_fd(struct luo_session *session, + struct luo_ucmd *ucmd) +{ + struct liveupdate_session_preserve_fd *argp = ucmd->cmd; + int err; + + guard(mutex)(&session->mutex); + err = luo_preserve_file(&session->file_set, argp->token, argp->fd); + if (err) + return err; + + err = luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + pr_warn("The file was successfully preserved, but response to user failed\n"); + + return err; +} + +static int luo_session_retrieve_fd(struct luo_session *session, + struct luo_ucmd *ucmd) +{ + struct liveupdate_session_retrieve_fd *argp = ucmd->cmd; + struct file *file; + int err; + + argp->fd = get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + guard(mutex)(&session->mutex); + err = luo_retrieve_file(&session->file_set, argp->token, &file); + if (err < 0) + goto err_put_fd; + + err = luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + goto err_put_file; + + fd_install(argp->fd, file); + + return 0; + +err_put_file: + fput(file); +err_put_fd: + put_unused_fd(argp->fd); + + return err; +} + +static int luo_session_finish(struct luo_session *session, + struct luo_ucmd *ucmd) +{ + struct liveupdate_session_finish *argp = ucmd->cmd; + int err = luo_session_finish_one(session); + + if (err) + return err; + + return luo_ucmd_respond(ucmd, sizeof(*argp)); +} + +union ucmd_buffer { + struct liveupdate_session_finish finish; + struct liveupdate_session_preserve_fd preserve; + struct liveupdate_session_retrieve_fd retrieve; +}; + +struct luo_ioctl_op { + unsigned int size; + unsigned int min_size; + unsigned int ioctl_num; + int (*execute)(struct luo_session *session, struct luo_ucmd *ucmd); +}; + +#define IOCTL_OP(_ioctl, _fn, _struct, _last) \ + [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_SESSION_BASE] = { \ + .size = sizeof(_struct) + \ + BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ + sizeof(_struct)), \ + .min_size = offsetofend(_struct, _last), \ + .ioctl_num = _ioctl, \ + .execute = _fn, \ + } + +static const struct luo_ioctl_op luo_session_ioctl_ops[] = { + IOCTL_OP(LIVEUPDATE_SESSION_FINISH, luo_session_finish, + struct liveupdate_session_finish, reserved), + IOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd, + struct liveupdate_session_preserve_fd, token), + IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd, + struct liveupdate_session_retrieve_fd, token), +}; + +static long luo_session_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct luo_session *session = filep->private_data; + const struct luo_ioctl_op *op; + struct luo_ucmd ucmd = {}; + union ucmd_buffer buf; + unsigned int nr; + int ret; + + nr = _IOC_NR(cmd); + if (nr < LIVEUPDATE_CMD_SESSION_BASE || (nr - LIVEUPDATE_CMD_SESSION_BASE) >= + ARRAY_SIZE(luo_session_ioctl_ops)) { + return -EINVAL; + } + + ucmd.ubuffer = (void __user *)arg; + ret = get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer); + if (ret) + return ret; + + op = &luo_session_ioctl_ops[nr - LIVEUPDATE_CMD_SESSION_BASE]; + if (op->ioctl_num != cmd) + return -ENOIOCTLCMD; + if (ucmd.user_size < op->min_size) + return -EINVAL; + + ucmd.cmd = &buf; + ret = copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer, + ucmd.user_size); + if (ret) + return ret; + + return op->execute(session, &ucmd); +} + static const struct file_operations luo_session_fops = { .owner = THIS_MODULE, .release = luo_session_release, + .unlocked_ioctl = luo_session_ioctl, }; /* Create a "struct file" for session */ @@ -392,6 +556,11 @@ int luo_session_deserialize(void) luo_session_free(session); return err; } + + scoped_guard(mutex, &session->mutex) { + luo_file_deserialize(&session->file_set, + &sh->ser[i].file_set_ser); + } } kho_restore_free(sh->header_ser); @@ -406,9 +575,14 @@ int luo_session_serialize(void) struct luo_session_header *sh = &luo_session_global.outgoing; struct luo_session *session; int i = 0; + int err; guard(rwsem_write)(&sh->rwsem); list_for_each_entry(session, &sh->list, list) { + err = luo_session_freeze_one(session, &sh->ser[i]); + if (err) + goto err_undo; + strscpy(sh->ser[i].name, session->name, sizeof(sh->ser[i].name)); i++; @@ -416,6 +590,15 @@ int luo_session_serialize(void) sh->header_ser->count = sh->count; return 0; + +err_undo: + list_for_each_entry_continue_reverse(session, &sh->list, list) { + i--; + luo_session_unfreeze_one(session, &sh->ser[i]); + memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name)); + } + + return err; } /** -- cgit v1.2.3 From 3fa805c37dd4d3e72ae5c58800f3f46ab3ca1f70 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 10 Oct 2025 03:36:50 -0700 Subject: vmcoreinfo: track and log recoverable hardware errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a generic infrastructure for tracking recoverable hardware errors (HW errors that are visible to the OS but does not cause a panic) and record them for vmcore consumption. This aids post-mortem crash analysis tools by preserving a count and timestamp for the last occurrence of such errors. On the other side, correctable errors, which the OS typically remains unaware of because the underlying hardware handles them transparently, are less relevant for crash dump and therefore are NOT tracked in this infrastructure. Add centralized logging for sources of recoverable hardware errors based on the subsystem it has been notified. hwerror_data is write-only at kernel runtime, and it is meant to be read from vmcore using tools like crash/drgn. For example, this is how it looks like when opening the crashdump from drgn. >>> prog['hwerror_data'] (struct hwerror_info[1]){ { .count = (int)844, .timestamp = (time64_t)1752852018, }, ... This helps fleet operators quickly triage whether a crash may be influenced by hardware recoverable errors (which executes a uncommon code path in the kernel), especially when recoverable errors occurred shortly before a panic, such as the bug fixed by commit ee62ce7a1d90 ("page_pool: Track DMA-mapped pages and unmap them when destroying the pool") This is not intended to replace full hardware diagnostics but provides a fast way to correlate hardware events with kernel panics quickly. Rare machine check exceptions—like those indicated by mce_flags.p5 or mce_flags.winchip—are not accounted for in this method, as they fall outside the intended usage scope for this feature's user base. [leitao@debian.org: add hw-recoverable-errors to toctree] Link: https://lkml.kernel.org/r/20251127-vmcoreinfo_fix-v1-1-26f5b1c43da9@debian.org Link: https://lkml.kernel.org/r/20251010-vmcore_hw_error-v5-1-636ede3efe44@debian.org Signed-off-by: Breno Leitao Suggested-by: Tony Luck Suggested-by: Shuai Xue Reviewed-by: Shuai Xue Reviewed-by: Hanjun Guo [APEI] Cc: Bjorn Helgaas Cc: Bob Moore Cc: Borislav Betkov Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: James Morse Cc: Konrad Rzessutek Wilk Cc: Len Brown Cc: Mahesh Salgaonkar Cc: Mauro Carvalho Chehab Cc: "Oliver O'Halloran" Cc: Omar Sandoval Cc: Thomas Gleinxer Signed-off-by: Andrew Morton --- Documentation/driver-api/hw-recoverable-errors.rst | 60 ++++++++++++++++++++++ Documentation/driver-api/index.rst | 1 + arch/x86/kernel/cpu/mce/core.c | 4 ++ drivers/acpi/apei/ghes.c | 36 +++++++++++++ drivers/pci/pcie/aer.c | 2 + include/linux/vmcore_info.h | 8 +++ include/uapi/linux/vmcore.h | 9 ++++ kernel/vmcore_info.c | 17 ++++++ 8 files changed, 137 insertions(+) create mode 100644 Documentation/driver-api/hw-recoverable-errors.rst (limited to 'include/uapi/linux') diff --git a/Documentation/driver-api/hw-recoverable-errors.rst b/Documentation/driver-api/hw-recoverable-errors.rst new file mode 100644 index 000000000000..fc526c3454bd --- /dev/null +++ b/Documentation/driver-api/hw-recoverable-errors.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================================= +Recoverable Hardware Error Tracking in vmcoreinfo +================================================= + +Overview +-------- + +This feature provides a generic infrastructure within the Linux kernel to track +and log recoverable hardware errors. These are hardware recoverable errors +visible that might not cause immediate panics but may influence health, mainly +because new code path will be executed in the kernel. + +By recording counts and timestamps of recoverable errors into the vmcoreinfo +crash dump notes, this infrastructure aids post-mortem crash analysis tools in +correlating hardware events with kernel failures. This enables faster triage +and better understanding of root causes, especially in large-scale cloud +environments where hardware issues are common. + +Benefits +-------- + +- Facilitates correlation of hardware recoverable errors with kernel panics or + unusual code paths that lead to system crashes. +- Provides operators and cloud providers quick insights, improving reliability + and reducing troubleshooting time. +- Complements existing full hardware diagnostics without replacing them. + +Data Exposure and Consumption +----------------------------- + +- The tracked error data consists of per-error-type counts and timestamps of + last occurrence. +- This data is stored in the `hwerror_data` array, categorized by error source + types like CPU, memory, PCI, CXL, and others. +- It is exposed via vmcoreinfo crash dump notes and can be read using tools + like `crash`, `drgn`, or other kernel crash analysis utilities. +- There is no other way to read these data other than from crash dumps. +- These errors are divided by area, which includes CPU, Memory, PCI, CXL and + others. + +Typical usage example (in drgn REPL): + +.. code-block:: python + + >>> prog['hwerror_data'] + (struct hwerror_info[HWERR_RECOV_MAX]){ + { + .count = (int)844, + .timestamp = (time64_t)1752852018, + }, + ... + } + +Enabling +-------- + +- This feature is enabled when CONFIG_VMCORE_INFO is set. + diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 3e2a270bd828..a35705b44799 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -96,6 +96,7 @@ Subsystem-specific APIs gpio/index hsi hte/index + hw-recoverable-errors i2c iio/index infiniband diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 460e90a1a0b1..08adbf4cd6ed 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -1700,6 +1701,9 @@ noinstr void do_machine_check(struct pt_regs *regs) } out: + /* Given it didn't panic, mark it as recoverable */ + hwerr_log_error_type(HWERR_RECOV_OTHERS); + instrumentation_end(); clear: diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 97ee19f2cae0..92b0e3c391b2 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -867,6 +868,40 @@ int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) } EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); +static void ghes_log_hwerr(int sev, guid_t *sec_type) +{ + if (sev != CPER_SEV_RECOVERABLE) + return; + + if (guid_equal(sec_type, &CPER_SEC_PROC_ARM) || + guid_equal(sec_type, &CPER_SEC_PROC_GENERIC) || + guid_equal(sec_type, &CPER_SEC_PROC_IA)) { + hwerr_log_error_type(HWERR_RECOV_CPU); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR) || + guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID) || + guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID) || + guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { + hwerr_log_error_type(HWERR_RECOV_CXL); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_PCIE) || + guid_equal(sec_type, &CPER_SEC_PCI_X_BUS)) { + hwerr_log_error_type(HWERR_RECOV_PCI); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { + hwerr_log_error_type(HWERR_RECOV_MEMORY); + return; + } + + hwerr_log_error_type(HWERR_RECOV_OTHERS); +} + static void ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { @@ -888,6 +923,7 @@ static void ghes_do_proc(struct ghes *ghes, if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) fru_text = gdata->fru_text; + ghes_log_hwerr(sev, sec_type); if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 0b5ed4722ac3..e0bcaa896803 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -765,6 +766,7 @@ static void pci_dev_aer_stats_incr(struct pci_dev *pdev, break; case AER_NONFATAL: aer_info->dev_total_nonfatal_errs++; + hwerr_log_error_type(HWERR_RECOV_PCI); counter = &aer_info->dev_nonfatal_errs[0]; max = AER_MAX_TYPEOF_UNCOR_ERRS; break; diff --git a/include/linux/vmcore_info.h b/include/linux/vmcore_info.h index 37e003ae5262..e71518caacdf 100644 --- a/include/linux/vmcore_info.h +++ b/include/linux/vmcore_info.h @@ -5,6 +5,7 @@ #include #include #include +#include #define CRASH_CORE_NOTE_HEAD_BYTES ALIGN(sizeof(struct elf_note), 4) #define CRASH_CORE_NOTE_NAME_BYTES ALIGN(sizeof(NN_PRSTATUS), 4) @@ -77,4 +78,11 @@ extern u32 *vmcoreinfo_note; Elf_Word *append_elf_note(Elf_Word *buf, char *name, unsigned int type, void *data, size_t data_len); void final_note(Elf_Word *buf); + +#ifdef CONFIG_VMCORE_INFO +void hwerr_log_error_type(enum hwerr_error_type src); +#else +static inline void hwerr_log_error_type(enum hwerr_error_type src) {}; +#endif + #endif /* LINUX_VMCORE_INFO_H */ diff --git a/include/uapi/linux/vmcore.h b/include/uapi/linux/vmcore.h index 3e9da91866ff..2ba89fafa518 100644 --- a/include/uapi/linux/vmcore.h +++ b/include/uapi/linux/vmcore.h @@ -15,4 +15,13 @@ struct vmcoredd_header { __u8 dump_name[VMCOREDD_MAX_NAME_BYTES]; /* Device dump's name */ }; +enum hwerr_error_type { + HWERR_RECOV_CPU, + HWERR_RECOV_MEMORY, + HWERR_RECOV_PCI, + HWERR_RECOV_CXL, + HWERR_RECOV_OTHERS, + HWERR_RECOV_MAX, +}; + #endif /* _UAPI_VMCORE_H */ diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c index e066d31d08f8..fe9bf8db1922 100644 --- a/kernel/vmcore_info.c +++ b/kernel/vmcore_info.c @@ -31,6 +31,13 @@ u32 *vmcoreinfo_note; /* trusted vmcoreinfo, e.g. we can make a copy in the crash memory */ static unsigned char *vmcoreinfo_data_safecopy; +struct hwerr_info { + atomic_t count; + time64_t timestamp; +}; + +static struct hwerr_info hwerr_data[HWERR_RECOV_MAX]; + Elf_Word *append_elf_note(Elf_Word *buf, char *name, unsigned int type, void *data, size_t data_len) { @@ -118,6 +125,16 @@ phys_addr_t __weak paddr_vmcoreinfo_note(void) } EXPORT_SYMBOL(paddr_vmcoreinfo_note); +void hwerr_log_error_type(enum hwerr_error_type src) +{ + if (src < 0 || src >= HWERR_RECOV_MAX) + return; + + atomic_inc(&hwerr_data[src].count); + WRITE_ONCE(hwerr_data[src].timestamp, ktime_get_real_seconds()); +} +EXPORT_SYMBOL_GPL(hwerr_log_error_type); + static int __init crash_save_vmcoreinfo_init(void) { vmcoreinfo_data = (unsigned char *)get_zeroed_page(GFP_KERNEL); -- cgit v1.2.3 From 414690746d2da0dc9a931f8c02d83e5834141251 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 24 Nov 2025 18:28:08 -0800 Subject: i2c: i2c.h: fix a bad kernel-doc line Change an empty line into a blank kernel-doc line to prevent a kernel-doc warning: Warning: ../include/uapi/linux/i2c.h:38 bad line: Fixes: bfb3939c51d5 ("i2c: refactor documentation of struct i2c_msg") Signed-off-by: Randy Dunlap Signed-off-by: Wolfram Sang --- include/uapi/linux/i2c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h index a2db2a56c8b0..2a226657d9f8 100644 --- a/include/uapi/linux/i2c.h +++ b/include/uapi/linux/i2c.h @@ -36,7 +36,7 @@ * * Only if I2C_FUNC_NOSTART is set: * %I2C_M_NOSTART: skip repeated start sequence - + * * Only if I2C_FUNC_PROTOCOL_MANGLING is set: * %I2C_M_NO_RD_ACK: in a read message, master ACK/NACK bit is skipped * %I2C_M_IGNORE_NAK: treat NACK from client as ACK -- cgit v1.2.3 From 205dd7a5d6ad6f4c8e8fcd3c3b95a7c0e7067fee Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 18 Nov 2025 18:06:31 -0500 Subject: virtio_pci: drop kernel.h virtio UAPI headers really have no business pulling in kernel.h Replace it with const.h which seems to be what's needed for __KERNEL_DIV_ROUND_UP. Fixes: 7c1ae151e812 ("virtio_pci: Introduce device parts access commands") Cc: Yishai Hadas Cc: Alex Williamson Message-ID: <7a73b6c6af67e13b86633cd7bf11ad56b5d9809b.1763535341.git.mst@redhat.com> Signed-off-by: Michael S. Tsirkin --- include/uapi/linux/virtio_pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index c691ac210ce2..e732e3456e27 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -40,7 +40,7 @@ #define _LINUX_VIRTIO_PCI_H #include -#include +#include #ifndef VIRTIO_PCI_NO_LEGACY -- cgit v1.2.3 From c3859de858aa7ae0d0a5ca21e8ee9792f2f256b6 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 2 Dec 2025 13:44:47 +1100 Subject: psp-sev: Assign numbers to all status codes and add new Make the definitions explicit. Add some more new codes. The following patches will be using SPDM_REQUEST and EXPAND_BUFFER_LENGTH_REQUEST, others are useful for the PSP FW diagnostics. Signed-off-by: Alexey Kardashevskiy Link: https://patch.msgid.link/20251202024449.542361-3-aik@amd.com Acked-by: Tom Lendacky Signed-off-by: Dan Williams --- include/uapi/linux/psp-sev.h | 66 +++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 25 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/psp-sev.h b/include/uapi/linux/psp-sev.h index c2fd324623c4..2b5b042eb73b 100644 --- a/include/uapi/linux/psp-sev.h +++ b/include/uapi/linux/psp-sev.h @@ -47,32 +47,32 @@ typedef enum { * with possible values from the specification. */ SEV_RET_NO_FW_CALL = -1, - SEV_RET_SUCCESS = 0, - SEV_RET_INVALID_PLATFORM_STATE, - SEV_RET_INVALID_GUEST_STATE, - SEV_RET_INAVLID_CONFIG, + SEV_RET_SUCCESS = 0, + SEV_RET_INVALID_PLATFORM_STATE = 0x0001, + SEV_RET_INVALID_GUEST_STATE = 0x0002, + SEV_RET_INAVLID_CONFIG = 0x0003, SEV_RET_INVALID_CONFIG = SEV_RET_INAVLID_CONFIG, - SEV_RET_INVALID_LEN, - SEV_RET_ALREADY_OWNED, - SEV_RET_INVALID_CERTIFICATE, - SEV_RET_POLICY_FAILURE, - SEV_RET_INACTIVE, - SEV_RET_INVALID_ADDRESS, - SEV_RET_BAD_SIGNATURE, - SEV_RET_BAD_MEASUREMENT, - SEV_RET_ASID_OWNED, - SEV_RET_INVALID_ASID, - SEV_RET_WBINVD_REQUIRED, - SEV_RET_DFFLUSH_REQUIRED, - SEV_RET_INVALID_GUEST, - SEV_RET_INVALID_COMMAND, - SEV_RET_ACTIVE, - SEV_RET_HWSEV_RET_PLATFORM, - SEV_RET_HWSEV_RET_UNSAFE, - SEV_RET_UNSUPPORTED, - SEV_RET_INVALID_PARAM, - SEV_RET_RESOURCE_LIMIT, - SEV_RET_SECURE_DATA_INVALID, + SEV_RET_INVALID_LEN = 0x0004, + SEV_RET_ALREADY_OWNED = 0x0005, + SEV_RET_INVALID_CERTIFICATE = 0x0006, + SEV_RET_POLICY_FAILURE = 0x0007, + SEV_RET_INACTIVE = 0x0008, + SEV_RET_INVALID_ADDRESS = 0x0009, + SEV_RET_BAD_SIGNATURE = 0x000A, + SEV_RET_BAD_MEASUREMENT = 0x000B, + SEV_RET_ASID_OWNED = 0x000C, + SEV_RET_INVALID_ASID = 0x000D, + SEV_RET_WBINVD_REQUIRED = 0x000E, + SEV_RET_DFFLUSH_REQUIRED = 0x000F, + SEV_RET_INVALID_GUEST = 0x0010, + SEV_RET_INVALID_COMMAND = 0x0011, + SEV_RET_ACTIVE = 0x0012, + SEV_RET_HWSEV_RET_PLATFORM = 0x0013, + SEV_RET_HWSEV_RET_UNSAFE = 0x0014, + SEV_RET_UNSUPPORTED = 0x0015, + SEV_RET_INVALID_PARAM = 0x0016, + SEV_RET_RESOURCE_LIMIT = 0x0017, + SEV_RET_SECURE_DATA_INVALID = 0x0018, SEV_RET_INVALID_PAGE_SIZE = 0x0019, SEV_RET_INVALID_PAGE_STATE = 0x001A, SEV_RET_INVALID_MDATA_ENTRY = 0x001B, @@ -87,6 +87,22 @@ typedef enum { SEV_RET_RESTORE_REQUIRED = 0x0025, SEV_RET_RMP_INITIALIZATION_FAILED = 0x0026, SEV_RET_INVALID_KEY = 0x0027, + SEV_RET_SHUTDOWN_INCOMPLETE = 0x0028, + SEV_RET_INCORRECT_BUFFER_LENGTH = 0x0030, + SEV_RET_EXPAND_BUFFER_LENGTH_REQUEST = 0x0031, + SEV_RET_SPDM_REQUEST = 0x0032, + SEV_RET_SPDM_ERROR = 0x0033, + SEV_RET_SEV_STATUS_ERR_IN_DEV_CONN = 0x0035, + SEV_RET_SEV_STATUS_INVALID_DEV_CTX = 0x0036, + SEV_RET_SEV_STATUS_INVALID_TDI_CTX = 0x0037, + SEV_RET_SEV_STATUS_INVALID_TDI = 0x0038, + SEV_RET_SEV_STATUS_RECLAIM_REQUIRED = 0x0039, + SEV_RET_IN_USE = 0x003A, + SEV_RET_SEV_STATUS_INVALID_DEV_STATE = 0x003B, + SEV_RET_SEV_STATUS_INVALID_TDI_STATE = 0x003C, + SEV_RET_SEV_STATUS_DEV_CERT_CHANGED = 0x003D, + SEV_RET_SEV_STATUS_RESYNC_REQ = 0x003E, + SEV_RET_SEV_STATUS_RESPONSE_TOO_LARGE = 0x003F, SEV_RET_MAX, } sev_ret_code; -- cgit v1.2.3 From f7231cff1f3ff8259bef02dc4999bc132abf29cf Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 3 Dec 2025 08:55:34 +0000 Subject: media: uapi: c3-isp: Fix documentation warning Building htmldocs generates a warning: WARNING: include/uapi/linux/media/amlogic/c3-isp-config.h:199 error: Cannot parse struct or union! Which correctly highlights that the c3_isp_params_block_header symbol is wrongly documented as a struct while it's a plain #define instead. Fix this by removing the 'struct' identifier from the documentation of the c3_isp_params_block_header symbol. [ribalda: Add Closes:] Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/all/20251127131425.4b5b6644@canb.auug.org.au/ Fixes: 45662082855c ("media: uapi: Convert Amlogic C3 to V4L2 extensible params") Cc: stable@vger.kernel.org Signed-off-by: Jacopo Mondi Signed-off-by: Ricardo Ribalda Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/media/amlogic/c3-isp-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/media/amlogic/c3-isp-config.h b/include/uapi/linux/media/amlogic/c3-isp-config.h index 0a3c1cc55ccb..92db5dcdda18 100644 --- a/include/uapi/linux/media/amlogic/c3-isp-config.h +++ b/include/uapi/linux/media/amlogic/c3-isp-config.h @@ -186,7 +186,7 @@ enum c3_isp_params_block_type { #define C3_ISP_PARAMS_BLOCK_FL_ENABLE V4L2_ISP_PARAMS_FL_BLOCK_ENABLE /** - * struct c3_isp_params_block_header - C3 ISP parameter block header + * c3_isp_params_block_header - C3 ISP parameter block header * * This structure represents the common part of all the ISP configuration * blocks and is identical to :c:type:`v4l2_isp_params_block_header`. -- cgit v1.2.3 From 22a1ffea5f805dfa21b64d1c7b5fe39c0c78c997 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 1 Dec 2025 16:43:28 -0500 Subject: block: add IOC_PR_READ_KEYS ioctl Add a Persistent Reservations ioctl to read the list of currently registered reservation keys. This calls the pr_ops->read_keys() function that was previously added in commit c787f1baa503 ("block: Add PR callouts for read keys and reservation") but was only used by the in-kernel SCSI target so far. The IOC_PR_READ_KEYS ioctl is necessary so that userspace applications that rely on Persistent Reservations ioctls have a way of inspecting the current state. Cluster managers and validation tests need this functionality. Signed-off-by: Stefan Hajnoczi Reviewed-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Signed-off-by: Jens Axboe --- block/ioctl.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/pr.h | 7 +++++++ 2 files changed, 63 insertions(+) (limited to 'include/uapi/linux') diff --git a/block/ioctl.c b/block/ioctl.c index 2b3ab9bfc413..c0802ebf54a6 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -423,6 +423,60 @@ static int blkdev_pr_clear(struct block_device *bdev, blk_mode_t mode, return ops->pr_clear(bdev, c.key); } +static int blkdev_pr_read_keys(struct block_device *bdev, blk_mode_t mode, + struct pr_read_keys __user *arg) +{ + const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; + struct pr_keys *keys_info; + struct pr_read_keys read_keys; + u64 __user *keys_ptr; + size_t keys_info_len; + size_t keys_copy_len; + int ret; + + if (!blkdev_pr_allowed(bdev, mode)) + return -EPERM; + if (!ops || !ops->pr_read_keys) + return -EOPNOTSUPP; + + if (copy_from_user(&read_keys, arg, sizeof(read_keys))) + return -EFAULT; + + keys_info_len = struct_size(keys_info, keys, read_keys.num_keys); + if (keys_info_len == SIZE_MAX) + return -EINVAL; + + keys_info = kzalloc(keys_info_len, GFP_KERNEL); + if (!keys_info) + return -ENOMEM; + + keys_info->num_keys = read_keys.num_keys; + + ret = ops->pr_read_keys(bdev, keys_info); + if (ret) + goto out; + + /* Copy out individual keys */ + keys_ptr = u64_to_user_ptr(read_keys.keys_ptr); + keys_copy_len = min(read_keys.num_keys, keys_info->num_keys) * + sizeof(keys_info->keys[0]); + + if (copy_to_user(keys_ptr, keys_info->keys, keys_copy_len)) { + ret = -EFAULT; + goto out; + } + + /* Copy out the arg struct */ + read_keys.generation = keys_info->generation; + read_keys.num_keys = keys_info->num_keys; + + if (copy_to_user(arg, &read_keys, sizeof(read_keys))) + ret = -EFAULT; +out: + kfree(keys_info); + return ret; +} + static int blkdev_flushbuf(struct block_device *bdev, unsigned cmd, unsigned long arg) { @@ -645,6 +699,8 @@ static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode, return blkdev_pr_preempt(bdev, mode, argp, true); case IOC_PR_CLEAR: return blkdev_pr_clear(bdev, mode, argp); + case IOC_PR_READ_KEYS: + return blkdev_pr_read_keys(bdev, mode, argp); default: return blk_get_meta_cap(bdev, cmd, argp); } diff --git a/include/uapi/linux/pr.h b/include/uapi/linux/pr.h index d8126415966f..fcb74eab92c8 100644 --- a/include/uapi/linux/pr.h +++ b/include/uapi/linux/pr.h @@ -56,6 +56,12 @@ struct pr_clear { __u32 __pad; }; +struct pr_read_keys { + __u32 generation; + __u32 num_keys; + __u64 keys_ptr; +}; + #define PR_FL_IGNORE_KEY (1 << 0) /* ignore existing key */ #define IOC_PR_REGISTER _IOW('p', 200, struct pr_registration) @@ -64,5 +70,6 @@ struct pr_clear { #define IOC_PR_PREEMPT _IOW('p', 203, struct pr_preempt) #define IOC_PR_PREEMPT_ABORT _IOW('p', 204, struct pr_preempt) #define IOC_PR_CLEAR _IOW('p', 205, struct pr_clear) +#define IOC_PR_READ_KEYS _IOWR('p', 206, struct pr_read_keys) #endif /* _UAPI_PR_H */ -- cgit v1.2.3 From 3e2cb9ee76c27f57bfdb7b4753b909594d4fa31a Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 1 Dec 2025 16:43:29 -0500 Subject: block: add IOC_PR_READ_RESERVATION ioctl Add a Persistent Reservations ioctl to read the current reservation. This calls the pr_ops->read_reservation() function that was previously added in commit c787f1baa503 ("block: Add PR callouts for read keys and reservation") but was only used by the in-kernel SCSI target so far. The IOC_PR_READ_RESERVATION ioctl is necessary so that userspace applications that rely on Persistent Reservations ioctls have a way of inspecting the current state. Cluster managers and validation tests need this functionality. Signed-off-by: Stefan Hajnoczi Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Signed-off-by: Jens Axboe --- block/ioctl.c | 28 ++++++++++++++++++++++++++++ include/uapi/linux/pr.h | 7 +++++++ 2 files changed, 35 insertions(+) (limited to 'include/uapi/linux') diff --git a/block/ioctl.c b/block/ioctl.c index c0802ebf54a6..61feed686418 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -477,6 +477,32 @@ out: return ret; } +static int blkdev_pr_read_reservation(struct block_device *bdev, + blk_mode_t mode, struct pr_read_reservation __user *arg) +{ + const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; + struct pr_held_reservation rsv = {}; + struct pr_read_reservation out = {}; + int ret; + + if (!blkdev_pr_allowed(bdev, mode)) + return -EPERM; + if (!ops || !ops->pr_read_reservation) + return -EOPNOTSUPP; + + ret = ops->pr_read_reservation(bdev, &rsv); + if (ret) + return ret; + + out.key = rsv.key; + out.generation = rsv.generation; + out.type = rsv.type; + + if (copy_to_user(arg, &out, sizeof(out))) + return -EFAULT; + return 0; +} + static int blkdev_flushbuf(struct block_device *bdev, unsigned cmd, unsigned long arg) { @@ -701,6 +727,8 @@ static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode, return blkdev_pr_clear(bdev, mode, argp); case IOC_PR_READ_KEYS: return blkdev_pr_read_keys(bdev, mode, argp); + case IOC_PR_READ_RESERVATION: + return blkdev_pr_read_reservation(bdev, mode, argp); default: return blk_get_meta_cap(bdev, cmd, argp); } diff --git a/include/uapi/linux/pr.h b/include/uapi/linux/pr.h index fcb74eab92c8..847f3051057a 100644 --- a/include/uapi/linux/pr.h +++ b/include/uapi/linux/pr.h @@ -62,6 +62,12 @@ struct pr_read_keys { __u64 keys_ptr; }; +struct pr_read_reservation { + __u64 key; + __u32 generation; + __u32 type; +}; + #define PR_FL_IGNORE_KEY (1 << 0) /* ignore existing key */ #define IOC_PR_REGISTER _IOW('p', 200, struct pr_registration) @@ -71,5 +77,6 @@ struct pr_read_keys { #define IOC_PR_PREEMPT_ABORT _IOW('p', 204, struct pr_preempt) #define IOC_PR_CLEAR _IOW('p', 205, struct pr_clear) #define IOC_PR_READ_KEYS _IOWR('p', 206, struct pr_read_keys) +#define IOC_PR_READ_RESERVATION _IOR('p', 207, struct pr_read_reservation) #endif /* _UAPI_PR_H */ -- cgit v1.2.3 From fe93446b5ebdaa89a8f97b15668c077921a65140 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 3 Dec 2025 14:57:57 +0100 Subject: vfs: use UAPI types for new struct delegation definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using libc types and headers from the UAPI headers is problematic as it introduces a dependency on a full C toolchain. Use the fixed-width integer types provided by the UAPI headers instead. Fixes: 1602bad16d7d ("vfs: expose delegation support to userland") Fixes: 4be9e04ebf75 ("vfs: add needed headers for new struct delegation definition") Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20251203-uapi-fcntl-v1-1-490c67bf3425@linutronix.de Acked-by: Arnd Bergmann Acked-by: Jeff Layton Signed-off-by: Christian Brauner --- include/uapi/linux/fcntl.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 5e277fd955aa..aadfbf6e0cb3 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -4,11 +4,7 @@ #include #include -#ifdef __KERNEL__ #include -#else -#include -#endif #define F_SETLEASE (F_LINUX_SPECIFIC_BASE + 0) #define F_GETLEASE (F_LINUX_SPECIFIC_BASE + 1) @@ -90,9 +86,9 @@ /* Argument structure for F_GETDELEG and F_SETDELEG */ struct delegation { - uint32_t d_flags; /* Must be 0 */ - uint16_t d_type; /* F_RDLCK, F_WRLCK, F_UNLCK */ - uint16_t __pad; /* Must be 0 */ + __u32 d_flags; /* Must be 0 */ + __u16 d_type; /* F_RDLCK, F_WRLCK, F_UNLCK */ + __u16 __pad; /* Must be 0 */ }; /* -- cgit v1.2.3 From 7bfe3b8ea6e30437e01fcb8e4f56ef6e4d986d0f Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Thu, 13 Nov 2025 04:41:49 +0000 Subject: Drivers: hv: Introduce mshv_vtl driver Provide an interface for Virtual Machine Monitor like OpenVMM and its use as OpenHCL paravisor to control VTL0 (Virtual trust Level). Expose devices and support IOCTLs for features like VTL creation, VTL0 memory management, context switch, making hypercalls, mapping VTL0 address space to VTL2 userspace, getting new VMBus messages and channel events in VTL2 etc. Co-developed-by: Roman Kisel Signed-off-by: Roman Kisel Co-developed-by: Saurabh Sengar Signed-off-by: Saurabh Sengar Reviewed-by: Michael Kelley Signed-off-by: Naman Jain Signed-off-by: Wei Liu --- arch/x86/hyperv/Makefile | 10 +- arch/x86/hyperv/hv_vtl.c | 30 + arch/x86/hyperv/mshv-asm-offsets.c | 37 + arch/x86/hyperv/mshv_vtl_asm.S | 116 +++ arch/x86/include/asm/mshyperv.h | 34 + drivers/hv/Kconfig | 27 +- drivers/hv/Makefile | 7 +- drivers/hv/mshv_vtl.h | 25 + drivers/hv/mshv_vtl_main.c | 1392 ++++++++++++++++++++++++++++++++++++ include/hyperv/hvgdk_mini.h | 106 +++ include/uapi/linux/mshv.h | 80 +++ 11 files changed, 1861 insertions(+), 3 deletions(-) create mode 100644 arch/x86/hyperv/mshv-asm-offsets.c create mode 100644 arch/x86/hyperv/mshv_vtl_asm.S create mode 100644 drivers/hv/mshv_vtl.h create mode 100644 drivers/hv/mshv_vtl_main.c (limited to 'include/uapi/linux') diff --git a/arch/x86/hyperv/Makefile b/arch/x86/hyperv/Makefile index 6f5d97cddd80..56292102af62 100644 --- a/arch/x86/hyperv/Makefile +++ b/arch/x86/hyperv/Makefile @@ -1,7 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y := hv_init.o mmu.o nested.o irqdomain.o ivm.o obj-$(CONFIG_X86_64) += hv_apic.o -obj-$(CONFIG_HYPERV_VTL_MODE) += hv_vtl.o +obj-$(CONFIG_HYPERV_VTL_MODE) += hv_vtl.o mshv_vtl_asm.o + +$(obj)/mshv_vtl_asm.o: $(obj)/mshv-asm-offsets.h + +$(obj)/mshv-asm-offsets.h: $(obj)/mshv-asm-offsets.s FORCE + $(call filechk,offsets,__MSHV_ASM_OFFSETS_H__) ifdef CONFIG_X86_64 obj-$(CONFIG_PARAVIRT_SPINLOCKS) += hv_spinlock.o @@ -12,3 +17,6 @@ obj-$(CONFIG_PARAVIRT_SPINLOCKS) += hv_spinlock.o obj-$(CONFIG_CRASH_DUMP) += hv_crash.o hv_trampoline.o endif endif + +targets += mshv-asm-offsets.s +clean-files += mshv-asm-offsets.h diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index 042e8712d8de..c0edaed0efb3 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -9,12 +9,17 @@ #include #include #include +#include +#include #include #include #include #include #include +#include +#include #include <../kernel/smpboot.h> +#include "../../kernel/fpu/legacy.h" extern struct boot_params boot_params; static struct real_mode_header hv_vtl_real_mode_header; @@ -249,3 +254,28 @@ int __init hv_vtl_early_init(void) return 0; } + +DEFINE_STATIC_CALL_NULL(__mshv_vtl_return_hypercall, void (*)(void)); + +void mshv_vtl_return_call_init(u64 vtl_return_offset) +{ + static_call_update(__mshv_vtl_return_hypercall, + (void *)((u8 *)hv_hypercall_pg + vtl_return_offset)); +} +EXPORT_SYMBOL(mshv_vtl_return_call_init); + +void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) +{ + struct hv_vp_assist_page *hvp; + + hvp = hv_vp_assist_page[smp_processor_id()]; + hvp->vtl_ret_x64rax = vtl0->rax; + hvp->vtl_ret_x64rcx = vtl0->rcx; + + kernel_fpu_begin_mask(0); + fxrstor(&vtl0->fx_state); + __mshv_vtl_return_call(vtl0); + fxsave(&vtl0->fx_state); + kernel_fpu_end(); +} +EXPORT_SYMBOL(mshv_vtl_return_call); diff --git a/arch/x86/hyperv/mshv-asm-offsets.c b/arch/x86/hyperv/mshv-asm-offsets.c new file mode 100644 index 000000000000..882c1db6df16 --- /dev/null +++ b/arch/x86/hyperv/mshv-asm-offsets.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + * + * Copyright (c) 2025, Microsoft Corporation. + * + * Author: + * Naman Jain + */ +#define COMPILE_OFFSETS + +#include +#include + +static void __used common(void) +{ + if (IS_ENABLED(CONFIG_HYPERV_VTL_MODE)) { + OFFSET(MSHV_VTL_CPU_CONTEXT_rax, mshv_vtl_cpu_context, rax); + OFFSET(MSHV_VTL_CPU_CONTEXT_rcx, mshv_vtl_cpu_context, rcx); + OFFSET(MSHV_VTL_CPU_CONTEXT_rdx, mshv_vtl_cpu_context, rdx); + OFFSET(MSHV_VTL_CPU_CONTEXT_rbx, mshv_vtl_cpu_context, rbx); + OFFSET(MSHV_VTL_CPU_CONTEXT_rbp, mshv_vtl_cpu_context, rbp); + OFFSET(MSHV_VTL_CPU_CONTEXT_rsi, mshv_vtl_cpu_context, rsi); + OFFSET(MSHV_VTL_CPU_CONTEXT_rdi, mshv_vtl_cpu_context, rdi); + OFFSET(MSHV_VTL_CPU_CONTEXT_r8, mshv_vtl_cpu_context, r8); + OFFSET(MSHV_VTL_CPU_CONTEXT_r9, mshv_vtl_cpu_context, r9); + OFFSET(MSHV_VTL_CPU_CONTEXT_r10, mshv_vtl_cpu_context, r10); + OFFSET(MSHV_VTL_CPU_CONTEXT_r11, mshv_vtl_cpu_context, r11); + OFFSET(MSHV_VTL_CPU_CONTEXT_r12, mshv_vtl_cpu_context, r12); + OFFSET(MSHV_VTL_CPU_CONTEXT_r13, mshv_vtl_cpu_context, r13); + OFFSET(MSHV_VTL_CPU_CONTEXT_r14, mshv_vtl_cpu_context, r14); + OFFSET(MSHV_VTL_CPU_CONTEXT_r15, mshv_vtl_cpu_context, r15); + OFFSET(MSHV_VTL_CPU_CONTEXT_cr2, mshv_vtl_cpu_context, cr2); + } +} diff --git a/arch/x86/hyperv/mshv_vtl_asm.S b/arch/x86/hyperv/mshv_vtl_asm.S new file mode 100644 index 000000000000..f595eefad9ab --- /dev/null +++ b/arch/x86/hyperv/mshv_vtl_asm.S @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Assembly level code for mshv_vtl VTL transition + * + * Copyright (c) 2025, Microsoft Corporation. + * + * Author: + * Naman Jain + */ + +#include +#include +#include +#include +#include +#include "mshv-asm-offsets.h" + + .text + .section .noinstr.text, "ax" +/* + * void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) + * + * This function is used to context switch between different Virtual Trust Levels. + * It is marked as 'noinstr' to prevent against instrumentation and debugging facilities. + * NMIs aren't a problem because the NMI handler saves/restores CR2 specifically to guard + * against #PFs in NMI context clobbering the guest state. + */ +SYM_FUNC_START(__mshv_vtl_return_call) + /* Push callee save registers */ + pushq %rbp + mov %rsp, %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + pushq %rbx + + /* register switch to VTL0 clobbers all registers except rax/rcx */ + mov %_ASM_ARG1, %rax + + /* grab rbx/rbp/rsi/rdi/r8-r15 */ + mov MSHV_VTL_CPU_CONTEXT_rbx(%rax), %rbx + mov MSHV_VTL_CPU_CONTEXT_rbp(%rax), %rbp + mov MSHV_VTL_CPU_CONTEXT_rsi(%rax), %rsi + mov MSHV_VTL_CPU_CONTEXT_rdi(%rax), %rdi + mov MSHV_VTL_CPU_CONTEXT_r8(%rax), %r8 + mov MSHV_VTL_CPU_CONTEXT_r9(%rax), %r9 + mov MSHV_VTL_CPU_CONTEXT_r10(%rax), %r10 + mov MSHV_VTL_CPU_CONTEXT_r11(%rax), %r11 + mov MSHV_VTL_CPU_CONTEXT_r12(%rax), %r12 + mov MSHV_VTL_CPU_CONTEXT_r13(%rax), %r13 + mov MSHV_VTL_CPU_CONTEXT_r14(%rax), %r14 + mov MSHV_VTL_CPU_CONTEXT_r15(%rax), %r15 + + mov MSHV_VTL_CPU_CONTEXT_cr2(%rax), %rdx + mov %rdx, %cr2 + mov MSHV_VTL_CPU_CONTEXT_rdx(%rax), %rdx + + /* stash host registers on stack */ + pushq %rax + pushq %rcx + + xor %ecx, %ecx + + /* make a hypercall to switch VTL */ + call STATIC_CALL_TRAMP_STR(__mshv_vtl_return_hypercall) + + /* stash guest registers on stack, restore saved host copies */ + pushq %rax + pushq %rcx + mov 16(%rsp), %rcx + mov 24(%rsp), %rax + + mov %rdx, MSHV_VTL_CPU_CONTEXT_rdx(%rax) + mov %cr2, %rdx + mov %rdx, MSHV_VTL_CPU_CONTEXT_cr2(%rax) + pop MSHV_VTL_CPU_CONTEXT_rcx(%rax) + pop MSHV_VTL_CPU_CONTEXT_rax(%rax) + add $16, %rsp + + /* save rbx/rbp/rsi/rdi/r8-r15 */ + mov %rbx, MSHV_VTL_CPU_CONTEXT_rbx(%rax) + mov %rbp, MSHV_VTL_CPU_CONTEXT_rbp(%rax) + mov %rsi, MSHV_VTL_CPU_CONTEXT_rsi(%rax) + mov %rdi, MSHV_VTL_CPU_CONTEXT_rdi(%rax) + mov %r8, MSHV_VTL_CPU_CONTEXT_r8(%rax) + mov %r9, MSHV_VTL_CPU_CONTEXT_r9(%rax) + mov %r10, MSHV_VTL_CPU_CONTEXT_r10(%rax) + mov %r11, MSHV_VTL_CPU_CONTEXT_r11(%rax) + mov %r12, MSHV_VTL_CPU_CONTEXT_r12(%rax) + mov %r13, MSHV_VTL_CPU_CONTEXT_r13(%rax) + mov %r14, MSHV_VTL_CPU_CONTEXT_r14(%rax) + mov %r15, MSHV_VTL_CPU_CONTEXT_r15(%rax) + + /* pop callee-save registers r12-r15, rbx */ + pop %rbx + pop %r15 + pop %r14 + pop %r13 + pop %r12 + + pop %rbp + RET +SYM_FUNC_END(__mshv_vtl_return_call) +/* + * Make sure that static_call_key symbol: __SCK____mshv_vtl_return_hypercall is accessible here. + * Below code is inspired from __ADDRESSABLE(sym) macro. Symbol name is kept simple, to avoid + * naming it something like "__UNIQUE_ID_addressable___SCK____mshv_vtl_return_hypercall_662.0" + * which would otherwise have been generated by the macro. + */ + .section .discard.addressable,"aw" + .align 8 + .type mshv_vtl_return_sym, @object + .size mshv_vtl_return_sym, 8 +mshv_vtl_return_sym: + .quad __SCK____mshv_vtl_return_hypercall diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 1342d55c2545..10037125099a 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -11,6 +11,7 @@ #include #include #include +#include /* * Hyper-V always provides a single IO-APIC at this MMIO address. @@ -269,13 +270,46 @@ static inline u64 hv_get_non_nested_msr(unsigned int reg) { return 0; } static inline int hv_apicid_to_vp_index(u32 apic_id) { return -EINVAL; } #endif /* CONFIG_HYPERV */ +struct mshv_vtl_cpu_context { + union { + struct { + u64 rax; + u64 rcx; + u64 rdx; + u64 rbx; + u64 cr2; + u64 rbp; + u64 rsi; + u64 rdi; + u64 r8; + u64 r9; + u64 r10; + u64 r11; + u64 r12; + u64 r13; + u64 r14; + u64 r15; + }; + u64 gp_regs[16]; + }; + + struct fxregs_state fx_state; +}; #ifdef CONFIG_HYPERV_VTL_MODE void __init hv_vtl_init_platform(void); int __init hv_vtl_early_init(void); +void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0); +void mshv_vtl_return_call_init(u64 vtl_return_offset); +void mshv_vtl_return_hypercall(void); +void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0); #else static inline void __init hv_vtl_init_platform(void) {} static inline int __init hv_vtl_early_init(void) { return 0; } +static inline void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {} +static inline void mshv_vtl_return_call_init(u64 vtl_return_offset) {} +static inline void mshv_vtl_return_hypercall(void) {} +static inline void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {} #endif #include diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index 0b8c391a0342..d4a8d349200c 100644 --- a/drivers/hv/Kconfig +++ b/drivers/hv/Kconfig @@ -17,7 +17,8 @@ config HYPERV config HYPERV_VTL_MODE bool "Enable Linux to boot in VTL context" - depends on (X86_64 || ARM64) && HYPERV + depends on (X86_64 && HAVE_STATIC_CALL) || ARM64 + depends on HYPERV depends on SMP default n help @@ -82,4 +83,28 @@ config MSHV_ROOT If unsure, say N. +config MSHV_VTL + tristate "Microsoft Hyper-V VTL driver" + depends on X86_64 && HYPERV_VTL_MODE + depends on HYPERV_VMBUS + # Mapping VTL0 memory to a userspace process in VTL2 is supported in OpenHCL. + # VTL2 for OpenHCL makes use of Huge Pages to improve performance on VMs, + # specially with large memory requirements. + depends on TRANSPARENT_HUGEPAGE + # MTRRs are controlled by VTL0, and are not specific to individual VTLs. + # Therefore, do not attempt to access or modify MTRRs here. + depends on !MTRR + select CPUMASK_OFFSTACK + select VIRT_XFER_TO_GUEST_WORK + default n + help + Select this option to enable Hyper-V VTL driver support. + This driver provides interfaces for Virtual Machine Manager (VMM) running in VTL2 + userspace to create VTLs and partitions, setup and manage VTL0 memory and + allow userspace to make direct hypercalls. This also allows to map VTL0's address + space to a usermode process in VTL2 and supports getting new VMBus messages and channel + events in VTL2. + + If unsure, say N. + endmenu diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index 1a1677bf4dac..6d929fb0e13d 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_HYPERV_VMBUS) += hv_vmbus.o obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o obj-$(CONFIG_MSHV_ROOT) += mshv_root.o +obj-$(CONFIG_MSHV_VTL) += mshv_vtl.o CFLAGS_hv_trace.o = -I$(src) CFLAGS_hv_balloon.o = -I$(src) @@ -14,7 +15,11 @@ hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o mshv_root-y := mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \ mshv_root_hv_call.o mshv_portid_table.o +mshv_vtl-y := mshv_vtl_main.o # Code that must be built-in obj-$(CONFIG_HYPERV) += hv_common.o -obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o mshv_common.o +obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o +ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL),) + obj-y += mshv_common.o +endif diff --git a/drivers/hv/mshv_vtl.h b/drivers/hv/mshv_vtl.h new file mode 100644 index 000000000000..a6eea52f7aa2 --- /dev/null +++ b/drivers/hv/mshv_vtl.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _MSHV_VTL_H +#define _MSHV_VTL_H + +#include +#include + +struct mshv_vtl_run { + u32 cancel; + u32 vtl_ret_action_size; + u32 pad[2]; + char exit_message[MSHV_MAX_RUN_MSG_SIZE]; + union { + struct mshv_vtl_cpu_context cpu_context; + + /* + * Reserving room for the cpu context to grow and to maintain compatibility + * with user mode. + */ + char reserved[1024]; + }; + char vtl_ret_actions[MSHV_MAX_RUN_MSG_SIZE]; +}; + +#endif /* _MSHV_VTL_H */ diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c new file mode 100644 index 000000000000..2cebe9de5a5a --- /dev/null +++ b/drivers/hv/mshv_vtl_main.c @@ -0,0 +1,1392 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Microsoft Corporation. + * + * Author: + * Roman Kisel + * Saurabh Sengar + * Naman Jain + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../kernel/fpu/legacy.h" +#include "mshv.h" +#include "mshv_vtl.h" +#include "hyperv_vmbus.h" + +MODULE_AUTHOR("Microsoft"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Microsoft Hyper-V VTL Driver"); + +#define MSHV_ENTRY_REASON_LOWER_VTL_CALL 0x1 +#define MSHV_ENTRY_REASON_INTERRUPT 0x2 +#define MSHV_ENTRY_REASON_INTERCEPT 0x3 + +#define MSHV_REAL_OFF_SHIFT 16 +#define MSHV_PG_OFF_CPU_MASK (BIT_ULL(MSHV_REAL_OFF_SHIFT) - 1) +#define MSHV_RUN_PAGE_OFFSET 0 +#define MSHV_REG_PAGE_OFFSET 1 +#define VTL2_VMBUS_SINT_INDEX 7 + +static struct device *mem_dev; + +static struct tasklet_struct msg_dpc; +static wait_queue_head_t fd_wait_queue; +static bool has_message; +static struct eventfd_ctx *flag_eventfds[HV_EVENT_FLAGS_COUNT]; +static DEFINE_MUTEX(flag_lock); +static bool __read_mostly mshv_has_reg_page; + +/* hvcall code is of type u16, allocate a bitmap of size (1 << 16) to accommodate it */ +#define MAX_BITMAP_SIZE ((U16_MAX + 1) / 8) + +struct mshv_vtl_hvcall_fd { + u8 allow_bitmap[MAX_BITMAP_SIZE]; + bool allow_map_initialized; + /* + * Used to protect hvcall setup in IOCTLs + */ + struct mutex init_mutex; + struct miscdevice *dev; +}; + +struct mshv_vtl_poll_file { + struct file *file; + wait_queue_entry_t wait; + wait_queue_head_t *wqh; + poll_table pt; + int cpu; +}; + +struct mshv_vtl { + struct device *module_dev; + u64 id; +}; + +struct mshv_vtl_per_cpu { + struct mshv_vtl_run *run; + struct page *reg_page; +}; + +/* SYNIC_OVERLAY_PAGE_MSR - internal, identical to hv_synic_simp */ +union hv_synic_overlay_page_msr { + u64 as_uint64; + struct { + u64 enabled: 1; + u64 reserved: 11; + u64 pfn: 52; + } __packed; +}; + +static struct mutex mshv_vtl_poll_file_lock; +static union hv_register_vsm_page_offsets mshv_vsm_page_offsets; +static union hv_register_vsm_capabilities mshv_vsm_capabilities; + +static DEFINE_PER_CPU(struct mshv_vtl_poll_file, mshv_vtl_poll_file); +static DEFINE_PER_CPU(unsigned long long, num_vtl0_transitions); +static DEFINE_PER_CPU(struct mshv_vtl_per_cpu, mshv_vtl_per_cpu); + +static const union hv_input_vtl input_vtl_zero; +static const union hv_input_vtl input_vtl_normal = { + .use_target_vtl = 1, +}; + +static const struct file_operations mshv_vtl_fops; + +static long +mshv_ioctl_create_vtl(void __user *user_arg, struct device *module_dev) +{ + struct mshv_vtl *vtl; + struct file *file; + int fd; + + vtl = kzalloc(sizeof(*vtl), GFP_KERNEL); + if (!vtl) + return -ENOMEM; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + kfree(vtl); + return fd; + } + file = anon_inode_getfile("mshv_vtl", &mshv_vtl_fops, + vtl, O_RDWR); + if (IS_ERR(file)) { + kfree(vtl); + return PTR_ERR(file); + } + vtl->module_dev = module_dev; + fd_install(fd, file); + + return fd; +} + +static long +mshv_ioctl_check_extension(void __user *user_arg) +{ + u32 arg; + + if (copy_from_user(&arg, user_arg, sizeof(arg))) + return -EFAULT; + + switch (arg) { + case MSHV_CAP_CORE_API_STABLE: + return 0; + case MSHV_CAP_REGISTER_PAGE: + return mshv_has_reg_page; + case MSHV_CAP_VTL_RETURN_ACTION: + return mshv_vsm_capabilities.return_action_available; + case MSHV_CAP_DR6_SHARED: + return mshv_vsm_capabilities.dr6_shared; + } + + return -EOPNOTSUPP; +} + +static long +mshv_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) +{ + struct miscdevice *misc = filp->private_data; + + switch (ioctl) { + case MSHV_CHECK_EXTENSION: + return mshv_ioctl_check_extension((void __user *)arg); + case MSHV_CREATE_VTL: + return mshv_ioctl_create_vtl((void __user *)arg, misc->this_device); + } + + return -ENOTTY; +} + +static const struct file_operations mshv_dev_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = mshv_dev_ioctl, + .llseek = noop_llseek, +}; + +static struct miscdevice mshv_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mshv", + .fops = &mshv_dev_fops, + .mode = 0600, +}; + +static struct mshv_vtl_run *mshv_vtl_this_run(void) +{ + return *this_cpu_ptr(&mshv_vtl_per_cpu.run); +} + +static struct mshv_vtl_run *mshv_vtl_cpu_run(int cpu) +{ + return *per_cpu_ptr(&mshv_vtl_per_cpu.run, cpu); +} + +static struct page *mshv_vtl_cpu_reg_page(int cpu) +{ + return *per_cpu_ptr(&mshv_vtl_per_cpu.reg_page, cpu); +} + +static void mshv_vtl_configure_reg_page(struct mshv_vtl_per_cpu *per_cpu) +{ + struct hv_register_assoc reg_assoc = {}; + union hv_synic_overlay_page_msr overlay = {}; + struct page *reg_page; + + reg_page = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_RETRY_MAYFAIL); + if (!reg_page) { + WARN(1, "failed to allocate register page\n"); + return; + } + + overlay.enabled = 1; + overlay.pfn = page_to_hvpfn(reg_page); + reg_assoc.name = HV_X64_REGISTER_REG_PAGE; + reg_assoc.value.reg64 = overlay.as_uint64; + + if (hv_call_set_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF, + 1, input_vtl_zero, ®_assoc)) { + WARN(1, "failed to setup register page\n"); + __free_page(reg_page); + return; + } + + per_cpu->reg_page = reg_page; + mshv_has_reg_page = true; +} + +static void mshv_vtl_synic_enable_regs(unsigned int cpu) +{ + union hv_synic_sint sint; + + sint.as_uint64 = 0; + sint.vector = HYPERVISOR_CALLBACK_VECTOR; + sint.masked = false; + sint.auto_eoi = hv_recommend_using_aeoi(); + + /* Enable intercepts */ + if (!mshv_vsm_capabilities.intercept_page_available) + hv_set_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX, + sint.as_uint64); + + /* VTL2 Host VSP SINT is (un)masked when the user mode requests that */ +} + +static int mshv_vtl_get_vsm_regs(void) +{ + struct hv_register_assoc registers[2]; + int ret, count = 2; + + registers[0].name = HV_REGISTER_VSM_CODE_PAGE_OFFSETS; + registers[1].name = HV_REGISTER_VSM_CAPABILITIES; + + ret = hv_call_get_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF, + count, input_vtl_zero, registers); + if (ret) + return ret; + + mshv_vsm_page_offsets.as_uint64 = registers[0].value.reg64; + mshv_vsm_capabilities.as_uint64 = registers[1].value.reg64; + + return ret; +} + +static int mshv_vtl_configure_vsm_partition(struct device *dev) +{ + union hv_register_vsm_partition_config config; + struct hv_register_assoc reg_assoc; + + config.as_uint64 = 0; + config.default_vtl_protection_mask = HV_MAP_GPA_PERMISSIONS_MASK; + config.enable_vtl_protection = 1; + config.zero_memory_on_reset = 1; + config.intercept_vp_startup = 1; + config.intercept_cpuid_unimplemented = 1; + + if (mshv_vsm_capabilities.intercept_page_available) { + dev_dbg(dev, "using intercept page\n"); + config.intercept_page = 1; + } + + reg_assoc.name = HV_REGISTER_VSM_PARTITION_CONFIG; + reg_assoc.value.reg64 = config.as_uint64; + + return hv_call_set_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF, + 1, input_vtl_zero, ®_assoc); +} + +static void mshv_vtl_vmbus_isr(void) +{ + struct hv_per_cpu_context *per_cpu; + struct hv_message *msg; + u32 message_type; + union hv_synic_event_flags *event_flags; + struct eventfd_ctx *eventfd; + u16 i; + + per_cpu = this_cpu_ptr(hv_context.cpu_context); + if (smp_processor_id() == 0) { + msg = (struct hv_message *)per_cpu->hyp_synic_message_page + VTL2_VMBUS_SINT_INDEX; + message_type = READ_ONCE(msg->header.message_type); + if (message_type != HVMSG_NONE) + tasklet_schedule(&msg_dpc); + } + + event_flags = (union hv_synic_event_flags *)per_cpu->hyp_synic_event_page + + VTL2_VMBUS_SINT_INDEX; + for_each_set_bit(i, event_flags->flags, HV_EVENT_FLAGS_COUNT) { + if (!sync_test_and_clear_bit(i, event_flags->flags)) + continue; + rcu_read_lock(); + eventfd = READ_ONCE(flag_eventfds[i]); + if (eventfd) + eventfd_signal(eventfd); + rcu_read_unlock(); + } + + vmbus_isr(); +} + +static int mshv_vtl_alloc_context(unsigned int cpu) +{ + struct mshv_vtl_per_cpu *per_cpu = this_cpu_ptr(&mshv_vtl_per_cpu); + + per_cpu->run = (struct mshv_vtl_run *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + if (!per_cpu->run) + return -ENOMEM; + + if (mshv_vsm_capabilities.intercept_page_available) + mshv_vtl_configure_reg_page(per_cpu); + + mshv_vtl_synic_enable_regs(cpu); + + return 0; +} + +static int mshv_vtl_cpuhp_online; + +static int hv_vtl_setup_synic(void) +{ + int ret; + + /* Use our isr to first filter out packets destined for userspace */ + hv_setup_vmbus_handler(mshv_vtl_vmbus_isr); + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vtl:online", + mshv_vtl_alloc_context, NULL); + if (ret < 0) { + hv_setup_vmbus_handler(vmbus_isr); + return ret; + } + + mshv_vtl_cpuhp_online = ret; + + return 0; +} + +static void hv_vtl_remove_synic(void) +{ + cpuhp_remove_state(mshv_vtl_cpuhp_online); + hv_setup_vmbus_handler(vmbus_isr); +} + +static int vtl_get_vp_register(struct hv_register_assoc *reg) +{ + return hv_call_get_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF, + 1, input_vtl_normal, reg); +} + +static int vtl_set_vp_register(struct hv_register_assoc *reg) +{ + return hv_call_set_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF, + 1, input_vtl_normal, reg); +} + +static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) +{ + struct mshv_vtl_ram_disposition vtl0_mem; + struct dev_pagemap *pgmap; + void *addr; + + if (copy_from_user(&vtl0_mem, arg, sizeof(vtl0_mem))) + return -EFAULT; + /* vtl0_mem.last_pfn is excluded in the pagemap range for VTL0 as per design */ + if (vtl0_mem.last_pfn <= vtl0_mem.start_pfn) { + dev_err(vtl->module_dev, "range start pfn (%llx) > end pfn (%llx)\n", + vtl0_mem.start_pfn, vtl0_mem.last_pfn); + return -EFAULT; + } + + pgmap = kzalloc(sizeof(*pgmap), GFP_KERNEL); + if (!pgmap) + return -ENOMEM; + + pgmap->ranges[0].start = PFN_PHYS(vtl0_mem.start_pfn); + pgmap->ranges[0].end = PFN_PHYS(vtl0_mem.last_pfn) - 1; + pgmap->nr_range = 1; + pgmap->type = MEMORY_DEVICE_GENERIC; + + /* + * Determine the highest page order that can be used for the given memory range. + * This works best when the range is aligned; i.e. both the start and the length. + */ + pgmap->vmemmap_shift = count_trailing_zeros(vtl0_mem.start_pfn | vtl0_mem.last_pfn); + dev_dbg(vtl->module_dev, + "Add VTL0 memory: start: 0x%llx, end_pfn: 0x%llx, page order: %lu\n", + vtl0_mem.start_pfn, vtl0_mem.last_pfn, pgmap->vmemmap_shift); + + addr = devm_memremap_pages(mem_dev, pgmap); + if (IS_ERR(addr)) { + dev_err(vtl->module_dev, "devm_memremap_pages error: %ld\n", PTR_ERR(addr)); + kfree(pgmap); + return -EFAULT; + } + + /* Don't free pgmap, since it has to stick around until the memory + * is unmapped, which will never happen as there is no scenario + * where VTL0 can be released/shutdown without bringing down VTL2. + */ + return 0; +} + +static void mshv_vtl_cancel(int cpu) +{ + int here = get_cpu(); + + if (here != cpu) { + if (!xchg_relaxed(&mshv_vtl_cpu_run(cpu)->cancel, 1)) + smp_send_reschedule(cpu); + } else { + WRITE_ONCE(mshv_vtl_this_run()->cancel, 1); + } + put_cpu(); +} + +static int mshv_vtl_poll_file_wake(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key) +{ + struct mshv_vtl_poll_file *poll_file = container_of(wait, struct mshv_vtl_poll_file, wait); + + mshv_vtl_cancel(poll_file->cpu); + + return 0; +} + +static void mshv_vtl_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh, poll_table *pt) +{ + struct mshv_vtl_poll_file *poll_file = container_of(pt, struct mshv_vtl_poll_file, pt); + + WARN_ON(poll_file->wqh); + poll_file->wqh = wqh; + add_wait_queue(wqh, &poll_file->wait); +} + +static int mshv_vtl_ioctl_set_poll_file(struct mshv_vtl_set_poll_file __user *user_input) +{ + struct file *file, *old_file; + struct mshv_vtl_poll_file *poll_file; + struct mshv_vtl_set_poll_file input; + + if (copy_from_user(&input, user_input, sizeof(input))) + return -EFAULT; + + if (input.cpu >= num_possible_cpus() || !cpu_online(input.cpu)) + return -EINVAL; + /* + * CPU Hotplug is not supported in VTL2 in OpenHCL, where this kernel driver exists. + * CPU is expected to remain online after above cpu_online() check. + */ + + file = NULL; + file = fget(input.fd); + if (!file) + return -EBADFD; + + poll_file = per_cpu_ptr(&mshv_vtl_poll_file, READ_ONCE(input.cpu)); + if (!poll_file) + return -EINVAL; + + mutex_lock(&mshv_vtl_poll_file_lock); + + if (poll_file->wqh) + remove_wait_queue(poll_file->wqh, &poll_file->wait); + poll_file->wqh = NULL; + + old_file = poll_file->file; + poll_file->file = file; + poll_file->cpu = input.cpu; + + if (file) { + init_waitqueue_func_entry(&poll_file->wait, mshv_vtl_poll_file_wake); + init_poll_funcptr(&poll_file->pt, mshv_vtl_ptable_queue_proc); + vfs_poll(file, &poll_file->pt); + } + + mutex_unlock(&mshv_vtl_poll_file_lock); + + if (old_file) + fput(old_file); + + return 0; +} + +/* Static table mapping register names to their corresponding actions */ +static const struct { + enum hv_register_name reg_name; + int debug_reg_num; /* -1 if not a debug register */ + u32 msr_addr; /* 0 if not an MSR */ +} reg_table[] = { + /* Debug registers */ + {HV_X64_REGISTER_DR0, 0, 0}, + {HV_X64_REGISTER_DR1, 1, 0}, + {HV_X64_REGISTER_DR2, 2, 0}, + {HV_X64_REGISTER_DR3, 3, 0}, + {HV_X64_REGISTER_DR6, 6, 0}, + /* MTRR MSRs */ + {HV_X64_REGISTER_MSR_MTRR_CAP, -1, MSR_MTRRcap}, + {HV_X64_REGISTER_MSR_MTRR_DEF_TYPE, -1, MSR_MTRRdefType}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0, -1, MTRRphysBase_MSR(0)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1, -1, MTRRphysBase_MSR(1)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2, -1, MTRRphysBase_MSR(2)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3, -1, MTRRphysBase_MSR(3)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4, -1, MTRRphysBase_MSR(4)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5, -1, MTRRphysBase_MSR(5)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6, -1, MTRRphysBase_MSR(6)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7, -1, MTRRphysBase_MSR(7)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE8, -1, MTRRphysBase_MSR(8)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASE9, -1, MTRRphysBase_MSR(9)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASEA, -1, MTRRphysBase_MSR(0xa)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASEB, -1, MTRRphysBase_MSR(0xb)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASEC, -1, MTRRphysBase_MSR(0xc)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASED, -1, MTRRphysBase_MSR(0xd)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASEE, -1, MTRRphysBase_MSR(0xe)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_BASEF, -1, MTRRphysBase_MSR(0xf)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0, -1, MTRRphysMask_MSR(0)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1, -1, MTRRphysMask_MSR(1)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2, -1, MTRRphysMask_MSR(2)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3, -1, MTRRphysMask_MSR(3)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4, -1, MTRRphysMask_MSR(4)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5, -1, MTRRphysMask_MSR(5)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6, -1, MTRRphysMask_MSR(6)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7, -1, MTRRphysMask_MSR(7)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK8, -1, MTRRphysMask_MSR(8)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASK9, -1, MTRRphysMask_MSR(9)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASKA, -1, MTRRphysMask_MSR(0xa)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASKB, -1, MTRRphysMask_MSR(0xb)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASKC, -1, MTRRphysMask_MSR(0xc)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASKD, -1, MTRRphysMask_MSR(0xd)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASKE, -1, MTRRphysMask_MSR(0xe)}, + {HV_X64_REGISTER_MSR_MTRR_PHYS_MASKF, -1, MTRRphysMask_MSR(0xf)}, + {HV_X64_REGISTER_MSR_MTRR_FIX64K00000, -1, MSR_MTRRfix64K_00000}, + {HV_X64_REGISTER_MSR_MTRR_FIX16K80000, -1, MSR_MTRRfix16K_80000}, + {HV_X64_REGISTER_MSR_MTRR_FIX16KA0000, -1, MSR_MTRRfix16K_A0000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KC0000, -1, MSR_MTRRfix4K_C0000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KC8000, -1, MSR_MTRRfix4K_C8000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KD0000, -1, MSR_MTRRfix4K_D0000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KD8000, -1, MSR_MTRRfix4K_D8000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KE0000, -1, MSR_MTRRfix4K_E0000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KE8000, -1, MSR_MTRRfix4K_E8000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KF0000, -1, MSR_MTRRfix4K_F0000}, + {HV_X64_REGISTER_MSR_MTRR_FIX4KF8000, -1, MSR_MTRRfix4K_F8000}, +}; + +static int mshv_vtl_get_set_reg(struct hv_register_assoc *regs, bool set) +{ + u64 *reg64; + enum hv_register_name gpr_name; + int i; + + gpr_name = regs->name; + reg64 = ®s->value.reg64; + + /* Search for the register in the table */ + for (i = 0; i < ARRAY_SIZE(reg_table); i++) { + if (reg_table[i].reg_name != gpr_name) + continue; + if (reg_table[i].debug_reg_num != -1) { + /* Handle debug registers */ + if (gpr_name == HV_X64_REGISTER_DR6 && + !mshv_vsm_capabilities.dr6_shared) + goto hypercall; + if (set) + native_set_debugreg(reg_table[i].debug_reg_num, *reg64); + else + *reg64 = native_get_debugreg(reg_table[i].debug_reg_num); + } else { + /* Handle MSRs */ + if (set) + wrmsrl(reg_table[i].msr_addr, *reg64); + else + rdmsrl(reg_table[i].msr_addr, *reg64); + } + return 0; + } + +hypercall: + return 1; +} + +static void mshv_vtl_return(struct mshv_vtl_cpu_context *vtl0) +{ + struct hv_vp_assist_page *hvp; + + hvp = hv_vp_assist_page[smp_processor_id()]; + + /* + * Process signal event direct set in the run page, if any. + */ + if (mshv_vsm_capabilities.return_action_available) { + u32 offset = READ_ONCE(mshv_vtl_this_run()->vtl_ret_action_size); + + WRITE_ONCE(mshv_vtl_this_run()->vtl_ret_action_size, 0); + + /* + * Hypervisor will take care of clearing out the actions + * set in the assist page. + */ + memcpy(hvp->vtl_ret_actions, + mshv_vtl_this_run()->vtl_ret_actions, + min_t(u32, offset, sizeof(hvp->vtl_ret_actions))); + } + + mshv_vtl_return_call(vtl0); +} + +static bool mshv_vtl_process_intercept(void) +{ + struct hv_per_cpu_context *mshv_cpu; + void *synic_message_page; + struct hv_message *msg; + u32 message_type; + + mshv_cpu = this_cpu_ptr(hv_context.cpu_context); + synic_message_page = mshv_cpu->hyp_synic_message_page; + if (unlikely(!synic_message_page)) + return true; + + msg = (struct hv_message *)synic_message_page + HV_SYNIC_INTERCEPTION_SINT_INDEX; + message_type = READ_ONCE(msg->header.message_type); + if (message_type == HVMSG_NONE) + return true; + + memcpy(mshv_vtl_this_run()->exit_message, msg, sizeof(*msg)); + vmbus_signal_eom(msg, message_type); + + return false; +} + +static int mshv_vtl_ioctl_return_to_lower_vtl(void) +{ + preempt_disable(); + for (;;) { + unsigned long irq_flags; + struct hv_vp_assist_page *hvp; + int ret; + + if (__xfer_to_guest_mode_work_pending()) { + preempt_enable(); + ret = xfer_to_guest_mode_handle_work(); + if (ret) + return ret; + preempt_disable(); + } + + local_irq_save(irq_flags); + if (READ_ONCE(mshv_vtl_this_run()->cancel)) { + local_irq_restore(irq_flags); + preempt_enable(); + return -EINTR; + } + + mshv_vtl_return(&mshv_vtl_this_run()->cpu_context); + local_irq_restore(irq_flags); + + hvp = hv_vp_assist_page[smp_processor_id()]; + this_cpu_inc(num_vtl0_transitions); + switch (hvp->vtl_entry_reason) { + case MSHV_ENTRY_REASON_INTERRUPT: + if (!mshv_vsm_capabilities.intercept_page_available && + likely(!mshv_vtl_process_intercept())) + goto done; + break; + + case MSHV_ENTRY_REASON_INTERCEPT: + WARN_ON(!mshv_vsm_capabilities.intercept_page_available); + memcpy(mshv_vtl_this_run()->exit_message, hvp->intercept_message, + sizeof(hvp->intercept_message)); + goto done; + + default: + panic("unknown entry reason: %d", hvp->vtl_entry_reason); + } + } + +done: + preempt_enable(); + + return 0; +} + +static long +mshv_vtl_ioctl_get_regs(void __user *user_args) +{ + struct mshv_vp_registers args; + struct hv_register_assoc reg; + long ret; + + if (copy_from_user(&args, user_args, sizeof(args))) + return -EFAULT; + + /* This IOCTL supports processing only one register at a time. */ + if (args.count != 1) + return -EINVAL; + + if (copy_from_user(®, (void __user *)args.regs_ptr, + sizeof(reg))) + return -EFAULT; + + ret = mshv_vtl_get_set_reg(®, false); + if (!ret) + goto copy_args; /* No need of hypercall */ + ret = vtl_get_vp_register(®); + if (ret) + return ret; + +copy_args: + if (copy_to_user((void __user *)args.regs_ptr, ®, sizeof(reg))) + ret = -EFAULT; + + return ret; +} + +static long +mshv_vtl_ioctl_set_regs(void __user *user_args) +{ + struct mshv_vp_registers args; + struct hv_register_assoc reg; + long ret; + + if (copy_from_user(&args, user_args, sizeof(args))) + return -EFAULT; + + /* This IOCTL supports processing only one register at a time. */ + if (args.count != 1) + return -EINVAL; + + if (copy_from_user(®, (void __user *)args.regs_ptr, sizeof(reg))) + return -EFAULT; + + ret = mshv_vtl_get_set_reg(®, true); + if (!ret) + return ret; /* No need of hypercall */ + ret = vtl_set_vp_register(®); + + return ret; +} + +static long +mshv_vtl_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) +{ + long ret; + struct mshv_vtl *vtl = filp->private_data; + + switch (ioctl) { + case MSHV_SET_POLL_FILE: + ret = mshv_vtl_ioctl_set_poll_file((struct mshv_vtl_set_poll_file __user *)arg); + break; + case MSHV_GET_VP_REGISTERS: + ret = mshv_vtl_ioctl_get_regs((void __user *)arg); + break; + case MSHV_SET_VP_REGISTERS: + ret = mshv_vtl_ioctl_set_regs((void __user *)arg); + break; + case MSHV_RETURN_TO_LOWER_VTL: + ret = mshv_vtl_ioctl_return_to_lower_vtl(); + break; + case MSHV_ADD_VTL0_MEMORY: + ret = mshv_vtl_ioctl_add_vtl0_mem(vtl, (void __user *)arg); + break; + default: + dev_err(vtl->module_dev, "invalid vtl ioctl: %#x\n", ioctl); + ret = -ENOTTY; + } + + return ret; +} + +static vm_fault_t mshv_vtl_fault(struct vm_fault *vmf) +{ + struct page *page; + int cpu = vmf->pgoff & MSHV_PG_OFF_CPU_MASK; + int real_off = vmf->pgoff >> MSHV_REAL_OFF_SHIFT; + + if (!cpu_online(cpu)) + return VM_FAULT_SIGBUS; + /* + * CPU Hotplug is not supported in VTL2 in OpenHCL, where this kernel driver exists. + * CPU is expected to remain online after above cpu_online() check. + */ + + if (real_off == MSHV_RUN_PAGE_OFFSET) { + page = virt_to_page(mshv_vtl_cpu_run(cpu)); + } else if (real_off == MSHV_REG_PAGE_OFFSET) { + if (!mshv_has_reg_page) + return VM_FAULT_SIGBUS; + page = mshv_vtl_cpu_reg_page(cpu); + } else { + return VM_FAULT_NOPAGE; + } + + get_page(page); + vmf->page = page; + + return 0; +} + +static const struct vm_operations_struct mshv_vtl_vm_ops = { + .fault = mshv_vtl_fault, +}; + +static int mshv_vtl_mmap(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_ops = &mshv_vtl_vm_ops; + + return 0; +} + +static int mshv_vtl_release(struct inode *inode, struct file *filp) +{ + struct mshv_vtl *vtl = filp->private_data; + + kfree(vtl); + + return 0; +} + +static const struct file_operations mshv_vtl_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = mshv_vtl_ioctl, + .release = mshv_vtl_release, + .mmap = mshv_vtl_mmap, +}; + +static void mshv_vtl_synic_mask_vmbus_sint(const u8 *mask) +{ + union hv_synic_sint sint; + + sint.as_uint64 = 0; + sint.vector = HYPERVISOR_CALLBACK_VECTOR; + sint.masked = (*mask != 0); + sint.auto_eoi = hv_recommend_using_aeoi(); + + hv_set_msr(HV_MSR_SINT0 + VTL2_VMBUS_SINT_INDEX, + sint.as_uint64); + + if (!sint.masked) + pr_debug("%s: Unmasking VTL2 VMBUS SINT on VP %d\n", __func__, smp_processor_id()); + else + pr_debug("%s: Masking VTL2 VMBUS SINT on VP %d\n", __func__, smp_processor_id()); +} + +static void mshv_vtl_read_remote(void *buffer) +{ + struct hv_per_cpu_context *mshv_cpu = this_cpu_ptr(hv_context.cpu_context); + struct hv_message *msg = (struct hv_message *)mshv_cpu->hyp_synic_message_page + + VTL2_VMBUS_SINT_INDEX; + u32 message_type = READ_ONCE(msg->header.message_type); + + WRITE_ONCE(has_message, false); + if (message_type == HVMSG_NONE) + return; + + memcpy(buffer, msg, sizeof(*msg)); + vmbus_signal_eom(msg, message_type); +} + +static bool vtl_synic_mask_vmbus_sint_masked = true; + +static ssize_t mshv_vtl_sint_read(struct file *filp, char __user *arg, size_t size, loff_t *offset) +{ + struct hv_message msg = {}; + int ret; + + if (size < sizeof(msg)) + return -EINVAL; + + for (;;) { + smp_call_function_single(VMBUS_CONNECT_CPU, mshv_vtl_read_remote, &msg, true); + if (msg.header.message_type != HVMSG_NONE) + break; + + if (READ_ONCE(vtl_synic_mask_vmbus_sint_masked)) + return 0; /* EOF */ + + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(fd_wait_queue, + READ_ONCE(has_message) || + READ_ONCE(vtl_synic_mask_vmbus_sint_masked)); + if (ret) + return ret; + } + + if (copy_to_user(arg, &msg, sizeof(msg))) + return -EFAULT; + + return sizeof(msg); +} + +static __poll_t mshv_vtl_sint_poll(struct file *filp, poll_table *wait) +{ + __poll_t mask = 0; + + poll_wait(filp, &fd_wait_queue, wait); + if (READ_ONCE(has_message) || READ_ONCE(vtl_synic_mask_vmbus_sint_masked)) + mask |= EPOLLIN | EPOLLRDNORM; + + return mask; +} + +static void mshv_vtl_sint_on_msg_dpc(unsigned long data) +{ + WRITE_ONCE(has_message, true); + wake_up_interruptible_poll(&fd_wait_queue, EPOLLIN); +} + +static int mshv_vtl_sint_ioctl_post_msg(struct mshv_vtl_sint_post_msg __user *arg) +{ + struct mshv_vtl_sint_post_msg message; + u8 payload[HV_MESSAGE_PAYLOAD_BYTE_COUNT]; + + if (copy_from_user(&message, arg, sizeof(message))) + return -EFAULT; + if (message.payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) + return -EINVAL; + if (copy_from_user(payload, (void __user *)message.payload_ptr, + message.payload_size)) + return -EFAULT; + + return hv_post_message((union hv_connection_id)message.connection_id, + message.message_type, (void *)payload, + message.payload_size); +} + +static int mshv_vtl_sint_ioctl_signal_event(struct mshv_vtl_signal_event __user *arg) +{ + u64 input, status; + struct mshv_vtl_signal_event signal_event; + + if (copy_from_user(&signal_event, arg, sizeof(signal_event))) + return -EFAULT; + + input = signal_event.connection_id | ((u64)signal_event.flag << 32); + + status = hv_do_fast_hypercall8(HVCALL_SIGNAL_EVENT, input); + + return hv_result_to_errno(status); +} + +static int mshv_vtl_sint_ioctl_set_eventfd(struct mshv_vtl_set_eventfd __user *arg) +{ + struct mshv_vtl_set_eventfd set_eventfd; + struct eventfd_ctx *eventfd, *old_eventfd; + + if (copy_from_user(&set_eventfd, arg, sizeof(set_eventfd))) + return -EFAULT; + if (set_eventfd.flag >= HV_EVENT_FLAGS_COUNT) + return -EINVAL; + + eventfd = NULL; + if (set_eventfd.fd >= 0) { + eventfd = eventfd_ctx_fdget(set_eventfd.fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + } + + guard(mutex)(&flag_lock); + old_eventfd = READ_ONCE(flag_eventfds[set_eventfd.flag]); + WRITE_ONCE(flag_eventfds[set_eventfd.flag], eventfd); + + if (old_eventfd) { + synchronize_rcu(); + eventfd_ctx_put(old_eventfd); + } + + return 0; +} + +static int mshv_vtl_sint_ioctl_pause_msg_stream(struct mshv_sint_mask __user *arg) +{ + static DEFINE_MUTEX(vtl2_vmbus_sint_mask_mutex); + struct mshv_sint_mask mask; + + if (copy_from_user(&mask, arg, sizeof(mask))) + return -EFAULT; + guard(mutex)(&vtl2_vmbus_sint_mask_mutex); + on_each_cpu((smp_call_func_t)mshv_vtl_synic_mask_vmbus_sint, &mask.mask, 1); + WRITE_ONCE(vtl_synic_mask_vmbus_sint_masked, mask.mask != 0); + if (mask.mask) + wake_up_interruptible_poll(&fd_wait_queue, EPOLLIN); + + return 0; +} + +static long mshv_vtl_sint_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case MSHV_SINT_POST_MESSAGE: + return mshv_vtl_sint_ioctl_post_msg((struct mshv_vtl_sint_post_msg __user *)arg); + case MSHV_SINT_SIGNAL_EVENT: + return mshv_vtl_sint_ioctl_signal_event((struct mshv_vtl_signal_event __user *)arg); + case MSHV_SINT_SET_EVENTFD: + return mshv_vtl_sint_ioctl_set_eventfd((struct mshv_vtl_set_eventfd __user *)arg); + case MSHV_SINT_PAUSE_MESSAGE_STREAM: + return mshv_vtl_sint_ioctl_pause_msg_stream((struct mshv_sint_mask __user *)arg); + default: + return -ENOIOCTLCMD; + } +} + +static const struct file_operations mshv_vtl_sint_ops = { + .owner = THIS_MODULE, + .read = mshv_vtl_sint_read, + .poll = mshv_vtl_sint_poll, + .unlocked_ioctl = mshv_vtl_sint_ioctl, +}; + +static struct miscdevice mshv_vtl_sint_dev = { + .name = "mshv_sint", + .fops = &mshv_vtl_sint_ops, + .mode = 0600, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int mshv_vtl_hvcall_dev_open(struct inode *node, struct file *f) +{ + struct miscdevice *dev = f->private_data; + struct mshv_vtl_hvcall_fd *fd; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + fd = vzalloc(sizeof(*fd)); + if (!fd) + return -ENOMEM; + fd->dev = dev; + f->private_data = fd; + mutex_init(&fd->init_mutex); + + return 0; +} + +static int mshv_vtl_hvcall_dev_release(struct inode *node, struct file *f) +{ + struct mshv_vtl_hvcall_fd *fd; + + fd = f->private_data; + if (fd) { + vfree(fd); + f->private_data = NULL; + } + + return 0; +} + +static int mshv_vtl_hvcall_do_setup(struct mshv_vtl_hvcall_fd *fd, + struct mshv_vtl_hvcall_setup __user *hvcall_setup_user) +{ + struct mshv_vtl_hvcall_setup hvcall_setup; + + guard(mutex)(&fd->init_mutex); + + if (fd->allow_map_initialized) { + dev_err(fd->dev->this_device, + "Hypercall allow map has already been set, pid %d\n", + current->pid); + return -EINVAL; + } + + if (copy_from_user(&hvcall_setup, hvcall_setup_user, + sizeof(struct mshv_vtl_hvcall_setup))) { + return -EFAULT; + } + if (hvcall_setup.bitmap_array_size > ARRAY_SIZE(fd->allow_bitmap)) + return -EINVAL; + + if (copy_from_user(&fd->allow_bitmap, + (void __user *)hvcall_setup.allow_bitmap_ptr, + hvcall_setup.bitmap_array_size)) { + return -EFAULT; + } + + dev_info(fd->dev->this_device, "Hypercall allow map has been set, pid %d\n", + current->pid); + fd->allow_map_initialized = true; + return 0; +} + +static bool mshv_vtl_hvcall_is_allowed(struct mshv_vtl_hvcall_fd *fd, u16 call_code) +{ + return test_bit(call_code, (unsigned long *)fd->allow_bitmap); +} + +static int mshv_vtl_hvcall_call(struct mshv_vtl_hvcall_fd *fd, + struct mshv_vtl_hvcall __user *hvcall_user) +{ + struct mshv_vtl_hvcall hvcall; + void *in, *out; + int ret; + + if (copy_from_user(&hvcall, hvcall_user, sizeof(struct mshv_vtl_hvcall))) + return -EFAULT; + if (hvcall.input_size > HV_HYP_PAGE_SIZE) + return -EINVAL; + if (hvcall.output_size > HV_HYP_PAGE_SIZE) + return -EINVAL; + + /* + * By default, all hypercalls are not allowed. + * The user mode code has to set up the allow bitmap once. + */ + + if (!mshv_vtl_hvcall_is_allowed(fd, hvcall.control & 0xFFFF)) { + dev_err(fd->dev->this_device, + "Hypercall with control data %#llx isn't allowed\n", + hvcall.control); + return -EPERM; + } + + /* + * This may create a problem for Confidential VM (CVM) usecase where we need to use + * Hyper-V driver allocated per-cpu input and output pages (hyperv_pcpu_input_arg and + * hyperv_pcpu_output_arg) for making a hypervisor call. + * + * TODO: Take care of this when CVM support is added. + */ + in = (void *)__get_free_page(GFP_KERNEL); + out = (void *)__get_free_page(GFP_KERNEL); + + if (copy_from_user(in, (void __user *)hvcall.input_ptr, hvcall.input_size)) { + ret = -EFAULT; + goto free_pages; + } + + hvcall.status = hv_do_hypercall(hvcall.control, in, out); + + if (copy_to_user((void __user *)hvcall.output_ptr, out, hvcall.output_size)) { + ret = -EFAULT; + goto free_pages; + } + ret = put_user(hvcall.status, &hvcall_user->status); +free_pages: + free_page((unsigned long)in); + free_page((unsigned long)out); + + return ret; +} + +static long mshv_vtl_hvcall_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + struct mshv_vtl_hvcall_fd *fd = f->private_data; + + switch (cmd) { + case MSHV_HVCALL_SETUP: + return mshv_vtl_hvcall_do_setup(fd, (struct mshv_vtl_hvcall_setup __user *)arg); + case MSHV_HVCALL: + return mshv_vtl_hvcall_call(fd, (struct mshv_vtl_hvcall __user *)arg); + default: + break; + } + + return -ENOIOCTLCMD; +} + +static const struct file_operations mshv_vtl_hvcall_dev_file_ops = { + .owner = THIS_MODULE, + .open = mshv_vtl_hvcall_dev_open, + .release = mshv_vtl_hvcall_dev_release, + .unlocked_ioctl = mshv_vtl_hvcall_dev_ioctl, +}; + +static struct miscdevice mshv_vtl_hvcall_dev = { + .name = "mshv_hvcall", + .nodename = "mshv_hvcall", + .fops = &mshv_vtl_hvcall_dev_file_ops, + .mode = 0600, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int mshv_vtl_low_open(struct inode *inodep, struct file *filp) +{ + pid_t pid = task_pid_vnr(current); + uid_t uid = current_uid().val; + int ret = 0; + + pr_debug("%s: Opening VTL low, task group %d, uid %d\n", __func__, pid, uid); + + if (capable(CAP_SYS_ADMIN)) { + filp->private_data = inodep; + } else { + pr_err("%s: VTL low open failed: CAP_SYS_ADMIN required. task group %d, uid %d", + __func__, pid, uid); + ret = -EPERM; + } + + return ret; +} + +static bool can_fault(struct vm_fault *vmf, unsigned long size, unsigned long *pfn) +{ + unsigned long mask = size - 1; + unsigned long start = vmf->address & ~mask; + unsigned long end = start + size; + bool is_valid; + + is_valid = (vmf->address & mask) == ((vmf->pgoff << PAGE_SHIFT) & mask) && + start >= vmf->vma->vm_start && + end <= vmf->vma->vm_end; + + if (is_valid) + *pfn = vmf->pgoff & ~(mask >> PAGE_SHIFT); + + return is_valid; +} + +static vm_fault_t mshv_vtl_low_huge_fault(struct vm_fault *vmf, unsigned int order) +{ + unsigned long pfn = vmf->pgoff; + vm_fault_t ret = VM_FAULT_FALLBACK; + + switch (order) { + case 0: + return vmf_insert_mixed(vmf->vma, vmf->address, pfn); + + case PMD_ORDER: + if (can_fault(vmf, PMD_SIZE, &pfn)) + ret = vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE); + return ret; + + case PUD_ORDER: + if (can_fault(vmf, PUD_SIZE, &pfn)) + ret = vmf_insert_pfn_pud(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE); + return ret; + + default: + return VM_FAULT_SIGBUS; + } +} + +static vm_fault_t mshv_vtl_low_fault(struct vm_fault *vmf) +{ + return mshv_vtl_low_huge_fault(vmf, 0); +} + +static const struct vm_operations_struct mshv_vtl_low_vm_ops = { + .fault = mshv_vtl_low_fault, + .huge_fault = mshv_vtl_low_huge_fault, +}; + +static int mshv_vtl_low_mmap(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_ops = &mshv_vtl_low_vm_ops; + vm_flags_set(vma, VM_HUGEPAGE | VM_MIXEDMAP); + + return 0; +} + +static const struct file_operations mshv_vtl_low_file_ops = { + .owner = THIS_MODULE, + .open = mshv_vtl_low_open, + .mmap = mshv_vtl_low_mmap, +}; + +static struct miscdevice mshv_vtl_low = { + .name = "mshv_vtl_low", + .nodename = "mshv_vtl_low", + .fops = &mshv_vtl_low_file_ops, + .mode = 0600, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int __init mshv_vtl_init(void) +{ + int ret; + struct device *dev = mshv_dev.this_device; + + /* + * This creates /dev/mshv which provides functionality to create VTLs and partitions. + */ + ret = misc_register(&mshv_dev); + if (ret) { + dev_err(dev, "mshv device register failed: %d\n", ret); + goto free_dev; + } + + tasklet_init(&msg_dpc, mshv_vtl_sint_on_msg_dpc, 0); + init_waitqueue_head(&fd_wait_queue); + + if (mshv_vtl_get_vsm_regs()) { + dev_emerg(dev, "Unable to get VSM capabilities !!\n"); + ret = -ENODEV; + goto free_dev; + } + if (mshv_vtl_configure_vsm_partition(dev)) { + dev_emerg(dev, "VSM configuration failed !!\n"); + ret = -ENODEV; + goto free_dev; + } + + mshv_vtl_return_call_init(mshv_vsm_page_offsets.vtl_return_offset); + ret = hv_vtl_setup_synic(); + if (ret) + goto free_dev; + + /* + * mshv_sint device adds VMBus relay ioctl support. + * This provides a channel for VTL0 to communicate with VTL2. + */ + ret = misc_register(&mshv_vtl_sint_dev); + if (ret) + goto free_synic; + + /* + * mshv_hvcall device adds interface to enable userspace for direct hypercalls support. + */ + ret = misc_register(&mshv_vtl_hvcall_dev); + if (ret) + goto free_sint; + + /* + * mshv_vtl_low device is used to map VTL0 address space to a user-mode process in VTL2. + * It implements mmap() to allow a user-mode process in VTL2 to map to the address of VTL0. + */ + ret = misc_register(&mshv_vtl_low); + if (ret) + goto free_hvcall; + + /* + * "mshv vtl mem dev" device is later used to setup VTL0 memory. + */ + mem_dev = kzalloc(sizeof(*mem_dev), GFP_KERNEL); + if (!mem_dev) { + ret = -ENOMEM; + goto free_low; + } + + mutex_init(&mshv_vtl_poll_file_lock); + + device_initialize(mem_dev); + dev_set_name(mem_dev, "mshv vtl mem dev"); + ret = device_add(mem_dev); + if (ret) { + dev_err(dev, "mshv vtl mem dev add: %d\n", ret); + goto free_mem; + } + + return 0; + +free_mem: + kfree(mem_dev); +free_low: + misc_deregister(&mshv_vtl_low); +free_hvcall: + misc_deregister(&mshv_vtl_hvcall_dev); +free_sint: + misc_deregister(&mshv_vtl_sint_dev); +free_synic: + hv_vtl_remove_synic(); +free_dev: + misc_deregister(&mshv_dev); + + return ret; +} + +static void __exit mshv_vtl_exit(void) +{ + device_del(mem_dev); + kfree(mem_dev); + misc_deregister(&mshv_vtl_low); + misc_deregister(&mshv_vtl_hvcall_dev); + misc_deregister(&mshv_vtl_sint_dev); + hv_vtl_remove_synic(); + misc_deregister(&mshv_dev); +} + +module_init(mshv_vtl_init); +module_exit(mshv_vtl_exit); diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 7499a679e60a..1d5ce11be8b6 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -885,6 +885,48 @@ struct hv_get_vp_from_apic_id_in { u32 apic_ids[]; } __packed; +union hv_register_vsm_partition_config { + u64 as_uint64; + struct { + u64 enable_vtl_protection : 1; + u64 default_vtl_protection_mask : 4; + u64 zero_memory_on_reset : 1; + u64 deny_lower_vtl_startup : 1; + u64 intercept_acceptance : 1; + u64 intercept_enable_vtl_protection : 1; + u64 intercept_vp_startup : 1; + u64 intercept_cpuid_unimplemented : 1; + u64 intercept_unrecoverable_exception : 1; + u64 intercept_page : 1; + u64 mbz : 51; + } __packed; +}; + +union hv_register_vsm_capabilities { + u64 as_uint64; + struct { + u64 dr6_shared: 1; + u64 mbec_vtl_mask: 16; + u64 deny_lower_vtl_startup: 1; + u64 supervisor_shadow_stack: 1; + u64 hardware_hvpt_available: 1; + u64 software_hvpt_available: 1; + u64 hardware_hvpt_range_bits: 6; + u64 intercept_page_available: 1; + u64 return_action_available: 1; + u64 reserved: 35; + } __packed; +}; + +union hv_register_vsm_page_offsets { + struct { + u64 vtl_call_offset : 12; + u64 vtl_return_offset : 12; + u64 reserved_mbz : 40; + } __packed; + u64 as_uint64; +}; + struct hv_nested_enlightenments_control { struct { u32 directhypercall : 1; @@ -1007,6 +1049,70 @@ enum hv_register_name { /* VSM */ HV_REGISTER_VSM_VP_STATUS = 0x000D0003, + + /* Synthetic VSM registers */ + HV_REGISTER_VSM_CODE_PAGE_OFFSETS = 0x000D0002, + HV_REGISTER_VSM_CAPABILITIES = 0x000D0006, + HV_REGISTER_VSM_PARTITION_CONFIG = 0x000D0007, + +#if defined(CONFIG_X86) + /* X64 Debug Registers */ + HV_X64_REGISTER_DR0 = 0x00050000, + HV_X64_REGISTER_DR1 = 0x00050001, + HV_X64_REGISTER_DR2 = 0x00050002, + HV_X64_REGISTER_DR3 = 0x00050003, + HV_X64_REGISTER_DR6 = 0x00050004, + HV_X64_REGISTER_DR7 = 0x00050005, + + /* X64 Cache control MSRs */ + HV_X64_REGISTER_MSR_MTRR_CAP = 0x0008000D, + HV_X64_REGISTER_MSR_MTRR_DEF_TYPE = 0x0008000E, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 = 0x00080010, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1 = 0x00080011, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2 = 0x00080012, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3 = 0x00080013, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4 = 0x00080014, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5 = 0x00080015, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6 = 0x00080016, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7 = 0x00080017, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE8 = 0x00080018, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASE9 = 0x00080019, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEA = 0x0008001A, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEB = 0x0008001B, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEC = 0x0008001C, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASED = 0x0008001D, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEE = 0x0008001E, + HV_X64_REGISTER_MSR_MTRR_PHYS_BASEF = 0x0008001F, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 = 0x00080040, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1 = 0x00080041, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2 = 0x00080042, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3 = 0x00080043, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4 = 0x00080044, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5 = 0x00080045, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6 = 0x00080046, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7 = 0x00080047, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK8 = 0x00080048, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASK9 = 0x00080049, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKA = 0x0008004A, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKB = 0x0008004B, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKC = 0x0008004C, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKD = 0x0008004D, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKE = 0x0008004E, + HV_X64_REGISTER_MSR_MTRR_PHYS_MASKF = 0x0008004F, + HV_X64_REGISTER_MSR_MTRR_FIX64K00000 = 0x00080070, + HV_X64_REGISTER_MSR_MTRR_FIX16K80000 = 0x00080071, + HV_X64_REGISTER_MSR_MTRR_FIX16KA0000 = 0x00080072, + HV_X64_REGISTER_MSR_MTRR_FIX4KC0000 = 0x00080073, + HV_X64_REGISTER_MSR_MTRR_FIX4KC8000 = 0x00080074, + HV_X64_REGISTER_MSR_MTRR_FIX4KD0000 = 0x00080075, + HV_X64_REGISTER_MSR_MTRR_FIX4KD8000 = 0x00080076, + HV_X64_REGISTER_MSR_MTRR_FIX4KE0000 = 0x00080077, + HV_X64_REGISTER_MSR_MTRR_FIX4KE8000 = 0x00080078, + HV_X64_REGISTER_MSR_MTRR_FIX4KF0000 = 0x00080079, + HV_X64_REGISTER_MSR_MTRR_FIX4KF8000 = 0x0008007A, + + HV_X64_REGISTER_REG_PAGE = 0x0009001C, +#endif }; /* diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index b645d17cc531..dee3ece28ce5 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -322,4 +322,84 @@ struct mshv_get_set_vp_state { * #define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall) */ +/* Structure definitions, macros and IOCTLs for mshv_vtl */ + +#define MSHV_CAP_CORE_API_STABLE 0x0 +#define MSHV_CAP_REGISTER_PAGE 0x1 +#define MSHV_CAP_VTL_RETURN_ACTION 0x2 +#define MSHV_CAP_DR6_SHARED 0x3 +#define MSHV_MAX_RUN_MSG_SIZE 256 + +struct mshv_vp_registers { + __u32 count; /* supports only 1 register at a time */ + __u32 reserved; /* Reserved for alignment or future use */ + __u64 regs_ptr; /* pointer to struct hv_register_assoc */ +}; + +struct mshv_vtl_set_eventfd { + __s32 fd; + __u32 flag; +}; + +struct mshv_vtl_signal_event { + __u32 connection_id; + __u32 flag; +}; + +struct mshv_vtl_sint_post_msg { + __u64 message_type; + __u32 connection_id; + __u32 payload_size; /* Must not exceed HV_MESSAGE_PAYLOAD_BYTE_COUNT */ + __u64 payload_ptr; /* pointer to message payload (bytes) */ +}; + +struct mshv_vtl_ram_disposition { + __u64 start_pfn; + __u64 last_pfn; +}; + +struct mshv_vtl_set_poll_file { + __u32 cpu; + __u32 fd; +}; + +struct mshv_vtl_hvcall_setup { + __u64 bitmap_array_size; /* stores number of bytes */ + __u64 allow_bitmap_ptr; +}; + +struct mshv_vtl_hvcall { + __u64 control; /* Hypercall control code */ + __u64 input_size; /* Size of the input data */ + __u64 input_ptr; /* Pointer to the input struct */ + __u64 status; /* Status of the hypercall (output) */ + __u64 output_size; /* Size of the output data */ + __u64 output_ptr; /* Pointer to the output struct */ +}; + +struct mshv_sint_mask { + __u8 mask; + __u8 reserved[7]; +}; + +/* /dev/mshv device IOCTL */ +#define MSHV_CHECK_EXTENSION _IOW(MSHV_IOCTL, 0x00, __u32) + +/* vtl device */ +#define MSHV_CREATE_VTL _IOR(MSHV_IOCTL, 0x1D, char) +#define MSHV_ADD_VTL0_MEMORY _IOW(MSHV_IOCTL, 0x21, struct mshv_vtl_ram_disposition) +#define MSHV_SET_POLL_FILE _IOW(MSHV_IOCTL, 0x25, struct mshv_vtl_set_poll_file) +#define MSHV_RETURN_TO_LOWER_VTL _IO(MSHV_IOCTL, 0x27) +#define MSHV_GET_VP_REGISTERS _IOWR(MSHV_IOCTL, 0x05, struct mshv_vp_registers) +#define MSHV_SET_VP_REGISTERS _IOW(MSHV_IOCTL, 0x06, struct mshv_vp_registers) + +/* VMBus device IOCTLs */ +#define MSHV_SINT_SIGNAL_EVENT _IOW(MSHV_IOCTL, 0x22, struct mshv_vtl_signal_event) +#define MSHV_SINT_POST_MESSAGE _IOW(MSHV_IOCTL, 0x23, struct mshv_vtl_sint_post_msg) +#define MSHV_SINT_SET_EVENTFD _IOW(MSHV_IOCTL, 0x24, struct mshv_vtl_set_eventfd) +#define MSHV_SINT_PAUSE_MESSAGE_STREAM _IOW(MSHV_IOCTL, 0x25, struct mshv_sint_mask) + +/* hv_hvcall device */ +#define MSHV_HVCALL_SETUP _IOW(MSHV_IOCTL, 0x1E, struct mshv_vtl_hvcall_setup) +#define MSHV_HVCALL _IOWR(MSHV_IOCTL, 0x1F, struct mshv_vtl_hvcall) #endif -- cgit v1.2.3 From e56cadaa27fd156106c5583ed98976927c6febc9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 6 Dec 2025 16:47:40 -0800 Subject: ynl: add regen hint to new headers Recent commit 68e83f347266 ("tools: ynl-gen: add regeneration comment") added a hint how to regenerate the code to the headers. Update the new headers from this release cycle to also include it. Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251207004740.1657799-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- include/uapi/linux/energy_model.h | 1 + kernel/power/em_netlink_autogen.c | 1 + kernel/power/em_netlink_autogen.h | 1 + 3 files changed, 3 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/energy_model.h b/include/uapi/linux/energy_model.h index 4ec4c0eabbbb..0bcad967854f 100644 --- a/include/uapi/linux/energy_model.h +++ b/include/uapi/linux/energy_model.h @@ -2,6 +2,7 @@ /* Do not edit directly, auto-generated from: */ /* Documentation/netlink/specs/em.yaml */ /* YNL-GEN uapi header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ #ifndef _UAPI_LINUX_ENERGY_MODEL_H #define _UAPI_LINUX_ENERGY_MODEL_H diff --git a/kernel/power/em_netlink_autogen.c b/kernel/power/em_netlink_autogen.c index a7a09ab1d1c2..ceb3b2bb6ebe 100644 --- a/kernel/power/em_netlink_autogen.c +++ b/kernel/power/em_netlink_autogen.c @@ -2,6 +2,7 @@ /* Do not edit directly, auto-generated from: */ /* Documentation/netlink/specs/em.yaml */ /* YNL-GEN kernel source */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ #include #include diff --git a/kernel/power/em_netlink_autogen.h b/kernel/power/em_netlink_autogen.h index 78ce609641f1..140ab548103c 100644 --- a/kernel/power/em_netlink_autogen.h +++ b/kernel/power/em_netlink_autogen.h @@ -2,6 +2,7 @@ /* Do not edit directly, auto-generated from: */ /* Documentation/netlink/specs/em.yaml */ /* YNL-GEN kernel header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ #ifndef _LINUX_EM_GEN_H #define _LINUX_EM_GEN_H -- cgit v1.2.3 From 0ace3297a7301911e52d8195cb1006414897c859 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 5 Dec 2025 19:55:14 +0100 Subject: mptcp: pm: ignore unknown endpoint flags Before this patch, the kernel was saving any flags set by the userspace, even unknown ones. This doesn't cause critical issues because the kernel is only looking at specific ones. But on the other hand, endpoints dumps could tell the userspace some recent flags seem to be supported on older kernel versions. Instead, ignore all unknown flags when parsing them. By doing that, the userspace can continue to set unsupported flags, but it has a way to verify what is supported by the kernel. Note that it sounds better to continue accepting unsupported flags not to change the behaviour, but also that eases things on the userspace side by adding "optional" endpoint types only supported by newer kernel versions without having to deal with the different kernel versions. A note for the backports: there will be conflicts in mptcp.h on older versions not having the mentioned flags, the new line should still be added last, and the '5' needs to be adapted to have the same value as the last entry. Fixes: 01cacb00b35c ("mptcp: add netlink-based PM") Cc: stable@vger.kernel.org Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251205-net-mptcp-misc-fixes-6-19-rc1-v1-1-9e4781a6c1b8@kernel.org Signed-off-by: Jakub Kicinski --- include/uapi/linux/mptcp.h | 1 + net/mptcp/pm_netlink.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h index 04eea6d1d0a9..72a5d030154e 100644 --- a/include/uapi/linux/mptcp.h +++ b/include/uapi/linux/mptcp.h @@ -40,6 +40,7 @@ #define MPTCP_PM_ADDR_FLAG_FULLMESH _BITUL(3) #define MPTCP_PM_ADDR_FLAG_IMPLICIT _BITUL(4) #define MPTCP_PM_ADDR_FLAG_LAMINAR _BITUL(5) +#define MPTCP_PM_ADDR_FLAGS_MASK GENMASK(5, 0) struct mptcp_info { __u8 mptcpi_subflows; diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index d5b383870f79..7aa42de9c47b 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -119,7 +119,8 @@ int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info, } if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) - entry->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]); + entry->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]) & + MPTCP_PM_ADDR_FLAGS_MASK; if (tb[MPTCP_PM_ADDR_ATTR_PORT]) entry->addr.port = htons(nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT])); -- cgit v1.2.3 From a58383fa45c706bda3bf4a1955c3a0327dbec7e7 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Wed, 17 Dec 2025 07:17:12 +0530 Subject: block: add allocation size check in blkdev_pr_read_keys() blkdev_pr_read_keys() takes num_keys from userspace and uses it to calculate the allocation size for keys_info via struct_size(). While there is a check for SIZE_MAX (integer overflow), there is no upper bound validation on the allocation size itself. A malicious or buggy userspace can pass a large num_keys value that doesn't trigger overflow but still results in an excessive allocation attempt, causing a warning in the page allocator when the order exceeds MAX_PAGE_ORDER. Fix this by introducing PR_KEYS_MAX to limit the number of keys to a sane value. This makes the SIZE_MAX check redundant, so remove it. Also switch to kvzalloc/kvfree to handle larger allocations gracefully. Fixes: 22a1ffea5f80 ("block: add IOC_PR_READ_KEYS ioctl") Tested-by: syzbot+660d079d90f8a1baf54d@syzkaller.appspotmail.com Reported-by: syzbot+660d079d90f8a1baf54d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=660d079d90f8a1baf54d Link: https://lore.kernel.org/all/20251212013510.3576091-1-kartikey406@gmail.com/T/ [v1] Signed-off-by: Deepanshu Kartikey Reviewed-by: Martin K. Petersen Reviewed-by: Stefan Hajnoczi Signed-off-by: Jens Axboe --- block/ioctl.c | 9 +++++---- include/uapi/linux/pr.h | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include/uapi/linux') diff --git a/block/ioctl.c b/block/ioctl.c index 61feed686418..344478348a54 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -442,11 +442,12 @@ static int blkdev_pr_read_keys(struct block_device *bdev, blk_mode_t mode, if (copy_from_user(&read_keys, arg, sizeof(read_keys))) return -EFAULT; - keys_info_len = struct_size(keys_info, keys, read_keys.num_keys); - if (keys_info_len == SIZE_MAX) + if (read_keys.num_keys > PR_KEYS_MAX) return -EINVAL; - keys_info = kzalloc(keys_info_len, GFP_KERNEL); + keys_info_len = struct_size(keys_info, keys, read_keys.num_keys); + + keys_info = kvzalloc(keys_info_len, GFP_KERNEL); if (!keys_info) return -ENOMEM; @@ -473,7 +474,7 @@ static int blkdev_pr_read_keys(struct block_device *bdev, blk_mode_t mode, if (copy_to_user(arg, &read_keys, sizeof(read_keys))) ret = -EFAULT; out: - kfree(keys_info); + kvfree(keys_info); return ret; } diff --git a/include/uapi/linux/pr.h b/include/uapi/linux/pr.h index 847f3051057a..f0ecb1677317 100644 --- a/include/uapi/linux/pr.h +++ b/include/uapi/linux/pr.h @@ -79,4 +79,6 @@ struct pr_read_reservation { #define IOC_PR_READ_KEYS _IOWR('p', 206, struct pr_read_keys) #define IOC_PR_READ_RESERVATION _IOR('p', 207, struct pr_read_reservation) +#define PR_KEYS_MAX (1u << 16) + #endif /* _UAPI_PR_H */ -- cgit v1.2.3 From 733a8924229ff8c0385121a30fcd00bf70644743 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Thu, 13 Nov 2025 17:02:58 +0100 Subject: Input: add ABS_SND_PROFILE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ABS_SND_PROFILE used to describe the state of a multi-value sound profile switch. This will be used for the alert-slider on OnePlus phones or other phones. Profile values added as SND_PROFLE_(SILENT|VIBRATE|RING) identifiers to input-event-codes.h so they can be used from DTS. Signed-off-by: Gergo Koteles Reviewed-by: Bjorn Andersson Tested-by: Guido Günther # oneplus,fajita & oneplus,enchilada Reviewed-by: Guido Günther Signed-off-by: David Heidelberg Reviewed-by: Pavel Machek Link: https://patch.msgid.link/20251113-op6-tri-state-v8-1-54073f3874bc@ixit.cz Signed-off-by: Dmitry Torokhov --- Documentation/input/event-codes.rst | 6 ++++++ drivers/hid/hid-debug.c | 1 + include/uapi/linux/input-event-codes.h | 9 +++++++++ 3 files changed, 16 insertions(+) (limited to 'include/uapi/linux') diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst index 4424cbff251f..77a6c9b3956d 100644 --- a/Documentation/input/event-codes.rst +++ b/Documentation/input/event-codes.rst @@ -241,6 +241,12 @@ A few EV_ABS codes have special meanings: emitted only when the selected profile changes, indicating the newly selected profile value. +* ABS_SND_PROFILE: + + - Used to describe the state of a multi-value sound profile switch. + An event is emitted only when the selected profile changes, + indicating the newly selected profile value. + * ABS_MT_: - Used to describe multitouch input events. Please see diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 337d2dc81b4c..c5865b0d2aaa 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3513,6 +3513,7 @@ static const char *absolutes[ABS_CNT] = { [ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt", [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth", [ABS_VOLUME] = "Volume", [ABS_PROFILE] = "Profile", + [ABS_SND_PROFILE] = "SoundProfile", [ABS_MISC] = "Misc", [ABS_MT_SLOT] = "MTSlot", [ABS_MT_TOUCH_MAJOR] = "MTMajor", diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 30f3c9eaafaa..4bdb6a165987 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -891,6 +891,7 @@ #define ABS_VOLUME 0x20 #define ABS_PROFILE 0x21 +#define ABS_SND_PROFILE 0x22 #define ABS_MISC 0x28 @@ -1000,4 +1001,12 @@ #define SND_MAX 0x07 #define SND_CNT (SND_MAX+1) +/* + * ABS_SND_PROFILE values + */ + +#define SND_PROFILE_SILENT 0x00 +#define SND_PROFILE_VIBRATE 0x01 +#define SND_PROFILE_RING 0x02 + #endif -- cgit v1.2.3 From c6c209ceb87f64a6ceebe61761951dcbbf4a0baa Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 9 Dec 2025 19:28:49 -0500 Subject: NFSD: Remove NFSERR_EAGAIN I haven't found an NFSERR_EAGAIN in RFCs 1094, 1813, 7530, or 8881. None of these RFCs have an NFS status code that match the numeric value "11". Based on the meaning of the EAGAIN errno, I presume the use of this status in NFSD means NFS4ERR_DELAY. So replace the one usage of nfserr_eagain, and remove it from NFSD's NFS status conversion tables. As far as I can tell, NFSERR_EAGAIN has existed since the pre-git era, but was not actually used by any code until commit f4e44b393389 ("NFSD: delay unmount source's export after inter-server copy completed."), at which time it become possible for NFSD to return a status code of 11 (which is not valid NFS protocol). Fixes: f4e44b393389 ("NFSD: delay unmount source's export after inter-server copy completed.") Cc: stable@vger.kernel.org Reviewed-by: NeilBrown Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfs_common/common.c | 1 - fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfsd.h | 1 - include/trace/misc/nfs.h | 2 -- include/uapi/linux/nfs.h | 1 - 5 files changed, 1 insertion(+), 6 deletions(-) (limited to 'include/uapi/linux') diff --git a/fs/nfs_common/common.c b/fs/nfs_common/common.c index af09aed09fd2..0778743ae2c2 100644 --- a/fs/nfs_common/common.c +++ b/fs/nfs_common/common.c @@ -17,7 +17,6 @@ static const struct { { NFSERR_NOENT, -ENOENT }, { NFSERR_IO, -EIO }, { NFSERR_NXIO, -ENXIO }, -/* { NFSERR_EAGAIN, -EAGAIN }, */ { NFSERR_ACCES, -EACCES }, { NFSERR_EXIST, -EEXIST }, { NFSERR_XDEV, -EXDEV }, diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 7f7e6bb23a90..42a6b914c0fe 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1506,7 +1506,7 @@ try_again: (schedule_timeout(20*HZ) == 0)) { finish_wait(&nn->nfsd_ssc_waitq, &wait); kfree(work); - return nfserr_eagain; + return nfserr_jukebox; } finish_wait(&nn->nfsd_ssc_waitq, &wait); goto try_again; diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 50be785f1d2c..b0283213a8f5 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -233,7 +233,6 @@ void nfsd_lockd_shutdown(void); #define nfserr_noent cpu_to_be32(NFSERR_NOENT) #define nfserr_io cpu_to_be32(NFSERR_IO) #define nfserr_nxio cpu_to_be32(NFSERR_NXIO) -#define nfserr_eagain cpu_to_be32(NFSERR_EAGAIN) #define nfserr_acces cpu_to_be32(NFSERR_ACCES) #define nfserr_exist cpu_to_be32(NFSERR_EXIST) #define nfserr_xdev cpu_to_be32(NFSERR_XDEV) diff --git a/include/trace/misc/nfs.h b/include/trace/misc/nfs.h index c82233e950ac..a394b4d38e18 100644 --- a/include/trace/misc/nfs.h +++ b/include/trace/misc/nfs.h @@ -16,7 +16,6 @@ TRACE_DEFINE_ENUM(NFSERR_PERM); TRACE_DEFINE_ENUM(NFSERR_NOENT); TRACE_DEFINE_ENUM(NFSERR_IO); TRACE_DEFINE_ENUM(NFSERR_NXIO); -TRACE_DEFINE_ENUM(NFSERR_EAGAIN); TRACE_DEFINE_ENUM(NFSERR_ACCES); TRACE_DEFINE_ENUM(NFSERR_EXIST); TRACE_DEFINE_ENUM(NFSERR_XDEV); @@ -52,7 +51,6 @@ TRACE_DEFINE_ENUM(NFSERR_JUKEBOX); { NFSERR_NXIO, "NXIO" }, \ { ECHILD, "CHILD" }, \ { ETIMEDOUT, "TIMEDOUT" }, \ - { NFSERR_EAGAIN, "AGAIN" }, \ { NFSERR_ACCES, "ACCES" }, \ { NFSERR_EXIST, "EXIST" }, \ { NFSERR_XDEV, "XDEV" }, \ diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h index f356f2ba3814..71c7196d3281 100644 --- a/include/uapi/linux/nfs.h +++ b/include/uapi/linux/nfs.h @@ -49,7 +49,6 @@ NFSERR_NOENT = 2, /* v2 v3 v4 */ NFSERR_IO = 5, /* v2 v3 v4 */ NFSERR_NXIO = 6, /* v2 v3 v4 */ - NFSERR_EAGAIN = 11, /* v2 v3 */ NFSERR_ACCES = 13, /* v2 v3 v4 */ NFSERR_EXIST = 17, /* v2 v3 v4 */ NFSERR_XDEV = 18, /* v3 v4 */ -- cgit v1.2.3