mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 15:03:52 +08:00
Merge tag 'staging-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging driver updates from Greg KH:
"Here is the big set of staging driver changes for 6.13-rc1.
Lots of changes this merge cycle, drivers removed and drivers added.
Highlights include:
- removals of the following staging drivers due to no forward
progress and no one having either the hardware or the time/energy
to deal with them anymore:
- fieldbus
- gdm724x
- olpc_dcon
- rtl8712
- rts5208
- vt6655
- vt6656
If anyone has this hardware and wants to work on the drivers, it
can be an easy revert to get them back.
- addition of the gpib driver subsystem. Lots of drivers for really
old and semi-old interfaces to lab equipments. We expect lots of
churn in these drivers as they get cleaned up to "working" order.
These were added at the request of a user and the maintainer/author
of them is helping out with the effort
- loads and loads of tiny coding style cleanups for almost all
staging drivers. Too many to list, see the shortlog for details.
All of these have been in linux-next for a very long time with no
reported issues"
* tag 'staging-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (216 commits)
Staging: gpib: gpib_os.c - Remove unnecessary OOM message
staging: gpib: avoid unintended sign extension
staging: vchiq_debugfs: Use forward declarations
staging: vchiq_core: Rectify header include for vchiq_dump_state()
staging: vc04_services: Cleanup TODO entry
staging: most: Remove TODO contact information
staging: rtl8723bs: Remove TODO contact information
staging: sm750fb: Remove TODO contact information
staging: iio: Remove TODO file
staging: greybus: uart: Fix atomicity violation in get_serial_info()
staging: rtl8723bs: Remove unused function Efuse_GetCurrentSize
staging: rtl8723bs: Remove unused function efuse_WordEnableDataRead
staging: rtl8723bs: Remove function hal_EfusePgPacketWrite1ByteHeader
staging: rtl8723bs: Remove function hal_EfusePgPacketWrite2ByteHeader
staging: rtl8723bs: Remove unused function hal_EfusePgCheckAvailableAddr
staging: rtl8723bs: Remove unused function hal_EfuseConstructPGPkt
staging: rtl8723bs: Remove unused function hal_EfusePartialWriteCheck
staging: rtl8723bs: Remove unused function hal_EfusePgPacketWriteHeader
staging: rtl8723bs: Remove unused function hal_EfusePgPacketWriteData
staging: rtl8723bs: Remove unused function Hal_EfusePgPacketWrite_BT
...
This commit is contained in:
33
MAINTAINERS
33
MAINTAINERS
@@ -9718,6 +9718,11 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/gpd-pocket-fan.c
|
||||
|
||||
GPIB DRIVERS
|
||||
M: Dave Penkler <dpenkler@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/gpib/
|
||||
|
||||
GPIO ACPI SUPPORT
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
@@ -22135,17 +22140,6 @@ L: linux-media@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/media/atomisp/
|
||||
|
||||
STAGING - FIELDBUS SUBSYSTEM
|
||||
M: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/fieldbus/*
|
||||
F: drivers/staging/fieldbus/Documentation/
|
||||
|
||||
STAGING - HMS ANYBUS-S BUS
|
||||
M: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/fieldbus/anybuss/
|
||||
|
||||
STAGING - INDUSTRIAL IO
|
||||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@@ -22160,18 +22154,6 @@ L: linux-tegra@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/nvec/
|
||||
|
||||
STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON)
|
||||
M: Jens Frederich <jfrederich@gmail.com>
|
||||
M: Jon Nettleton <jon.nettleton@gmail.com>
|
||||
S: Maintained
|
||||
W: http://wiki.laptop.org/go/DCON
|
||||
F: drivers/staging/olpc_dcon/
|
||||
|
||||
STAGING - REALTEK RTL8712U DRIVERS
|
||||
M: Florian Schilhabel <florian.c.schilhabel@googlemail.com>.
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/rtl8712/
|
||||
|
||||
STAGING - SEPS525 LCD CONTROLLER DRIVERS
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
@@ -22187,11 +22169,6 @@ L: linux-fbdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/sm750fb/
|
||||
|
||||
STAGING - VIA VT665X DRIVERS
|
||||
M: Philipp Hortmann <philipp.g.hortmann@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/vt665?/
|
||||
|
||||
STAGING SUBSYSTEM
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
L: linux-staging@lists.linux.dev
|
||||
|
||||
@@ -24,20 +24,10 @@ menuconfig STAGING
|
||||
|
||||
if STAGING
|
||||
|
||||
source "drivers/staging/olpc_dcon/Kconfig"
|
||||
|
||||
source "drivers/staging/rtl8723bs/Kconfig"
|
||||
|
||||
source "drivers/staging/rtl8712/Kconfig"
|
||||
|
||||
source "drivers/staging/rts5208/Kconfig"
|
||||
|
||||
source "drivers/staging/octeon/Kconfig"
|
||||
|
||||
source "drivers/staging/vt6655/Kconfig"
|
||||
|
||||
source "drivers/staging/vt6656/Kconfig"
|
||||
|
||||
source "drivers/staging/iio/Kconfig"
|
||||
|
||||
source "drivers/staging/sm750fb/Kconfig"
|
||||
@@ -46,8 +36,6 @@ source "drivers/staging/nvec/Kconfig"
|
||||
|
||||
source "drivers/staging/media/Kconfig"
|
||||
|
||||
source "drivers/staging/gdm724x/Kconfig"
|
||||
|
||||
source "drivers/staging/fbtft/Kconfig"
|
||||
|
||||
source "drivers/staging/most/Kconfig"
|
||||
@@ -58,8 +46,8 @@ source "drivers/staging/vc04_services/Kconfig"
|
||||
|
||||
source "drivers/staging/axis-fifo/Kconfig"
|
||||
|
||||
source "drivers/staging/fieldbus/Kconfig"
|
||||
|
||||
source "drivers/staging/vme_user/Kconfig"
|
||||
|
||||
source "drivers/staging/gpib/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
||||
@@ -2,21 +2,15 @@
|
||||
# Makefile for staging directory
|
||||
|
||||
obj-y += media/
|
||||
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
|
||||
obj-$(CONFIG_RTL8723BS) += rtl8723bs/
|
||||
obj-$(CONFIG_R8712U) += rtl8712/
|
||||
obj-$(CONFIG_RTS5208) += rts5208/
|
||||
obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
|
||||
obj-$(CONFIG_VT6655) += vt6655/
|
||||
obj-$(CONFIG_VT6656) += vt6656/
|
||||
obj-$(CONFIG_VME_BUS) += vme_user/
|
||||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_FB_SM750) += sm750fb/
|
||||
obj-$(CONFIG_MFD_NVEC) += nvec/
|
||||
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
|
||||
obj-$(CONFIG_FB_TFT) += fbtft/
|
||||
obj-$(CONFIG_MOST) += most/
|
||||
obj-$(CONFIG_GREYBUS) += greybus/
|
||||
obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
|
||||
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
|
||||
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
|
||||
obj-$(CONFIG_GPIB) += gpib/
|
||||
|
||||
@@ -919,7 +919,7 @@ static struct platform_driver axis_fifo_driver = {
|
||||
.of_match_table = axis_fifo_of_match,
|
||||
},
|
||||
.probe = axis_fifo_probe,
|
||||
.remove_new = axis_fifo_remove,
|
||||
.remove = axis_fifo_remove,
|
||||
};
|
||||
|
||||
static int __init axis_fifo_init(void)
|
||||
|
||||
@@ -330,7 +330,7 @@ static struct platform_driver fbtft_driver_platform_driver = { \
|
||||
.of_match_table = dt_ids, \
|
||||
}, \
|
||||
.probe = fbtft_driver_probe_pdev, \
|
||||
.remove_new = fbtft_driver_remove_pdev, \
|
||||
.remove = fbtft_driver_remove_pdev, \
|
||||
}; \
|
||||
\
|
||||
static int __init fbtft_driver_module_init(void) \
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
What: /dev/fieldbus_devX
|
||||
Date: December 2018
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
The cdev interface to drivers for Fieldbus Device Memory
|
||||
(aka. Process Memory).
|
||||
|
||||
The following file operations are supported:
|
||||
|
||||
open(2)
|
||||
Create an I/O context associated with the file descriptor.
|
||||
|
||||
read(2)
|
||||
Read from Process Memory's "read area".
|
||||
Clears POLLERR | POLLPRI from the file descriptor.
|
||||
|
||||
write(2)
|
||||
Write to Process Memory's "write area".
|
||||
|
||||
poll(2), select(2), epoll_wait(2) etc.
|
||||
When a "Process Memory Read Area Changed" event occurs,
|
||||
POLLERR | POLLPRI will be set on the file descriptor.
|
||||
Note that POLLIN | POLLOUT events are always set, because the
|
||||
process memory area is always readable and writable.
|
||||
|
||||
close(2)
|
||||
Free up the I/O context that was associated
|
||||
with the file descriptor.
|
||||
|
||||
Users: TBD
|
||||
@@ -1,62 +0,0 @@
|
||||
What: /sys/class/fieldbus_dev/fieldbus_devX/card_name
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
Human-readable name of the Fieldbus Device.
|
||||
|
||||
What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_type
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
The type of fieldbus implemented by this device.
|
||||
Possible values:
|
||||
'unknown'
|
||||
'profinet'
|
||||
|
||||
What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_id
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
The unique fieldbus id associated with this device.
|
||||
The exact format of this id is fieldbus type dependent, e.g.
|
||||
a mac address for profinet.
|
||||
|
||||
What: /sys/class/fieldbus_dev/fieldbus_devX/read_area_size
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
The size, in bytes, of the Process Memory read area.
|
||||
Note: this area is accessible by reading from the associated
|
||||
character device (/dev/fieldbus_devX).
|
||||
|
||||
What: /sys/class/fieldbus_dev/fieldbus_devX/write_area_size
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
The size, in bytes, of the Process Memory write area.
|
||||
Note: this area is accessible by writing to the associated
|
||||
character device (/dev/fieldbus_devX)
|
||||
|
||||
What: /sys/class/fieldbus_dev/fieldbus_devX/online
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
Whether the fieldbus is online or offline.
|
||||
Possible values:
|
||||
'1' meaning 'online'
|
||||
'0' meaning 'offline'
|
||||
Note: an uevent is generated when this property changes.
|
||||
|
||||
What: /sys/class/fieldbus_dev/fieldbus_devX/enabled
|
||||
KernelVersion: 5.1 (staging)
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
Description:
|
||||
Whether the device is enabled (power on) or
|
||||
disabled (power off).
|
||||
Possible values:
|
||||
'1' meaning enabled
|
||||
'0' meaning disabled
|
||||
Normally a r/o property, but optionally r/w:
|
||||
Writing '1' enables the device (power on) with default
|
||||
settings.
|
||||
Writing '0' disables the card (power off).
|
||||
@@ -1,71 +0,0 @@
|
||||
* Arcx Anybus-S controller
|
||||
|
||||
This chip communicates with the SoC over a parallel bus. It is
|
||||
expected that its Device Tree node is specified as the child of a node
|
||||
corresponding to the parallel bus used for communication.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
|
||||
- compatible : The following chip-specific string:
|
||||
"arcx,anybus-controller"
|
||||
|
||||
- reg : three areas:
|
||||
index 0: bus memory area where the cpld registers are located.
|
||||
index 1: bus memory area of the first host's dual-port ram.
|
||||
index 2: bus memory area of the second host's dual-port ram.
|
||||
|
||||
- reset-gpios : the GPIO pin connected to the reset line of the controller.
|
||||
|
||||
- interrupts : two interrupts:
|
||||
index 0: interrupt connected to the first host
|
||||
index 1: interrupt connected to the second host
|
||||
Generic interrupt client node bindings are described in
|
||||
interrupt-controller/interrupts.txt
|
||||
|
||||
Optional: use of subnodes
|
||||
-------------------------
|
||||
|
||||
The card connected to a host may need additional properties. These can be
|
||||
specified in subnodes to the controller node.
|
||||
|
||||
The subnodes are identified by the standard 'reg' property. Which information
|
||||
exactly can be specified depends on the bindings for the function driver
|
||||
for the subnode.
|
||||
|
||||
Required controller node properties when using subnodes:
|
||||
- #address-cells: should be one.
|
||||
- #size-cells: should be zero.
|
||||
|
||||
Required subnode properties:
|
||||
- reg: Must contain the host index of the card this subnode describes:
|
||||
<0> for the first host on the controller
|
||||
<1> for the second host on the controller
|
||||
Note that only a single card can be plugged into a host, so the host
|
||||
index uniquely describes the card location.
|
||||
|
||||
Example of usage:
|
||||
-----------------
|
||||
|
||||
This example places the bridge on top of the i.MX WEIM parallel bus, see:
|
||||
Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml
|
||||
|
||||
&weim {
|
||||
controller@0,0 {
|
||||
compatible = "arcx,anybus-controller";
|
||||
reg = <0 0 0x100>, <0 0x400000 0x800>, <1 0x400000 0x800>;
|
||||
reset-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <1 IRQ_TYPE_LEVEL_LOW>, <5 IRQ_TYPE_LEVEL_LOW>;
|
||||
/* fsl,weim-cs-timing is a i.MX WEIM bus specific property */
|
||||
fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100
|
||||
0x00000000 0xa0000240 0x00000000>;
|
||||
/* optional subnode for a card plugged into the first host */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
card@0 {
|
||||
reg = <0>;
|
||||
/* card specific properties go here */
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,66 +0,0 @@
|
||||
Fieldbus-Device Subsystem
|
||||
============================================
|
||||
|
||||
Part 0 - What is a Fieldbus Device ?
|
||||
------------------------------------
|
||||
|
||||
Fieldbus is the name of a family of industrial computer network protocols used
|
||||
for real-time distributed control, standardized as IEC 61158.
|
||||
|
||||
A complex automated industrial system -- such as manufacturing assembly line --
|
||||
usually needs a distributed control system -- an organized hierarchy of
|
||||
controller systems -- to function. In this hierarchy, there is usually a
|
||||
Human Machine Interface (HMI) at the top, where an operator can monitor or
|
||||
operate the system. This is typically linked to a middle layer of programmable
|
||||
logic controllers (PLC) via a non-time-critical communications system
|
||||
(e.g. Ethernet). At the bottom of the control chain is the fieldbus that links
|
||||
the PLCs to the components that actually do the work, such as sensors,
|
||||
actuators, electric motors, console lights, switches, valves and contactors.
|
||||
|
||||
(Source: Wikipedia)
|
||||
|
||||
A "Fieldbus Device" is such an actuator, motor, console light, switch, ...
|
||||
controlled via the Fieldbus by a PLC aka "Fieldbus Controller".
|
||||
|
||||
Communication between PLC and device typically happens via process data memory,
|
||||
separated into input and output areas. The Fieldbus then cyclically transfers
|
||||
the PLC's output area to the device's input area, and vice versa.
|
||||
|
||||
Part I - Why do we need this subsystem?
|
||||
---------------------------------------
|
||||
|
||||
Fieldbus device (client) adapters are commercially available. They allow data
|
||||
exchange with a PLC aka "Fieldbus Controller" via process data memory.
|
||||
|
||||
They are typically used when a Linux device wants to expose itself as an
|
||||
actuator, motor, console light, switch, etc. over the fieldbus.
|
||||
|
||||
The purpose of this subsystem is:
|
||||
a) present a general, standardized, extensible API/ABI to userspace; and
|
||||
b) present a convenient interface to drivers.
|
||||
|
||||
Part II - How can drivers use the subsystem?
|
||||
--------------------------------------------
|
||||
|
||||
Any driver that wants to register as a Fieldbus Device should allocate and
|
||||
populate a 'struct fieldbus_dev' (from include/linux/fieldbus_dev.h).
|
||||
Registration then happens by calling fieldbus_dev_register().
|
||||
|
||||
Part III - How can userspace use the subsystem?
|
||||
-----------------------------------------------
|
||||
|
||||
Fieldbus protocols and adapters are diverse and varied. However, they share
|
||||
a limited few common behaviours and properties. This allows us to define
|
||||
a simple interface consisting of a character device and a set of sysfs files:
|
||||
|
||||
See:
|
||||
drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev
|
||||
drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev
|
||||
|
||||
Note that this simple interface does not provide a way to modify adapter
|
||||
configuration settings. It is therefore useful only for adapters that get their
|
||||
configuration settings some other way, e.g. non-volatile memory on the adapter,
|
||||
through the network, ...
|
||||
|
||||
At a later phase, this simple interface can easily co-exist with a future
|
||||
(netlink-based ?) configuration settings interface.
|
||||
@@ -1,19 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menuconfig FIELDBUS_DEV
|
||||
tristate "Fieldbus Device Support"
|
||||
help
|
||||
Support for Fieldbus Device Adapters.
|
||||
|
||||
Fieldbus device (client) adapters allow data exchange with a PLC aka.
|
||||
"Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)
|
||||
|
||||
They are typically used when a Linux device wants to expose itself
|
||||
as an actuator, motor, console light, switch, etc. over the fieldbus.
|
||||
|
||||
This framework is designed to provide a generic interface to Fieldbus
|
||||
Devices from both the Linux Kernel and the userspace.
|
||||
|
||||
If unsure, say no.
|
||||
|
||||
source "drivers/staging/fieldbus/anybuss/Kconfig"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for fieldbus_dev drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus_dev.o anybuss/
|
||||
fieldbus_dev-y := dev_core.o
|
||||
@@ -1,5 +0,0 @@
|
||||
TODO:
|
||||
-Get more people/drivers to use the Fieldbus userspace ABI. It requires
|
||||
verification/sign-off by multiple users.
|
||||
|
||||
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
@@ -1,41 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config HMS_ANYBUSS_BUS
|
||||
tristate "HMS Anybus-S Bus Support"
|
||||
select REGMAP
|
||||
depends on OF && FIELDBUS_DEV
|
||||
help
|
||||
Driver for the HMS Industrial Networks Anybus-S bus.
|
||||
You can attach a single Anybus-S compatible card to it, which
|
||||
typically provides fieldbus and industrial ethernet
|
||||
functionality.
|
||||
|
||||
if HMS_ANYBUSS_BUS
|
||||
|
||||
config ARCX_ANYBUS_CONTROLLER
|
||||
tristate "Arcx Anybus-S Controller"
|
||||
depends on OF && GPIOLIB && HAS_IOMEM && REGULATOR
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Select this to get support for the Arcx Anybus controller.
|
||||
It connects to the SoC via a parallel memory bus, and
|
||||
embeds up to two Anybus-S buses (slots).
|
||||
There is also a CAN power readout, unrelated to the Anybus,
|
||||
modelled as a regulator.
|
||||
|
||||
config HMS_PROFINET
|
||||
tristate "HMS Profinet IRT Controller (Anybus-S)"
|
||||
depends on FIELDBUS_DEV && HMS_ANYBUSS_BUS
|
||||
help
|
||||
If you say yes here you get support for the HMS Industrial
|
||||
Networks Profinet IRT Controller.
|
||||
|
||||
It will be registered with the kernel as a fieldbus_dev,
|
||||
so userspace can interact with it via the fieldbus_dev userspace
|
||||
interface(s).
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called hms-profinet.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endif
|
||||
@@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for anybuss drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HMS_ANYBUSS_BUS) += anybuss_core.o
|
||||
anybuss_core-y += host.o
|
||||
|
||||
obj-$(CONFIG_ARCX_ANYBUS_CONTROLLER) += arcx-anybus.o
|
||||
obj-$(CONFIG_HMS_PROFINET) += hms-profinet.o
|
||||
@@ -1,95 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Anybus-S client adapter definitions
|
||||
*
|
||||
* Copyright 2018 Arcx Inc
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_ANYBUSS_CLIENT_H__
|
||||
#define __LINUX_ANYBUSS_CLIENT_H__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
/* move to <linux/fieldbus_dev.h> when taking this out of staging */
|
||||
#include "../fieldbus_dev.h"
|
||||
|
||||
struct anybuss_host;
|
||||
|
||||
struct anybuss_client {
|
||||
struct device dev;
|
||||
struct anybuss_host *host;
|
||||
__be16 anybus_id;
|
||||
/*
|
||||
* these can be optionally set by the client to receive event
|
||||
* notifications from the host.
|
||||
*/
|
||||
void (*on_area_updated)(struct anybuss_client *client);
|
||||
void (*on_online_changed)(struct anybuss_client *client, bool online);
|
||||
};
|
||||
|
||||
struct anybuss_client_driver {
|
||||
struct device_driver driver;
|
||||
int (*probe)(struct anybuss_client *adev);
|
||||
void (*remove)(struct anybuss_client *adev);
|
||||
u16 anybus_id;
|
||||
};
|
||||
|
||||
int anybuss_client_driver_register(struct anybuss_client_driver *drv);
|
||||
void anybuss_client_driver_unregister(struct anybuss_client_driver *drv);
|
||||
|
||||
static inline struct anybuss_client *to_anybuss_client(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct anybuss_client, dev);
|
||||
}
|
||||
|
||||
#define to_anybuss_client_driver(__drv) container_of_const(__drv, struct anybuss_client_driver, driver)
|
||||
|
||||
static inline void *
|
||||
anybuss_get_drvdata(const struct anybuss_client *client)
|
||||
{
|
||||
return dev_get_drvdata(&client->dev);
|
||||
}
|
||||
|
||||
static inline void
|
||||
anybuss_set_drvdata(struct anybuss_client *client, void *data)
|
||||
{
|
||||
dev_set_drvdata(&client->dev, data);
|
||||
}
|
||||
|
||||
int anybuss_set_power(struct anybuss_client *client, bool power_on);
|
||||
|
||||
struct anybuss_memcfg {
|
||||
u16 input_io;
|
||||
u16 input_dpram;
|
||||
u16 input_total;
|
||||
|
||||
u16 output_io;
|
||||
u16 output_dpram;
|
||||
u16 output_total;
|
||||
|
||||
enum fieldbus_dev_offl_mode offl_mode;
|
||||
};
|
||||
|
||||
int anybuss_start_init(struct anybuss_client *client,
|
||||
const struct anybuss_memcfg *cfg);
|
||||
int anybuss_finish_init(struct anybuss_client *client);
|
||||
int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr,
|
||||
void *buf, size_t count);
|
||||
int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num,
|
||||
const void *buf, size_t count);
|
||||
int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num,
|
||||
const void *buf, size_t count);
|
||||
int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num,
|
||||
void *buf, size_t count);
|
||||
|
||||
/* these help clients make a struct file_operations */
|
||||
int anybuss_write_input(struct anybuss_client *client,
|
||||
const char __user *buf, size_t size,
|
||||
loff_t *offset);
|
||||
int anybuss_read_output(struct anybuss_client *client,
|
||||
char __user *buf, size_t size,
|
||||
loff_t *offset);
|
||||
|
||||
#endif /* __LINUX_ANYBUSS_CLIENT_H__ */
|
||||
@@ -1,47 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Anybus-S controller definitions
|
||||
*
|
||||
* Copyright 2018 Arcx Inc
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_ANYBUSS_CONTROLLER_H__
|
||||
#define __LINUX_ANYBUSS_CONTROLLER_H__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/*
|
||||
* To instantiate an Anybus-S host, a controller should provide the following:
|
||||
* - a reset function which resets the attached card;
|
||||
* - a regmap which provides access to the attached card's dpram;
|
||||
* - the irq of the attached card
|
||||
*/
|
||||
/**
|
||||
* struct anybuss_ops - Controller resources to instantiate an Anybus-S host
|
||||
*
|
||||
* @reset: asserts/deasserts the anybus card's reset line.
|
||||
* @regmap: provides access to the card's dual-port RAM area.
|
||||
* @irq: number of the interrupt connected to the card's interrupt line.
|
||||
* @host_idx: for multi-host controllers, the host index:
|
||||
* 0 for the first host on the controller, 1 for the second, etc.
|
||||
*/
|
||||
struct anybuss_ops {
|
||||
void (*reset)(struct device *dev, bool assert);
|
||||
struct regmap *regmap;
|
||||
int irq;
|
||||
int host_idx;
|
||||
};
|
||||
|
||||
struct anybuss_host;
|
||||
|
||||
struct anybuss_host * __must_check
|
||||
anybuss_host_common_probe(struct device *dev,
|
||||
const struct anybuss_ops *ops);
|
||||
void anybuss_host_common_remove(struct anybuss_host *host);
|
||||
|
||||
struct anybuss_host * __must_check
|
||||
devm_anybuss_host_common_probe(struct device *dev,
|
||||
const struct anybuss_ops *ops);
|
||||
|
||||
#endif /* __LINUX_ANYBUSS_CONTROLLER_H__ */
|
||||
@@ -1,379 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Arcx Anybus-S Controller driver
|
||||
*
|
||||
* Copyright (C) 2018 Arcx Inc
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* move to <linux/anybuss-controller.h> when taking this out of staging */
|
||||
#include "anybuss-controller.h"
|
||||
|
||||
#define CPLD_STATUS1 0x80
|
||||
#define CPLD_CONTROL 0x80
|
||||
#define CPLD_CONTROL_CRST 0x40
|
||||
#define CPLD_CONTROL_RST1 0x04
|
||||
#define CPLD_CONTROL_RST2 0x80
|
||||
#define CPLD_STATUS1_AB 0x02
|
||||
#define CPLD_STATUS1_CAN_POWER 0x01
|
||||
#define CPLD_DESIGN_LO 0x81
|
||||
#define CPLD_DESIGN_HI 0x82
|
||||
#define CPLD_CAP 0x83
|
||||
#define CPLD_CAP_COMPAT 0x01
|
||||
#define CPLD_CAP_SEP_RESETS 0x02
|
||||
|
||||
struct controller_priv {
|
||||
struct device *class_dev;
|
||||
bool common_reset;
|
||||
struct gpio_desc *reset_gpiod;
|
||||
void __iomem *cpld_base;
|
||||
struct mutex ctrl_lock; /* protects CONTROL register */
|
||||
u8 control_reg;
|
||||
char version[3];
|
||||
u16 design_no;
|
||||
};
|
||||
|
||||
static void do_reset(struct controller_priv *cd, u8 rst_bit, bool reset)
|
||||
{
|
||||
mutex_lock(&cd->ctrl_lock);
|
||||
/*
|
||||
* CPLD_CONTROL is write-only, so cache its value in
|
||||
* cd->control_reg
|
||||
*/
|
||||
if (reset)
|
||||
cd->control_reg &= ~rst_bit;
|
||||
else
|
||||
cd->control_reg |= rst_bit;
|
||||
writeb(cd->control_reg, cd->cpld_base + CPLD_CONTROL);
|
||||
/*
|
||||
* h/w work-around:
|
||||
* the hardware is 'too fast', so a reset followed by an immediate
|
||||
* not-reset will _not_ change the anybus reset line in any way,
|
||||
* losing the reset. to prevent this from happening, introduce
|
||||
* a minimum reset duration.
|
||||
* Verified minimum safe duration required using a scope
|
||||
* on 14-June-2018: 100 us.
|
||||
*/
|
||||
if (reset)
|
||||
usleep_range(100, 200);
|
||||
mutex_unlock(&cd->ctrl_lock);
|
||||
}
|
||||
|
||||
static int anybuss_reset(struct controller_priv *cd,
|
||||
unsigned long id, bool reset)
|
||||
{
|
||||
if (id >= 2)
|
||||
return -EINVAL;
|
||||
if (cd->common_reset)
|
||||
do_reset(cd, CPLD_CONTROL_CRST, reset);
|
||||
else
|
||||
do_reset(cd, id ? CPLD_CONTROL_RST2 : CPLD_CONTROL_RST1, reset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void export_reset_0(struct device *dev, bool assert)
|
||||
{
|
||||
struct controller_priv *cd = dev_get_drvdata(dev);
|
||||
|
||||
anybuss_reset(cd, 0, assert);
|
||||
}
|
||||
|
||||
static void export_reset_1(struct device *dev, bool assert)
|
||||
{
|
||||
struct controller_priv *cd = dev_get_drvdata(dev);
|
||||
|
||||
anybuss_reset(cd, 1, assert);
|
||||
}
|
||||
|
||||
/*
|
||||
* parallel bus limitation:
|
||||
*
|
||||
* the anybus is 8-bit wide. we can't assume that the hardware will translate
|
||||
* word accesses on the parallel bus to multiple byte-accesses on the anybus.
|
||||
*
|
||||
* the imx WEIM bus does not provide this type of translation.
|
||||
*
|
||||
* to be safe, we will limit parallel bus accesses to a single byte
|
||||
* at a time for now.
|
||||
*/
|
||||
|
||||
static const struct regmap_config arcx_regmap_cfg = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x7ff,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
/*
|
||||
* single-byte parallel bus accesses are atomic, so don't
|
||||
* require any synchronization.
|
||||
*/
|
||||
.disable_locking = true,
|
||||
};
|
||||
|
||||
static struct regmap *create_parallel_regmap(struct platform_device *pdev,
|
||||
int idx)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, idx + 1);
|
||||
if (IS_ERR(base))
|
||||
return ERR_CAST(base);
|
||||
return devm_regmap_init_mmio(dev, base, &arcx_regmap_cfg);
|
||||
}
|
||||
|
||||
static struct anybuss_host *
|
||||
create_anybus_host(struct platform_device *pdev, int idx)
|
||||
{
|
||||
struct anybuss_ops ops = {};
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
ops.reset = export_reset_0;
|
||||
break;
|
||||
case 1:
|
||||
ops.reset = export_reset_1;
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
ops.host_idx = idx;
|
||||
ops.regmap = create_parallel_regmap(pdev, idx);
|
||||
if (IS_ERR(ops.regmap))
|
||||
return ERR_CAST(ops.regmap);
|
||||
ops.irq = platform_get_irq(pdev, idx);
|
||||
if (ops.irq < 0)
|
||||
return ERR_PTR(ops.irq);
|
||||
return devm_anybuss_host_common_probe(&pdev->dev, &ops);
|
||||
}
|
||||
|
||||
static ssize_t version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct controller_priv *cd = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", cd->version);
|
||||
}
|
||||
static DEVICE_ATTR_RO(version);
|
||||
|
||||
static ssize_t design_number_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct controller_priv *cd = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", cd->design_no);
|
||||
}
|
||||
static DEVICE_ATTR_RO(design_number);
|
||||
|
||||
static struct attribute *controller_attributes[] = {
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_design_number.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group controller_attribute_group = {
|
||||
.attrs = controller_attributes,
|
||||
};
|
||||
|
||||
static const struct attribute_group *controller_attribute_groups[] = {
|
||||
&controller_attribute_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void controller_device_release(struct device *dev)
|
||||
{
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static int can_power_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct controller_priv *cd = rdev_get_drvdata(rdev);
|
||||
|
||||
return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
|
||||
}
|
||||
|
||||
static const struct regulator_ops can_power_ops = {
|
||||
.is_enabled = can_power_is_enabled,
|
||||
};
|
||||
|
||||
static const struct regulator_desc can_power_desc = {
|
||||
.name = "regulator-can-power",
|
||||
.id = -1,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.ops = &can_power_ops,
|
||||
};
|
||||
|
||||
static const struct class controller_class = {
|
||||
.name = "arcx_anybus_controller",
|
||||
};
|
||||
|
||||
static DEFINE_IDA(controller_index_ida);
|
||||
|
||||
static int controller_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct controller_priv *cd;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev *regulator;
|
||||
int err, id;
|
||||
struct anybuss_host *host;
|
||||
u8 status1, cap;
|
||||
|
||||
cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
|
||||
if (!cd)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, cd);
|
||||
mutex_init(&cd->ctrl_lock);
|
||||
cd->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(cd->reset_gpiod))
|
||||
return PTR_ERR(cd->reset_gpiod);
|
||||
|
||||
/* CPLD control memory, sits at index 0 */
|
||||
cd->cpld_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(cd->cpld_base)) {
|
||||
dev_err(dev,
|
||||
"failed to map cpld base address\n");
|
||||
err = PTR_ERR(cd->cpld_base);
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
/* identify cpld */
|
||||
status1 = readb(cd->cpld_base + CPLD_STATUS1);
|
||||
cd->design_no = (readb(cd->cpld_base + CPLD_DESIGN_HI) << 8) |
|
||||
readb(cd->cpld_base + CPLD_DESIGN_LO);
|
||||
snprintf(cd->version, sizeof(cd->version), "%c%d",
|
||||
'A' + ((status1 >> 5) & 0x7),
|
||||
(status1 >> 2) & 0x7);
|
||||
dev_info(dev, "design number %d, revision %s\n",
|
||||
cd->design_no,
|
||||
cd->version);
|
||||
cap = readb(cd->cpld_base + CPLD_CAP);
|
||||
if (!(cap & CPLD_CAP_COMPAT)) {
|
||||
dev_err(dev, "unsupported controller [cap=0x%02X]", cap);
|
||||
err = -ENODEV;
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
if (status1 & CPLD_STATUS1_AB) {
|
||||
dev_info(dev, "has anybus-S slot(s)");
|
||||
cd->common_reset = !(cap & CPLD_CAP_SEP_RESETS);
|
||||
dev_info(dev, "supports %s", cd->common_reset ?
|
||||
"a common reset" : "separate resets");
|
||||
for (id = 0; id < 2; id++) {
|
||||
host = create_anybus_host(pdev, id);
|
||||
if (!IS_ERR(host))
|
||||
continue;
|
||||
err = PTR_ERR(host);
|
||||
/* -ENODEV is fine, it just means no card detected */
|
||||
if (err != -ENODEV)
|
||||
goto out_reset;
|
||||
}
|
||||
}
|
||||
|
||||
id = ida_alloc(&controller_index_ida, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
err = id;
|
||||
goto out_reset;
|
||||
}
|
||||
/* export can power readout as a regulator */
|
||||
config.dev = dev;
|
||||
config.driver_data = cd;
|
||||
regulator = devm_regulator_register(dev, &can_power_desc, &config);
|
||||
if (IS_ERR(regulator)) {
|
||||
err = PTR_ERR(regulator);
|
||||
goto out_ida;
|
||||
}
|
||||
/* make controller info visible to userspace */
|
||||
cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL);
|
||||
if (!cd->class_dev) {
|
||||
err = -ENOMEM;
|
||||
goto out_ida;
|
||||
}
|
||||
cd->class_dev->class = &controller_class;
|
||||
cd->class_dev->groups = controller_attribute_groups;
|
||||
cd->class_dev->parent = dev;
|
||||
cd->class_dev->id = id;
|
||||
cd->class_dev->release = controller_device_release;
|
||||
dev_set_name(cd->class_dev, "%d", cd->class_dev->id);
|
||||
dev_set_drvdata(cd->class_dev, cd);
|
||||
err = device_register(cd->class_dev);
|
||||
if (err)
|
||||
goto out_dev;
|
||||
return 0;
|
||||
out_dev:
|
||||
put_device(cd->class_dev);
|
||||
out_ida:
|
||||
ida_free(&controller_index_ida, id);
|
||||
out_reset:
|
||||
gpiod_set_value_cansleep(cd->reset_gpiod, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void controller_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct controller_priv *cd = platform_get_drvdata(pdev);
|
||||
int id = cd->class_dev->id;
|
||||
|
||||
device_unregister(cd->class_dev);
|
||||
ida_free(&controller_index_ida, id);
|
||||
gpiod_set_value_cansleep(cd->reset_gpiod, 1);
|
||||
}
|
||||
|
||||
static const struct of_device_id controller_of_match[] = {
|
||||
{ .compatible = "arcx,anybus-controller" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, controller_of_match);
|
||||
|
||||
static struct platform_driver controller_driver = {
|
||||
.probe = controller_probe,
|
||||
.remove_new = controller_remove,
|
||||
.driver = {
|
||||
.name = "arcx-anybus-controller",
|
||||
.of_match_table = controller_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init controller_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = class_register(&controller_class);
|
||||
if (err)
|
||||
return err;
|
||||
err = platform_driver_register(&controller_driver);
|
||||
if (err)
|
||||
class_unregister(&controller_class);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit controller_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&controller_driver);
|
||||
class_unregister(&controller_class);
|
||||
ida_destroy(&controller_index_ida);
|
||||
}
|
||||
|
||||
module_init(controller_init);
|
||||
module_exit(controller_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Arcx Anybus-S Controller driver");
|
||||
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -1,224 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* HMS Profinet Client Driver
|
||||
*
|
||||
* Copyright (C) 2018 Arcx Inc
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* move to <linux/fieldbus_dev.h> when taking this out of staging */
|
||||
#include "../fieldbus_dev.h"
|
||||
|
||||
/* move to <linux/anybuss-client.h> when taking this out of staging */
|
||||
#include "anybuss-client.h"
|
||||
|
||||
#define PROFI_DPRAM_SIZE 512
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* Anybus Profinet mailbox messages - definitions
|
||||
* ---------------------------------------------------------------
|
||||
* note that we're depending on the layout of these structures being
|
||||
* exactly as advertised.
|
||||
*/
|
||||
|
||||
struct msg_mac_addr {
|
||||
u8 addr[6];
|
||||
};
|
||||
|
||||
struct profi_priv {
|
||||
struct fieldbus_dev fbdev;
|
||||
struct anybuss_client *client;
|
||||
struct mutex enable_lock; /* serializes card enable */
|
||||
bool power_on;
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
profi_read_area(struct fieldbus_dev *fbdev, char __user *buf, size_t size,
|
||||
loff_t *offset)
|
||||
{
|
||||
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
|
||||
|
||||
return anybuss_read_output(priv->client, buf, size, offset);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
profi_write_area(struct fieldbus_dev *fbdev, const char __user *buf,
|
||||
size_t size, loff_t *offset)
|
||||
{
|
||||
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
|
||||
|
||||
return anybuss_write_input(priv->client, buf, size, offset);
|
||||
}
|
||||
|
||||
static int profi_id_get(struct fieldbus_dev *fbdev, char *buf,
|
||||
size_t max_size)
|
||||
{
|
||||
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
|
||||
struct msg_mac_addr response;
|
||||
int ret;
|
||||
|
||||
ret = anybuss_recv_msg(priv->client, 0x0010, &response,
|
||||
sizeof(response));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return snprintf(buf, max_size, "%pM\n", response.addr);
|
||||
}
|
||||
|
||||
static bool profi_enable_get(struct fieldbus_dev *fbdev)
|
||||
{
|
||||
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
|
||||
bool power_on;
|
||||
|
||||
mutex_lock(&priv->enable_lock);
|
||||
power_on = priv->power_on;
|
||||
mutex_unlock(&priv->enable_lock);
|
||||
|
||||
return power_on;
|
||||
}
|
||||
|
||||
static int __profi_enable(struct profi_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
struct anybuss_client *client = priv->client;
|
||||
/* Initialization Sequence, Generic Anybus Mode */
|
||||
const struct anybuss_memcfg mem_cfg = {
|
||||
.input_io = 220,
|
||||
.input_dpram = PROFI_DPRAM_SIZE,
|
||||
.input_total = PROFI_DPRAM_SIZE,
|
||||
.output_io = 220,
|
||||
.output_dpram = PROFI_DPRAM_SIZE,
|
||||
.output_total = PROFI_DPRAM_SIZE,
|
||||
.offl_mode = FIELDBUS_DEV_OFFL_MODE_CLEAR,
|
||||
};
|
||||
|
||||
/*
|
||||
* switch anybus off then on, this ensures we can do a complete
|
||||
* configuration cycle in case anybus was already on.
|
||||
*/
|
||||
anybuss_set_power(client, false);
|
||||
ret = anybuss_set_power(client, true);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = anybuss_start_init(client, &mem_cfg);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = anybuss_finish_init(client);
|
||||
if (ret)
|
||||
goto err;
|
||||
priv->power_on = true;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
anybuss_set_power(client, false);
|
||||
priv->power_on = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __profi_disable(struct profi_priv *priv)
|
||||
{
|
||||
struct anybuss_client *client = priv->client;
|
||||
|
||||
anybuss_set_power(client, false);
|
||||
priv->power_on = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int profi_simple_enable(struct fieldbus_dev *fbdev, bool enable)
|
||||
{
|
||||
int ret;
|
||||
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
|
||||
|
||||
mutex_lock(&priv->enable_lock);
|
||||
if (enable)
|
||||
ret = __profi_enable(priv);
|
||||
else
|
||||
ret = __profi_disable(priv);
|
||||
mutex_unlock(&priv->enable_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void profi_on_area_updated(struct anybuss_client *client)
|
||||
{
|
||||
struct profi_priv *priv = anybuss_get_drvdata(client);
|
||||
|
||||
fieldbus_dev_area_updated(&priv->fbdev);
|
||||
}
|
||||
|
||||
static void profi_on_online_changed(struct anybuss_client *client, bool online)
|
||||
{
|
||||
struct profi_priv *priv = anybuss_get_drvdata(client);
|
||||
|
||||
fieldbus_dev_online_changed(&priv->fbdev, online);
|
||||
}
|
||||
|
||||
static int profinet_probe(struct anybuss_client *client)
|
||||
{
|
||||
struct profi_priv *priv;
|
||||
struct device *dev = &client->dev;
|
||||
int err;
|
||||
|
||||
client->on_area_updated = profi_on_area_updated;
|
||||
client->on_online_changed = profi_on_online_changed;
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
mutex_init(&priv->enable_lock);
|
||||
priv->client = client;
|
||||
priv->fbdev.read_area_sz = PROFI_DPRAM_SIZE;
|
||||
priv->fbdev.write_area_sz = PROFI_DPRAM_SIZE;
|
||||
priv->fbdev.card_name = "HMS Profinet IRT (Anybus-S)";
|
||||
priv->fbdev.fieldbus_type = FIELDBUS_DEV_TYPE_PROFINET;
|
||||
priv->fbdev.read_area = profi_read_area;
|
||||
priv->fbdev.write_area = profi_write_area;
|
||||
priv->fbdev.fieldbus_id_get = profi_id_get;
|
||||
priv->fbdev.enable_get = profi_enable_get;
|
||||
priv->fbdev.simple_enable_set = profi_simple_enable;
|
||||
priv->fbdev.parent = dev;
|
||||
err = fieldbus_dev_register(&priv->fbdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
dev_info(dev, "card detected, registered as %s",
|
||||
dev_name(priv->fbdev.dev));
|
||||
anybuss_set_drvdata(client, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void profinet_remove(struct anybuss_client *client)
|
||||
{
|
||||
struct profi_priv *priv = anybuss_get_drvdata(client);
|
||||
|
||||
fieldbus_dev_unregister(&priv->fbdev);
|
||||
}
|
||||
|
||||
static struct anybuss_client_driver profinet_driver = {
|
||||
.probe = profinet_probe,
|
||||
.remove = profinet_remove,
|
||||
.driver = {
|
||||
.name = "hms-profinet",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.anybus_id = 0x0089,
|
||||
};
|
||||
|
||||
static int __init profinet_init(void)
|
||||
{
|
||||
return anybuss_client_driver_register(&profinet_driver);
|
||||
}
|
||||
module_init(profinet_init);
|
||||
|
||||
static void __exit profinet_exit(void)
|
||||
{
|
||||
return anybuss_client_driver_unregister(&profinet_driver);
|
||||
}
|
||||
module_exit(profinet_exit);
|
||||
|
||||
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
|
||||
MODULE_DESCRIPTION("HMS Profinet IRT Driver (Anybus-S)");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,344 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Fieldbus Device Driver Core
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
/* move to <linux/fieldbus_dev.h> when taking this out of staging */
|
||||
#include "fieldbus_dev.h"
|
||||
|
||||
/* Maximum number of fieldbus devices */
|
||||
#define MAX_FIELDBUSES 32
|
||||
|
||||
/* the dev_t structure to store the dynamically allocated fieldbus devices */
|
||||
static dev_t fieldbus_devt;
|
||||
static DEFINE_IDA(fieldbus_ida);
|
||||
static DEFINE_MUTEX(fieldbus_mtx);
|
||||
|
||||
static ssize_t online_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", !!fb->online);
|
||||
}
|
||||
static DEVICE_ATTR_RO(online);
|
||||
|
||||
static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
|
||||
if (!fb->enable_get)
|
||||
return -EINVAL;
|
||||
return sysfs_emit(buf, "%d\n", !!fb->enable_get(fb));
|
||||
}
|
||||
|
||||
static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
bool value;
|
||||
int ret;
|
||||
|
||||
if (!fb->simple_enable_set)
|
||||
return -ENOTSUPP;
|
||||
ret = kstrtobool(buf, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = fb->simple_enable_set(fb, value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR_RW(enabled);
|
||||
|
||||
static ssize_t card_name_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
|
||||
/* card_name was provided by child driver. */
|
||||
return sysfs_emit(buf, "%s\n", fb->card_name);
|
||||
}
|
||||
static DEVICE_ATTR_RO(card_name);
|
||||
|
||||
static ssize_t read_area_size_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%zu\n", fb->read_area_sz);
|
||||
}
|
||||
static DEVICE_ATTR_RO(read_area_size);
|
||||
|
||||
static ssize_t write_area_size_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%zu\n", fb->write_area_sz);
|
||||
}
|
||||
static DEVICE_ATTR_RO(write_area_size);
|
||||
|
||||
static ssize_t fieldbus_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
|
||||
return fb->fieldbus_id_get(fb, buf, PAGE_SIZE);
|
||||
}
|
||||
static DEVICE_ATTR_RO(fieldbus_id);
|
||||
|
||||
static ssize_t fieldbus_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
const char *t;
|
||||
|
||||
switch (fb->fieldbus_type) {
|
||||
case FIELDBUS_DEV_TYPE_PROFINET:
|
||||
t = "profinet";
|
||||
break;
|
||||
default:
|
||||
t = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%s\n", t);
|
||||
}
|
||||
static DEVICE_ATTR_RO(fieldbus_type);
|
||||
|
||||
static struct attribute *fieldbus_attrs[] = {
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_card_name.attr,
|
||||
&dev_attr_fieldbus_id.attr,
|
||||
&dev_attr_read_area_size.attr,
|
||||
&dev_attr_write_area_size.attr,
|
||||
&dev_attr_online.attr,
|
||||
&dev_attr_fieldbus_type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
int n)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct fieldbus_dev *fb = dev_get_drvdata(dev);
|
||||
umode_t mode = attr->mode;
|
||||
|
||||
if (attr == &dev_attr_enabled.attr) {
|
||||
mode = 0;
|
||||
if (fb->enable_get)
|
||||
mode |= 0444;
|
||||
if (fb->simple_enable_set)
|
||||
mode |= 0200;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group fieldbus_group = {
|
||||
.attrs = fieldbus_attrs,
|
||||
.is_visible = fieldbus_is_visible,
|
||||
};
|
||||
__ATTRIBUTE_GROUPS(fieldbus);
|
||||
|
||||
static const struct class fieldbus_class = {
|
||||
.name = "fieldbus_dev",
|
||||
.dev_groups = fieldbus_groups,
|
||||
};
|
||||
|
||||
struct fb_open_file {
|
||||
struct fieldbus_dev *fbdev;
|
||||
int dc_event;
|
||||
};
|
||||
|
||||
static int fieldbus_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct fb_open_file *of;
|
||||
struct fieldbus_dev *fbdev = container_of(inode->i_cdev,
|
||||
struct fieldbus_dev,
|
||||
cdev);
|
||||
|
||||
of = kzalloc(sizeof(*of), GFP_KERNEL);
|
||||
if (!of)
|
||||
return -ENOMEM;
|
||||
of->fbdev = fbdev;
|
||||
filp->private_data = of;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fieldbus_release(struct inode *node, struct file *filp)
|
||||
{
|
||||
struct fb_open_file *of = filp->private_data;
|
||||
|
||||
kfree(of);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size,
|
||||
loff_t *offset)
|
||||
{
|
||||
struct fb_open_file *of = filp->private_data;
|
||||
struct fieldbus_dev *fbdev = of->fbdev;
|
||||
|
||||
of->dc_event = fbdev->dc_event;
|
||||
return fbdev->read_area(fbdev, buf, size, offset);
|
||||
}
|
||||
|
||||
static ssize_t fieldbus_write(struct file *filp, const char __user *buf,
|
||||
size_t size, loff_t *offset)
|
||||
{
|
||||
struct fb_open_file *of = filp->private_data;
|
||||
struct fieldbus_dev *fbdev = of->fbdev;
|
||||
|
||||
return fbdev->write_area(fbdev, buf, size, offset);
|
||||
}
|
||||
|
||||
static __poll_t fieldbus_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
struct fb_open_file *of = filp->private_data;
|
||||
struct fieldbus_dev *fbdev = of->fbdev;
|
||||
__poll_t mask = EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM;
|
||||
|
||||
poll_wait(filp, &fbdev->dc_wq, wait);
|
||||
/* data changed ? */
|
||||
if (fbdev->dc_event != of->dc_event)
|
||||
mask |= EPOLLPRI | EPOLLERR;
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations fieldbus_fops = {
|
||||
.open = fieldbus_open,
|
||||
.release = fieldbus_release,
|
||||
.read = fieldbus_read,
|
||||
.write = fieldbus_write,
|
||||
.poll = fieldbus_poll,
|
||||
.llseek = generic_file_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
|
||||
{
|
||||
fb->dc_event++;
|
||||
wake_up_all(&fb->dc_wq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
|
||||
|
||||
void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
|
||||
{
|
||||
fb->online = online;
|
||||
kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
|
||||
|
||||
static void __fieldbus_dev_unregister(struct fieldbus_dev *fb)
|
||||
{
|
||||
if (!fb)
|
||||
return;
|
||||
device_destroy(&fieldbus_class, fb->cdev.dev);
|
||||
cdev_del(&fb->cdev);
|
||||
ida_free(&fieldbus_ida, fb->id);
|
||||
}
|
||||
|
||||
void fieldbus_dev_unregister(struct fieldbus_dev *fb)
|
||||
{
|
||||
mutex_lock(&fieldbus_mtx);
|
||||
__fieldbus_dev_unregister(fb);
|
||||
mutex_unlock(&fieldbus_mtx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
|
||||
|
||||
static int __fieldbus_dev_register(struct fieldbus_dev *fb)
|
||||
{
|
||||
dev_t devno;
|
||||
int err;
|
||||
|
||||
if (!fb)
|
||||
return -EINVAL;
|
||||
if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get)
|
||||
return -EINVAL;
|
||||
fb->id = ida_alloc_max(&fieldbus_ida, MAX_FIELDBUSES - 1, GFP_KERNEL);
|
||||
if (fb->id < 0)
|
||||
return fb->id;
|
||||
devno = MKDEV(MAJOR(fieldbus_devt), fb->id);
|
||||
init_waitqueue_head(&fb->dc_wq);
|
||||
cdev_init(&fb->cdev, &fieldbus_fops);
|
||||
err = cdev_add(&fb->cdev, devno, 1);
|
||||
if (err) {
|
||||
pr_err("fieldbus_dev%d unable to add device %d:%d\n",
|
||||
fb->id, MAJOR(fieldbus_devt), fb->id);
|
||||
goto err_cdev;
|
||||
}
|
||||
fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb,
|
||||
"fieldbus_dev%d", fb->id);
|
||||
if (IS_ERR(fb->dev)) {
|
||||
err = PTR_ERR(fb->dev);
|
||||
goto err_dev_create;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_dev_create:
|
||||
cdev_del(&fb->cdev);
|
||||
err_cdev:
|
||||
ida_free(&fieldbus_ida, fb->id);
|
||||
return err;
|
||||
}
|
||||
|
||||
int fieldbus_dev_register(struct fieldbus_dev *fb)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&fieldbus_mtx);
|
||||
err = __fieldbus_dev_register(fb);
|
||||
mutex_unlock(&fieldbus_mtx);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fieldbus_dev_register);
|
||||
|
||||
static int __init fieldbus_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = class_register(&fieldbus_class);
|
||||
if (err < 0) {
|
||||
pr_err("fieldbus_dev: could not register class\n");
|
||||
return err;
|
||||
}
|
||||
err = alloc_chrdev_region(&fieldbus_devt, 0,
|
||||
MAX_FIELDBUSES, "fieldbus_dev");
|
||||
if (err < 0) {
|
||||
pr_err("fieldbus_dev: unable to allocate char dev region\n");
|
||||
goto err_alloc;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_alloc:
|
||||
class_unregister(&fieldbus_class);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit fieldbus_exit(void)
|
||||
{
|
||||
unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES);
|
||||
class_unregister(&fieldbus_class);
|
||||
ida_destroy(&fieldbus_ida);
|
||||
}
|
||||
|
||||
subsys_initcall(fieldbus_init);
|
||||
module_exit(fieldbus_exit);
|
||||
|
||||
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
|
||||
MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
|
||||
MODULE_DESCRIPTION("Fieldbus Device Driver Core");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -1,114 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Fieldbus Device Driver Core
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __FIELDBUS_DEV_H
|
||||
#define __FIELDBUS_DEV_H
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
enum fieldbus_dev_type {
|
||||
FIELDBUS_DEV_TYPE_UNKNOWN = 0,
|
||||
FIELDBUS_DEV_TYPE_PROFINET,
|
||||
};
|
||||
|
||||
enum fieldbus_dev_offl_mode {
|
||||
FIELDBUS_DEV_OFFL_MODE_CLEAR = 0,
|
||||
FIELDBUS_DEV_OFFL_MODE_FREEZE,
|
||||
FIELDBUS_DEV_OFFL_MODE_SET
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fieldbus_dev - Fieldbus device
|
||||
* @read_area: [DRIVER] function to read the process data area of the
|
||||
* device. same parameters/return values as
|
||||
* the read function in struct file_operations
|
||||
* @write_area: [DRIVER] function to write to the process data area of
|
||||
* the device. same parameters/return values as
|
||||
* the write function in struct file_operations
|
||||
* @write_area_sz [DRIVER] size of the writable process data area
|
||||
* @read_area_sz [DRIVER] size of the readable process data area
|
||||
* @card_name [DRIVER] name of the card, e.g. "ACME Inc. profinet"
|
||||
* @fieldbus_type [DRIVER] fieldbus type of this device, e.g.
|
||||
* FIELDBUS_DEV_TYPE_PROFINET
|
||||
* @enable_get [DRIVER] function which returns true if the card
|
||||
* is enabled, false otherwise
|
||||
* @fieldbus_id_get [DRIVER] function to retrieve the unique fieldbus id
|
||||
* by which this device can be identified;
|
||||
* return value follows the snprintf convention
|
||||
* @simple_enable_set [DRIVER] (optional) function to enable the device
|
||||
* according to its default settings
|
||||
* @parent [DRIVER] (optional) the device's parent device
|
||||
*/
|
||||
struct fieldbus_dev {
|
||||
ssize_t (*read_area)(struct fieldbus_dev *fbdev, char __user *buf,
|
||||
size_t size, loff_t *offset);
|
||||
ssize_t (*write_area)(struct fieldbus_dev *fbdev,
|
||||
const char __user *buf, size_t size,
|
||||
loff_t *offset);
|
||||
size_t write_area_sz, read_area_sz;
|
||||
const char *card_name;
|
||||
enum fieldbus_dev_type fieldbus_type;
|
||||
bool (*enable_get)(struct fieldbus_dev *fbdev);
|
||||
int (*fieldbus_id_get)(struct fieldbus_dev *fbdev, char *buf,
|
||||
size_t max_size);
|
||||
int (*simple_enable_set)(struct fieldbus_dev *fbdev, bool enable);
|
||||
struct device *parent;
|
||||
|
||||
/* private data */
|
||||
int id;
|
||||
struct cdev cdev;
|
||||
struct device *dev;
|
||||
int dc_event;
|
||||
wait_queue_head_t dc_wq;
|
||||
bool online;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_FIELDBUS_DEV)
|
||||
|
||||
/**
|
||||
* fieldbus_dev_unregister()
|
||||
* - unregister a previously registered fieldbus device
|
||||
* @fb: Device structure previously registered
|
||||
**/
|
||||
void fieldbus_dev_unregister(struct fieldbus_dev *fb);
|
||||
|
||||
/**
|
||||
* fieldbus_dev_register()
|
||||
* - register a device with the fieldbus device subsystem
|
||||
* @fb: Device structure filled by the device driver
|
||||
**/
|
||||
int __must_check fieldbus_dev_register(struct fieldbus_dev *fb);
|
||||
|
||||
/**
|
||||
* fieldbus_dev_area_updated()
|
||||
* - notify the subsystem that an external fieldbus controller updated
|
||||
* the process data area
|
||||
* @fb: Device structure
|
||||
**/
|
||||
void fieldbus_dev_area_updated(struct fieldbus_dev *fb);
|
||||
|
||||
/**
|
||||
* fieldbus_dev_online_changed()
|
||||
* - notify the subsystem that the fieldbus online status changed
|
||||
* @fb: Device structure
|
||||
**/
|
||||
void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online);
|
||||
|
||||
#else /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
|
||||
|
||||
static inline void fieldbus_dev_unregister(struct fieldbus_dev *fb) {}
|
||||
static inline int __must_check fieldbus_dev_register(struct fieldbus_dev *fb)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fieldbus_dev_area_updated(struct fieldbus_dev *fb) {}
|
||||
static inline void fieldbus_dev_online_changed(struct fieldbus_dev *fb,
|
||||
bool online) {}
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
|
||||
#endif /* __FIELDBUS_DEV_H */
|
||||
@@ -1,16 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# GCT GDM724x LTE driver configuration
|
||||
#
|
||||
|
||||
config LTE_GDM724X
|
||||
tristate "GCT GDM724x LTE support"
|
||||
depends on NET && USB && TTY && m
|
||||
help
|
||||
This driver supports GCT GDM724x LTE chip based USB modem devices.
|
||||
It exposes 4 network devices to be used per PDN and 2 tty devices to be
|
||||
used for AT commands and DM monitoring applications.
|
||||
The modules will be called gdmulte.ko and gdmtty.ko
|
||||
|
||||
GCT-ATCx can be used for AT Commands
|
||||
GCT-DMx can be used for LTE protocol monitoring
|
||||
@@ -1,8 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_LTE_GDM724X) := gdmulte.o
|
||||
gdmulte-y += gdm_lte.o netlink_k.o
|
||||
gdmulte-y += gdm_usb.o gdm_endian.o
|
||||
|
||||
obj-$(CONFIG_LTE_GDM724X) += gdmtty.o
|
||||
gdmtty-y := gdm_tty.o gdm_mux.o
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
TODO:
|
||||
- Clean up coding style to meet kernel standard. (80 line limit, netdev_err)
|
||||
- Remove test for host endian
|
||||
- Remove confusing macros (endian, hci_send, sdu_send, rcv_with_cb)
|
||||
- Fixes for every instances of function returning -1
|
||||
- Check for skb->len in gdm_lte_emulate_arp()
|
||||
- Use ALIGN() macro for dummy_cnt in up_to_host()
|
||||
- Error handling in init_usb()
|
||||
- Explain reason for multiples of 512 bytes in alloc_tx_struct()
|
||||
- Review use of atomic allocation for tx structs
|
||||
- No error checking for alloc_tx_struct in do_tx()
|
||||
- fix up static tty port allocation to be dynamic
|
||||
|
||||
Patches to:
|
||||
Jonathan Kim <jonathankim@gctsemi.com>
|
||||
Dean ahn <deanahn@gctsemi.com>
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "gdm_endian.h"
|
||||
|
||||
__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x)
|
||||
{
|
||||
if (dev_ed == ENDIANNESS_LITTLE)
|
||||
return (__force __dev16)cpu_to_le16(x);
|
||||
else
|
||||
return (__force __dev16)cpu_to_be16(x);
|
||||
}
|
||||
|
||||
u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x)
|
||||
{
|
||||
if (dev_ed == ENDIANNESS_LITTLE)
|
||||
return le16_to_cpu((__force __le16)x);
|
||||
else
|
||||
return be16_to_cpu((__force __be16)x);
|
||||
}
|
||||
|
||||
__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x)
|
||||
{
|
||||
if (dev_ed == ENDIANNESS_LITTLE)
|
||||
return (__force __dev32)cpu_to_le32(x);
|
||||
else
|
||||
return (__force __dev32)cpu_to_be32(x);
|
||||
}
|
||||
|
||||
u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x)
|
||||
{
|
||||
if (dev_ed == ENDIANNESS_LITTLE)
|
||||
return le32_to_cpu((__force __le32)x);
|
||||
else
|
||||
return be32_to_cpu((__force __be32)x);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef __GDM_ENDIAN_H__
|
||||
#define __GDM_ENDIAN_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* For data in "device-endian" byte order (device endianness is model
|
||||
* dependent). Analogous to __leXX or __beXX.
|
||||
*/
|
||||
typedef __u32 __bitwise __dev32;
|
||||
typedef __u16 __bitwise __dev16;
|
||||
|
||||
enum {
|
||||
ENDIANNESS_MIN = 0,
|
||||
ENDIANNESS_UNKNOWN,
|
||||
ENDIANNESS_LITTLE,
|
||||
ENDIANNESS_BIG,
|
||||
ENDIANNESS_MIDDLE,
|
||||
ENDIANNESS_MAX
|
||||
};
|
||||
|
||||
__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x);
|
||||
u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x);
|
||||
__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x);
|
||||
u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x);
|
||||
|
||||
#endif /*__GDM_ENDIAN_H__*/
|
||||
@@ -1,937 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/errno.h>
|
||||
#include <net/ndisc.h>
|
||||
|
||||
#include "gdm_lte.h"
|
||||
#include "netlink_k.h"
|
||||
#include "hci.h"
|
||||
#include "hci_packet.h"
|
||||
#include "gdm_endian.h"
|
||||
|
||||
/*
|
||||
* Netlink protocol number
|
||||
*/
|
||||
#define NETLINK_LTE 30
|
||||
|
||||
/*
|
||||
* Default MTU Size
|
||||
*/
|
||||
#define DEFAULT_MTU_SIZE 1500
|
||||
|
||||
#define IP_VERSION_4 4
|
||||
#define IP_VERSION_6 6
|
||||
|
||||
static struct {
|
||||
int ref_cnt;
|
||||
struct sock *sock;
|
||||
} lte_event;
|
||||
|
||||
static const struct device_type wwan_type = {
|
||||
.name = "wwan",
|
||||
};
|
||||
|
||||
static int gdm_lte_open(struct net_device *dev)
|
||||
{
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_lte_close(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_lte_set_config(struct net_device *dev, struct ifmap *map)
|
||||
{
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tx_complete(void *arg)
|
||||
{
|
||||
struct nic *nic = arg;
|
||||
|
||||
if (netif_queue_stopped(nic->netdev))
|
||||
netif_wake_queue(nic->netdev);
|
||||
}
|
||||
|
||||
static int gdm_lte_rx(struct sk_buff *skb, struct nic *nic, int nic_type)
|
||||
{
|
||||
int ret, len;
|
||||
|
||||
len = skb->len + ETH_HLEN;
|
||||
ret = netif_rx(skb);
|
||||
if (ret == NET_RX_DROP) {
|
||||
nic->stats.rx_dropped++;
|
||||
} else {
|
||||
nic->stats.rx_packets++;
|
||||
nic->stats.rx_bytes += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_lte_emulate_arp(struct sk_buff *skb_in, u32 nic_type)
|
||||
{
|
||||
struct nic *nic = netdev_priv(skb_in->dev);
|
||||
struct sk_buff *skb_out;
|
||||
struct ethhdr eth;
|
||||
struct vlan_ethhdr vlan_eth;
|
||||
struct arphdr *arp_in;
|
||||
struct arphdr *arp_out;
|
||||
struct arpdata {
|
||||
u8 ar_sha[ETH_ALEN];
|
||||
u8 ar_sip[4];
|
||||
u8 ar_tha[ETH_ALEN];
|
||||
u8 ar_tip[4];
|
||||
};
|
||||
struct arpdata *arp_data_in;
|
||||
struct arpdata *arp_data_out;
|
||||
u8 arp_temp[60];
|
||||
void *mac_header_data;
|
||||
u32 mac_header_len;
|
||||
|
||||
/* Check for skb->len, discard if empty */
|
||||
if (skb_in->len == 0)
|
||||
return -ENODATA;
|
||||
|
||||
/* Format the mac header so that it can be put to skb */
|
||||
if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) {
|
||||
memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr));
|
||||
mac_header_data = &vlan_eth;
|
||||
mac_header_len = VLAN_ETH_HLEN;
|
||||
} else {
|
||||
memcpy(ð, skb_in->data, sizeof(struct ethhdr));
|
||||
mac_header_data = ð
|
||||
mac_header_len = ETH_HLEN;
|
||||
}
|
||||
|
||||
/* Get the pointer of the original request */
|
||||
arp_in = (struct arphdr *)(skb_in->data + mac_header_len);
|
||||
arp_data_in = (struct arpdata *)(skb_in->data + mac_header_len +
|
||||
sizeof(struct arphdr));
|
||||
|
||||
/* Get the pointer of the outgoing response */
|
||||
arp_out = (struct arphdr *)arp_temp;
|
||||
arp_data_out = (struct arpdata *)(arp_temp + sizeof(struct arphdr));
|
||||
|
||||
/* Copy the arp header */
|
||||
memcpy(arp_out, arp_in, sizeof(struct arphdr));
|
||||
arp_out->ar_op = htons(ARPOP_REPLY);
|
||||
|
||||
/* Copy the arp payload: based on 2 bytes of mac and fill the IP */
|
||||
arp_data_out->ar_sha[0] = arp_data_in->ar_sha[0];
|
||||
arp_data_out->ar_sha[1] = arp_data_in->ar_sha[1];
|
||||
memcpy(&arp_data_out->ar_sha[2], &arp_data_in->ar_tip[0], 4);
|
||||
memcpy(&arp_data_out->ar_sip[0], &arp_data_in->ar_tip[0], 4);
|
||||
memcpy(&arp_data_out->ar_tha[0], &arp_data_in->ar_sha[0], 6);
|
||||
memcpy(&arp_data_out->ar_tip[0], &arp_data_in->ar_sip[0], 4);
|
||||
|
||||
/* Fill the destination mac with source mac of the received packet */
|
||||
memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN);
|
||||
/* Fill the source mac with nic's source mac */
|
||||
memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
|
||||
|
||||
/* Alloc skb and reserve align */
|
||||
skb_out = dev_alloc_skb(skb_in->len);
|
||||
if (!skb_out)
|
||||
return -ENOMEM;
|
||||
skb_reserve(skb_out, NET_IP_ALIGN);
|
||||
|
||||
skb_put_data(skb_out, mac_header_data, mac_header_len);
|
||||
skb_put_data(skb_out, arp_out, sizeof(struct arphdr));
|
||||
skb_put_data(skb_out, arp_data_out, sizeof(struct arpdata));
|
||||
|
||||
skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
|
||||
skb_out->dev = skb_in->dev;
|
||||
skb_reset_mac_header(skb_out);
|
||||
skb_pull(skb_out, ETH_HLEN);
|
||||
|
||||
gdm_lte_rx(skb_out, nic, nic_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __sum16 icmp6_checksum(struct ipv6hdr *ipv6, u16 *ptr, int len)
|
||||
{
|
||||
unsigned short *w;
|
||||
__wsum sum = 0;
|
||||
int i;
|
||||
u16 pa;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u8 ph_src[16];
|
||||
u8 ph_dst[16];
|
||||
u32 ph_len;
|
||||
u8 ph_zero[3];
|
||||
u8 ph_nxt;
|
||||
} ph __packed;
|
||||
u16 pa[20];
|
||||
} pseudo_header;
|
||||
|
||||
memset(&pseudo_header, 0, sizeof(pseudo_header));
|
||||
memcpy(&pseudo_header.ph.ph_src, &ipv6->saddr.in6_u.u6_addr8, 16);
|
||||
memcpy(&pseudo_header.ph.ph_dst, &ipv6->daddr.in6_u.u6_addr8, 16);
|
||||
pseudo_header.ph.ph_len = be16_to_cpu(ipv6->payload_len);
|
||||
pseudo_header.ph.ph_nxt = ipv6->nexthdr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pseudo_header.pa); i++) {
|
||||
pa = pseudo_header.pa[i];
|
||||
sum = csum_add(sum, csum_unfold((__force __sum16)pa));
|
||||
}
|
||||
|
||||
w = ptr;
|
||||
while (len > 1) {
|
||||
sum = csum_add(sum, csum_unfold((__force __sum16)*w++));
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
return csum_fold(sum);
|
||||
}
|
||||
|
||||
static int gdm_lte_emulate_ndp(struct sk_buff *skb_in, u32 nic_type)
|
||||
{
|
||||
struct nic *nic = netdev_priv(skb_in->dev);
|
||||
struct sk_buff *skb_out;
|
||||
struct ethhdr eth;
|
||||
struct vlan_ethhdr vlan_eth;
|
||||
struct neighbour_advertisement {
|
||||
u8 target_address[16];
|
||||
u8 type;
|
||||
u8 length;
|
||||
u8 link_layer_address[6];
|
||||
};
|
||||
struct neighbour_advertisement na;
|
||||
struct neighbour_solicitation {
|
||||
u8 target_address[16];
|
||||
};
|
||||
struct neighbour_solicitation *ns;
|
||||
struct ipv6hdr *ipv6_in;
|
||||
struct ipv6hdr ipv6_out;
|
||||
struct icmp6hdr *icmp6_in;
|
||||
struct icmp6hdr icmp6_out;
|
||||
|
||||
void *mac_header_data;
|
||||
u32 mac_header_len;
|
||||
|
||||
/* Format the mac header so that it can be put to skb */
|
||||
if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) {
|
||||
memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr));
|
||||
if (ntohs(vlan_eth.h_vlan_encapsulated_proto) != ETH_P_IPV6)
|
||||
return -EPROTONOSUPPORT;
|
||||
mac_header_data = &vlan_eth;
|
||||
mac_header_len = VLAN_ETH_HLEN;
|
||||
} else {
|
||||
memcpy(ð, skb_in->data, sizeof(struct ethhdr));
|
||||
if (ntohs(eth.h_proto) != ETH_P_IPV6)
|
||||
return -EPROTONOSUPPORT;
|
||||
mac_header_data = ð
|
||||
mac_header_len = ETH_HLEN;
|
||||
}
|
||||
|
||||
/* Check if this is IPv6 ICMP packet */
|
||||
ipv6_in = (struct ipv6hdr *)(skb_in->data + mac_header_len);
|
||||
if (ipv6_in->version != 6 || ipv6_in->nexthdr != IPPROTO_ICMPV6)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
/* Check if this is NDP packet */
|
||||
icmp6_in = (struct icmp6hdr *)(skb_in->data + mac_header_len +
|
||||
sizeof(struct ipv6hdr));
|
||||
if (icmp6_in->icmp6_type == NDISC_ROUTER_SOLICITATION) { /* Check RS */
|
||||
return -EPROTONOSUPPORT;
|
||||
} else if (icmp6_in->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
|
||||
/* Check NS */
|
||||
u8 icmp_na[sizeof(struct icmp6hdr) +
|
||||
sizeof(struct neighbour_advertisement)];
|
||||
u8 zero_addr8[16] = {0,};
|
||||
|
||||
if (memcmp(ipv6_in->saddr.in6_u.u6_addr8, zero_addr8, 16) == 0)
|
||||
/* Duplicate Address Detection: Source IP is all zero */
|
||||
return 0;
|
||||
|
||||
icmp6_out.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
|
||||
icmp6_out.icmp6_code = 0;
|
||||
icmp6_out.icmp6_cksum = 0;
|
||||
/* R=0, S=1, O=1 */
|
||||
icmp6_out.icmp6_dataun.un_data32[0] = htonl(0x60000000);
|
||||
|
||||
ns = (struct neighbour_solicitation *)
|
||||
(skb_in->data + mac_header_len +
|
||||
sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr));
|
||||
memcpy(&na.target_address, ns->target_address, 16);
|
||||
na.type = 0x02;
|
||||
na.length = 1;
|
||||
na.link_layer_address[0] = 0x00;
|
||||
na.link_layer_address[1] = 0x0a;
|
||||
na.link_layer_address[2] = 0x3b;
|
||||
na.link_layer_address[3] = 0xaf;
|
||||
na.link_layer_address[4] = 0x63;
|
||||
na.link_layer_address[5] = 0xc7;
|
||||
|
||||
memcpy(&ipv6_out, ipv6_in, sizeof(struct ipv6hdr));
|
||||
memcpy(ipv6_out.saddr.in6_u.u6_addr8, &na.target_address, 16);
|
||||
memcpy(ipv6_out.daddr.in6_u.u6_addr8,
|
||||
ipv6_in->saddr.in6_u.u6_addr8, 16);
|
||||
ipv6_out.payload_len = htons(sizeof(struct icmp6hdr) +
|
||||
sizeof(struct neighbour_advertisement));
|
||||
|
||||
memcpy(icmp_na, &icmp6_out, sizeof(struct icmp6hdr));
|
||||
memcpy(icmp_na + sizeof(struct icmp6hdr), &na,
|
||||
sizeof(struct neighbour_advertisement));
|
||||
|
||||
icmp6_out.icmp6_cksum = icmp6_checksum(&ipv6_out,
|
||||
(u16 *)icmp_na,
|
||||
sizeof(icmp_na));
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Fill the destination mac with source mac of the received packet */
|
||||
memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN);
|
||||
/* Fill the source mac with nic's source mac */
|
||||
memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
|
||||
|
||||
/* Alloc skb and reserve align */
|
||||
skb_out = dev_alloc_skb(skb_in->len);
|
||||
if (!skb_out)
|
||||
return -ENOMEM;
|
||||
skb_reserve(skb_out, NET_IP_ALIGN);
|
||||
|
||||
skb_put_data(skb_out, mac_header_data, mac_header_len);
|
||||
skb_put_data(skb_out, &ipv6_out, sizeof(struct ipv6hdr));
|
||||
skb_put_data(skb_out, &icmp6_out, sizeof(struct icmp6hdr));
|
||||
skb_put_data(skb_out, &na, sizeof(struct neighbour_advertisement));
|
||||
|
||||
skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
|
||||
skb_out->dev = skb_in->dev;
|
||||
skb_reset_mac_header(skb_out);
|
||||
skb_pull(skb_out, ETH_HLEN);
|
||||
|
||||
gdm_lte_rx(skb_out, nic, nic_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32 gdm_lte_tx_nic_type(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
struct ethhdr *eth;
|
||||
struct vlan_ethhdr *vlan_eth;
|
||||
struct iphdr *ip;
|
||||
struct ipv6hdr *ipv6;
|
||||
int mac_proto;
|
||||
void *network_data;
|
||||
u32 nic_type;
|
||||
|
||||
/* NIC TYPE is based on the nic_id of this net_device */
|
||||
nic_type = 0x00000010 | nic->nic_id;
|
||||
|
||||
/* Get ethernet protocol */
|
||||
eth = (struct ethhdr *)skb->data;
|
||||
if (ntohs(eth->h_proto) == ETH_P_8021Q) {
|
||||
vlan_eth = skb_vlan_eth_hdr(skb);
|
||||
mac_proto = ntohs(vlan_eth->h_vlan_encapsulated_proto);
|
||||
network_data = skb->data + VLAN_ETH_HLEN;
|
||||
nic_type |= NIC_TYPE_F_VLAN;
|
||||
} else {
|
||||
mac_proto = ntohs(eth->h_proto);
|
||||
network_data = skb->data + ETH_HLEN;
|
||||
}
|
||||
|
||||
/* Process packet for nic type */
|
||||
switch (mac_proto) {
|
||||
case ETH_P_ARP:
|
||||
nic_type |= NIC_TYPE_ARP;
|
||||
break;
|
||||
case ETH_P_IP:
|
||||
nic_type |= NIC_TYPE_F_IPV4;
|
||||
ip = network_data;
|
||||
|
||||
/* Check DHCPv4 */
|
||||
if (ip->protocol == IPPROTO_UDP) {
|
||||
struct udphdr *udp =
|
||||
network_data + sizeof(struct iphdr);
|
||||
if (ntohs(udp->dest) == 67 || ntohs(udp->dest) == 68)
|
||||
nic_type |= NIC_TYPE_F_DHCP;
|
||||
}
|
||||
break;
|
||||
case ETH_P_IPV6:
|
||||
nic_type |= NIC_TYPE_F_IPV6;
|
||||
ipv6 = network_data;
|
||||
|
||||
if (ipv6->nexthdr == IPPROTO_ICMPV6) /* Check NDP request */ {
|
||||
struct icmp6hdr *icmp6 =
|
||||
network_data + sizeof(struct ipv6hdr);
|
||||
if (icmp6->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
|
||||
nic_type |= NIC_TYPE_ICMPV6;
|
||||
} else if (ipv6->nexthdr == IPPROTO_UDP) /* Check DHCPv6 */ {
|
||||
struct udphdr *udp =
|
||||
network_data + sizeof(struct ipv6hdr);
|
||||
if (ntohs(udp->dest) == 546 || ntohs(udp->dest) == 547)
|
||||
nic_type |= NIC_TYPE_F_DHCP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nic_type;
|
||||
}
|
||||
|
||||
static netdev_tx_t gdm_lte_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
u32 nic_type;
|
||||
void *data_buf;
|
||||
int data_len;
|
||||
int idx;
|
||||
int ret = 0;
|
||||
|
||||
nic_type = gdm_lte_tx_nic_type(dev, skb);
|
||||
if (nic_type == 0) {
|
||||
netdev_err(dev, "tx - invalid nic_type\n");
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
if (nic_type & NIC_TYPE_ARP) {
|
||||
if (gdm_lte_emulate_arp(skb, nic_type) == 0) {
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (nic_type & NIC_TYPE_ICMPV6) {
|
||||
if (gdm_lte_emulate_ndp(skb, nic_type) == 0) {
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Need byte shift (that is, remove VLAN tag) if there is one
|
||||
* For the case of ARP, this breaks the offset as vlan_ethhdr+4
|
||||
* is treated as ethhdr However, it shouldn't be a problem as
|
||||
* the response starts from arp_hdr and ethhdr is created by this
|
||||
* driver based on the NIC mac
|
||||
*/
|
||||
if (nic_type & NIC_TYPE_F_VLAN) {
|
||||
struct vlan_ethhdr *vlan_eth = skb_vlan_eth_hdr(skb);
|
||||
|
||||
nic->vlan_id = ntohs(vlan_eth->h_vlan_TCI) & VLAN_VID_MASK;
|
||||
data_buf = skb->data + (VLAN_ETH_HLEN - ETH_HLEN);
|
||||
data_len = skb->len - (VLAN_ETH_HLEN - ETH_HLEN);
|
||||
} else {
|
||||
nic->vlan_id = 0;
|
||||
data_buf = skb->data;
|
||||
data_len = skb->len;
|
||||
}
|
||||
|
||||
/* If it is a ICMPV6 packet, clear all the other bits :
|
||||
* for backward compatibility with the firmware
|
||||
*/
|
||||
if (nic_type & NIC_TYPE_ICMPV6)
|
||||
nic_type = NIC_TYPE_ICMPV6;
|
||||
|
||||
/* If it is not a dhcp packet, clear all the flag bits :
|
||||
* original NIC, otherwise the special flag (IPVX | DHCP)
|
||||
*/
|
||||
if (!(nic_type & NIC_TYPE_F_DHCP))
|
||||
nic_type &= NIC_TYPE_MASK;
|
||||
|
||||
ret = sscanf(dev->name, "lte%d", &idx);
|
||||
if (ret != 1) {
|
||||
dev_kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = nic->phy_dev->send_sdu_func(nic->phy_dev->priv_dev,
|
||||
data_buf, data_len,
|
||||
nic->pdn_table.dft_eps_id, 0,
|
||||
tx_complete, nic, idx,
|
||||
nic_type);
|
||||
|
||||
if (ret == TX_NO_BUFFER || ret == TX_NO_SPC) {
|
||||
netif_stop_queue(dev);
|
||||
if (ret == TX_NO_BUFFER)
|
||||
ret = 0;
|
||||
else
|
||||
ret = -ENOSPC;
|
||||
} else if (ret == TX_NO_DEV) {
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
/* Updates tx stats */
|
||||
if (ret) {
|
||||
nic->stats.tx_dropped++;
|
||||
} else {
|
||||
nic->stats.tx_packets++;
|
||||
nic->stats.tx_bytes += data_len;
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_device_stats *gdm_lte_stats(struct net_device *dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
|
||||
return &nic->stats;
|
||||
}
|
||||
|
||||
static int gdm_lte_event_send(struct net_device *dev, char *buf, int len)
|
||||
{
|
||||
struct phy_dev *phy_dev = ((struct nic *)netdev_priv(dev))->phy_dev;
|
||||
struct hci_packet *hci = (struct hci_packet *)buf;
|
||||
int length;
|
||||
int idx;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(dev->name, "lte%d", &idx);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
length = gdm_dev16_to_cpu(phy_dev->get_endian(phy_dev->priv_dev),
|
||||
hci->len) + HCI_HEADER_SIZE;
|
||||
return netlink_send(lte_event.sock, idx, 0, buf, length, dev);
|
||||
}
|
||||
|
||||
static void gdm_lte_event_rcv(struct net_device *dev, u16 type,
|
||||
void *msg, int len)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
|
||||
nic->phy_dev->send_hci_func(nic->phy_dev->priv_dev, msg, len, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int gdm_lte_event_init(void)
|
||||
{
|
||||
if (lte_event.ref_cnt == 0)
|
||||
lte_event.sock = netlink_init(NETLINK_LTE, gdm_lte_event_rcv);
|
||||
|
||||
if (lte_event.sock) {
|
||||
lte_event.ref_cnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_err("event init failed\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
void gdm_lte_event_exit(void)
|
||||
{
|
||||
if (lte_event.sock && --lte_event.ref_cnt == 0) {
|
||||
sock_release(lte_event.sock->sk_socket);
|
||||
lte_event.sock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int find_dev_index(u32 nic_type)
|
||||
{
|
||||
u8 index;
|
||||
|
||||
index = (u8)(nic_type & 0x0000000f);
|
||||
if (index >= MAX_NIC_TYPE)
|
||||
return -EINVAL;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static void gdm_lte_netif_rx(struct net_device *dev, char *buf,
|
||||
int len, int flagged_nic_type)
|
||||
{
|
||||
u32 nic_type;
|
||||
struct nic *nic;
|
||||
struct sk_buff *skb;
|
||||
struct ethhdr eth;
|
||||
struct vlan_ethhdr vlan_eth;
|
||||
void *mac_header_data;
|
||||
u32 mac_header_len;
|
||||
char ip_version = 0;
|
||||
|
||||
nic_type = flagged_nic_type & NIC_TYPE_MASK;
|
||||
nic = netdev_priv(dev);
|
||||
|
||||
if (flagged_nic_type & NIC_TYPE_F_DHCP) {
|
||||
/* Change the destination mac address
|
||||
* with the one requested the IP
|
||||
*/
|
||||
if (flagged_nic_type & NIC_TYPE_F_IPV4) {
|
||||
struct dhcp_packet {
|
||||
u8 op; /* BOOTREQUEST or BOOTREPLY */
|
||||
u8 htype; /* hardware address type.
|
||||
* 1 = 10mb ethernet
|
||||
*/
|
||||
u8 hlen; /* hardware address length */
|
||||
u8 hops; /* used by relay agents only */
|
||||
u32 xid; /* unique id */
|
||||
u16 secs; /* elapsed since client began
|
||||
* acquisition/renewal
|
||||
*/
|
||||
u16 flags; /* only one flag so far: */
|
||||
#define BROADCAST_FLAG 0x8000
|
||||
/* "I need broadcast replies" */
|
||||
u32 ciaddr; /* client IP (if client is in
|
||||
* BOUND, RENEW or REBINDING state)
|
||||
*/
|
||||
u32 yiaddr; /* 'your' (client) IP address */
|
||||
/* IP address of next server to use in
|
||||
* bootstrap, returned in DHCPOFFER,
|
||||
* DHCPACK by server
|
||||
*/
|
||||
u32 siaddr_nip;
|
||||
u32 gateway_nip; /* relay agent IP address */
|
||||
u8 chaddr[16]; /* link-layer client hardware
|
||||
* address (MAC)
|
||||
*/
|
||||
u8 sname[64]; /* server host name (ASCIZ) */
|
||||
u8 file[128]; /* boot file name (ASCIZ) */
|
||||
u32 cookie; /* fixed first four option
|
||||
* bytes (99,130,83,99 dec)
|
||||
*/
|
||||
} __packed;
|
||||
int offset = sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr) +
|
||||
offsetof(struct dhcp_packet, chaddr);
|
||||
if (offset + ETH_ALEN > len)
|
||||
return;
|
||||
ether_addr_copy(nic->dest_mac_addr, buf + offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (nic->vlan_id > 0) {
|
||||
mac_header_data = (void *)&vlan_eth;
|
||||
mac_header_len = VLAN_ETH_HLEN;
|
||||
} else {
|
||||
mac_header_data = (void *)ð
|
||||
mac_header_len = ETH_HLEN;
|
||||
}
|
||||
|
||||
/* Format the data so that it can be put to skb */
|
||||
ether_addr_copy(mac_header_data, nic->dest_mac_addr);
|
||||
memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
|
||||
|
||||
vlan_eth.h_vlan_TCI = htons(nic->vlan_id);
|
||||
vlan_eth.h_vlan_proto = htons(ETH_P_8021Q);
|
||||
|
||||
if (nic_type == NIC_TYPE_ARP) {
|
||||
/* Should be response: Only happens because
|
||||
* there was a request from the host
|
||||
*/
|
||||
eth.h_proto = htons(ETH_P_ARP);
|
||||
vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_ARP);
|
||||
} else {
|
||||
ip_version = buf[0] >> 4;
|
||||
if (ip_version == IP_VERSION_4) {
|
||||
eth.h_proto = htons(ETH_P_IP);
|
||||
vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IP);
|
||||
} else if (ip_version == IP_VERSION_6) {
|
||||
eth.h_proto = htons(ETH_P_IPV6);
|
||||
vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IPV6);
|
||||
} else {
|
||||
netdev_err(dev, "Unknown IP version %d\n", ip_version);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Alloc skb and reserve align */
|
||||
skb = dev_alloc_skb(len + mac_header_len + NET_IP_ALIGN);
|
||||
if (!skb)
|
||||
return;
|
||||
skb_reserve(skb, NET_IP_ALIGN);
|
||||
|
||||
skb_put_data(skb, mac_header_data, mac_header_len);
|
||||
skb_put_data(skb, buf, len);
|
||||
|
||||
skb->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
|
||||
skb->dev = dev;
|
||||
skb_reset_mac_header(skb);
|
||||
skb_pull(skb, ETH_HLEN);
|
||||
|
||||
gdm_lte_rx(skb, nic, nic_type);
|
||||
}
|
||||
|
||||
static void gdm_lte_multi_sdu_pkt(struct phy_dev *phy_dev, char *buf, int len)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct multi_sdu *multi_sdu = (struct multi_sdu *)buf;
|
||||
struct sdu *sdu = NULL;
|
||||
u8 endian = phy_dev->get_endian(phy_dev->priv_dev);
|
||||
u8 *data = (u8 *)multi_sdu->data;
|
||||
int copied;
|
||||
u16 i = 0;
|
||||
u16 num_packet;
|
||||
u16 hci_len;
|
||||
u16 cmd_evt;
|
||||
u32 nic_type;
|
||||
int index;
|
||||
|
||||
num_packet = gdm_dev16_to_cpu(endian, multi_sdu->num_packet);
|
||||
|
||||
for (i = 0; i < num_packet; i++) {
|
||||
copied = data - multi_sdu->data;
|
||||
if (len < copied + sizeof(*sdu)) {
|
||||
pr_err("rx prevent buffer overflow");
|
||||
return;
|
||||
}
|
||||
|
||||
sdu = (struct sdu *)data;
|
||||
|
||||
cmd_evt = gdm_dev16_to_cpu(endian, sdu->cmd_evt);
|
||||
hci_len = gdm_dev16_to_cpu(endian, sdu->len);
|
||||
nic_type = gdm_dev32_to_cpu(endian, sdu->nic_type);
|
||||
|
||||
if (cmd_evt != LTE_RX_SDU) {
|
||||
pr_err("rx sdu wrong hci %04x\n", cmd_evt);
|
||||
return;
|
||||
}
|
||||
if (hci_len < 12 ||
|
||||
len < copied + sizeof(*sdu) + (hci_len - 12)) {
|
||||
pr_err("rx sdu invalid len %d\n", hci_len);
|
||||
return;
|
||||
}
|
||||
|
||||
index = find_dev_index(nic_type);
|
||||
if (index < 0) {
|
||||
pr_err("rx sdu invalid nic_type :%x\n", nic_type);
|
||||
return;
|
||||
}
|
||||
dev = phy_dev->dev[index];
|
||||
gdm_lte_netif_rx(dev, (char *)sdu->data,
|
||||
(int)(hci_len - 12), nic_type);
|
||||
|
||||
data += ((hci_len + 3) & 0xfffc) + HCI_HEADER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static void gdm_lte_pdn_table(struct net_device *dev, char *buf, int len)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
|
||||
u8 ed = nic->phy_dev->get_endian(nic->phy_dev->priv_dev);
|
||||
|
||||
if (!pdn_table->activate) {
|
||||
memset(&nic->pdn_table, 0x00, sizeof(struct pdn_table));
|
||||
netdev_info(dev, "pdn deactivated\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
nic->pdn_table.activate = pdn_table->activate;
|
||||
nic->pdn_table.dft_eps_id = gdm_dev32_to_cpu(ed, pdn_table->dft_eps_id);
|
||||
nic->pdn_table.nic_type = gdm_dev32_to_cpu(ed, pdn_table->nic_type);
|
||||
|
||||
netdev_info(dev, "pdn activated, nic_type=0x%x\n",
|
||||
nic->pdn_table.nic_type);
|
||||
}
|
||||
|
||||
static int gdm_lte_receive_pkt(struct phy_dev *phy_dev, char *buf, int len)
|
||||
{
|
||||
struct hci_packet *hci = (struct hci_packet *)buf;
|
||||
struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
|
||||
struct sdu *sdu;
|
||||
struct net_device *dev;
|
||||
u8 endian = phy_dev->get_endian(phy_dev->priv_dev);
|
||||
int ret = 0;
|
||||
u16 cmd_evt;
|
||||
u32 nic_type;
|
||||
int index;
|
||||
|
||||
if (!len)
|
||||
return ret;
|
||||
|
||||
cmd_evt = gdm_dev16_to_cpu(endian, hci->cmd_evt);
|
||||
|
||||
dev = phy_dev->dev[0];
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
switch (cmd_evt) {
|
||||
case LTE_RX_SDU:
|
||||
sdu = (struct sdu *)hci->data;
|
||||
nic_type = gdm_dev32_to_cpu(endian, sdu->nic_type);
|
||||
index = find_dev_index(nic_type);
|
||||
if (index < 0)
|
||||
return index;
|
||||
dev = phy_dev->dev[index];
|
||||
gdm_lte_netif_rx(dev, hci->data, len, nic_type);
|
||||
break;
|
||||
case LTE_RX_MULTI_SDU:
|
||||
gdm_lte_multi_sdu_pkt(phy_dev, buf, len);
|
||||
break;
|
||||
case LTE_LINK_ON_OFF_INDICATION:
|
||||
netdev_info(dev, "link %s\n",
|
||||
((struct hci_connect_ind *)buf)->connect
|
||||
? "on" : "off");
|
||||
break;
|
||||
case LTE_PDN_TABLE_IND:
|
||||
pdn_table = (struct hci_pdn_table_ind *)buf;
|
||||
nic_type = gdm_dev32_to_cpu(endian, pdn_table->nic_type);
|
||||
index = find_dev_index(nic_type);
|
||||
if (index < 0)
|
||||
return index;
|
||||
dev = phy_dev->dev[index];
|
||||
gdm_lte_pdn_table(dev, buf, len);
|
||||
fallthrough;
|
||||
default:
|
||||
ret = gdm_lte_event_send(dev, buf, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rx_complete(void *arg, void *data, int len, int context)
|
||||
{
|
||||
struct phy_dev *phy_dev = arg;
|
||||
|
||||
return gdm_lte_receive_pkt(phy_dev, data, len);
|
||||
}
|
||||
|
||||
void start_rx_proc(struct phy_dev *phy_dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_RX_SUBMIT_COUNT; i++)
|
||||
phy_dev->rcv_func(phy_dev->priv_dev,
|
||||
rx_complete, phy_dev, USB_COMPLETE);
|
||||
}
|
||||
|
||||
static const struct net_device_ops gdm_netdev_ops = {
|
||||
.ndo_open = gdm_lte_open,
|
||||
.ndo_stop = gdm_lte_close,
|
||||
.ndo_set_config = gdm_lte_set_config,
|
||||
.ndo_start_xmit = gdm_lte_tx,
|
||||
.ndo_get_stats = gdm_lte_stats,
|
||||
};
|
||||
|
||||
static u8 gdm_lte_macaddr[ETH_ALEN] = {0x00, 0x0a, 0x3b, 0x00, 0x00, 0x00};
|
||||
|
||||
static void form_mac_address(u8 *dev_addr, u8 *nic_src, u8 *nic_dest,
|
||||
u8 *mac_address, u8 index)
|
||||
{
|
||||
/* Form the dev_addr */
|
||||
if (!mac_address)
|
||||
ether_addr_copy(dev_addr, gdm_lte_macaddr);
|
||||
else
|
||||
ether_addr_copy(dev_addr, mac_address);
|
||||
|
||||
/* The last byte of the mac address
|
||||
* should be less than or equal to 0xFC
|
||||
*/
|
||||
dev_addr[ETH_ALEN - 1] += index;
|
||||
|
||||
/* Create random nic src and copy the first
|
||||
* 3 bytes to be the same as dev_addr
|
||||
*/
|
||||
eth_random_addr(nic_src);
|
||||
memcpy(nic_src, dev_addr, 3);
|
||||
|
||||
/* Copy the nic_dest from dev_addr*/
|
||||
ether_addr_copy(nic_dest, dev_addr);
|
||||
}
|
||||
|
||||
static void validate_mac_address(u8 *mac_address)
|
||||
{
|
||||
/* if zero address or multicast bit set, restore the default value */
|
||||
if (is_zero_ether_addr(mac_address) || (mac_address[0] & 0x01)) {
|
||||
pr_err("MAC invalid, restoring default\n");
|
||||
memcpy(mac_address, gdm_lte_macaddr, 6);
|
||||
}
|
||||
}
|
||||
|
||||
int register_lte_device(struct phy_dev *phy_dev,
|
||||
struct device *dev, u8 *mac_address)
|
||||
{
|
||||
struct nic *nic;
|
||||
struct net_device *net;
|
||||
char pdn_dev_name[16];
|
||||
u8 addr[ETH_ALEN];
|
||||
int ret = 0;
|
||||
u8 index;
|
||||
|
||||
validate_mac_address(mac_address);
|
||||
|
||||
for (index = 0; index < MAX_NIC_TYPE; index++) {
|
||||
/* Create device name lteXpdnX */
|
||||
sprintf(pdn_dev_name, "lte%%dpdn%d", index);
|
||||
|
||||
/* Allocate netdev */
|
||||
net = alloc_netdev(sizeof(struct nic), pdn_dev_name,
|
||||
NET_NAME_UNKNOWN, ether_setup);
|
||||
if (!net) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
net->netdev_ops = &gdm_netdev_ops;
|
||||
net->flags &= ~IFF_MULTICAST;
|
||||
net->mtu = DEFAULT_MTU_SIZE;
|
||||
|
||||
nic = netdev_priv(net);
|
||||
memset(nic, 0, sizeof(struct nic));
|
||||
nic->netdev = net;
|
||||
nic->phy_dev = phy_dev;
|
||||
nic->nic_id = index;
|
||||
|
||||
form_mac_address(addr,
|
||||
nic->src_mac_addr,
|
||||
nic->dest_mac_addr,
|
||||
mac_address,
|
||||
index);
|
||||
eth_hw_addr_set(net, addr);
|
||||
|
||||
SET_NETDEV_DEV(net, dev);
|
||||
SET_NETDEV_DEVTYPE(net, &wwan_type);
|
||||
|
||||
ret = register_netdev(net);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
netif_carrier_on(net);
|
||||
|
||||
phy_dev->dev[index] = net;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
unregister_lte_device(phy_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unregister_lte_device(struct phy_dev *phy_dev)
|
||||
{
|
||||
struct net_device *net;
|
||||
int index;
|
||||
|
||||
for (index = 0; index < MAX_NIC_TYPE; index++) {
|
||||
net = phy_dev->dev[index];
|
||||
if (!net)
|
||||
continue;
|
||||
|
||||
unregister_netdev(net);
|
||||
free_netdev(net);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _GDM_LTE_H_
|
||||
#define _GDM_LTE_H_
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "gdm_endian.h"
|
||||
|
||||
#define MAX_NIC_TYPE 4
|
||||
#define MAX_RX_SUBMIT_COUNT 3
|
||||
#define DRIVER_VERSION "3.7.17.0"
|
||||
|
||||
enum TX_ERROR_CODE {
|
||||
TX_NO_ERROR = 0,
|
||||
TX_NO_DEV,
|
||||
TX_NO_SPC,
|
||||
TX_NO_BUFFER,
|
||||
};
|
||||
|
||||
enum CALLBACK_CONTEXT {
|
||||
KERNEL_THREAD = 0,
|
||||
USB_COMPLETE,
|
||||
};
|
||||
|
||||
struct pdn_table {
|
||||
u8 activate;
|
||||
u32 dft_eps_id;
|
||||
u32 nic_type;
|
||||
} __packed;
|
||||
|
||||
struct nic;
|
||||
|
||||
struct phy_dev {
|
||||
void *priv_dev;
|
||||
struct net_device *dev[MAX_NIC_TYPE];
|
||||
int (*send_hci_func)(void *priv_dev, void *data, int len,
|
||||
void (*cb)(void *cb_data), void *cb_data);
|
||||
int (*send_sdu_func)(void *priv_dev, void *data, int len,
|
||||
unsigned int dft_eps_id, unsigned int eps_id,
|
||||
void (*cb)(void *cb_data), void *cb_data,
|
||||
int dev_idx, int nic_type);
|
||||
int (*rcv_func)(void *priv_dev,
|
||||
int (*cb)(void *cb_data, void *data, int len,
|
||||
int context),
|
||||
void *cb_data, int context);
|
||||
u8 (*get_endian)(void *priv_dev);
|
||||
};
|
||||
|
||||
struct nic {
|
||||
struct net_device *netdev;
|
||||
struct phy_dev *phy_dev;
|
||||
struct net_device_stats stats;
|
||||
struct pdn_table pdn_table;
|
||||
u8 dest_mac_addr[ETH_ALEN];
|
||||
u8 src_mac_addr[ETH_ALEN];
|
||||
u32 nic_id;
|
||||
u16 vlan_id;
|
||||
};
|
||||
|
||||
int gdm_lte_event_init(void);
|
||||
void gdm_lte_event_exit(void);
|
||||
|
||||
void start_rx_proc(struct phy_dev *phy_dev);
|
||||
int register_lte_device(struct phy_dev *phy_dev, struct device *dev,
|
||||
u8 *mac_address);
|
||||
void unregister_lte_device(struct phy_dev *phy_dev);
|
||||
|
||||
#endif /* _GDM_LTE_H_ */
|
||||
@@ -1,668 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
|
||||
#include "gdm_mux.h"
|
||||
|
||||
static u16 packet_type_for_tty_index[TTY_MAX_COUNT] = {0xF011, 0xF010};
|
||||
|
||||
#define USB_DEVICE_CDC_DATA(vid, pid) \
|
||||
.match_flags = \
|
||||
USB_DEVICE_ID_MATCH_DEVICE |\
|
||||
USB_DEVICE_ID_MATCH_INT_CLASS |\
|
||||
USB_DEVICE_ID_MATCH_INT_SUBCLASS,\
|
||||
.idVendor = vid,\
|
||||
.idProduct = pid,\
|
||||
.bInterfaceClass = USB_CLASS_COMM,\
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM
|
||||
|
||||
static const struct usb_device_id id_table[] = {
|
||||
{ USB_DEVICE_CDC_DATA(0x1076, 0x8000) }, /* GCT GDM7240 */
|
||||
{ USB_DEVICE_CDC_DATA(0x1076, 0x8f00) }, /* GCT GDM7243 */
|
||||
{ USB_DEVICE_CDC_DATA(0x1076, 0x9000) }, /* GCT GDM7243 */
|
||||
{ USB_DEVICE_CDC_DATA(0x1d74, 0x2300) }, /* LGIT Phoenix */
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static int packet_type_to_tty_index(u16 packet_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TTY_MAX_COUNT; i++) {
|
||||
if (packet_type_for_tty_index[i] == packet_type)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct mux_tx *alloc_mux_tx(int len)
|
||||
{
|
||||
struct mux_tx *t;
|
||||
|
||||
t = kzalloc(sizeof(*t), GFP_ATOMIC);
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
t->urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
t->buf = kmalloc(MUX_TX_MAX_SIZE, GFP_ATOMIC);
|
||||
if (!t->urb || !t->buf) {
|
||||
usb_free_urb(t->urb);
|
||||
kfree(t->buf);
|
||||
kfree(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void free_mux_tx(struct mux_tx *t)
|
||||
{
|
||||
if (t) {
|
||||
usb_free_urb(t->urb);
|
||||
kfree(t->buf);
|
||||
kfree(t);
|
||||
}
|
||||
}
|
||||
|
||||
static struct mux_rx *alloc_mux_rx(void)
|
||||
{
|
||||
struct mux_rx *r;
|
||||
|
||||
r = kzalloc(sizeof(*r), GFP_KERNEL);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
r->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
r->buf = kmalloc(MUX_RX_MAX_SIZE, GFP_KERNEL);
|
||||
if (!r->urb || !r->buf) {
|
||||
usb_free_urb(r->urb);
|
||||
kfree(r->buf);
|
||||
kfree(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void free_mux_rx(struct mux_rx *r)
|
||||
{
|
||||
if (r) {
|
||||
usb_free_urb(r->urb);
|
||||
kfree(r->buf);
|
||||
kfree(r);
|
||||
}
|
||||
}
|
||||
|
||||
static struct mux_rx *get_rx_struct(struct rx_cxt *rx)
|
||||
{
|
||||
struct mux_rx *r;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rx->free_list_lock, flags);
|
||||
|
||||
if (list_empty(&rx->rx_free_list)) {
|
||||
spin_unlock_irqrestore(&rx->free_list_lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = list_entry(rx->rx_free_list.prev, struct mux_rx, free_list);
|
||||
list_del(&r->free_list);
|
||||
|
||||
spin_unlock_irqrestore(&rx->free_list_lock, flags);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void put_rx_struct(struct rx_cxt *rx, struct mux_rx *r)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rx->free_list_lock, flags);
|
||||
list_add_tail(&r->free_list, &rx->rx_free_list);
|
||||
spin_unlock_irqrestore(&rx->free_list_lock, flags);
|
||||
}
|
||||
|
||||
static int up_to_host(struct mux_rx *r)
|
||||
{
|
||||
struct mux_dev *mux_dev = r->mux_dev;
|
||||
struct mux_pkt_header *mux_header;
|
||||
unsigned int start_flag;
|
||||
unsigned int payload_size;
|
||||
unsigned short packet_type;
|
||||
int total_len;
|
||||
u32 packet_size_sum = r->offset;
|
||||
int index;
|
||||
int ret = TO_HOST_INVALID_PACKET;
|
||||
int len = r->len;
|
||||
|
||||
while (1) {
|
||||
mux_header = (struct mux_pkt_header *)(r->buf +
|
||||
packet_size_sum);
|
||||
start_flag = __le32_to_cpu(mux_header->start_flag);
|
||||
payload_size = __le32_to_cpu(mux_header->payload_size);
|
||||
packet_type = __le16_to_cpu(mux_header->packet_type);
|
||||
|
||||
if (start_flag != START_FLAG) {
|
||||
pr_err("invalid START_FLAG %x\n", start_flag);
|
||||
break;
|
||||
}
|
||||
|
||||
total_len = ALIGN(MUX_HEADER_SIZE + payload_size, 4);
|
||||
|
||||
if (len - packet_size_sum < total_len) {
|
||||
pr_err("invalid payload : %d %d %04x\n",
|
||||
payload_size, len, packet_type);
|
||||
break;
|
||||
}
|
||||
|
||||
index = packet_type_to_tty_index(packet_type);
|
||||
if (index < 0) {
|
||||
pr_err("invalid index %d\n", index);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = r->callback(mux_header->data,
|
||||
payload_size,
|
||||
index,
|
||||
mux_dev->tty_dev,
|
||||
RECV_PACKET_PROCESS_CONTINUE
|
||||
);
|
||||
if (ret == TO_HOST_BUFFER_REQUEST_FAIL) {
|
||||
r->offset += packet_size_sum;
|
||||
break;
|
||||
}
|
||||
|
||||
packet_size_sum += total_len;
|
||||
if (len - packet_size_sum <= MUX_HEADER_SIZE + 2) {
|
||||
ret = r->callback(NULL,
|
||||
0,
|
||||
index,
|
||||
mux_dev->tty_dev,
|
||||
RECV_PACKET_PROCESS_COMPLETE
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void do_rx(struct work_struct *work)
|
||||
{
|
||||
struct mux_dev *mux_dev =
|
||||
container_of(work, struct mux_dev, work_rx.work);
|
||||
struct mux_rx *r;
|
||||
struct rx_cxt *rx = &mux_dev->rx;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
while (1) {
|
||||
spin_lock_irqsave(&rx->to_host_lock, flags);
|
||||
if (list_empty(&rx->to_host_list)) {
|
||||
spin_unlock_irqrestore(&rx->to_host_lock, flags);
|
||||
break;
|
||||
}
|
||||
r = list_entry(rx->to_host_list.next, struct mux_rx,
|
||||
to_host_list);
|
||||
list_del(&r->to_host_list);
|
||||
spin_unlock_irqrestore(&rx->to_host_lock, flags);
|
||||
|
||||
ret = up_to_host(r);
|
||||
if (ret == TO_HOST_BUFFER_REQUEST_FAIL)
|
||||
pr_err("failed to send mux data to host\n");
|
||||
else
|
||||
put_rx_struct(rx, r);
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_rx_submit_list(struct mux_rx *r, struct rx_cxt *rx)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct mux_rx *r_remove, *r_remove_next;
|
||||
|
||||
spin_lock_irqsave(&rx->submit_list_lock, flags);
|
||||
list_for_each_entry_safe(r_remove, r_remove_next, &rx->rx_submit_list,
|
||||
rx_submit_list) {
|
||||
if (r == r_remove)
|
||||
list_del(&r->rx_submit_list);
|
||||
}
|
||||
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
|
||||
}
|
||||
|
||||
static void gdm_mux_rcv_complete(struct urb *urb)
|
||||
{
|
||||
struct mux_rx *r = urb->context;
|
||||
struct mux_dev *mux_dev = r->mux_dev;
|
||||
struct rx_cxt *rx = &mux_dev->rx;
|
||||
unsigned long flags;
|
||||
|
||||
remove_rx_submit_list(r, rx);
|
||||
|
||||
if (urb->status) {
|
||||
if (mux_dev->usb_state == PM_NORMAL)
|
||||
dev_err(&urb->dev->dev, "%s: urb status error %d\n",
|
||||
__func__, urb->status);
|
||||
put_rx_struct(rx, r);
|
||||
} else {
|
||||
r->len = r->urb->actual_length;
|
||||
spin_lock_irqsave(&rx->to_host_lock, flags);
|
||||
list_add_tail(&r->to_host_list, &rx->to_host_list);
|
||||
schedule_work(&mux_dev->work_rx.work);
|
||||
spin_unlock_irqrestore(&rx->to_host_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static int gdm_mux_recv(void *priv_dev,
|
||||
int (*cb)(void *data, int len, int tty_index,
|
||||
struct tty_dev *tty_dev, int complete))
|
||||
{
|
||||
struct mux_dev *mux_dev = priv_dev;
|
||||
struct usb_device *usbdev = mux_dev->usbdev;
|
||||
struct mux_rx *r;
|
||||
struct rx_cxt *rx = &mux_dev->rx;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!usbdev) {
|
||||
pr_err("device is disconnected\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
r = get_rx_struct(rx);
|
||||
if (!r) {
|
||||
pr_err("get_rx_struct fail\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r->offset = 0;
|
||||
r->mux_dev = (void *)mux_dev;
|
||||
r->callback = cb;
|
||||
mux_dev->rx_cb = cb;
|
||||
|
||||
usb_fill_bulk_urb(r->urb,
|
||||
usbdev,
|
||||
usb_rcvbulkpipe(usbdev, 0x86),
|
||||
r->buf,
|
||||
MUX_RX_MAX_SIZE,
|
||||
gdm_mux_rcv_complete,
|
||||
r);
|
||||
|
||||
spin_lock_irqsave(&rx->submit_list_lock, flags);
|
||||
list_add_tail(&r->rx_submit_list, &rx->rx_submit_list);
|
||||
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
|
||||
|
||||
ret = usb_submit_urb(r->urb, GFP_KERNEL);
|
||||
|
||||
if (ret) {
|
||||
spin_lock_irqsave(&rx->submit_list_lock, flags);
|
||||
list_del(&r->rx_submit_list);
|
||||
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
|
||||
|
||||
put_rx_struct(rx, r);
|
||||
|
||||
pr_err("usb_submit_urb ret=%d\n", ret);
|
||||
}
|
||||
|
||||
usb_mark_last_busy(usbdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gdm_mux_send_complete(struct urb *urb)
|
||||
{
|
||||
struct mux_tx *t = urb->context;
|
||||
|
||||
if (urb->status == -ECONNRESET) {
|
||||
dev_info(&urb->dev->dev, "CONNRESET\n");
|
||||
free_mux_tx(t);
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->callback)
|
||||
t->callback(t->cb_data);
|
||||
|
||||
free_mux_tx(t);
|
||||
}
|
||||
|
||||
static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index,
|
||||
void (*cb)(void *data), void *cb_data)
|
||||
{
|
||||
struct mux_dev *mux_dev = priv_dev;
|
||||
struct usb_device *usbdev = mux_dev->usbdev;
|
||||
struct mux_pkt_header *mux_header;
|
||||
struct mux_tx *t = NULL;
|
||||
static u32 seq_num = 1;
|
||||
int total_len;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (mux_dev->usb_state == PM_SUSPEND) {
|
||||
ret = usb_autopm_get_interface(mux_dev->intf);
|
||||
if (!ret)
|
||||
usb_autopm_put_interface(mux_dev->intf);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mux_dev->write_lock, flags);
|
||||
|
||||
total_len = ALIGN(MUX_HEADER_SIZE + len, 4);
|
||||
|
||||
t = alloc_mux_tx(total_len);
|
||||
if (!t) {
|
||||
pr_err("alloc_mux_tx fail\n");
|
||||
spin_unlock_irqrestore(&mux_dev->write_lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mux_header = (struct mux_pkt_header *)t->buf;
|
||||
mux_header->start_flag = __cpu_to_le32(START_FLAG);
|
||||
mux_header->seq_num = __cpu_to_le32(seq_num++);
|
||||
mux_header->payload_size = __cpu_to_le32((u32)len);
|
||||
mux_header->packet_type = __cpu_to_le16(packet_type_for_tty_index[tty_index]);
|
||||
|
||||
memcpy(t->buf + MUX_HEADER_SIZE, data, len);
|
||||
memset(t->buf + MUX_HEADER_SIZE + len, 0,
|
||||
total_len - MUX_HEADER_SIZE - len);
|
||||
|
||||
t->len = total_len;
|
||||
t->callback = cb;
|
||||
t->cb_data = cb_data;
|
||||
|
||||
usb_fill_bulk_urb(t->urb,
|
||||
usbdev,
|
||||
usb_sndbulkpipe(usbdev, 5),
|
||||
t->buf,
|
||||
total_len,
|
||||
gdm_mux_send_complete,
|
||||
t);
|
||||
|
||||
ret = usb_submit_urb(t->urb, GFP_ATOMIC);
|
||||
|
||||
spin_unlock_irqrestore(&mux_dev->write_lock, flags);
|
||||
|
||||
if (ret)
|
||||
pr_err("usb_submit_urb Error: %d\n", ret);
|
||||
|
||||
usb_mark_last_busy(usbdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gdm_mux_send_control(void *priv_dev, int request, int value,
|
||||
void *buf, int len)
|
||||
{
|
||||
struct mux_dev *mux_dev = priv_dev;
|
||||
struct usb_device *usbdev = mux_dev->usbdev;
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(usbdev,
|
||||
usb_sndctrlpipe(usbdev, 0),
|
||||
request,
|
||||
USB_RT_ACM,
|
||||
value,
|
||||
2,
|
||||
buf,
|
||||
len,
|
||||
5000
|
||||
);
|
||||
|
||||
if (ret < 0)
|
||||
pr_err("usb_control_msg error: %d\n", ret);
|
||||
|
||||
return min(ret, 0);
|
||||
}
|
||||
|
||||
static void release_usb(struct mux_dev *mux_dev)
|
||||
{
|
||||
struct rx_cxt *rx = &mux_dev->rx;
|
||||
struct mux_rx *r, *r_next;
|
||||
unsigned long flags;
|
||||
|
||||
cancel_delayed_work(&mux_dev->work_rx);
|
||||
|
||||
spin_lock_irqsave(&rx->submit_list_lock, flags);
|
||||
list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
|
||||
rx_submit_list) {
|
||||
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
|
||||
usb_kill_urb(r->urb);
|
||||
spin_lock_irqsave(&rx->submit_list_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
|
||||
|
||||
spin_lock_irqsave(&rx->free_list_lock, flags);
|
||||
list_for_each_entry_safe(r, r_next, &rx->rx_free_list, free_list) {
|
||||
list_del(&r->free_list);
|
||||
free_mux_rx(r);
|
||||
}
|
||||
spin_unlock_irqrestore(&rx->free_list_lock, flags);
|
||||
|
||||
spin_lock_irqsave(&rx->to_host_lock, flags);
|
||||
list_for_each_entry_safe(r, r_next, &rx->to_host_list, to_host_list) {
|
||||
if (r->mux_dev == (void *)mux_dev) {
|
||||
list_del(&r->to_host_list);
|
||||
free_mux_rx(r);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&rx->to_host_lock, flags);
|
||||
}
|
||||
|
||||
static int init_usb(struct mux_dev *mux_dev)
|
||||
{
|
||||
struct mux_rx *r;
|
||||
struct rx_cxt *rx = &mux_dev->rx;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
spin_lock_init(&mux_dev->write_lock);
|
||||
INIT_LIST_HEAD(&rx->to_host_list);
|
||||
INIT_LIST_HEAD(&rx->rx_submit_list);
|
||||
INIT_LIST_HEAD(&rx->rx_free_list);
|
||||
spin_lock_init(&rx->to_host_lock);
|
||||
spin_lock_init(&rx->submit_list_lock);
|
||||
spin_lock_init(&rx->free_list_lock);
|
||||
|
||||
for (i = 0; i < MAX_ISSUE_NUM * 2; i++) {
|
||||
r = alloc_mux_rx();
|
||||
if (!r) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
list_add(&r->free_list, &rx->rx_free_list);
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&mux_dev->work_rx, do_rx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gdm_mux_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct mux_dev *mux_dev;
|
||||
struct tty_dev *tty_dev;
|
||||
u16 idVendor, idProduct;
|
||||
int bInterfaceNumber;
|
||||
int ret;
|
||||
int i;
|
||||
struct usb_device *usbdev = interface_to_usbdev(intf);
|
||||
|
||||
bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber;
|
||||
|
||||
idVendor = __le16_to_cpu(usbdev->descriptor.idVendor);
|
||||
idProduct = __le16_to_cpu(usbdev->descriptor.idProduct);
|
||||
|
||||
pr_info("mux vid = 0x%04x pid = 0x%04x\n", idVendor, idProduct);
|
||||
|
||||
if (bInterfaceNumber != 2)
|
||||
return -ENODEV;
|
||||
|
||||
mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL);
|
||||
if (!mux_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
tty_dev = kzalloc(sizeof(*tty_dev), GFP_KERNEL);
|
||||
if (!tty_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_mux;
|
||||
}
|
||||
|
||||
mux_dev->usbdev = usbdev;
|
||||
mux_dev->control_intf = intf;
|
||||
|
||||
ret = init_usb(mux_dev);
|
||||
if (ret)
|
||||
goto err_free_usb;
|
||||
|
||||
tty_dev->priv_dev = (void *)mux_dev;
|
||||
tty_dev->send_func = gdm_mux_send;
|
||||
tty_dev->recv_func = gdm_mux_recv;
|
||||
tty_dev->send_control = gdm_mux_send_control;
|
||||
|
||||
ret = register_lte_tty_device(tty_dev, &intf->dev);
|
||||
if (ret)
|
||||
goto err_unregister_tty;
|
||||
|
||||
for (i = 0; i < TTY_MAX_COUNT; i++)
|
||||
mux_dev->tty_dev = tty_dev;
|
||||
|
||||
mux_dev->intf = intf;
|
||||
mux_dev->usb_state = PM_NORMAL;
|
||||
|
||||
usb_get_dev(usbdev);
|
||||
usb_set_intfdata(intf, tty_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_tty:
|
||||
unregister_lte_tty_device(tty_dev);
|
||||
err_free_usb:
|
||||
release_usb(mux_dev);
|
||||
kfree(tty_dev);
|
||||
err_free_mux:
|
||||
kfree(mux_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gdm_mux_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct tty_dev *tty_dev;
|
||||
struct mux_dev *mux_dev;
|
||||
struct usb_device *usbdev = interface_to_usbdev(intf);
|
||||
|
||||
tty_dev = usb_get_intfdata(intf);
|
||||
|
||||
mux_dev = tty_dev->priv_dev;
|
||||
|
||||
release_usb(mux_dev);
|
||||
unregister_lte_tty_device(tty_dev);
|
||||
|
||||
kfree(mux_dev);
|
||||
kfree(tty_dev);
|
||||
|
||||
usb_put_dev(usbdev);
|
||||
}
|
||||
|
||||
static int gdm_mux_suspend(struct usb_interface *intf, pm_message_t pm_msg)
|
||||
{
|
||||
struct tty_dev *tty_dev;
|
||||
struct mux_dev *mux_dev;
|
||||
struct rx_cxt *rx;
|
||||
struct mux_rx *r, *r_next;
|
||||
unsigned long flags;
|
||||
|
||||
tty_dev = usb_get_intfdata(intf);
|
||||
mux_dev = tty_dev->priv_dev;
|
||||
rx = &mux_dev->rx;
|
||||
|
||||
cancel_work_sync(&mux_dev->work_rx.work);
|
||||
|
||||
if (mux_dev->usb_state != PM_NORMAL) {
|
||||
dev_err(intf->usb_dev, "usb suspend - invalid state\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mux_dev->usb_state = PM_SUSPEND;
|
||||
|
||||
spin_lock_irqsave(&rx->submit_list_lock, flags);
|
||||
list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
|
||||
rx_submit_list) {
|
||||
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
|
||||
usb_kill_urb(r->urb);
|
||||
spin_lock_irqsave(&rx->submit_list_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_mux_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct tty_dev *tty_dev;
|
||||
struct mux_dev *mux_dev;
|
||||
u8 i;
|
||||
|
||||
tty_dev = usb_get_intfdata(intf);
|
||||
mux_dev = tty_dev->priv_dev;
|
||||
|
||||
if (mux_dev->usb_state != PM_SUSPEND) {
|
||||
dev_err(intf->usb_dev, "usb resume - invalid state\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mux_dev->usb_state = PM_NORMAL;
|
||||
|
||||
for (i = 0; i < MAX_ISSUE_NUM; i++)
|
||||
gdm_mux_recv(mux_dev, mux_dev->rx_cb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_driver gdm_mux_driver = {
|
||||
.name = "gdm_mux",
|
||||
.probe = gdm_mux_probe,
|
||||
.disconnect = gdm_mux_disconnect,
|
||||
.id_table = id_table,
|
||||
.supports_autosuspend = 1,
|
||||
.suspend = gdm_mux_suspend,
|
||||
.resume = gdm_mux_resume,
|
||||
.reset_resume = gdm_mux_resume,
|
||||
};
|
||||
|
||||
static int __init gdm_usb_mux_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_lte_tty_driver();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb_register(&gdm_mux_driver);
|
||||
}
|
||||
|
||||
static void __exit gdm_usb_mux_exit(void)
|
||||
{
|
||||
usb_deregister(&gdm_mux_driver);
|
||||
unregister_lte_tty_driver();
|
||||
}
|
||||
|
||||
module_init(gdm_usb_mux_init);
|
||||
module_exit(gdm_usb_mux_exit);
|
||||
|
||||
MODULE_DESCRIPTION("GCT LTE TTY Device Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1,85 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _GDM_MUX_H_
|
||||
#define _GDM_MUX_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "gdm_tty.h"
|
||||
|
||||
#define PM_NORMAL 0
|
||||
#define PM_SUSPEND 1
|
||||
|
||||
#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
|
||||
|
||||
#define START_FLAG 0xA512485A
|
||||
#define MUX_HEADER_SIZE 14
|
||||
#define MUX_TX_MAX_SIZE (1024 * 10)
|
||||
#define MUX_RX_MAX_SIZE (1024 * 30)
|
||||
#define AT_PKT_TYPE 0xF011
|
||||
#define DM_PKT_TYPE 0xF010
|
||||
|
||||
#define RETRY_TIMER 30 /* msec */
|
||||
|
||||
struct mux_pkt_header {
|
||||
__le32 start_flag;
|
||||
__le32 seq_num;
|
||||
__le32 payload_size;
|
||||
__le16 packet_type;
|
||||
unsigned char data[];
|
||||
};
|
||||
|
||||
struct mux_tx {
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
int len;
|
||||
void (*callback)(void *cb_data);
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct mux_rx {
|
||||
struct list_head free_list;
|
||||
struct list_head rx_submit_list;
|
||||
struct list_head to_host_list;
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
void *mux_dev;
|
||||
u32 offset;
|
||||
u32 len;
|
||||
int (*callback)(void *data,
|
||||
int len,
|
||||
int tty_index,
|
||||
struct tty_dev *tty_dev,
|
||||
int complete);
|
||||
};
|
||||
|
||||
struct rx_cxt {
|
||||
struct list_head to_host_list;
|
||||
struct list_head rx_submit_list;
|
||||
struct list_head rx_free_list;
|
||||
spinlock_t to_host_lock;
|
||||
spinlock_t submit_list_lock;
|
||||
spinlock_t free_list_lock;
|
||||
};
|
||||
|
||||
struct mux_dev {
|
||||
struct usb_device *usbdev;
|
||||
struct usb_interface *control_intf;
|
||||
struct usb_interface *data_intf;
|
||||
struct rx_cxt rx;
|
||||
struct delayed_work work_rx;
|
||||
struct usb_interface *intf;
|
||||
int usb_state;
|
||||
int (*rx_cb)(void *data,
|
||||
int len,
|
||||
int tty_index,
|
||||
struct tty_dev *tty_dev,
|
||||
int complete);
|
||||
spinlock_t write_lock;
|
||||
struct tty_dev *tty_dev;
|
||||
};
|
||||
|
||||
#endif /* _GDM_MUX_H_ */
|
||||
@@ -1,316 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/serial.h>
|
||||
#include "gdm_tty.h"
|
||||
|
||||
#define GDM_TTY_MAJOR 0
|
||||
#define GDM_TTY_MINOR 32
|
||||
|
||||
#define WRITE_SIZE 2048
|
||||
|
||||
#define MUX_TX_MAX_SIZE 2048
|
||||
|
||||
static inline bool gdm_tty_ready(struct gdm *gdm)
|
||||
{
|
||||
return gdm && gdm->tty_dev && gdm->port.count;
|
||||
}
|
||||
|
||||
static struct tty_driver *gdm_driver[TTY_MAX_COUNT];
|
||||
static struct gdm *gdm_table[TTY_MAX_COUNT][GDM_TTY_MINOR];
|
||||
static DEFINE_MUTEX(gdm_table_lock);
|
||||
|
||||
static const char *DRIVER_STRING[TTY_MAX_COUNT] = {"GCTATC", "GCTDM"};
|
||||
static char *DEVICE_STRING[TTY_MAX_COUNT] = {"GCT-ATC", "GCT-DM"};
|
||||
|
||||
static void gdm_port_destruct(struct tty_port *port)
|
||||
{
|
||||
struct gdm *gdm = container_of(port, struct gdm, port);
|
||||
|
||||
mutex_lock(&gdm_table_lock);
|
||||
gdm_table[gdm->index][gdm->minor] = NULL;
|
||||
mutex_unlock(&gdm_table_lock);
|
||||
|
||||
kfree(gdm);
|
||||
}
|
||||
|
||||
static const struct tty_port_operations gdm_port_ops = {
|
||||
.destruct = gdm_port_destruct,
|
||||
};
|
||||
|
||||
static int gdm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
{
|
||||
struct gdm *gdm = NULL;
|
||||
int ret;
|
||||
|
||||
ret = match_string(DRIVER_STRING, TTY_MAX_COUNT,
|
||||
tty->driver->driver_name);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&gdm_table_lock);
|
||||
gdm = gdm_table[ret][tty->index];
|
||||
if (!gdm) {
|
||||
mutex_unlock(&gdm_table_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tty_port_get(&gdm->port);
|
||||
|
||||
ret = tty_standard_install(driver, tty);
|
||||
if (ret) {
|
||||
tty_port_put(&gdm->port);
|
||||
mutex_unlock(&gdm_table_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tty->driver_data = gdm;
|
||||
mutex_unlock(&gdm_table_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct gdm *gdm = tty->driver_data;
|
||||
|
||||
return tty_port_open(&gdm->port, tty, filp);
|
||||
}
|
||||
|
||||
static void gdm_tty_cleanup(struct tty_struct *tty)
|
||||
{
|
||||
struct gdm *gdm = tty->driver_data;
|
||||
|
||||
tty_port_put(&gdm->port);
|
||||
}
|
||||
|
||||
static void gdm_tty_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct gdm *gdm = tty->driver_data;
|
||||
|
||||
tty_port_hangup(&gdm->port);
|
||||
}
|
||||
|
||||
static void gdm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct gdm *gdm = tty->driver_data;
|
||||
|
||||
tty_port_close(&gdm->port, tty, filp);
|
||||
}
|
||||
|
||||
static int gdm_tty_recv_complete(void *data,
|
||||
int len,
|
||||
int index,
|
||||
struct tty_dev *tty_dev,
|
||||
int complete)
|
||||
{
|
||||
struct gdm *gdm = tty_dev->gdm[index];
|
||||
|
||||
if (!gdm_tty_ready(gdm)) {
|
||||
if (complete == RECV_PACKET_PROCESS_COMPLETE)
|
||||
gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
|
||||
gdm_tty_recv_complete);
|
||||
return TO_HOST_PORT_CLOSE;
|
||||
}
|
||||
|
||||
if (data && len) {
|
||||
if (tty_buffer_request_room(&gdm->port, len) == len) {
|
||||
tty_insert_flip_string(&gdm->port, data, len);
|
||||
tty_flip_buffer_push(&gdm->port);
|
||||
} else {
|
||||
return TO_HOST_BUFFER_REQUEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (complete == RECV_PACKET_PROCESS_COMPLETE)
|
||||
gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
|
||||
gdm_tty_recv_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gdm_tty_send_complete(void *arg)
|
||||
{
|
||||
struct gdm *gdm = arg;
|
||||
|
||||
if (!gdm_tty_ready(gdm))
|
||||
return;
|
||||
|
||||
tty_port_tty_wakeup(&gdm->port);
|
||||
}
|
||||
|
||||
static ssize_t gdm_tty_write(struct tty_struct *tty, const u8 *buf, size_t len)
|
||||
{
|
||||
struct gdm *gdm = tty->driver_data;
|
||||
size_t remain = len;
|
||||
size_t sent_len = 0;
|
||||
|
||||
if (!gdm_tty_ready(gdm))
|
||||
return -ENODEV;
|
||||
|
||||
while (remain) {
|
||||
size_t sending_len = min_t(size_t, MUX_TX_MAX_SIZE, remain);
|
||||
|
||||
gdm->tty_dev->send_func(gdm->tty_dev->priv_dev,
|
||||
(void *)(buf + sent_len),
|
||||
sending_len,
|
||||
gdm->index,
|
||||
gdm_tty_send_complete,
|
||||
gdm);
|
||||
sent_len += sending_len;
|
||||
remain -= sending_len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static unsigned int gdm_tty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct gdm *gdm = tty->driver_data;
|
||||
|
||||
if (!gdm_tty_ready(gdm))
|
||||
return 0;
|
||||
|
||||
return WRITE_SIZE;
|
||||
}
|
||||
|
||||
int register_lte_tty_device(struct tty_dev *tty_dev, struct device *device)
|
||||
{
|
||||
struct gdm *gdm;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < TTY_MAX_COUNT; i++) {
|
||||
gdm = kmalloc(sizeof(*gdm), GFP_KERNEL);
|
||||
if (!gdm)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&gdm_table_lock);
|
||||
for (j = 0; j < GDM_TTY_MINOR; j++) {
|
||||
if (!gdm_table[i][j])
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == GDM_TTY_MINOR) {
|
||||
kfree(gdm);
|
||||
mutex_unlock(&gdm_table_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gdm_table[i][j] = gdm;
|
||||
mutex_unlock(&gdm_table_lock);
|
||||
|
||||
tty_dev->gdm[i] = gdm;
|
||||
tty_port_init(&gdm->port);
|
||||
|
||||
gdm->port.ops = &gdm_port_ops;
|
||||
gdm->index = i;
|
||||
gdm->minor = j;
|
||||
gdm->tty_dev = tty_dev;
|
||||
|
||||
tty_port_register_device(&gdm->port, gdm_driver[i],
|
||||
gdm->minor, device);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_ISSUE_NUM; i++)
|
||||
gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
|
||||
gdm_tty_recv_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unregister_lte_tty_device(struct tty_dev *tty_dev)
|
||||
{
|
||||
struct gdm *gdm;
|
||||
struct tty_struct *tty;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TTY_MAX_COUNT; i++) {
|
||||
gdm = tty_dev->gdm[i];
|
||||
if (!gdm)
|
||||
continue;
|
||||
|
||||
mutex_lock(&gdm_table_lock);
|
||||
gdm_table[gdm->index][gdm->minor] = NULL;
|
||||
mutex_unlock(&gdm_table_lock);
|
||||
|
||||
tty = tty_port_tty_get(&gdm->port);
|
||||
if (tty) {
|
||||
tty_vhangup(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
tty_unregister_device(gdm_driver[i], gdm->minor);
|
||||
tty_port_put(&gdm->port);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct tty_operations gdm_tty_ops = {
|
||||
.install = gdm_tty_install,
|
||||
.open = gdm_tty_open,
|
||||
.close = gdm_tty_close,
|
||||
.cleanup = gdm_tty_cleanup,
|
||||
.hangup = gdm_tty_hangup,
|
||||
.write = gdm_tty_write,
|
||||
.write_room = gdm_tty_write_room,
|
||||
};
|
||||
|
||||
int register_lte_tty_driver(void)
|
||||
{
|
||||
struct tty_driver *tty_driver;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < TTY_MAX_COUNT; i++) {
|
||||
tty_driver = tty_alloc_driver(GDM_TTY_MINOR,
|
||||
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
|
||||
if (IS_ERR(tty_driver))
|
||||
return PTR_ERR(tty_driver);
|
||||
|
||||
tty_driver->owner = THIS_MODULE;
|
||||
tty_driver->driver_name = DRIVER_STRING[i];
|
||||
tty_driver->name = DEVICE_STRING[i];
|
||||
tty_driver->major = GDM_TTY_MAJOR;
|
||||
tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
tty_driver->init_termios = tty_std_termios;
|
||||
tty_driver->init_termios.c_cflag = B9600 | CS8 | HUPCL | CLOCAL;
|
||||
tty_driver->init_termios.c_lflag = ISIG | ICANON | IEXTEN;
|
||||
tty_set_operations(tty_driver, &gdm_tty_ops);
|
||||
|
||||
ret = tty_register_driver(tty_driver);
|
||||
if (ret) {
|
||||
tty_driver_kref_put(tty_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gdm_driver[i] = tty_driver;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unregister_lte_tty_driver(void)
|
||||
{
|
||||
struct tty_driver *tty_driver;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TTY_MAX_COUNT; i++) {
|
||||
tty_driver = gdm_driver[i];
|
||||
if (tty_driver) {
|
||||
tty_unregister_driver(tty_driver);
|
||||
tty_driver_kref_put(tty_driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _GDM_TTY_H_
|
||||
#define _GDM_TTY_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
#define TTY_MAX_COUNT 2
|
||||
|
||||
#define MAX_ISSUE_NUM 3
|
||||
|
||||
enum TO_HOST_RESULT {
|
||||
TO_HOST_BUFFER_REQUEST_FAIL = 1,
|
||||
TO_HOST_PORT_CLOSE = 2,
|
||||
TO_HOST_INVALID_PACKET = 3,
|
||||
};
|
||||
|
||||
enum RECV_PACKET_PROCESS {
|
||||
RECV_PACKET_PROCESS_COMPLETE = 0,
|
||||
RECV_PACKET_PROCESS_CONTINUE = 1,
|
||||
};
|
||||
|
||||
struct gdm {
|
||||
struct tty_dev *tty_dev;
|
||||
struct tty_port port;
|
||||
unsigned int index;
|
||||
unsigned int minor;
|
||||
};
|
||||
|
||||
struct tty_dev {
|
||||
void *priv_dev;
|
||||
int (*send_func)(void *priv_dev,
|
||||
void *data,
|
||||
int len,
|
||||
int tty_index,
|
||||
void (*cb)(void *cb_data),
|
||||
void *cb_data);
|
||||
int (*recv_func)(void *priv_dev,
|
||||
int (*cb)(void *data,
|
||||
int len,
|
||||
int tty_index,
|
||||
struct tty_dev *tty_dev,
|
||||
int complete));
|
||||
int (*send_control)(void *priv_dev,
|
||||
int request,
|
||||
int value,
|
||||
void *data,
|
||||
int len);
|
||||
struct gdm *gdm[2];
|
||||
};
|
||||
|
||||
int register_lte_tty_driver(void);
|
||||
void unregister_lte_tty_driver(void);
|
||||
int register_lte_tty_device(struct tty_dev *tty_dev, struct device *dev);
|
||||
void unregister_lte_tty_device(struct tty_dev *tty_dev);
|
||||
|
||||
#endif /* _GDM_USB_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,99 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _GDM_USB_H_
|
||||
#define _GDM_USB_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#include "gdm_endian.h"
|
||||
#include "hci_packet.h"
|
||||
|
||||
#define PM_NORMAL 0
|
||||
#define PM_SUSPEND 1
|
||||
#define AUTO_SUSPEND_TIMER 5000 /* ms */
|
||||
|
||||
#define RX_BUF_SIZE (1024 * 32)
|
||||
#define TX_BUF_SIZE (1024 * 32)
|
||||
#define SDU_BUF_SIZE 2048
|
||||
#define MAX_SDU_SIZE (1024 * 30)
|
||||
#define MAX_PACKET_IN_MULTI_SDU 256
|
||||
|
||||
#define VID_GCT 0x1076
|
||||
#define PID_GDM7240 0x8000
|
||||
#define PID_GDM7243 0x9000
|
||||
|
||||
#define NETWORK_INTERFACE 1
|
||||
#define USB_SC_SCSI 0x06
|
||||
#define USB_PR_BULK 0x50
|
||||
|
||||
#define MAX_NUM_SDU_BUF 64
|
||||
|
||||
struct usb_tx {
|
||||
struct list_head list;
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
u32 len;
|
||||
void (*callback)(void *cb_data);
|
||||
void *cb_data;
|
||||
struct tx_cxt *tx;
|
||||
u8 is_sdu;
|
||||
};
|
||||
|
||||
struct usb_tx_sdu {
|
||||
struct list_head list;
|
||||
u8 *buf;
|
||||
u32 len;
|
||||
void (*callback)(void *cb_data);
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct usb_rx {
|
||||
struct list_head to_host_list;
|
||||
struct list_head free_list;
|
||||
struct list_head rx_submit_list;
|
||||
struct rx_cxt *rx;
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
int (*callback)(void *cb_data, void *data, int len, int context);
|
||||
void *cb_data;
|
||||
void *index;
|
||||
};
|
||||
|
||||
struct tx_cxt {
|
||||
struct list_head sdu_list;
|
||||
struct list_head hci_list;
|
||||
struct list_head free_list;
|
||||
u32 avail_count;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct rx_cxt {
|
||||
struct list_head to_host_list;
|
||||
struct list_head rx_submit_list;
|
||||
struct list_head free_list;
|
||||
u32 avail_count;
|
||||
spinlock_t to_host_lock;
|
||||
spinlock_t rx_lock;
|
||||
spinlock_t submit_lock;
|
||||
};
|
||||
|
||||
struct lte_udev {
|
||||
struct usb_device *usbdev;
|
||||
struct tx_cxt tx;
|
||||
struct rx_cxt rx;
|
||||
struct delayed_work work_tx;
|
||||
struct delayed_work work_rx;
|
||||
u8 gdm_ed;
|
||||
u8 send_complete;
|
||||
u8 tx_stop;
|
||||
struct usb_interface *intf;
|
||||
int (*rx_cb)(void *cb_data, void *data, int len, int context);
|
||||
int usb_state;
|
||||
u8 request_mac_addr;
|
||||
};
|
||||
|
||||
#endif /* _GDM_USB_H_ */
|
||||
@@ -1,45 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _HCI_H_
|
||||
#define _HCI_H_
|
||||
|
||||
#define LTE_GET_INFORMATION 0x3002
|
||||
#define LTE_GET_INFORMATION_RESULT 0xB003
|
||||
#define MAC_ADDRESS 0xA2
|
||||
|
||||
#define LTE_LINK_ON_OFF_INDICATION 0xB133
|
||||
#define LTE_PDN_TABLE_IND 0xB143
|
||||
|
||||
#define LTE_TX_SDU 0x3200
|
||||
#define LTE_RX_SDU 0xB201
|
||||
#define LTE_TX_MULTI_SDU 0x3202
|
||||
#define LTE_RX_MULTI_SDU 0xB203
|
||||
|
||||
#define LTE_DL_SDU_FLOW_CONTROL 0x3305
|
||||
#define LTE_UL_SDU_FLOW_CONTROL 0xB306
|
||||
|
||||
#define LTE_AT_CMD_TO_DEVICE 0x3307
|
||||
#define LTE_AT_CMD_FROM_DEVICE 0xB308
|
||||
|
||||
#define LTE_SDIO_DM_SEND_PKT 0x3312
|
||||
#define LTE_SDIO_DM_RECV_PKT 0xB313
|
||||
|
||||
#define LTE_NV_RESTORE_REQUEST 0xB30C
|
||||
#define LTE_NV_RESTORE_RESPONSE 0x330D
|
||||
#define LTE_NV_SAVE_REQUEST 0xB30E
|
||||
#define NV_TYPE_LTE_INFO 0x00
|
||||
#define NV_TYPE_BOARD_CONFIG 0x01
|
||||
#define NV_TYPE_RF_CAL 0x02
|
||||
#define NV_TYPE_TEMP 0x03
|
||||
#define NV_TYPE_NET_INFO 0x04
|
||||
#define NV_TYPE_SAFETY_INFO 0x05
|
||||
#define NV_TYPE_CDMA_CAL 0x06
|
||||
#define NV_TYPE_VENDOR 0x07
|
||||
#define NV_TYPE_ALL 0xff
|
||||
#define LTE_NV_SAVE_RESPONSE 0x330F
|
||||
|
||||
#define LTE_AT_CMD_TO_DEVICE_EXT 0x3323
|
||||
#define LTE_AT_CMD_FROM_DEVICE_EXT 0xB324
|
||||
|
||||
#endif /* _HCI_H_ */
|
||||
@@ -1,82 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _HCI_PACKET_H_
|
||||
#define _HCI_PACKET_H_
|
||||
|
||||
#define HCI_HEADER_SIZE 4
|
||||
|
||||
/*
|
||||
* The NIC type definition:
|
||||
* For backward compatibility, lower 16 bits used as they were.
|
||||
* Lower 16 bit: NIC_TYPE values
|
||||
* Uppoer 16 bit: NIC_TYPE Flags
|
||||
*/
|
||||
#define NIC_TYPE_NIC0 0x00000010
|
||||
#define NIC_TYPE_NIC1 0x00000011
|
||||
#define NIC_TYPE_NIC2 0x00000012
|
||||
#define NIC_TYPE_NIC3 0x00000013
|
||||
#define NIC_TYPE_ARP 0x00000100
|
||||
#define NIC_TYPE_ICMPV6 0x00000200
|
||||
#define NIC_TYPE_MASK 0x0000FFFF
|
||||
#define NIC_TYPE_F_IPV4 0x00010000
|
||||
#define NIC_TYPE_F_IPV6 0x00020000
|
||||
#define NIC_TYPE_F_DHCP 0x00040000
|
||||
#define NIC_TYPE_F_NDP 0x00080000
|
||||
#define NIC_TYPE_F_VLAN 0x00100000
|
||||
|
||||
struct hci_packet {
|
||||
__dev16 cmd_evt;
|
||||
__dev16 len;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct tlv {
|
||||
u8 type;
|
||||
u8 len;
|
||||
u8 *data[];
|
||||
} __packed;
|
||||
|
||||
struct sdu_header {
|
||||
__dev16 cmd_evt;
|
||||
__dev16 len;
|
||||
__dev32 dft_eps_id;
|
||||
__dev32 bearer_ID;
|
||||
__dev32 nic_type;
|
||||
} __packed;
|
||||
|
||||
struct sdu {
|
||||
__dev16 cmd_evt;
|
||||
__dev16 len;
|
||||
__dev32 dft_eps_ID;
|
||||
__dev32 bearer_ID;
|
||||
__dev32 nic_type;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct multi_sdu {
|
||||
__dev16 cmd_evt;
|
||||
__dev16 len;
|
||||
__dev16 num_packet;
|
||||
__dev16 reserved;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct hci_pdn_table_ind {
|
||||
__dev16 cmd_evt;
|
||||
__dev16 len;
|
||||
u8 activate;
|
||||
__dev32 dft_eps_id;
|
||||
__dev32 nic_type;
|
||||
u8 pdn_type;
|
||||
u8 ipv4_addr[4];
|
||||
u8 ipv6_intf_id[8];
|
||||
} __packed;
|
||||
|
||||
struct hci_connect_ind {
|
||||
__dev16 cmd_evt;
|
||||
__dev16 len;
|
||||
__dev32 connect;
|
||||
} __packed;
|
||||
|
||||
#endif /* _HCI_PACKET_H_ */
|
||||
@@ -1,128 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "netlink_k.h"
|
||||
|
||||
static DEFINE_MUTEX(netlink_mutex);
|
||||
|
||||
#define ND_MAX_GROUP 30
|
||||
#define ND_IFINDEX_LEN sizeof(int)
|
||||
#define ND_NLMSG_SPACE(len) (NLMSG_SPACE(len) + ND_IFINDEX_LEN)
|
||||
#define ND_NLMSG_DATA(nlh) ((void *)((char *)NLMSG_DATA(nlh) + \
|
||||
ND_IFINDEX_LEN))
|
||||
#define ND_NLMSG_S_LEN(len) ((len) + ND_IFINDEX_LEN)
|
||||
#define ND_NLMSG_R_LEN(nlh) ((nlh)->nlmsg_len - ND_IFINDEX_LEN)
|
||||
#define ND_NLMSG_IFIDX(nlh) NLMSG_DATA(nlh)
|
||||
#define ND_MAX_MSG_LEN (1024 * 32)
|
||||
|
||||
static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
|
||||
|
||||
static void netlink_rcv_cb(struct sk_buff *skb)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct net_device *dev;
|
||||
u32 mlen;
|
||||
void *msg;
|
||||
int ifindex;
|
||||
|
||||
if (!rcv_cb) {
|
||||
pr_err("nl cb - unregistered\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb->len < NLMSG_HDRLEN) {
|
||||
pr_err("nl cb - invalid skb length\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nlh = (struct nlmsghdr *)skb->data;
|
||||
|
||||
if (skb->len < nlh->nlmsg_len || nlh->nlmsg_len > ND_MAX_MSG_LEN) {
|
||||
pr_err("nl cb - invalid length (%d,%d)\n",
|
||||
skb->len, nlh->nlmsg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
|
||||
msg = ND_NLMSG_DATA(nlh);
|
||||
mlen = ND_NLMSG_R_LEN(nlh);
|
||||
|
||||
dev = dev_get_by_index(&init_net, ifindex);
|
||||
if (dev) {
|
||||
rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
|
||||
dev_put(dev);
|
||||
} else {
|
||||
pr_err("nl cb - dev (%d) not found\n", ifindex);
|
||||
}
|
||||
}
|
||||
|
||||
static void netlink_rcv(struct sk_buff *skb)
|
||||
{
|
||||
mutex_lock(&netlink_mutex);
|
||||
netlink_rcv_cb(skb);
|
||||
mutex_unlock(&netlink_mutex);
|
||||
}
|
||||
|
||||
struct sock *netlink_init(int unit,
|
||||
void (*cb)(struct net_device *dev, u16 type,
|
||||
void *msg, int len))
|
||||
{
|
||||
struct sock *sock;
|
||||
struct netlink_kernel_cfg cfg = {
|
||||
.input = netlink_rcv,
|
||||
};
|
||||
|
||||
sock = netlink_kernel_create(&init_net, unit, &cfg);
|
||||
|
||||
if (sock)
|
||||
rcv_cb = cb;
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len,
|
||||
struct net_device *dev)
|
||||
{
|
||||
static u32 seq;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct nlmsghdr *nlh;
|
||||
int ret = 0;
|
||||
|
||||
if (group > ND_MAX_GROUP)
|
||||
return -EINVAL;
|
||||
|
||||
if (!netlink_has_listeners(sock, group + 1))
|
||||
return -ESRCH;
|
||||
|
||||
skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
seq++;
|
||||
|
||||
nlh = nlmsg_put(skb, 0, seq, type, len, 0);
|
||||
memcpy(NLMSG_DATA(nlh), msg, len);
|
||||
NETLINK_CB(skb).portid = 0;
|
||||
NETLINK_CB(skb).dst_group = 0;
|
||||
|
||||
ret = netlink_broadcast(sock, skb, 0, group + 1, GFP_ATOMIC);
|
||||
if (!ret)
|
||||
return len;
|
||||
|
||||
if (ret != -ESRCH)
|
||||
netdev_err(dev, "nl broadcast g=%d, t=%d, l=%d, r=%d\n",
|
||||
group, type, len, ret);
|
||||
else if (netlink_has_listeners(sock, group + 1))
|
||||
return -EAGAIN;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
|
||||
|
||||
#ifndef _NETLINK_K_H
|
||||
#define _NETLINK_K_H
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
struct sock *netlink_init(int unit,
|
||||
void (*cb)(struct net_device *dev,
|
||||
u16 type, void *msg, int len));
|
||||
int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len,
|
||||
struct net_device *dev);
|
||||
|
||||
#endif /* _NETLINK_K_H_ */
|
||||
251
drivers/staging/gpib/Kconfig
Normal file
251
drivers/staging/gpib/Kconfig
Normal file
@@ -0,0 +1,251 @@
|
||||
# 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
|
||||
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
|
||||
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 BROKEN
|
||||
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 BROKEN
|
||||
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 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
|
||||
bool "PCMCIA/Cardbus support for NI MC and Ines boards"
|
||||
depends on PCCARD && (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
|
||||
20
drivers/staging/gpib/Makefile
Normal file
20
drivers/staging/gpib/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
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/
|
||||
21
drivers/staging/gpib/TODO
Normal file
21
drivers/staging/gpib/TODO
Normal file
@@ -0,0 +1,21 @@
|
||||
TODO:
|
||||
- checkpatch.pl fixes
|
||||
- fix device drivers that are broken ("depends on BROKEN" in Kconfig)
|
||||
- tidy-up comments:
|
||||
- there are some "//comments" and "// comments" scattered around
|
||||
- sometimes they are misaligned
|
||||
- sometimes "// comments" are interleaved with "/* comments */"
|
||||
- multiline comments should start with initial almost-blank line:
|
||||
/*
|
||||
* Good
|
||||
* multiline
|
||||
* comment
|
||||
*/
|
||||
/* Bad
|
||||
* multiline
|
||||
* comment
|
||||
*/
|
||||
- resolve XXX notes where possible
|
||||
- fix FIXME notes
|
||||
- clean-up commented-out code
|
||||
- fix typos
|
||||
2
drivers/staging/gpib/agilent_82350b/Makefile
Normal file
2
drivers/staging/gpib/agilent_82350b/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
obj-m += agilent_82350b.o
|
||||
932
drivers/staging/gpib/agilent_82350b/agilent_82350b.c
Normal file
932
drivers/staging/gpib/agilent_82350b/agilent_82350b.c
Normal file
@@ -0,0 +1,932 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2002, 2004 by Frank Mori Hess *
|
||||
***************************************************************************/
|
||||
|
||||
#include "agilent_82350b.h"
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("GPIB driver for Agilent 82350b");
|
||||
|
||||
int agilent_82350b_accel_read(gpib_board_t *board, uint8_t *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) {
|
||||
//pr_info("ag-rd: using tms9914 read for REOS %x EOS %x\n",tms_priv->eos_flags,
|
||||
// tms_priv->eos);
|
||||
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)
|
||||
dev_err(board->gpib_dev, "%s: tms9914_read failed retval=%i\n",
|
||||
driver_name, retval);
|
||||
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;
|
||||
write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BIIE, IMR0); // disable BI interrupts
|
||||
while (i < num_fifo_bytes && *end == 0) {
|
||||
int block_size;
|
||||
int j;
|
||||
int count;
|
||||
|
||||
if (num_fifo_bytes - i < agilent_82350b_fifo_size)
|
||||
block_size = num_fifo_bytes - i;
|
||||
else
|
||||
block_size = 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) {
|
||||
dev_dbg(board->gpib_dev, "%s: read wait interrupted\n", driver_name);
|
||||
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)) {
|
||||
dev_err(board->gpib_dev, "%s: read timed out\n", driver_name);
|
||||
retval = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
|
||||
dev_err(board->gpib_dev, "%s: device clear interrupted read\n",
|
||||
driver_name);
|
||||
retval = -EINTR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_byte(tms_priv, tms_priv->imr0_bits, IMR0); // re-enable BI interrupts
|
||||
*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(gpib_board_t *board, int retval)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *a_priv = board->private_data;
|
||||
struct tms9914_priv *tms_priv = &a_priv->tms9914_priv;
|
||||
|
||||
if (retval) {
|
||||
dev_err(board->gpib_dev, "%s: write wait interrupted\n", driver_name);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
if (test_bit(TIMO_NUM, &board->status)) {
|
||||
dev_err(board->gpib_dev, "%s: write timed out\n", driver_name);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
|
||||
dev_err(board->gpib_dev, "%s: device clear interrupted write\n", driver_name);
|
||||
return -EINTR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int agilent_82350b_accel_write(gpib_board_t *board, uint8_t *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);
|
||||
|
||||
//pr_info("ag_ac_wr: event status 0x%x tms state 0x%lx\n", event_status, tms_priv->state);
|
||||
|
||||
#ifdef EXPERIMENTAL
|
||||
pr_info("ag_ac_wr: wait for previous BO to complete if any\n");
|
||||
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
|
||||
|
||||
//pr_info("ag_ac_wr: sending first byte\n");
|
||||
retval = agilent_82350b_write(board, buffer, 1, 0, &num_bytes);
|
||||
*bytes_written += num_bytes;
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
//pr_info("ag_ac_wr: %ld bytes eoi %d tms state 0x%lx\n",length, send_eoi, tms_priv->state);
|
||||
|
||||
write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BOIE, IMR0);
|
||||
for (i = 1; i < fifotransferlength;) {
|
||||
clear_bit(WRITE_READY_BN, &tms_priv->state);
|
||||
|
||||
if (fifotransferlength - i < agilent_82350b_fifo_size)
|
||||
block_size = fifotransferlength - i;
|
||||
else
|
||||
block_size = 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);
|
||||
|
||||
//pr_info("ag_ac_wr: send block: %d bytes tms 0x%lx\n", block_size,
|
||||
// tms_priv->state);
|
||||
|
||||
if (agilent_82350b_fifo_is_halted(a_priv)) {
|
||||
writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG);
|
||||
// pr_info("ag_ac_wr: needed restart\n");
|
||||
}
|
||||
|
||||
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);
|
||||
//pr_info("ag_ac_wr: sent %ld bytes tms 0x%lx\n", num_bytes, tms_priv->state);
|
||||
|
||||
*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)
|
||||
return retval;
|
||||
|
||||
if (send_eoi) {
|
||||
//pr_info("ag_ac_wr: sending last byte with eoi byte no: %d\n",
|
||||
// fifotransferlength+1);
|
||||
|
||||
retval = agilent_82350b_write(board, buffer + fifotransferlength, 1, send_eoi,
|
||||
&num_bytes);
|
||||
*bytes_written += num_bytes;
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned short read_and_clear_event_status(gpib_board_t *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;
|
||||
}
|
||||
|
||||
irqreturn_t agilent_82350b_interrupt(int irq, void *arg)
|
||||
|
||||
{
|
||||
int tms9914_status1 = 0, tms9914_status2 = 0;
|
||||
int event_status;
|
||||
gpib_board_t *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);
|
||||
}
|
||||
//pr_info("event_status=0x%x s1 %x s2 %x\n", event_status,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;
|
||||
}
|
||||
|
||||
void agilent_82350b_detach(gpib_board_t *board);
|
||||
|
||||
const char *driver_name = "agilent_82350b";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
int agilent_82350b_read(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int agilent_82350b_write(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int agilent_82350b_command(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int agilent_82350b_take_control(gpib_board_t *board, int synchronous)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_take_control_workaround(board, &priv->tms9914_priv, synchronous);
|
||||
}
|
||||
|
||||
int agilent_82350b_go_to_standby(gpib_board_t *board)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_go_to_standby(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
void agilent_82350b_request_system_control(gpib_board_t *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);
|
||||
tms9914_request_system_control(board, &a_priv->tms9914_priv, request_control);
|
||||
}
|
||||
|
||||
void agilent_82350b_interface_clear(gpib_board_t *board, int assert)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
tms9914_interface_clear(board, &priv->tms9914_priv, assert);
|
||||
}
|
||||
|
||||
void agilent_82350b_remote_enable(gpib_board_t *board, int enable)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
tms9914_remote_enable(board, &priv->tms9914_priv, enable);
|
||||
}
|
||||
|
||||
int agilent_82350b_enable_eos(gpib_board_t *board, uint8_t 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);
|
||||
}
|
||||
|
||||
void agilent_82350b_disable_eos(gpib_board_t *board)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
tms9914_disable_eos(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
unsigned int agilent_82350b_update_status(gpib_board_t *board, unsigned int clear_mask)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
|
||||
}
|
||||
|
||||
int agilent_82350b_primary_address(gpib_board_t *board, unsigned int address)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_primary_address(board, &priv->tms9914_priv, address);
|
||||
}
|
||||
|
||||
int agilent_82350b_secondary_address(gpib_board_t *board, unsigned int address, int enable)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
|
||||
}
|
||||
|
||||
int agilent_82350b_parallel_poll(gpib_board_t *board, uint8_t *result)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
|
||||
}
|
||||
|
||||
void agilent_82350b_parallel_poll_configure(gpib_board_t *board, uint8_t config)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
|
||||
}
|
||||
|
||||
void agilent_82350b_parallel_poll_response(gpib_board_t *board, int ist)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
|
||||
}
|
||||
|
||||
void agilent_82350b_serial_poll_response(gpib_board_t *board, uint8_t status)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
|
||||
}
|
||||
|
||||
uint8_t agilent_82350b_serial_poll_status(gpib_board_t *board)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_serial_poll_status(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
int agilent_82350b_line_status(const gpib_board_t *board)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_line_status(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
unsigned int agilent_82350b_t1_delay(gpib_board_t *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;
|
||||
}
|
||||
|
||||
void agilent_82350b_return_to_local(gpib_board_t *board)
|
||||
|
||||
{
|
||||
struct agilent_82350b_priv *priv = board->private_data;
|
||||
|
||||
tms9914_return_to_local(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
int agilent_82350b_allocate_private(gpib_board_t *board)
|
||||
|
||||
{
|
||||
board->private_data = kzalloc(sizeof(struct agilent_82350b_priv), GFP_KERNEL);
|
||||
if (!board->private_data)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void agilent_82350b_free_private(gpib_board_t *board)
|
||||
|
||||
{
|
||||
kfree(board->private_data);
|
||||
board->private_data = NULL;
|
||||
}
|
||||
|
||||
static int init_82350a_hardware(gpib_board_t *board, const gpib_board_config_t *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, "%s: the 82350A board requires firmware after powering on.\n",
|
||||
driver_name);
|
||||
return -EIO;
|
||||
}
|
||||
dev_info(board->gpib_dev, "%s: Loading firmware...\n", driver_name);
|
||||
|
||||
// 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, "%s: timed out loading firmware.\n", driver_name);
|
||||
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, "%s: timed out waiting for firmware load to complete.\n",
|
||||
driver_name);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
dev_info(board->gpib_dev, "%s: ...done.\n", driver_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_sram(gpib_board_t *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, "%s: SRAM test failed at %d wanted %d got %d\n",
|
||||
driver_name, i, (i & byte_mask), read_value);
|
||||
return -EIO;
|
||||
}
|
||||
if (need_resched())
|
||||
schedule();
|
||||
}
|
||||
dev_info(board->gpib_dev, "%s: SRAM test passed 0x%x bytes checked\n",
|
||||
driver_name, sram_length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int agilent_82350b_generic_attach(gpib_board_t *board, const gpib_board_config_t *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_info(board->gpib_dev, "%s: Agilent 82350B board found\n", driver_name);
|
||||
|
||||
} 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_info(board->gpib_dev, "%s: Agilent 82351B board found\n", driver_name);
|
||||
|
||||
} 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_info(board->gpib_dev, "%s: HP/Agilent 82350A board found\n",
|
||||
driver_name);
|
||||
} else {
|
||||
dev_err(board->gpib_dev, "%s: no 82350/82351 board found\n",
|
||||
driver_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pci_enable_device(a_priv->pci_device)) {
|
||||
dev_err(board->gpib_dev, "%s: error enabling pci device\n", driver_name);
|
||||
return -EIO;
|
||||
}
|
||||
if (pci_request_regions(a_priv->pci_device, driver_name))
|
||||
return -EIO;
|
||||
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, "%s: plx base address remapped to 0x%p\n",
|
||||
driver_name, 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, "%s: gpib base address remapped to 0x%p\n",
|
||||
driver_name, a_priv->gpib_base);
|
||||
tms_priv->iobase = 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, "%s: sram base address remapped to 0x%p\n",
|
||||
driver_name, 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, "%s: borg base address remapped to 0x%p\n",
|
||||
driver_name, 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, "%s: gpib base address remapped to 0x%p\n",
|
||||
driver_name, a_priv->gpib_base);
|
||||
tms_priv->iobase = 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, "%s: sram base address remapped to 0x%p\n",
|
||||
driver_name, 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, "%s: misc base address remapped to 0x%p\n",
|
||||
driver_name, a_priv->misc_base);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: invalid board\n", driver_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
retval = test_sram(board);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if (request_irq(a_priv->pci_device->irq, agilent_82350b_interrupt,
|
||||
IRQF_SHARED, driver_name, board)) {
|
||||
pr_err("%s: can't request IRQ %d\n", driver_name, a_priv->pci_device->irq);
|
||||
return -EIO;
|
||||
}
|
||||
a_priv->irq = a_priv->pci_device->irq;
|
||||
dev_dbg(board->gpib_dev, "%s: IRQ %d\n", driver_name, 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;
|
||||
}
|
||||
|
||||
int agilent_82350b_unaccel_attach(gpib_board_t *board, const gpib_board_config_t *config)
|
||||
|
||||
{
|
||||
return agilent_82350b_generic_attach(board, config, 0);
|
||||
}
|
||||
|
||||
int agilent_82350b_accel_attach(gpib_board_t *board, const gpib_board_config_t *config)
|
||||
|
||||
{
|
||||
return agilent_82350b_generic_attach(board, config, 1);
|
||||
}
|
||||
|
||||
void agilent_82350b_detach(gpib_board_t *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((void *)a_priv->misc_base);
|
||||
if (a_priv->borg_base)
|
||||
iounmap((void *)a_priv->borg_base);
|
||||
if (a_priv->sram_base)
|
||||
iounmap((void *)a_priv->sram_base);
|
||||
if (a_priv->gpib_base)
|
||||
iounmap((void *)a_priv->gpib_base);
|
||||
if (a_priv->plx_base)
|
||||
iounmap((void *)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);
|
||||
}
|
||||
|
||||
gpib_interface_t 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,
|
||||
t1_delay : agilent_82350b_t1_delay,
|
||||
return_to_local : agilent_82350b_return_to_local,
|
||||
};
|
||||
|
||||
gpib_interface_t 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,
|
||||
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 = "agilent_82350b",
|
||||
.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("agilent_82350b: pci_driver_register failed!\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
gpib_register_driver(&agilent_82350b_unaccel_interface, THIS_MODULE);
|
||||
gpib_register_driver(&agilent_82350b_interface, THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
209
drivers/staging/gpib/agilent_82350b/agilent_82350b.h
Normal file
209
drivers/staging/gpib/agilent_82350b/agilent_82350b.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/* 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 *plx_base; //82350a only
|
||||
void *gpib_base;
|
||||
void *sram_base;
|
||||
void *misc_base;
|
||||
void *borg_base;
|
||||
int irq;
|
||||
unsigned short card_mode_bits;
|
||||
unsigned short event_status_bits;
|
||||
enum board_model model;
|
||||
bool using_fifos;
|
||||
};
|
||||
|
||||
// driver name
|
||||
extern const char *driver_name;
|
||||
|
||||
// interfaces
|
||||
extern gpib_interface_t agilent_82350b_interface;
|
||||
// init functions
|
||||
|
||||
int agilent_82350b_unaccel_attach(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
int agilent_82350b_accel_attach(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
|
||||
// interface functions
|
||||
int agilent_82350b_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
|
||||
size_t *bytes_read);
|
||||
int agilent_82350b_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
|
||||
size_t *bytes_written);
|
||||
int agilent_82350b_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
|
||||
size_t *bytes_read);
|
||||
int agilent_82350b_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
|
||||
size_t *bytes_written);
|
||||
int agilent_82350b_command(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
size_t *bytes_written);
|
||||
int agilent_82350b_take_control(gpib_board_t *board, int synchronous);
|
||||
int agilent_82350b_go_to_standby(gpib_board_t *board);
|
||||
void agilent_82350b_request_system_control(gpib_board_t *board, int request_control);
|
||||
void agilent_82350b_interface_clear(gpib_board_t *board, int assert);
|
||||
void agilent_82350b_remote_enable(gpib_board_t *board, int enable);
|
||||
int agilent_82350b_enable_eos(gpib_board_t *board, uint8_t eos_byte, int
|
||||
compare_8_bits);
|
||||
void agilent_82350b_disable_eos(gpib_board_t *board);
|
||||
unsigned int agilent_82350b_update_status(gpib_board_t *board, unsigned int clear_mask);
|
||||
int agilent_82350b_primary_address(gpib_board_t *board, unsigned int address);
|
||||
int agilent_82350b_secondary_address(gpib_board_t *board, unsigned int address, int
|
||||
enable);
|
||||
int agilent_82350b_parallel_poll(gpib_board_t *board, uint8_t *result);
|
||||
void agilent_82350b_parallel_poll_configure(gpib_board_t *board, uint8_t config);
|
||||
void agilent_82350b_parallel_poll_response(gpib_board_t *board, int ist);
|
||||
void agilent_82350b_serial_poll_response(gpib_board_t *board, uint8_t status);
|
||||
void agilent_82350b_return_to_local(gpib_board_t *board);
|
||||
uint8_t agilent_82350b_serial_poll_status(gpib_board_t *board);
|
||||
int agilent_82350b_line_status(const gpib_board_t *board);
|
||||
unsigned int agilent_82350b_t1_delay(gpib_board_t *board, unsigned int nanosec);
|
||||
|
||||
// interrupt service routines
|
||||
irqreturn_t agilent_82350b_interrupt(int irq, void *arg);
|
||||
|
||||
// utility functions
|
||||
int agilent_82350b_allocate_private(gpib_board_t *board);
|
||||
void agilent_82350b_free_private(gpib_board_t *board);
|
||||
unsigned short read_and_clear_event_status(gpib_board_t *board);
|
||||
int read_transfer_counter(struct agilent_82350b_priv *a_priv);
|
||||
void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
4
drivers/staging/gpib/agilent_82357a/Makefile
Normal file
4
drivers/staging/gpib/agilent_82357a/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
obj-m += agilent_82357a.o
|
||||
|
||||
|
||||
1712
drivers/staging/gpib/agilent_82357a/agilent_82357a.c
Normal file
1712
drivers/staging/gpib/agilent_82357a/agilent_82357a.c
Normal file
File diff suppressed because it is too large
Load Diff
182
drivers/staging/gpib/agilent_82357a/agilent_82357a.h
Normal file
182
drivers/staging/gpib/agilent_82357a/agilent_82357a.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2004 by Frank Mori Hess *
|
||||
***************************************************************************/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/compiler_attributes.h>
|
||||
#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 semaphore 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;
|
||||
|
||||
4
drivers/staging/gpib/cb7210/Makefile
Normal file
4
drivers/staging/gpib/cb7210/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA
|
||||
obj-m += cb7210.o
|
||||
|
||||
|
||||
1556
drivers/staging/gpib/cb7210/cb7210.c
Normal file
1556
drivers/staging/gpib/cb7210/cb7210.c
Normal file
File diff suppressed because it is too large
Load Diff
251
drivers/staging/gpib/cb7210/cb7210.h
Normal file
251
drivers/staging/gpib/cb7210/cb7210.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2002 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#include "nec7210.h"
|
||||
#include "gpibP.h"
|
||||
#include "amccs5933.h"
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// interfaces
|
||||
extern gpib_interface_t cb_pcmcia_interface;
|
||||
extern gpib_interface_t cb_pcmcia_accel_interface;
|
||||
extern gpib_interface_t cb_pcmcia_unaccel_interface;
|
||||
|
||||
// interrupt service routines
|
||||
irqreturn_t cb_pci_interrupt(int irq, void *arg);
|
||||
irqreturn_t cb7210_interrupt(int irq, void *arg);
|
||||
irqreturn_t cb7210_internal_interrupt(gpib_board_t *board);
|
||||
|
||||
// interface functions
|
||||
int cb7210_read(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int *end, size_t *bytes_read);
|
||||
int cb7210_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int *end, size_t *bytes_read);
|
||||
int cb7210_write(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int send_eoi, size_t *bytes_written);
|
||||
int cb7210_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int send_eoi, size_t *bytes_written);
|
||||
int cb7210_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
|
||||
int cb7210_take_control(gpib_board_t *board, int synchronous);
|
||||
int cb7210_go_to_standby(gpib_board_t *board);
|
||||
void cb7210_request_system_control(gpib_board_t *board, int request_control);
|
||||
void cb7210_interface_clear(gpib_board_t *board, int assert);
|
||||
void cb7210_remote_enable(gpib_board_t *board, int enable);
|
||||
int cb7210_enable_eos(gpib_board_t *board, uint8_t eos_byte,
|
||||
int compare_8_bits);
|
||||
void cb7210_disable_eos(gpib_board_t *board);
|
||||
unsigned int cb7210_update_status(gpib_board_t *board, unsigned int clear_mask);
|
||||
int cb7210_primary_address(gpib_board_t *board, unsigned int address);
|
||||
int cb7210_secondary_address(gpib_board_t *board, unsigned int address,
|
||||
int enable);
|
||||
int cb7210_parallel_poll(gpib_board_t *board, uint8_t *result);
|
||||
void cb7210_serial_poll_response(gpib_board_t *board, uint8_t status);
|
||||
uint8_t cb7210_serial_poll_status(gpib_board_t *board);
|
||||
void cb7210_parallel_poll_configure(gpib_board_t *board, uint8_t configuration);
|
||||
void cb7210_parallel_poll_response(gpib_board_t *board, int ist);
|
||||
int cb7210_line_status(const gpib_board_t *board);
|
||||
unsigned int cb7210_t1_delay(gpib_board_t *board, unsigned int nano_sec);
|
||||
void cb7210_return_to_local(gpib_board_t *board);
|
||||
|
||||
// utility functions
|
||||
void cb7210_generic_detach(gpib_board_t *board);
|
||||
int cb7210_generic_attach(gpib_board_t *board);
|
||||
int cb7210_init(struct cb7210_priv *priv, gpib_board_t *board);
|
||||
|
||||
// pcmcia init/cleanup
|
||||
int cb_pcmcia_init_module(void);
|
||||
void cb_pcmcia_cleanup_module(void);
|
||||
|
||||
// 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 unsigned long nec7210_iobase(const struct cb7210_priv *cb_priv)
|
||||
{
|
||||
return (unsigned long)(cb_priv->nec7210_priv.iobase);
|
||||
}
|
||||
|
||||
static inline int cb7210_page_in_bits(unsigned int page)
|
||||
{
|
||||
return 0x50 | (page & 0xf);
|
||||
}
|
||||
|
||||
static inline uint8_t 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 uint8_t 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, uint8_t 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, uint8_t 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,
|
||||
};
|
||||
3
drivers/staging/gpib/cec/Makefile
Normal file
3
drivers/staging/gpib/cec/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
obj-m += cec_gpib.o
|
||||
|
||||
53
drivers/staging/gpib/cec/cec.h
Normal file
53
drivers/staging/gpib/cec/cec.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* 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;
|
||||
};
|
||||
|
||||
// interfaces
|
||||
extern gpib_interface_t cec_pci_interface;
|
||||
extern gpib_interface_t cec_pcmcia_interface;
|
||||
|
||||
// interface functions
|
||||
int cec_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read);
|
||||
int cec_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
|
||||
size_t *bytes_written);
|
||||
int cec_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
|
||||
int cec_take_control(gpib_board_t *board, int synchronous);
|
||||
int cec_go_to_standby(gpib_board_t *board);
|
||||
void cec_request_system_control(gpib_board_t *board, int request_control);
|
||||
void cec_interface_clear(gpib_board_t *board, int assert);
|
||||
void cec_remote_enable(gpib_board_t *board, int enable);
|
||||
int cec_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits);
|
||||
void cec_disable_eos(gpib_board_t *board);
|
||||
unsigned int cec_update_status(gpib_board_t *board, unsigned int clear_mask);
|
||||
int cec_primary_address(gpib_board_t *board, unsigned int address);
|
||||
int cec_secondary_address(gpib_board_t *board, unsigned int address, int enable);
|
||||
int cec_parallel_poll(gpib_board_t *board, uint8_t *result);
|
||||
void cec_parallel_poll_configure(gpib_board_t *board, uint8_t configuration);
|
||||
void cec_parallel_poll_response(gpib_board_t *board, int ist);
|
||||
void cec_serial_poll_response(gpib_board_t *board, uint8_t status);
|
||||
void cec_return_to_local(gpib_board_t *board);
|
||||
|
||||
// interrupt service routines
|
||||
irqreturn_t cec_interrupt(int irq, void *arg);
|
||||
|
||||
// utility functions
|
||||
void cec_free_private(gpib_board_t *board);
|
||||
int cec_generic_attach(gpib_board_t *board);
|
||||
void cec_init(struct cec_priv *priv, const gpib_board_t *board);
|
||||
|
||||
// offset between consecutive nec7210 registers
|
||||
static const int cec_reg_offset = 1;
|
||||
385
drivers/staging/gpib/cec/cec_gpib.c
Normal file
385
drivers/staging/gpib/cec/cec_gpib.c
Normal file
@@ -0,0 +1,385 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2002 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#include "cec.h"
|
||||
#include <linux/pci.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("GPIB driver for CEC PCI and PCMCIA boards");
|
||||
|
||||
/*
|
||||
* GPIB interrupt service routines
|
||||
*/
|
||||
|
||||
irqreturn_t cec_interrupt(int irq, void *arg)
|
||||
{
|
||||
gpib_board_t *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(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
|
||||
static void cec_pci_detach(gpib_board_t *board);
|
||||
|
||||
// wrappers for interface functions
|
||||
int cec_read(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int cec_write(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int cec_command(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int cec_take_control(gpib_board_t *board, int synchronous)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
|
||||
}
|
||||
|
||||
int cec_go_to_standby(gpib_board_t *board)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_go_to_standby(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
void cec_request_system_control(gpib_board_t *board, int request_control)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
|
||||
}
|
||||
|
||||
void cec_interface_clear(gpib_board_t *board, int assert)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_interface_clear(board, &priv->nec7210_priv, assert);
|
||||
}
|
||||
|
||||
void cec_remote_enable(gpib_board_t *board, int enable)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_remote_enable(board, &priv->nec7210_priv, enable);
|
||||
}
|
||||
|
||||
int cec_enable_eos(gpib_board_t *board, uint8_t 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);
|
||||
}
|
||||
|
||||
void cec_disable_eos(gpib_board_t *board)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_disable_eos(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
unsigned int cec_update_status(gpib_board_t *board, unsigned int clear_mask)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
|
||||
}
|
||||
|
||||
int cec_primary_address(gpib_board_t *board, unsigned int address)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_primary_address(board, &priv->nec7210_priv, address);
|
||||
}
|
||||
|
||||
int cec_secondary_address(gpib_board_t *board, unsigned int address, int enable)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
|
||||
}
|
||||
|
||||
int cec_parallel_poll(gpib_board_t *board, uint8_t *result)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
|
||||
}
|
||||
|
||||
void cec_parallel_poll_configure(gpib_board_t *board, uint8_t config)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
|
||||
}
|
||||
|
||||
void cec_parallel_poll_response(gpib_board_t *board, int ist)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
|
||||
}
|
||||
|
||||
void cec_serial_poll_response(gpib_board_t *board, uint8_t status)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
|
||||
}
|
||||
|
||||
static uint8_t cec_serial_poll_status(gpib_board_t *board)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_serial_poll_status(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
static unsigned int cec_t1_delay(gpib_board_t *board, unsigned int nano_sec)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec);
|
||||
}
|
||||
|
||||
void cec_return_to_local(gpib_board_t *board)
|
||||
{
|
||||
struct cec_priv *priv = board->private_data;
|
||||
|
||||
nec7210_return_to_local(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
gpib_interface_t 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(gpib_board_t *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;
|
||||
}
|
||||
|
||||
void cec_free_private(gpib_board_t *board)
|
||||
{
|
||||
kfree(board->private_data);
|
||||
board->private_data = NULL;
|
||||
}
|
||||
|
||||
int cec_generic_attach(gpib_board_t *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;
|
||||
}
|
||||
|
||||
void cec_init(struct cec_priv *cec_priv, const gpib_board_t *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);
|
||||
}
|
||||
|
||||
int cec_pci_attach(gpib_board_t *board, const gpib_board_config_t *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) {
|
||||
pr_err("gpib: no cec PCI board found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pci_enable_device(cec_priv->pci_device)) {
|
||||
pr_err("error enabling pci device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pci_request_regions(cec_priv->pci_device, "cec-gpib"))
|
||||
return -1;
|
||||
|
||||
cec_priv->plx_iobase = pci_resource_start(cec_priv->pci_device, 1);
|
||||
pr_info(" plx9050 base address 0x%lx\n", cec_priv->plx_iobase);
|
||||
nec_priv->iobase = (void *)(pci_resource_start(cec_priv->pci_device, 3));
|
||||
pr_info(" nec7210 base address 0x%p\n", nec_priv->iobase);
|
||||
|
||||
isr_flags |= IRQF_SHARED;
|
||||
if (request_irq(cec_priv->pci_device->irq, cec_interrupt, isr_flags, "pci-gpib", board)) {
|
||||
pr_err("gpib: can't request IRQ %d\n", cec_priv->pci_device->irq);
|
||||
return -1;
|
||||
}
|
||||
cec_priv->irq = cec_priv->pci_device->irq;
|
||||
if (gpib_request_pseudo_irq(board, cec_interrupt)) {
|
||||
pr_err("cec: 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;
|
||||
}
|
||||
|
||||
void cec_pci_detach(gpib_board_t *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 = "cec_gpib",
|
||||
.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("cec_gpib: pci_driver_register failed!\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
gpib_register_driver(&cec_pci_interface, THIS_MODULE);
|
||||
|
||||
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);
|
||||
6
drivers/staging/gpib/common/Makefile
Normal file
6
drivers/staging/gpib/common/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
obj-m += gpib_common.o
|
||||
|
||||
gpib_common-objs := gpib_os.o iblib.o
|
||||
|
||||
|
||||
2328
drivers/staging/gpib/common/gpib_os.c
Normal file
2328
drivers/staging/gpib/common/gpib_os.c
Normal file
File diff suppressed because it is too large
Load Diff
740
drivers/staging/gpib/common/iblib.c
Normal file
740
drivers/staging/gpib/common/iblib.c
Normal file
@@ -0,0 +1,740 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2001, 2002 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#include "ibsys.h"
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/*
|
||||
* 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(gpib_board_t *board, int sync, int fallback_to_async)
|
||||
{
|
||||
int status = ibstatus(board);
|
||||
int retval;
|
||||
|
||||
if ((status & CIC) == 0) {
|
||||
pr_err("gpib: not CIC during %s()\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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(gpib_board_t *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 & ValidATN) {
|
||||
if ((lines & BusATN) == 0) {
|
||||
pr_err("gpib: ATN not asserted in %s()?", __func__);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((lines & ValidNRFD) && (lines & ValidNDAC)) {
|
||||
if ((lines & BusNRFD) == 0 && (lines & BusNDAC) == 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(gpib_board_t *board, uint8_t *buf, size_t length, size_t *bytes_written)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
int status;
|
||||
|
||||
*bytes_written = 0;
|
||||
|
||||
status = ibstatus(board);
|
||||
|
||||
if ((status & CIC) == 0) {
|
||||
pr_err("gpib: cannot send command when not controller-in-charge\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
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(gpib_board_t *board)
|
||||
{
|
||||
int status = ibstatus(board);
|
||||
int retval;
|
||||
|
||||
if ((status & CIC) == 0) {
|
||||
pr_err("gpib: not CIC during %s()\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
retval = board->interface->go_to_standby(board); /* go to standby */
|
||||
if (retval < 0)
|
||||
pr_err("gpib: error while going to standby\n");
|
||||
|
||||
board->interface->update_status(board, 0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int autospoll_wait_should_wake_up(gpib_board_t *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)
|
||||
{
|
||||
gpib_board_t *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 {
|
||||
pr_err("gpib%i: %s: try_module_get() failed!\n", board->minor, __func__);
|
||||
}
|
||||
if (retval <= 0) {
|
||||
pr_err("gpib%i: %s: stuck SRQ\n", board->minor, __func__);
|
||||
|
||||
atomic_set(&board->stuck_srq, 1); // XXX could be better
|
||||
set_bit(SRQI_NUM, &board->status);
|
||||
}
|
||||
}
|
||||
pr_info("gpib%i: exiting autospoll thread\n", board->minor);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int ibonline(gpib_board_t *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);
|
||||
pr_err("gpib: interface attach failed\n");
|
||||
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) {
|
||||
pr_err("gpib: failed to create autospoll thread\n");
|
||||
board->interface->detach(board);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
board->online = 1;
|
||||
dev_dbg(board->gpib_dev, "gpib: board online\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX need to make sure board is generally not in use (grab board lock?) */
|
||||
int iboffline(gpib_board_t *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)
|
||||
pr_err("gpib: 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, "gpib: 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 gpib_board_t *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(gpib_board_t *board, uint8_t *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) {
|
||||
pr_warn("gpib: %s() called with zero length?\n", __func__);
|
||||
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) {
|
||||
pr_err("gpib read error\n");
|
||||
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(gpib_board_t *board, uint8_t *result)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
os_start_timer(board, board->usec_timeout);
|
||||
retval = ibcac(board, 1, 1);
|
||||
if (retval)
|
||||
return -1;
|
||||
|
||||
if (board->interface->parallel_poll(board, result)) {
|
||||
pr_err("gpib: parallel poll failed\n");
|
||||
retval = -1;
|
||||
}
|
||||
os_remove_timer(board);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int ibppc(gpib_board_t *board, uint8_t configuration)
|
||||
{
|
||||
configuration &= 0x1f;
|
||||
board->interface->parallel_poll_configure(board, configuration);
|
||||
board->parallel_poll_configuration = configuration;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ibrsv2(gpib_board_t *board, uint8_t 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)) {
|
||||
pr_err("gpib: interface requested service while CIC\n");
|
||||
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(gpib_board_t *board, unsigned int usec_duration)
|
||||
{
|
||||
if (board->master == 0) {
|
||||
pr_err("gpib: tried to assert IFC when not system controller\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (usec_duration < 100)
|
||||
usec_duration = 100;
|
||||
if (usec_duration > 1000) {
|
||||
usec_duration = 1000;
|
||||
pr_warn("gpib: warning, shortening long udelay\n");
|
||||
}
|
||||
|
||||
dev_dbg(board->gpib_dev, "sending interface clear\n");
|
||||
board->interface->interface_clear(board, 1);
|
||||
udelay(usec_duration);
|
||||
board->interface->interface_clear(board, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ibrsc(gpib_board_t *board, int request_control)
|
||||
{
|
||||
board->master = request_control != 0;
|
||||
if (!board->interface->request_system_control) {
|
||||
pr_err("gpib: bug! driver does not implement request_system_control()\n");
|
||||
return;
|
||||
}
|
||||
board->interface->request_system_control(board, request_control);
|
||||
}
|
||||
|
||||
/*
|
||||
* IBSRE
|
||||
* Send REN true if v is non-zero or false if v is zero.
|
||||
*/
|
||||
int ibsre(gpib_board_t *board, int enable)
|
||||
{
|
||||
if (board->master == 0) {
|
||||
pr_err("gpib: tried to set REN when not system controller\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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(gpib_board_t *board, unsigned int addr)
|
||||
{
|
||||
if (addr > MAX_GPIB_PRIMARY_ADDRESS) {
|
||||
pr_err("gpib: invalid primary address %u\n", addr);
|
||||
return -1;
|
||||
}
|
||||
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(gpib_board_t *board, int addr)
|
||||
{
|
||||
if (addr > MAX_GPIB_SECONDARY_ADDRESS) {
|
||||
pr_err("gpib: invalid secondary address %i\n", addr);
|
||||
return -1;
|
||||
}
|
||||
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(gpib_board_t *board, int eos, int eosflags)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (eosflags & ~EOS_MASK) {
|
||||
pr_err("bad EOS modes\n");
|
||||
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(gpib_board_t *board)
|
||||
{
|
||||
return general_ibstatus(board, NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
int general_ibstatus(gpib_board_t *board, const gpib_status_queue_t *device,
|
||||
int clear_mask, int set_mask, gpib_descriptor_t *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 & ValidSRQ)) {
|
||||
if ((line_status & BusSRQ))
|
||||
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 {
|
||||
gpib_board_t *board;
|
||||
struct timer_list timer;
|
||||
int timed_out;
|
||||
unsigned long usec_timeout;
|
||||
};
|
||||
|
||||
static void wait_timeout(struct timer_list *t)
|
||||
{
|
||||
struct wait_info *winfo = from_timer(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, gpib_status_queue_t *status_queue,
|
||||
int wait_mask, int *status, gpib_descriptor_t *desc)
|
||||
{
|
||||
gpib_board_t *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)
|
||||
{
|
||||
del_timer_sync(&winfo->timer);
|
||||
destroy_timer_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(gpib_board_t *board, int wait_mask, int clear_mask, int set_mask,
|
||||
int *status, unsigned long usec_timeout, gpib_descriptor_t *desc)
|
||||
{
|
||||
int retval = 0;
|
||||
gpib_status_queue_t *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, 0);
|
||||
|
||||
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(gpib_board_t *board, uint8_t *buf, size_t cnt, int send_eoi, size_t *bytes_written)
|
||||
{
|
||||
int ret = 0;
|
||||
int retval;
|
||||
|
||||
if (cnt == 0) {
|
||||
pr_warn("gpib: %s() called with zero length?\n", __func__);
|
||||
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;
|
||||
}
|
||||
|
||||
31
drivers/staging/gpib/common/ibsys.h
Normal file
31
drivers/staging/gpib/common/ibsys.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#include "gpibP.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
#define MAX_GPIB_PRIMARY_ADDRESS 30
|
||||
#define MAX_GPIB_SECONDARY_ADDRESS 31
|
||||
|
||||
int gpib_allocate_board(gpib_board_t *board);
|
||||
void gpib_deallocate_board(gpib_board_t *board);
|
||||
|
||||
unsigned int num_status_bytes(const gpib_status_queue_t *dev);
|
||||
int push_status_byte(gpib_board_t *board, gpib_status_queue_t *device, uint8_t poll_byte);
|
||||
int pop_status_byte(gpib_board_t *board, gpib_status_queue_t *device, uint8_t *poll_byte);
|
||||
gpib_status_queue_t *get_gpib_status_queue(gpib_board_t *board, unsigned int pad, int sad);
|
||||
int get_serial_poll_byte(gpib_board_t *board, unsigned int pad, int sad,
|
||||
unsigned int usec_timeout, uint8_t *poll_byte);
|
||||
int autopoll_all_devices(gpib_board_t *board);
|
||||
3
drivers/staging/gpib/eastwood/Makefile
Normal file
3
drivers/staging/gpib/eastwood/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
obj-m += fluke_gpib.o
|
||||
|
||||
1179
drivers/staging/gpib/eastwood/fluke_gpib.c
Normal file
1179
drivers/staging/gpib/eastwood/fluke_gpib.c
Normal file
File diff suppressed because it is too large
Load Diff
143
drivers/staging/gpib/eastwood/fluke_gpib.h
Normal file
143
drivers/staging/gpib/eastwood/fluke_gpib.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* Author: Frank Mori Hess <fmh6jj@gmail.com>
|
||||
* copyright: (C) 2006, 2010, 2015 Fluke Corporation
|
||||
***************************************************************************/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#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 *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 uint8_t fluke_read_byte_nolock(struct nec7210_priv *nec_priv,
|
||||
int register_num)
|
||||
{
|
||||
u8 retval;
|
||||
|
||||
retval = readl(nec_priv->iobase + 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, uint8_t data,
|
||||
int register_num)
|
||||
{
|
||||
writel(data, nec_priv->iobase + register_num * nec_priv->offset);
|
||||
}
|
||||
|
||||
static inline uint8_t 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, uint8_t 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,
|
||||
};
|
||||
2
drivers/staging/gpib/fmh_gpib/Makefile
Normal file
2
drivers/staging/gpib/fmh_gpib/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
obj-$(CONFIG_GPIB_FMH) += fmh_gpib.o
|
||||
1725
drivers/staging/gpib/fmh_gpib/fmh_gpib.c
Normal file
1725
drivers/staging/gpib/fmh_gpib/fmh_gpib.c
Normal file
File diff suppressed because it is too large
Load Diff
177
drivers/staging/gpib/fmh_gpib/fmh_gpib.h
Normal file
177
drivers/staging/gpib/fmh_gpib/fmh_gpib.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* Author: Frank Mori Hess <fmh6jj@gmail.com>
|
||||
* Copyright: (C) 2006, 2010, 2015 Fluke Corporation
|
||||
* (C) 2017 Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/io.h>
|
||||
#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 *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 uint8_t gpib_cs_read_byte(struct nec7210_priv *nec_priv,
|
||||
unsigned int register_num)
|
||||
{
|
||||
return readb(nec_priv->iobase + register_num * nec_priv->offset);
|
||||
}
|
||||
|
||||
static inline void gpib_cs_write_byte(struct nec7210_priv *nec_priv, uint8_t data,
|
||||
unsigned int register_num)
|
||||
{
|
||||
writeb(data, nec_priv->iobase + 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
|
||||
};
|
||||
4
drivers/staging/gpib/gpio/Makefile
Normal file
4
drivers/staging/gpib/gpio/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
obj-m += gpib_bitbang.o
|
||||
|
||||
|
||||
1476
drivers/staging/gpib/gpio/gpib_bitbang.c
Normal file
1476
drivers/staging/gpib/gpio/gpib_bitbang.c
Normal file
File diff suppressed because it is too large
Load Diff
4
drivers/staging/gpib/hp_82335/Makefile
Normal file
4
drivers/staging/gpib/hp_82335/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
obj-m += hp82335.o
|
||||
|
||||
|
||||
360
drivers/staging/gpib/hp_82335/hp82335.c
Normal file
360
drivers/staging/gpib/hp_82335/hp82335.c
Normal file
@@ -0,0 +1,360 @@
|
||||
// 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)
|
||||
*/
|
||||
|
||||
#include "hp82335.h"
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("GPIB driver for HP 82335 interface cards");
|
||||
|
||||
static int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
|
||||
static void hp82335_detach(gpib_board_t *board);
|
||||
|
||||
// wrappers for interface functions
|
||||
int hp82335_read(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int hp82335_write(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int hp82335_command(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int hp82335_take_control(gpib_board_t *board, int synchronous)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_take_control(board, &priv->tms9914_priv, synchronous);
|
||||
}
|
||||
|
||||
int hp82335_go_to_standby(gpib_board_t *board)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_go_to_standby(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
void hp82335_request_system_control(gpib_board_t *board, int request_control)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_request_system_control(board, &priv->tms9914_priv, request_control);
|
||||
}
|
||||
|
||||
void hp82335_interface_clear(gpib_board_t *board, int assert)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_interface_clear(board, &priv->tms9914_priv, assert);
|
||||
}
|
||||
|
||||
void hp82335_remote_enable(gpib_board_t *board, int enable)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_remote_enable(board, &priv->tms9914_priv, enable);
|
||||
}
|
||||
|
||||
int hp82335_enable_eos(gpib_board_t *board, uint8_t 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);
|
||||
}
|
||||
|
||||
void hp82335_disable_eos(gpib_board_t *board)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_disable_eos(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
|
||||
}
|
||||
|
||||
int hp82335_primary_address(gpib_board_t *board, unsigned int address)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_primary_address(board, &priv->tms9914_priv, address);
|
||||
}
|
||||
|
||||
int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int enable)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
|
||||
}
|
||||
|
||||
int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
|
||||
}
|
||||
|
||||
void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
|
||||
}
|
||||
|
||||
void hp82335_parallel_poll_response(gpib_board_t *board, int ist)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
|
||||
}
|
||||
|
||||
void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
|
||||
}
|
||||
|
||||
static uint8_t hp82335_serial_poll_status(gpib_board_t *board)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_serial_poll_status(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
static int hp82335_line_status(const gpib_board_t *board)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_line_status(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
static unsigned int hp82335_t1_delay(gpib_board_t *board, unsigned int nano_sec)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec);
|
||||
}
|
||||
|
||||
void hp82335_return_to_local(gpib_board_t *board)
|
||||
{
|
||||
struct hp82335_priv *priv = board->private_data;
|
||||
|
||||
tms9914_return_to_local(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
gpib_interface_t 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,
|
||||
};
|
||||
|
||||
int hp82335_allocate_private(gpib_board_t *board)
|
||||
{
|
||||
board->private_data = kzalloc(sizeof(struct hp82335_priv), GFP_KERNEL);
|
||||
if (!board->private_data)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hp82335_free_private(gpib_board_t *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 uint8_t 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, uint8_t 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->iobase + HPREG_INTR_CLEAR);
|
||||
}
|
||||
|
||||
int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config)
|
||||
{
|
||||
struct hp82335_priv *hp_priv;
|
||||
struct tms9914_priv *tms_priv;
|
||||
int retval;
|
||||
const unsigned long upper_iomem_base = (unsigned long)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 ((unsigned long)(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:
|
||||
pr_err("hp82335: invalid base io address 0x%p\n", config->ibbase);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!request_mem_region(upper_iomem_base, hp82335_upper_iomem_size, "hp82335")) {
|
||||
pr_err("hp82335: 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->iobase = ioremap(upper_iomem_base, hp82335_upper_iomem_size);
|
||||
pr_info("hp82335: upper half of 82335 iomem region 0x%lx remapped to 0x%p\n",
|
||||
hp_priv->raw_iobase, tms_priv->iobase);
|
||||
|
||||
retval = request_irq(config->ibirq, hp82335_interrupt, 0, "hp82335", board);
|
||||
if (retval) {
|
||||
pr_err("hp82335: can't request IRQ %d\n", config->ibirq);
|
||||
return retval;
|
||||
}
|
||||
hp_priv->irq = config->ibirq;
|
||||
pr_info("hp82335: IRQ %d\n", config->ibirq);
|
||||
|
||||
tms9914_board_reset(tms_priv);
|
||||
|
||||
hp82335_clear_interrupt(hp_priv);
|
||||
|
||||
writeb(INTR_ENABLE, tms_priv->iobase + HPREG_CCR);
|
||||
|
||||
tms9914_online(board, tms_priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hp82335_detach(gpib_board_t *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->iobase) {
|
||||
writeb(0, tms_priv->iobase + HPREG_CCR);
|
||||
tms9914_board_reset(tms_priv);
|
||||
iounmap((void *)tms_priv->iobase);
|
||||
}
|
||||
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)
|
||||
{
|
||||
gpib_register_driver(&hp82335_interface, THIS_MODULE);
|
||||
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
|
||||
*/
|
||||
|
||||
irqreturn_t hp82335_interrupt(int irq, void *arg)
|
||||
{
|
||||
int status1, status2;
|
||||
gpib_board_t *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;
|
||||
}
|
||||
|
||||
85
drivers/staging/gpib/hp_82335/hp82335.h
Normal file
85
drivers/staging/gpib/hp_82335/hp82335.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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;
|
||||
};
|
||||
|
||||
// interfaces
|
||||
extern gpib_interface_t hp82335_interface;
|
||||
|
||||
// interface functions
|
||||
int hp82335_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read);
|
||||
int hp82335_write(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int send_eoi, size_t *bytes_written);
|
||||
int hp82335_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
|
||||
int hp82335_take_control(gpib_board_t *board, int synchronous);
|
||||
int hp82335_go_to_standby(gpib_board_t *board);
|
||||
void hp82335_request_system_control(gpib_board_t *board, int request_control);
|
||||
void hp82335_interface_clear(gpib_board_t *board, int assert);
|
||||
void hp82335_remote_enable(gpib_board_t *board, int enable);
|
||||
int hp82335_enable_eos(gpib_board_t *board, uint8_t eos_byte, int
|
||||
compare_8_bits);
|
||||
void hp82335_disable_eos(gpib_board_t *board);
|
||||
unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask);
|
||||
int hp82335_primary_address(gpib_board_t *board, unsigned int address);
|
||||
int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int
|
||||
enable);
|
||||
int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result);
|
||||
void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config);
|
||||
void hp82335_parallel_poll_response(gpib_board_t *board, int ist);
|
||||
void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status);
|
||||
void hp82335_return_to_local(gpib_board_t *board);
|
||||
|
||||
// interrupt service routines
|
||||
irqreturn_t hp82335_interrupt(int irq, void *arg);
|
||||
|
||||
// utility functions
|
||||
int hp82335_allocate_private(gpib_board_t *board);
|
||||
void hp82335_free_private(gpib_board_t *board);
|
||||
|
||||
// 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
|
||||
2
drivers/staging/gpib/hp_82341/Makefile
Normal file
2
drivers/staging/gpib/hp_82341/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
obj-m += hp_82341.o
|
||||
895
drivers/staging/gpib/hp_82341/hp_82341.c
Normal file
895
drivers/staging/gpib/hp_82341/hp_82341.c
Normal file
@@ -0,0 +1,895 @@
|
||||
// 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 *
|
||||
***************************************************************************/
|
||||
|
||||
#include "hp_82341.h"
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/isapnp.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
int hp_82341_accel_read(gpib_board_t *board, uint8_t *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)
|
||||
pr_err("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;
|
||||
|
||||
if (num_fifo_bytes - i < hp_82341_fifo_size)
|
||||
block_size = num_fifo_bytes - i;
|
||||
else
|
||||
block_size = 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) {
|
||||
pr_warn("%s: read wait interrupted\n", __func__);
|
||||
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)) {
|
||||
pr_debug("%s: minor %i: read timed out\n", __FILE__, board->minor);
|
||||
retval = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
|
||||
pr_warn("%s: device clear interrupted read\n", __FILE__);
|
||||
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(gpib_board_t *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 & BusNRFD) == 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;
|
||||
}
|
||||
|
||||
int hp_82341_accel_write(gpib_board_t *board, uint8_t *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;
|
||||
|
||||
if (fifo_xfer_len - i < hp_82341_fifo_size)
|
||||
block_size = fifo_xfer_len - i;
|
||||
else
|
||||
block_size = 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) {
|
||||
pr_err("hp82341: 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) {
|
||||
pr_warn("%s: write wait interrupted\n", __FILE__);
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
if (test_bit(TIMO_NUM, &board->status)) {
|
||||
pr_debug("%s: minor %i: write timed out\n", __FILE__, board->minor);
|
||||
retval = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
|
||||
pr_warn("%s: device clear interrupted write\n", __FILE__);
|
||||
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(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
|
||||
static void hp_82341_detach(gpib_board_t *board);
|
||||
|
||||
// wrappers for interface functions
|
||||
int hp_82341_read(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int hp_82341_write(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int hp_82341_command(gpib_board_t *board, uint8_t *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);
|
||||
}
|
||||
|
||||
int hp_82341_take_control(gpib_board_t *board, int synchronous)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_take_control(board, &priv->tms9914_priv, synchronous);
|
||||
}
|
||||
|
||||
int hp_82341_go_to_standby(gpib_board_t *board)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_go_to_standby(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
void hp_82341_request_system_control(gpib_board_t *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);
|
||||
tms9914_request_system_control(board, &priv->tms9914_priv, request_control);
|
||||
}
|
||||
|
||||
void hp_82341_interface_clear(gpib_board_t *board, int assert)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
tms9914_interface_clear(board, &priv->tms9914_priv, assert);
|
||||
}
|
||||
|
||||
void hp_82341_remote_enable(gpib_board_t *board, int enable)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
tms9914_remote_enable(board, &priv->tms9914_priv, enable);
|
||||
}
|
||||
|
||||
int hp_82341_enable_eos(gpib_board_t *board, uint8_t 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);
|
||||
}
|
||||
|
||||
void hp_82341_disable_eos(gpib_board_t *board)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
tms9914_disable_eos(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
unsigned int hp_82341_update_status(gpib_board_t *board, unsigned int clear_mask)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
|
||||
}
|
||||
|
||||
int hp_82341_primary_address(gpib_board_t *board, unsigned int address)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_primary_address(board, &priv->tms9914_priv, address);
|
||||
}
|
||||
|
||||
int hp_82341_secondary_address(gpib_board_t *board, unsigned int address, int enable)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
|
||||
}
|
||||
|
||||
int hp_82341_parallel_poll(gpib_board_t *board, uint8_t *result)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
|
||||
}
|
||||
|
||||
void hp_82341_parallel_poll_configure(gpib_board_t *board, uint8_t config)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
|
||||
}
|
||||
|
||||
void hp_82341_parallel_poll_response(gpib_board_t *board, int ist)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
|
||||
}
|
||||
|
||||
void hp_82341_serial_poll_response(gpib_board_t *board, uint8_t status)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
|
||||
}
|
||||
|
||||
static uint8_t hp_82341_serial_poll_status(gpib_board_t *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 gpib_board_t *board)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_line_status(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
static unsigned int hp_82341_t1_delay(gpib_board_t *board, unsigned int nano_sec)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec);
|
||||
}
|
||||
|
||||
void hp_82341_return_to_local(gpib_board_t *board)
|
||||
{
|
||||
struct hp_82341_priv *priv = board->private_data;
|
||||
|
||||
tms9914_return_to_local(board, &priv->tms9914_priv);
|
||||
}
|
||||
|
||||
gpib_interface_t 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,
|
||||
};
|
||||
|
||||
gpib_interface_t 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,
|
||||
};
|
||||
|
||||
int hp_82341_allocate_private(gpib_board_t *board)
|
||||
{
|
||||
board->private_data = kzalloc(sizeof(struct hp_82341_priv), GFP_KERNEL);
|
||||
if (!board->private_data)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hp_82341_free_private(gpib_board_t *board)
|
||||
{
|
||||
kfree(board->private_data);
|
||||
board->private_data = NULL;
|
||||
}
|
||||
|
||||
static uint8_t hp_82341_read_byte(struct tms9914_priv *priv, unsigned int register_num)
|
||||
{
|
||||
return inb((unsigned long)(priv->iobase) + register_num);
|
||||
}
|
||||
|
||||
static void hp_82341_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num)
|
||||
{
|
||||
outb(data, (unsigned long)(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("hp_82341: failed to find isapnp board\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (pnp_device_attach(*dev) < 0) {
|
||||
pr_err("hp_82341: board already active, skipping\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (pnp_activate_dev(*dev) < 0) {
|
||||
pnp_device_detach(*dev);
|
||||
pr_err("hp_82341: failed to activate() atgpib/tnt, aborting\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) {
|
||||
pnp_device_detach(*dev);
|
||||
pr_err("hp_82341: invalid port or irq for atgpib/tnt, 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("hp_82341: %s: bug! unknown hw_version\n", __func__);
|
||||
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("hp_82341: %s: bug! unknown hw_version\n", __func__);
|
||||
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("hp_82341: 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("hp_82341: %s: bug! unknown hw_version\n", __func__);
|
||||
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("hp_82341: 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("hp_82341: timed out waiting for Xilinx done.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_82341_load_firmware(struct hp_82341_priv *hp_priv, const gpib_board_config_t *config)
|
||||
{
|
||||
if (config->init_data_length == 0) {
|
||||
if (xilinx_done(hp_priv))
|
||||
return 0;
|
||||
pr_err("hp_82341: 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("hp_82341: 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("hp_82341: bad firmware length=%i for 82341d (expected %i).\n",
|
||||
config->init_data_length, hp_82341d_firmware_length);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__);
|
||||
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;
|
||||
}
|
||||
|
||||
int hp_82341_attach(gpib_board_t *board, const gpib_board_config_t *config)
|
||||
{
|
||||
struct hp_82341_priv *hp_priv;
|
||||
struct tms9914_priv *tms_priv;
|
||||
unsigned long start_addr;
|
||||
void *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 = (void *)(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;
|
||||
}
|
||||
pr_info("hp_82341: base io 0x%p\n", iobase);
|
||||
for (i = 0; i < hp_82341_num_io_regions; ++i) {
|
||||
start_addr = (unsigned long)(iobase) + i * hp_priv->io_region_offset;
|
||||
if (!request_region(start_addr, hp_82341_region_iosize, "hp_82341")) {
|
||||
pr_err("hp_82341: failed to allocate io ports 0x%lx-0x%lx\n",
|
||||
start_addr,
|
||||
start_addr + hp_82341_region_iosize - 1);
|
||||
return -EIO;
|
||||
}
|
||||
hp_priv->iobase[i] = start_addr;
|
||||
}
|
||||
tms_priv->iobase = (void *)(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) {
|
||||
pr_err("hp_82341: 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, "hp_82341", board)) {
|
||||
pr_err("hp_82341: failed to allocate IRQ %d\n", irq);
|
||||
return -EIO;
|
||||
}
|
||||
hp_priv->irq = irq;
|
||||
pr_info("hp_82341: IRQ %d\n", 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);
|
||||
pr_info("hp_82341: board id %x %x %x %x\n", inb(hp_priv->iobase[1] + ID0_REG),
|
||||
inb(hp_priv->iobase[1] + ID1_REG), inb(hp_priv->iobase[2] + ID2_REG),
|
||||
inb(hp_priv->iobase[2] + ID3_REG));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hp_82341_detach(gpib_board_t *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);
|
||||
}
|
||||
|
||||
static const struct pnp_device_id hp_82341_pnp_table[] = {
|
||||
{.id = "HWP1411"},
|
||||
{.id = ""}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table);
|
||||
|
||||
static int __init hp_82341_init_module(void)
|
||||
{
|
||||
gpib_register_driver(&hp_82341_unaccel_interface, THIS_MODULE);
|
||||
gpib_register_driver(&hp_82341_interface, THIS_MODULE);
|
||||
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
|
||||
*/
|
||||
unsigned short read_and_clear_event_status(gpib_board_t *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;
|
||||
}
|
||||
|
||||
irqreturn_t hp_82341_interrupt(int irq, void *arg)
|
||||
{
|
||||
int status1, status2;
|
||||
gpib_board_t *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);
|
||||
// printk("hp_82341: interrupt event_status=0x%x\n", event_status);
|
||||
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);
|
||||
/* printk("hp_82341: interrupt status1=0x%x status2=0x%x\n",
|
||||
* status1, status2);
|
||||
*/
|
||||
}
|
||||
spin_unlock_irqrestore(&board->spinlock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
207
drivers/staging/gpib/hp_82341/hp_82341.h
Normal file
207
drivers/staging/gpib/hp_82341/hp_82341.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/* 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;
|
||||
};
|
||||
|
||||
// interfaces
|
||||
extern gpib_interface_t hp_82341_interface;
|
||||
|
||||
// interface functions
|
||||
int hp_82341_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
|
||||
size_t *bytes_read);
|
||||
int hp_82341_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
|
||||
size_t *bytes_written);
|
||||
int hp_82341_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
|
||||
size_t *bytes_read);
|
||||
int hp_82341_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
|
||||
size_t *bytes_written);
|
||||
int hp_82341_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
|
||||
int hp_82341_take_control(gpib_board_t *board, int synchronous);
|
||||
int hp_82341_go_to_standby(gpib_board_t *board);
|
||||
void hp_82341_request_system_control(gpib_board_t *board, int request_control);
|
||||
void hp_82341_interface_clear(gpib_board_t *board, int assert);
|
||||
void hp_82341_remote_enable(gpib_board_t *board, int enable);
|
||||
int hp_82341_enable_eos(gpib_board_t *board, uint8_t eos_byte, int
|
||||
compare_8_bits);
|
||||
void hp_82341_disable_eos(gpib_board_t *board);
|
||||
unsigned int hp_82341_update_status(gpib_board_t *board, unsigned int clear_mask);
|
||||
int hp_82341_primary_address(gpib_board_t *board, unsigned int address);
|
||||
int hp_82341_secondary_address(gpib_board_t *board, unsigned int address, int
|
||||
enable);
|
||||
int hp_82341_parallel_poll(gpib_board_t *board, uint8_t *result);
|
||||
void hp_82341_parallel_poll_configure(gpib_board_t *board, uint8_t config);
|
||||
void hp_82341_parallel_poll_response(gpib_board_t *board, int ist);
|
||||
void hp_82341_serial_poll_response(gpib_board_t *board, uint8_t status);
|
||||
void hp_82341_return_to_local(gpib_board_t *board);
|
||||
|
||||
// interrupt service routines
|
||||
irqreturn_t hp_82341_interrupt(int irq, void *arg);
|
||||
|
||||
// utility functions
|
||||
int hp_82341_allocate_private(gpib_board_t *board);
|
||||
void hp_82341_free_private(gpib_board_t *board);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
unsigned short read_and_clear_event_status(gpib_board_t *board);
|
||||
int read_transfer_counter(struct hp_82341_priv *hp_priv);
|
||||
void set_transfer_counter(struct hp_82341_priv *hp_priv, int count);
|
||||
49
drivers/staging/gpib/include/amcc5920.h
Normal file
49
drivers/staging/gpib/include/amcc5920.h
Normal file
@@ -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);
|
||||
};
|
||||
|
||||
59
drivers/staging/gpib/include/amccs5933.h
Normal file
59
drivers/staging/gpib/include/amccs5933.h
Normal file
@@ -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
|
||||
};
|
||||
|
||||
50
drivers/staging/gpib/include/gpibP.h
Normal file
50
drivers/staging/gpib/include/gpibP.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2002,2003 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _GPIB_P_H
|
||||
#define _GPIB_P_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "gpib_types.h"
|
||||
#include "gpib_proto.h"
|
||||
#include "gpib_user.h"
|
||||
#include "gpib_ioctl.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
void gpib_register_driver(gpib_interface_t *interface, struct module *mod);
|
||||
void gpib_unregister_driver(gpib_interface_t *interface);
|
||||
struct pci_dev *gpib_pci_get_device(const gpib_board_config_t *config, unsigned int vendor_id,
|
||||
unsigned int device_id, struct pci_dev *from);
|
||||
struct pci_dev *gpib_pci_get_subsys(const gpib_board_config_t *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 gpib_event_queue_t *queue);
|
||||
int push_gpib_event(gpib_board_t *board, short event_type);
|
||||
int pop_gpib_event(gpib_board_t *board, gpib_event_queue_t *queue, short *event_type);
|
||||
int gpib_request_pseudo_irq(gpib_board_t *board, irqreturn_t (*handler)(int, void *));
|
||||
void gpib_free_pseudo_irq(gpib_board_t *board);
|
||||
int gpib_match_device_path(struct device *dev, const char *device_path_in);
|
||||
|
||||
extern gpib_board_t board_array[GPIB_MAX_NUM_BOARDS];
|
||||
|
||||
extern struct list_head registered_drivers;
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
void writeb_wrapper(unsigned int value, void *address);
|
||||
unsigned int readb_wrapper(void *address);
|
||||
void outb_wrapper(unsigned int value, void *address);
|
||||
unsigned int inb_wrapper(void *address);
|
||||
void writew_wrapper(unsigned int value, void *address);
|
||||
unsigned int readw_wrapper(void *address);
|
||||
void outw_wrapper(unsigned int value, void *address);
|
||||
unsigned int inw_wrapper(void *address);
|
||||
|
||||
#endif // _GPIB_P_H
|
||||
|
||||
23
drivers/staging/gpib/include/gpib_pci_ids.h
Normal file
23
drivers/staging/gpib/include/gpib_pci_ids.h
Normal file
@@ -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
|
||||
|
||||
56
drivers/staging/gpib/include/gpib_proto.h
Normal file
56
drivers/staging/gpib/include/gpib_proto.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef GPIB_PROTO_INCLUDED
|
||||
#define GPIB_PROTO_INCLUDED
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
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);
|
||||
int osInit(void);
|
||||
void osReset(void);
|
||||
void os_start_timer(gpib_board_t *board, unsigned int usec_timeout);
|
||||
void os_remove_timer(gpib_board_t *board);
|
||||
void osSendEOI(void);
|
||||
void osSendEOI(void);
|
||||
void init_gpib_board(gpib_board_t *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(gpib_board_t *board, unsigned int usec_timeout);
|
||||
void init_gpib_descriptor(gpib_descriptor_t *desc);
|
||||
int dvrsp(gpib_board_t *board, unsigned int pad, int sad,
|
||||
unsigned int usec_timeout, uint8_t *result);
|
||||
int ibAPWait(gpib_board_t *board, int pad);
|
||||
int ibAPrsp(gpib_board_t *board, int padsad, char *spb);
|
||||
void ibAPE(gpib_board_t *board, int pad, int v);
|
||||
int ibcac(gpib_board_t *board, int sync, int fallback_to_async);
|
||||
int ibcmd(gpib_board_t *board, uint8_t *buf, size_t length, size_t *bytes_written);
|
||||
int ibgts(gpib_board_t *board);
|
||||
int ibonline(gpib_board_t *board);
|
||||
int iboffline(gpib_board_t *board);
|
||||
int iblines(const gpib_board_t *board, short *lines);
|
||||
int ibrd(gpib_board_t *board, uint8_t *buf, size_t length, int *end_flag, size_t *bytes_read);
|
||||
int ibrpp(gpib_board_t *board, uint8_t *buf);
|
||||
int ibrsv2(gpib_board_t *board, uint8_t status_byte, int new_reason_for_service);
|
||||
void ibrsc(gpib_board_t *board, int request_control);
|
||||
int ibsic(gpib_board_t *board, unsigned int usec_duration);
|
||||
int ibsre(gpib_board_t *board, int enable);
|
||||
int ibpad(gpib_board_t *board, unsigned int addr);
|
||||
int ibsad(gpib_board_t *board, int addr);
|
||||
int ibeos(gpib_board_t *board, int eos, int eosflags);
|
||||
int ibwait(gpib_board_t *board, int wait_mask, int clear_mask, int set_mask,
|
||||
int *status, unsigned long usec_timeout, gpib_descriptor_t *desc);
|
||||
int ibwrt(gpib_board_t *board, uint8_t *buf, size_t cnt, int send_eoi, size_t *bytes_written);
|
||||
int ibstatus(gpib_board_t *board);
|
||||
int general_ibstatus(gpib_board_t *board, const gpib_status_queue_t *device,
|
||||
int clear_mask, int set_mask, gpib_descriptor_t *desc);
|
||||
int io_timed_out(gpib_board_t *board);
|
||||
int ibppc(gpib_board_t *board, uint8_t configuration);
|
||||
|
||||
#endif /* GPIB_PROTO_INCLUDED */
|
||||
23
drivers/staging/gpib/include/gpib_state_machines.h
Normal file
23
drivers/staging/gpib/include/gpib_state_machines.h
Normal file
@@ -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
|
||||
353
drivers/staging/gpib/include/gpib_types.h
Normal file
353
drivers/staging/gpib/include/gpib_types.h
Normal file
@@ -0,0 +1,353 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2002 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _GPIB_TYPES_H
|
||||
#define _GPIB_TYPES_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/* gpib_interface_t 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.
|
||||
*/
|
||||
#include "gpib_user.h"
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
typedef struct gpib_interface_struct gpib_interface_t;
|
||||
typedef struct gpib_board_struct gpib_board_t;
|
||||
|
||||
/* config parameters that are only used by driver attach functions */
|
||||
typedef struct {
|
||||
/* 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) */
|
||||
void *ibbase;
|
||||
/* 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;
|
||||
} gpib_board_config_t;
|
||||
|
||||
struct gpib_interface_struct {
|
||||
/* name of board */
|
||||
char *name;
|
||||
/* attach() initializes board and allocates resources */
|
||||
int (*attach)(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
/* detach() shuts down board and frees resources */
|
||||
void (*detach)(gpib_board_t *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)(gpib_board_t *board, uint8_t *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)(gpib_board_t *board, uint8_t *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)(gpib_board_t *board, uint8_t *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)(gpib_board_t *board, int asyncronous);
|
||||
/* De-assert ATN. Returns zero on success, nonzer on error.
|
||||
*/
|
||||
int (*go_to_standby)(gpib_board_t *board);
|
||||
/* request/release control of the IFC and REN lines (system controller) */
|
||||
void (*request_system_control)(gpib_board_t *board, int request_control);
|
||||
/* Asserts or de-asserts 'interface clear' (IFC) depending on
|
||||
* boolean value of 'assert'
|
||||
*/
|
||||
void (*interface_clear)(gpib_board_t *board, int assert);
|
||||
/* Sends remote enable command if 'enable' is nonzero, disables remote mode
|
||||
* if 'enable' is zero
|
||||
*/
|
||||
void (*remote_enable)(gpib_board_t *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)(gpib_board_t *board, uint8_t eos, int compare_8_bits);
|
||||
/* disable END on eos byte (END on EOI only)*/
|
||||
void (*disable_eos)(gpib_board_t *board);
|
||||
/* configure parallel poll */
|
||||
void (*parallel_poll_configure)(gpib_board_t *board, uint8_t configuration);
|
||||
/* conduct parallel poll */
|
||||
int (*parallel_poll)(gpib_board_t *board, uint8_t *result);
|
||||
/* set/clear ist (individual status bit) */
|
||||
void (*parallel_poll_response)(gpib_board_t *board, int ist);
|
||||
/* select local parallel poll configuration mode PP2 versus remote PP1 */
|
||||
void (*local_parallel_poll_mode)(gpib_board_t *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 gpib_board_t *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)(gpib_board_t *board, unsigned int clear_mask);
|
||||
/* Sets primary address 0-30 for gpib interface card.
|
||||
*/
|
||||
int (*primary_address)(gpib_board_t *board, unsigned int address);
|
||||
/* Sets and enables, or disables secondary address 0-30
|
||||
* for gpib interface card.
|
||||
*/
|
||||
int (*secondary_address)(gpib_board_t *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)(gpib_board_t *board, uint8_t 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)(gpib_board_t *board, uint8_t status_byte,
|
||||
int new_reason_for_service);
|
||||
/* returns the byte the board will send in response to a serial poll.
|
||||
*/
|
||||
uint8_t (*serial_poll_status)(gpib_board_t *board);
|
||||
/* adjust T1 delay */
|
||||
unsigned int (*t1_delay)(gpib_board_t *board, unsigned int nano_sec);
|
||||
/* go to local mode */
|
||||
void (*return_to_local)(gpib_board_t *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;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct list_head event_head;
|
||||
spinlock_t lock; // for access to event list
|
||||
unsigned int num_events;
|
||||
unsigned dropped_event : 1;
|
||||
} gpib_event_queue_t;
|
||||
|
||||
static inline void init_event_queue(gpib_event_queue_t *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);
|
||||
gpib_board_t *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 */
|
||||
typedef struct gpib_interface_list_struct {
|
||||
struct list_head list;
|
||||
gpib_interface_t *interface;
|
||||
struct module *module;
|
||||
} gpib_interface_list_t;
|
||||
|
||||
/* One gpib_board_t 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_struct {
|
||||
/* functions used by this board */
|
||||
gpib_interface_t *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;
|
||||
spinlock_t locking_pid_spinlock; // lock for setting locking pid
|
||||
/* 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 */
|
||||
gpib_event_queue_t 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;
|
||||
gpib_board_config_t 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 */
|
||||
typedef struct {
|
||||
struct list_head list;
|
||||
short event_type;
|
||||
} gpib_event_t;
|
||||
|
||||
/* Each board has a list of gpib_status_queue_t to keep track of all open devices
|
||||
* on the bus, so we know what address to poll when we get a service request
|
||||
*/
|
||||
typedef struct {
|
||||
/* 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;
|
||||
} gpib_status_queue_t;
|
||||
|
||||
typedef struct {
|
||||
struct list_head list;
|
||||
u8 poll_byte;
|
||||
} status_byte_t;
|
||||
|
||||
void init_gpib_status_queue(gpib_status_queue_t *device);
|
||||
|
||||
/* Used to store device-descriptor-specific information */
|
||||
typedef struct {
|
||||
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;
|
||||
} gpib_descriptor_t;
|
||||
|
||||
typedef struct {
|
||||
atomic_t holding_mutex;
|
||||
gpib_descriptor_t *descriptors[GPIB_MAX_NUM_DESCRIPTORS];
|
||||
/* locked while descriptors are being allocated/deallocated */
|
||||
struct mutex descriptors_mutex;
|
||||
unsigned got_module : 1;
|
||||
} gpib_file_private_t;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _GPIB_TYPES_H */
|
||||
138
drivers/staging/gpib/include/nec7210.h
Normal file
138
drivers/staging/gpib/include/nec7210.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/* 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 <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "gpib_types.h"
|
||||
#include "nec7210_registers.h"
|
||||
|
||||
/* struct used to provide variables local to a nec7210 chip */
|
||||
struct nec7210_priv {
|
||||
void *iobase;
|
||||
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(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer,
|
||||
size_t length, int *end, size_t *bytes_read);
|
||||
int nec7210_write(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer,
|
||||
size_t length, int send_eoi, size_t *bytes_written);
|
||||
int nec7210_command(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer,
|
||||
size_t length, size_t *bytes_written);
|
||||
int nec7210_take_control(gpib_board_t *board, struct nec7210_priv *priv, int syncronous);
|
||||
int nec7210_go_to_standby(gpib_board_t *board, struct nec7210_priv *priv);
|
||||
void nec7210_request_system_control(gpib_board_t *board,
|
||||
struct nec7210_priv *priv, int request_control);
|
||||
void nec7210_interface_clear(gpib_board_t *board, struct nec7210_priv *priv, int assert);
|
||||
void nec7210_remote_enable(gpib_board_t *board, struct nec7210_priv *priv, int enable);
|
||||
int nec7210_enable_eos(gpib_board_t *board, struct nec7210_priv *priv, uint8_t eos_bytes,
|
||||
int compare_8_bits);
|
||||
void nec7210_disable_eos(gpib_board_t *board, struct nec7210_priv *priv);
|
||||
unsigned int nec7210_update_status(gpib_board_t *board, struct nec7210_priv *priv,
|
||||
unsigned int clear_mask);
|
||||
unsigned int nec7210_update_status_nolock(gpib_board_t *board, struct nec7210_priv *priv);
|
||||
int nec7210_primary_address(const gpib_board_t *board,
|
||||
struct nec7210_priv *priv, unsigned int address);
|
||||
int nec7210_secondary_address(const gpib_board_t *board, struct nec7210_priv *priv,
|
||||
unsigned int address, int enable);
|
||||
int nec7210_parallel_poll(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *result);
|
||||
void nec7210_serial_poll_response(gpib_board_t *board, struct nec7210_priv *priv, uint8_t status);
|
||||
void nec7210_parallel_poll_configure(gpib_board_t *board,
|
||||
struct nec7210_priv *priv, unsigned int configuration);
|
||||
void nec7210_parallel_poll_response(gpib_board_t *board,
|
||||
struct nec7210_priv *priv, int ist);
|
||||
uint8_t nec7210_serial_poll_status(gpib_board_t *board,
|
||||
struct nec7210_priv *priv);
|
||||
unsigned int nec7210_t1_delay(gpib_board_t *board,
|
||||
struct nec7210_priv *priv, unsigned int nano_sec);
|
||||
void nec7210_return_to_local(const gpib_board_t *board, struct nec7210_priv *priv);
|
||||
|
||||
// utility functions
|
||||
void nec7210_board_reset(struct nec7210_priv *priv, const gpib_board_t *board);
|
||||
void nec7210_board_online(struct nec7210_priv *priv, const gpib_board_t *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(gpib_board_t *board, struct nec7210_priv *priv, int mode);
|
||||
void nec7210_release_rfd_holdoff(gpib_board_t *board, struct nec7210_priv *priv);
|
||||
uint8_t nec7210_read_data_in(gpib_board_t *board, struct nec7210_priv *priv, int *end);
|
||||
|
||||
// wrappers for io functions
|
||||
uint8_t nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num);
|
||||
void nec7210_ioport_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num);
|
||||
uint8_t nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num);
|
||||
void nec7210_iomem_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num);
|
||||
uint8_t nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num);
|
||||
void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, uint8_t data,
|
||||
unsigned int register_num);
|
||||
uint8_t nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num);
|
||||
void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, uint8_t data,
|
||||
unsigned int register_num);
|
||||
|
||||
// interrupt service routine
|
||||
irqreturn_t nec7210_interrupt(gpib_board_t *board, struct nec7210_priv *priv);
|
||||
irqreturn_t nec7210_interrupt_have_status(gpib_board_t *board,
|
||||
struct nec7210_priv *priv, int status1, int status2);
|
||||
|
||||
#endif //_NEC7210_H
|
||||
217
drivers/staging/gpib/include/nec7210_registers.h
Normal file
217
drivers/staging/gpib/include/nec7210_registers.h
Normal file
@@ -0,0 +1,217 @@
|
||||
/* 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
|
||||
72
drivers/staging/gpib/include/plx9050.h
Normal file
72
drivers/staging/gpib/include/plx9050.h
Normal file
@@ -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
|
||||
22
drivers/staging/gpib/include/quancom_pci.h
Normal file
22
drivers/staging/gpib/include/quancom_pci.h
Normal file
@@ -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
|
||||
274
drivers/staging/gpib/include/tms9914.h
Normal file
274
drivers/staging/gpib/include/tms9914.h
Normal file
@@ -0,0 +1,274 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2002 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _TMS9914_H
|
||||
#define _TMS9914_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#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 {
|
||||
void *iobase;
|
||||
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(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
|
||||
size_t length, int *end, size_t *bytes_read);
|
||||
int tms9914_write(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
|
||||
size_t length, int send_eoi, size_t *bytes_written);
|
||||
int tms9914_command(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
|
||||
size_t length, size_t *bytes_written);
|
||||
int tms9914_take_control(gpib_board_t *board, struct tms9914_priv *priv, int syncronous);
|
||||
/* alternate version of tms9914_take_control which works around buggy tcs
|
||||
* implementation.
|
||||
*/
|
||||
int tms9914_take_control_workaround(gpib_board_t *board, struct tms9914_priv *priv,
|
||||
int syncronous);
|
||||
int tms9914_go_to_standby(gpib_board_t *board, struct tms9914_priv *priv);
|
||||
void tms9914_request_system_control(gpib_board_t *board, struct tms9914_priv *priv,
|
||||
int request_control);
|
||||
void tms9914_interface_clear(gpib_board_t *board, struct tms9914_priv *priv, int assert);
|
||||
void tms9914_remote_enable(gpib_board_t *board, struct tms9914_priv *priv, int enable);
|
||||
int tms9914_enable_eos(gpib_board_t *board, struct tms9914_priv *priv, uint8_t eos_bytes,
|
||||
int compare_8_bits);
|
||||
void tms9914_disable_eos(gpib_board_t *board, struct tms9914_priv *priv);
|
||||
unsigned int tms9914_update_status(gpib_board_t *board, struct tms9914_priv *priv,
|
||||
unsigned int clear_mask);
|
||||
int tms9914_primary_address(gpib_board_t *board,
|
||||
struct tms9914_priv *priv, unsigned int address);
|
||||
int tms9914_secondary_address(gpib_board_t *board, struct tms9914_priv *priv,
|
||||
unsigned int address, int enable);
|
||||
int tms9914_parallel_poll(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *result);
|
||||
void tms9914_parallel_poll_configure(gpib_board_t *board,
|
||||
struct tms9914_priv *priv, uint8_t config);
|
||||
void tms9914_parallel_poll_response(gpib_board_t *board,
|
||||
struct tms9914_priv *priv, int ist);
|
||||
void tms9914_serial_poll_response(gpib_board_t *board, struct tms9914_priv *priv, uint8_t status);
|
||||
uint8_t tms9914_serial_poll_status(gpib_board_t *board, struct tms9914_priv *priv);
|
||||
int tms9914_line_status(const gpib_board_t *board, struct tms9914_priv *priv);
|
||||
unsigned int tms9914_t1_delay(gpib_board_t *board, struct tms9914_priv *priv,
|
||||
unsigned int nano_sec);
|
||||
void tms9914_return_to_local(const gpib_board_t *board, struct tms9914_priv *priv);
|
||||
|
||||
// utility functions
|
||||
void tms9914_board_reset(struct tms9914_priv *priv);
|
||||
void tms9914_online(gpib_board_t *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
|
||||
uint8_t tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num);
|
||||
void tms9914_ioport_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num);
|
||||
uint8_t tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num);
|
||||
void tms9914_iomem_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num);
|
||||
|
||||
// interrupt service routine
|
||||
irqreturn_t tms9914_interrupt(gpib_board_t *board, struct tms9914_priv *priv);
|
||||
irqreturn_t tms9914_interrupt_have_status(gpib_board_t *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
|
||||
192
drivers/staging/gpib/include/tnt4882_registers.h
Normal file
192
drivers/staging/gpib/include/tnt4882_registers.h
Normal file
@@ -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
|
||||
4
drivers/staging/gpib/ines/Makefile
Normal file
4
drivers/staging/gpib/ines/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA
|
||||
obj-m += ines_gpib.o
|
||||
|
||||
|
||||
215
drivers/staging/gpib/ines/ines.h
Normal file
215
drivers/staging/gpib/ines/ines.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/* 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 <linux/interrupt.h>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// interfaces
|
||||
extern gpib_interface_t ines_pci_interface;
|
||||
extern gpib_interface_t ines_pci_accel_interface;
|
||||
extern gpib_interface_t ines_pcmcia_interface;
|
||||
extern gpib_interface_t ines_pcmcia_accel_interface;
|
||||
extern gpib_interface_t ines_pcmcia_unaccel_interface;
|
||||
|
||||
// interface functions
|
||||
int ines_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read);
|
||||
int ines_write(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int send_eoi, size_t *bytes_written);
|
||||
int ines_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int *end, size_t *bytes_read);
|
||||
int ines_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length,
|
||||
int send_eoi, size_t *bytes_written);
|
||||
int ines_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
|
||||
int ines_take_control(gpib_board_t *board, int synchronous);
|
||||
int ines_go_to_standby(gpib_board_t *board);
|
||||
void ines_request_system_control(gpib_board_t *board, int request_control);
|
||||
void ines_interface_clear(gpib_board_t *board, int assert);
|
||||
void ines_remote_enable(gpib_board_t *board, int enable);
|
||||
int ines_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits);
|
||||
void ines_disable_eos(gpib_board_t *board);
|
||||
unsigned int ines_update_status(gpib_board_t *board, unsigned int clear_mask);
|
||||
int ines_primary_address(gpib_board_t *board, unsigned int address);
|
||||
int ines_secondary_address(gpib_board_t *board, unsigned int address, int enable);
|
||||
int ines_parallel_poll(gpib_board_t *board, uint8_t *result);
|
||||
void ines_parallel_poll_configure(gpib_board_t *board, uint8_t config);
|
||||
void ines_parallel_poll_response(gpib_board_t *board, int ist);
|
||||
void ines_serial_poll_response(gpib_board_t *board, uint8_t status);
|
||||
uint8_t ines_serial_poll_status(gpib_board_t *board);
|
||||
int ines_line_status(const gpib_board_t *board);
|
||||
unsigned int ines_t1_delay(gpib_board_t *board, unsigned int nano_sec);
|
||||
void ines_return_to_local(gpib_board_t *board);
|
||||
|
||||
// interrupt service routines
|
||||
irqreturn_t ines_pci_interrupt(int irq, void *arg);
|
||||
irqreturn_t ines_interrupt(gpib_board_t *board);
|
||||
|
||||
// utility functions
|
||||
void ines_free_private(gpib_board_t *board);
|
||||
int ines_generic_attach(gpib_board_t *board);
|
||||
void ines_online(struct ines_priv *priv, const gpib_board_t *board, int use_accel);
|
||||
void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count);
|
||||
|
||||
/* inb/outb wrappers */
|
||||
static inline unsigned int ines_inb(struct ines_priv *priv, unsigned int register_number)
|
||||
{
|
||||
return inb((unsigned long)(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, (unsigned long)(priv->nec7210_priv.iobase) +
|
||||
register_number * priv->nec7210_priv.offset);
|
||||
}
|
||||
|
||||
// pcmcia init/cleanup
|
||||
|
||||
int ines_pcmcia_init_module(void);
|
||||
void ines_pcmcia_cleanup_module(void);
|
||||
|
||||
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
|
||||
1464
drivers/staging/gpib/ines/ines_gpib.c
Normal file
1464
drivers/staging/gpib/ines/ines_gpib.c
Normal file
File diff suppressed because it is too large
Load Diff
3
drivers/staging/gpib/lpvo_usb_gpib/Makefile
Normal file
3
drivers/staging/gpib/lpvo_usb_gpib/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
obj-m += lpvo_usb_gpib.o
|
||||
|
||||
2136
drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
Normal file
2136
drivers/staging/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
Normal file
File diff suppressed because it is too large
Load Diff
4
drivers/staging/gpib/nec7210/Makefile
Normal file
4
drivers/staging/gpib/nec7210/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
obj-m += nec7210.o
|
||||
|
||||
|
||||
19
drivers/staging/gpib/nec7210/board.h
Normal file
19
drivers/staging/gpib/nec7210/board.h
Normal file
@@ -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 <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "nec7210.h"
|
||||
|
||||
#endif //_GPIB_PCIIA_BOARD_H
|
||||
|
||||
1134
drivers/staging/gpib/nec7210/nec7210.c
Normal file
1134
drivers/staging/gpib/nec7210/nec7210.c
Normal file
File diff suppressed because it is too large
Load Diff
4
drivers/staging/gpib/ni_usb/Makefile
Normal file
4
drivers/staging/gpib/ni_usb/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
obj-m += ni_usb_gpib.o
|
||||
|
||||
|
||||
2640
drivers/staging/gpib/ni_usb/ni_usb_gpib.c
Normal file
2640
drivers/staging/gpib/ni_usb/ni_usb_gpib.c
Normal file
File diff suppressed because it is too large
Load Diff
216
drivers/staging/gpib/ni_usb/ni_usb_gpib.h
Normal file
216
drivers/staging/gpib/ni_usb/ni_usb_gpib.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2004 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _NI_USB_GPIB_H
|
||||
#define _NI_USB_GPIB_H
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/timer.h>
|
||||
#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 semaphore 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
|
||||
5
drivers/staging/gpib/pc2/Makefile
Normal file
5
drivers/staging/gpib/pc2/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
obj-m += pc2_gpib.o
|
||||
|
||||
|
||||
|
||||
656
drivers/staging/gpib/pc2/pc2_gpib.c
Normal file
656
drivers/staging/gpib/pc2/pc2_gpib.c
Normal file
@@ -0,0 +1,656 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2001, 2002 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#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");
|
||||
|
||||
static int pc2_attach(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
static int pc2a_attach(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
static int pc2a_cb7210_attach(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
static int pc2_2a_attach(gpib_board_t *board, const gpib_board_config_t *config);
|
||||
|
||||
static void pc2_detach(gpib_board_t *board);
|
||||
static void pc2a_detach(gpib_board_t *board);
|
||||
static void pc2_2a_detach(gpib_board_t *board);
|
||||
|
||||
/*
|
||||
* GPIB interrupt service routines
|
||||
*/
|
||||
|
||||
irqreturn_t pc2_interrupt(int irq, void *arg)
|
||||
{
|
||||
gpib_board_t *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)
|
||||
{
|
||||
gpib_board_t *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(gpib_board_t *board, uint8_t *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(gpib_board_t *board, uint8_t *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(gpib_board_t *board, uint8_t *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(gpib_board_t *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(gpib_board_t *board)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_go_to_standby(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
static void pc2_request_system_control(gpib_board_t *board, int request_control)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
|
||||
}
|
||||
|
||||
static void pc2_interface_clear(gpib_board_t *board, int assert)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
nec7210_interface_clear(board, &priv->nec7210_priv, assert);
|
||||
}
|
||||
|
||||
static void pc2_remote_enable(gpib_board_t *board, int enable)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
nec7210_remote_enable(board, &priv->nec7210_priv, enable);
|
||||
}
|
||||
|
||||
static int pc2_enable_eos(gpib_board_t *board, uint8_t 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(gpib_board_t *board)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
nec7210_disable_eos(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
static unsigned int pc2_update_status(gpib_board_t *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(gpib_board_t *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(gpib_board_t *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(gpib_board_t *board, uint8_t *result)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
|
||||
}
|
||||
|
||||
static void pc2_parallel_poll_configure(gpib_board_t *board, uint8_t config)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
|
||||
}
|
||||
|
||||
static void pc2_parallel_poll_response(gpib_board_t *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(gpib_board_t *board, uint8_t status)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
|
||||
}
|
||||
|
||||
static uint8_t pc2_serial_poll_status(gpib_board_t *board)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
return nec7210_serial_poll_status(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
static unsigned int pc2_t1_delay(gpib_board_t *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(gpib_board_t *board)
|
||||
{
|
||||
struct pc2_priv *priv = board->private_data;
|
||||
|
||||
nec7210_return_to_local(board, &priv->nec7210_priv);
|
||||
}
|
||||
|
||||
gpib_interface_t 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,
|
||||
};
|
||||
|
||||
gpib_interface_t 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,
|
||||
};
|
||||
|
||||
gpib_interface_t 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,
|
||||
};
|
||||
|
||||
gpib_interface_t 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 allocate_private(gpib_board_t *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(gpib_board_t *board)
|
||||
{
|
||||
kfree(board->private_data);
|
||||
board->private_data = NULL;
|
||||
}
|
||||
|
||||
static int pc2_generic_attach(gpib_board_t *board, const gpib_board_config_t *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)
|
||||
pr_err("DMA disabled for pc2 gpib, driver needs to be adapted to use isa_register_driver to get a struct device*");
|
||||
#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")) {
|
||||
pr_err("gpib: can't request DMA %d\n", config->ibdma);
|
||||
return -1;
|
||||
}
|
||||
nec_priv->dma_channel = config->ibdma;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pc2_attach(gpib_board_t *board, const gpib_board_config_t *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((unsigned long)config->ibbase, pc2_iosize, "pc2") == 0) {
|
||||
pr_err("gpib: ioports are already in use\n");
|
||||
return -1;
|
||||
}
|
||||
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)) {
|
||||
pr_err("gpib: can't request IRQ %d\n", config->ibirq);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
pc2_priv->irq = config->ibirq;
|
||||
/* poll so we can detect assertion of ATN */
|
||||
if (gpib_request_pseudo_irq(board, pc2_interrupt)) {
|
||||
pr_err("pc2_gpib: 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;
|
||||
}
|
||||
|
||||
void pc2_detach(gpib_board_t *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((unsigned long)(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(gpib_board_t *board, const gpib_board_config_t *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 ((unsigned long)(config->ibbase)) {
|
||||
case 0x02e1:
|
||||
case 0x22e1:
|
||||
case 0x42e1:
|
||||
case 0x62e1:
|
||||
break;
|
||||
default:
|
||||
pr_err("PCIIa base range invalid, must be one of 0x[0246]2e1, but is 0x%p\n",
|
||||
config->ibbase);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (config->ibirq) {
|
||||
if (config->ibirq < 2 || config->ibirq > 7) {
|
||||
pr_err("pc2_gpib: illegal interrupt level %i\n", config->ibirq);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
pr_err("pc2_gpib: interrupt disabled, using polling mode (slow)\n");
|
||||
}
|
||||
#ifdef CHECK_IOPORTS
|
||||
unsigned int err = 0;
|
||||
|
||||
for (i = 0; i < num_registers; i++) {
|
||||
if (check_region((unsigned long)config->ibbase + i * pc2a_reg_offset, 1))
|
||||
err++;
|
||||
}
|
||||
if (config->ibirq && check_region(pc2a_clear_intr_iobase + config->ibirq, 1))
|
||||
err++;
|
||||
if (err) {
|
||||
pr_err("gpib: ioports are already in use");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < num_registers; i++) {
|
||||
if (!request_region((unsigned long)config->ibbase +
|
||||
i * pc2a_reg_offset, 1, "pc2a")) {
|
||||
pr_err("gpib: ioports are already in use");
|
||||
for (j = 0; j < i; j++)
|
||||
release_region((unsigned long)(config->ibbase) +
|
||||
j * pc2a_reg_offset, 1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
nec_priv->iobase = config->ibbase;
|
||||
if (config->ibirq) {
|
||||
if (!request_region(pc2a_clear_intr_iobase + config->ibirq, 1, "pc2a")) {
|
||||
pr_err("gpib: 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)) {
|
||||
pr_err("gpib: can't request IRQ %d\n", config->ibirq);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
pc2_priv->irq = config->ibirq;
|
||||
/* poll so we can detect assertion of ATN */
|
||||
if (gpib_request_pseudo_irq(board, pc2_interrupt)) {
|
||||
pr_err("pc2_gpib: 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;
|
||||
}
|
||||
|
||||
int pc2a_attach(gpib_board_t *board, const gpib_board_config_t *config)
|
||||
{
|
||||
return pc2a_common_attach(board, config, pc2a_iosize, NEC7210);
|
||||
}
|
||||
|
||||
int pc2a_cb7210_attach(gpib_board_t *board, const gpib_board_config_t *config)
|
||||
{
|
||||
return pc2a_common_attach(board, config, pc2a_iosize, CB7210);
|
||||
}
|
||||
|
||||
int pc2_2a_attach(gpib_board_t *board, const gpib_board_config_t *config)
|
||||
{
|
||||
return pc2a_common_attach(board, config, pc2_2a_iosize, NAT4882);
|
||||
}
|
||||
|
||||
static void pc2a_common_detach(gpib_board_t *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((unsigned long)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);
|
||||
}
|
||||
|
||||
void pc2a_detach(gpib_board_t *board)
|
||||
{
|
||||
pc2a_common_detach(board, pc2a_iosize);
|
||||
}
|
||||
|
||||
void pc2_2a_detach(gpib_board_t *board)
|
||||
{
|
||||
pc2a_common_detach(board, pc2_2a_iosize);
|
||||
}
|
||||
|
||||
static int __init pc2_init_module(void)
|
||||
{
|
||||
gpib_register_driver(&pc2_interface, THIS_MODULE);
|
||||
gpib_register_driver(&pc2a_interface, THIS_MODULE);
|
||||
gpib_register_driver(&pc2a_cb7210_interface, THIS_MODULE);
|
||||
gpib_register_driver(&pc2_2a_interface, THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
6
drivers/staging/gpib/tms9914/Makefile
Normal file
6
drivers/staging/gpib/tms9914/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
obj-m += tms9914.o
|
||||
|
||||
|
||||
|
||||
|
||||
910
drivers/staging/gpib/tms9914/tms9914.c
Normal file
910
drivers/staging/gpib/tms9914/tms9914.c
Normal file
@@ -0,0 +1,910 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/***************************************************************************
|
||||
* copyright : (C) 2001, 2002 by Frank Mori Hess
|
||||
***************************************************************************/
|
||||
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "gpibP.h"
|
||||
#include "tms9914.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("GPIB library for tms9914");
|
||||
|
||||
static unsigned int update_status_nolock(gpib_board_t *board, struct tms9914_priv *priv);
|
||||
|
||||
int tms9914_take_control(gpib_board_t *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(gpib_board_t *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(gpib_board_t *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) {
|
||||
pr_err("error waiting for NATN\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
clear_bit(COMMAND_READY_BN, &priv->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tms9914_go_to_standby);
|
||||
|
||||
void tms9914_interface_clear(gpib_board_t *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(gpib_board_t *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);
|
||||
|
||||
void tms9914_request_system_control(gpib_board_t *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);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tms9914_request_system_control);
|
||||
|
||||
unsigned int tms9914_t1_delay(gpib_board_t *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 gpib_board_t *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("%s: bug! bad holdoff mode %i\n", __func__, 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(gpib_board_t *board, struct tms9914_priv *priv, uint8_t 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(gpib_board_t *board, struct tms9914_priv *priv)
|
||||
{
|
||||
priv->eos_flags &= ~REOS;
|
||||
}
|
||||
EXPORT_SYMBOL(tms9914_disable_eos);
|
||||
|
||||
int tms9914_parallel_poll(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *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(gpib_board_t *board,
|
||||
struct tms9914_priv *priv, uint8_t 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(gpib_board_t *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(gpib_board_t *board, struct tms9914_priv *priv, uint8_t 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);
|
||||
|
||||
uint8_t tms9914_serial_poll_status(gpib_board_t *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(gpib_board_t *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(gpib_board_t *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(gpib_board_t *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(gpib_board_t *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 gpib_board_t *board, struct tms9914_priv *priv)
|
||||
{
|
||||
int bsr_bits;
|
||||
int status = ValidALL;
|
||||
|
||||
bsr_bits = read_byte(priv, BSR);
|
||||
|
||||
if (bsr_bits & BSR_REN_BIT)
|
||||
status |= BusREN;
|
||||
if (bsr_bits & BSR_IFC_BIT)
|
||||
status |= BusIFC;
|
||||
if (bsr_bits & BSR_SRQ_BIT)
|
||||
status |= BusSRQ;
|
||||
if (bsr_bits & BSR_EOI_BIT)
|
||||
status |= BusEOI;
|
||||
if (bsr_bits & BSR_NRFD_BIT)
|
||||
status |= BusNRFD;
|
||||
if (bsr_bits & BSR_NDAC_BIT)
|
||||
status |= BusNDAC;
|
||||
if (bsr_bits & BSR_DAV_BIT)
|
||||
status |= BusDAV;
|
||||
if (bsr_bits & BSR_ATN_BIT)
|
||||
status |= BusATN;
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(tms9914_line_status);
|
||||
|
||||
static int check_for_eos(struct tms9914_priv *priv, uint8_t 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(gpib_board_t *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))) {
|
||||
pr_debug("gpib: pio read wait interrupted\n");
|
||||
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 uint8_t tms9914_read_data_in(gpib_board_t *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:
|
||||
pr_err("%s: bug! bad holdoff mode %i\n", __func__, priv->holdoff_mode);
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&board->spinlock, flags);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int pio_read(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *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(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *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 havn'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(gpib_board_t *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))) {
|
||||
dev_dbg(board->gpib_dev, "gpib write interrupted!\n");
|
||||
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(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *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(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *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(gpib_board_t *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(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *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))) {
|
||||
pr_debug("gpib command wait interrupted\n");
|
||||
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(gpib_board_t *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(gpib_board_t *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 PPConfig:
|
||||
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 {
|
||||
// printk("tms9914: unrecognized gpib command pass thru 0x%x\n",
|
||||
// command_byte);
|
||||
// clear dac holdoff
|
||||
write_byte(priv, AUX_INVAL, AUXCR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (in_primary_command_group(command_byte) && command_byte != PPConfig)
|
||||
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, EventIFC);
|
||||
clear_bit(CIC_NUM, &board->status);
|
||||
}
|
||||
|
||||
if (status1 & HR_GET) {
|
||||
push_gpib_event(board, EventDevTrg);
|
||||
// clear dac holdoff
|
||||
write_byte(priv, AUX_VAL, AUXCR);
|
||||
}
|
||||
|
||||
if (status1 & HR_DCAS) {
|
||||
push_gpib_event(board, EventDevClr);
|
||||
// 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)
|
||||
pr_err("tms9914: 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(gpib_board_t *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
|
||||
uint8_t tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num)
|
||||
{
|
||||
return inb((unsigned long)(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, uint8_t data, unsigned int register_num)
|
||||
{
|
||||
outb(data, (unsigned long)(priv->iobase) + register_num * priv->offset);
|
||||
if (register_num == AUXCR)
|
||||
udelay(1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tms9914_ioport_write_byte);
|
||||
#endif
|
||||
|
||||
// wrapper for readb
|
||||
uint8_t tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num)
|
||||
{
|
||||
return readb(priv->iobase + register_num * priv->offset);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tms9914_iomem_read_byte);
|
||||
|
||||
// wrapper for writeb
|
||||
void tms9914_iomem_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num)
|
||||
{
|
||||
writeb(data, priv->iobase + 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);
|
||||
|
||||
7
drivers/staging/gpib/tnt4882/Makefile
Normal file
7
drivers/staging/gpib/tnt4882/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA
|
||||
obj-m += tnt4882.o
|
||||
|
||||
tnt4882-objs := tnt4882_gpib.o mite.o
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user