Staging driver patches for 4.1-rc1
Here's the big staging driver patchset for 4.1-rc1. There's a lot of patches here, the Outreachy application period happened during this development cycle, so that means that there was a lot of cleanup patches accepted. Other than the normal coding style and sparse fixes here, there are some driver updates and work toward making some of the drivers into "mergable" shape (like the Unisys drivers.) All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlUsIA0ACgkQMUfUDdst+ylHuACgm8QZ9S6lk45/Qd9YeR3NSDaS upoAn3gVAaHnuxkW3anivuwOcirgCp3l =7VHU -----END PGP SIGNATURE----- Merge tag 'staging-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver updates from Greg KH: "Here's the big staging driver patchset for 4.1-rc1. There's a lot of patches here, the Outreachy application period happened during this development cycle, so that means that there was a lot of cleanup patches accepted. Other than the normal coding style and sparse fixes here, there are some driver updates and work toward making some of the drivers into "mergable" shape (like the Unisys drivers.) All of these have been in linux-next for a while" * tag 'staging-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1214 commits) staging: lustre: orthography & coding style staging: lustre: lnet: lnet: fix error return code staging: lustre: fix sparse warning Revert "Staging: sm750fb: Fix C99 Comments" Staging: rtl8192u: use correct array for debug output staging: rtl8192e: Remove dead code staging: rtl8192e: Comment cleanup (style/format) staging: rtl8192e: Fix indentation in rtllib_rx_auth_resp() staging: rtl8192e: Decrease nesting of rtllib_rx_auth_resp() staging: rtl8192e: Divide rtllib_rx_auth() staging: rtl8192e: Fix PRINTK_WITHOUT_KERN_LEVEL warnings staging: rtl8192e: Fix DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON warning staging: rtl8192e: Fix BRACES warning staging: rtl8192e: Fix LINE_CONTINUATIONS warning staging: rtl8192e: Fix UNNECESSARY_PARENTHESES warnings staging: rtl8192e: remove unused EXPORT_SYMBOL_RSL macro staging: rtl8192e: Fix RETURN_VOID warnings staging: rtl8192e: Fix UNNECESSARY_ELSE warning staging: rtl8723au: Remove unneeded comments staging: rtl8723au: Use __func__ in trace logs ...
This commit is contained in:
Коммит
b79013b244
|
@ -253,6 +253,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_rot_offset
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -296,6 +298,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -336,6 +339,7 @@ what /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale
|
|||
what /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_calibscale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -347,7 +351,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Gender of the user (e.g.: male, female) used by some pedometers
|
||||
|
@ -358,7 +362,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Lists all available gender values (e.g.: male, female).
|
||||
|
@ -375,7 +379,7 @@ Description:
|
|||
type.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibweight
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Weight of the user (in kg). It is needed by some pedometers
|
||||
|
@ -612,6 +616,8 @@ Description:
|
|||
a given event type is enabled a future point (and not those for
|
||||
whatever event was previously enabled).
|
||||
|
||||
What: /sys/.../events/in_accel_thresh_rising_value
|
||||
What: /sys/.../events/in_accel_thresh_falling_value
|
||||
What: /sys/.../events/in_accel_x_raw_thresh_rising_value
|
||||
What: /sys/.../events/in_accel_x_raw_thresh_falling_value
|
||||
What: /sys/.../events/in_accel_y_raw_thresh_rising_value
|
||||
|
@ -661,6 +667,24 @@ Description:
|
|||
value is in raw device units or in processed units (as _raw
|
||||
and _input do on sysfs direct channel read attributes).
|
||||
|
||||
What: /sys/.../events/in_accel_scale
|
||||
What: /sys/.../events/in_accel_peak_scale
|
||||
What: /sys/.../events/in_anglvel_scale
|
||||
What: /sys/.../events/in_magn_scale
|
||||
What: /sys/.../events/in_rot_from_north_magnetic_scale
|
||||
What: /sys/.../events/in_rot_from_north_true_scale
|
||||
What: /sys/.../events/in_voltage_scale
|
||||
What: /sys/.../events/in_voltage_supply_scale
|
||||
What: /sys/.../events/in_temp_scale
|
||||
What: /sys/.../events/in_illuminance_scale
|
||||
What: /sys/.../events/in_proximity_scale
|
||||
KernelVersion: 3.21
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies the conversion factor from the standard units
|
||||
to device specific units used to set the event trigger
|
||||
threshold.
|
||||
|
||||
What: /sys/.../events/in_accel_x_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_accel_x_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_accel_x_thresh_either_hysteresis
|
||||
|
@ -776,7 +800,7 @@ Description:
|
|||
|
||||
What: /sys/.../events/in_accel_x_thresh_rising_period
|
||||
What: /sys/.../events/in_accel_x_thresh_falling_period
|
||||
hat: /sys/.../events/in_accel_x_roc_rising_period
|
||||
What: /sys/.../events/in_accel_x_roc_rising_period
|
||||
What: /sys/.../events/in_accel_x_roc_falling_period
|
||||
What: /sys/.../events/in_accel_y_thresh_rising_period
|
||||
What: /sys/.../events/in_accel_y_thresh_falling_period
|
||||
|
@ -923,7 +947,7 @@ Description:
|
|||
this type.
|
||||
|
||||
What: /sys/.../events/in_steps_change_en
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Event generated when channel passes a threshold on the absolute
|
||||
|
@ -932,7 +956,7 @@ Description:
|
|||
in_steps_change_value.
|
||||
|
||||
What: /sys/.../events/in_steps_change_value
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies the value of change threshold that the
|
||||
|
@ -997,6 +1021,7 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en
|
|||
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_pressure_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_proximity_en
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -1013,6 +1038,7 @@ What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type
|
|||
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_pressure_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_proximity_type
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -1064,6 +1090,7 @@ What: /sys/.../iio:deviceX/scan_elements/in_timestamp_index
|
|||
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_pressure_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_proximity_index
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -1104,7 +1131,7 @@ Description:
|
|||
|
||||
What: /sys/.../iio:deviceX/in_energy_input
|
||||
What: /sys/.../iio:deviceX/in_energy_raw
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to read the energy value reported by the
|
||||
|
@ -1113,7 +1140,7 @@ Description:
|
|||
|
||||
What: /sys/.../iio:deviceX/in_distance_input
|
||||
What: /sys/.../iio:deviceX/in_distance_raw
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to read the distance covered by the user
|
||||
|
@ -1143,9 +1170,13 @@ Description:
|
|||
values should behave in the same way as a distance, i.e. lower
|
||||
values indicate something is closer to the sensor.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_illuminance_input
|
||||
What: /sys/.../iio:deviceX/in_illuminance_raw
|
||||
What: /sys/.../iio:deviceX/in_illuminanceY_input
|
||||
What: /sys/.../iio:deviceX/in_illuminanceY_raw
|
||||
What: /sys/.../iio:deviceX/in_illuminanceY_mean_raw
|
||||
What: /sys/.../iio:deviceX/in_illuminance_ir_raw
|
||||
What: /sys/.../iio:deviceX/in_illuminance_clear_raw
|
||||
KernelVersion: 3.4
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -1174,7 +1205,7 @@ Description:
|
|||
seconds.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Number of seconds in which to compute speed.
|
||||
|
@ -1236,7 +1267,7 @@ Description:
|
|||
Units after application of scale are m/s.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_steps_debounce_count
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies the number of steps that must occur within
|
||||
|
@ -1244,8 +1275,92 @@ Description:
|
|||
consumer is making steps.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_steps_debounce_time
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies number of seconds in which we compute the steps
|
||||
that occur in order to decide if the consumer is making steps.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/watermark
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
A single positive integer specifying the maximum number of scan
|
||||
elements to wait for.
|
||||
Poll will block until the watermark is reached.
|
||||
Blocking read will wait until the minimum between the requested
|
||||
read amount or the low water mark is available.
|
||||
Non-blocking read will retrieve the available samples from the
|
||||
buffer even if there are less samples then watermark level. This
|
||||
allows the application to block on poll with a timeout and read
|
||||
the available samples after the timeout expires and thus have a
|
||||
maximum delay guarantee.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_enabled
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
A read-only boolean value that indicates if the hardware fifo is
|
||||
currently enabled or disabled. If the device does not have a
|
||||
hardware fifo this entry is not present.
|
||||
The hardware fifo is enabled when the buffer is enabled if the
|
||||
current hardware fifo watermark level is set and other current
|
||||
device settings allows it (e.g. if a trigger is set that samples
|
||||
data differently that the hardware fifo does then hardware fifo
|
||||
will not enabled).
|
||||
If the hardware fifo is enabled and the level of the hardware
|
||||
fifo reaches the hardware fifo watermark level the device will
|
||||
flush its hardware fifo to the device buffer. Doing a non
|
||||
blocking read on the device when no samples are present in the
|
||||
device buffer will also force a flush.
|
||||
When the hardware fifo is enabled there is no need to use a
|
||||
trigger to use buffer mode since the watermark settings
|
||||
guarantees that the hardware fifo is flushed to the device
|
||||
buffer.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only entry that contains a single integer specifying the
|
||||
current watermark level for the hardware fifo. If the device
|
||||
does not have a hardware fifo this entry is not present.
|
||||
The watermark level for the hardware fifo is set by the driver
|
||||
based on the value set by the user in buffer/watermark but
|
||||
taking into account hardware limitations (e.g. most hardware
|
||||
buffers are limited to 32-64 samples, some hardware buffers
|
||||
watermarks are fixed or have minimum levels). A value of 0
|
||||
means that the hardware watermark is unset.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark_min
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
A single positive integer specifying the minimum watermark level
|
||||
for the hardware fifo of this device. If the device does not
|
||||
have a hardware fifo this entry is not present.
|
||||
If the user sets buffer/watermark to a value less than this one,
|
||||
then the hardware watermark will remain unset.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark_max
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
A single positive integer specifying the maximum watermark level
|
||||
for the hardware fifo of this device. If the device does not
|
||||
have a hardware fifo this entry is not present.
|
||||
If the user sets buffer/watermark to a value greater than this
|
||||
one, then the hardware watermark will be capped at this value.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark_available
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
A list of positive integers specifying the available watermark
|
||||
levels for the hardware fifo. This entry is optional and if it
|
||||
is not present it means that all the values between
|
||||
hwfifo_watermark_min and hwfifo_watermark_max are supported.
|
||||
If the user sets buffer/watermark to a value greater than
|
||||
hwfifo_watermak_min but not equal to any of the values in this
|
||||
list, the driver will chose an appropriate value for the
|
||||
hardware fifo watermark level.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
* Microchip Analog to Digital Converter (ADC)
|
||||
|
||||
The node for this driver must be a child node of a SPI controller, hence
|
||||
all mandatory properties described in
|
||||
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
must be specified.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following, depending on the
|
||||
model:
|
||||
"mcp3001"
|
||||
"mcp3002"
|
||||
"mcp3004"
|
||||
"mcp3008"
|
||||
"mcp3201"
|
||||
"mcp3202"
|
||||
"mcp3204"
|
||||
"mcp3208"
|
||||
|
||||
|
||||
Examples:
|
||||
spi_controller {
|
||||
mcp3x0x@0 {
|
||||
compatible = "mcp3002";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
* Microchip mcp3422/3/4/6/7/8 chip family (ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be
|
||||
"microchip,mcp3422" or
|
||||
"microchip,mcp3423" or
|
||||
"microchip,mcp3424" or
|
||||
"microchip,mcp3426" or
|
||||
"microchip,mcp3427" or
|
||||
"microchip,mcp3428"
|
||||
- reg: I2C address for the device
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "microchip,mcp3424";
|
||||
reg = <0x68>;
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
* Texas Instruments' ADC128S052 ADC chip
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,adc128s052"
|
||||
- reg: spi chip select number for the device
|
||||
- vref-supply: The regulator supply for ADC reference voltage
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,adc128s052";
|
||||
reg = <0>;
|
||||
vref-supply = <&vdd_supply>;
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
|
@ -23,6 +23,7 @@ standard bindings from pinctrl/pinctrl-bindings.txt.
|
|||
Valid compatible strings:
|
||||
|
||||
Accelerometers:
|
||||
- st,lis3lv02dl-accel
|
||||
- st,lsm303dlh-accel
|
||||
- st,lsm303dlhc-accel
|
||||
- st,lis3dh-accel
|
||||
|
|
17
MAINTAINERS
17
MAINTAINERS
|
@ -724,7 +724,7 @@ F: staging/iio/trigger/iio-trig-bfin-timer.c
|
|||
|
||||
ANDROID DRIVERS
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
M: Arve Hjønnevåg <arve@android.com>
|
||||
M: Arve Hjønnevåg <arve@android.com>
|
||||
M: Riley Andrews <riandrews@android.com>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/gregkh/staging.git
|
||||
L: devel@driverdev.osuosl.org
|
||||
|
@ -4169,6 +4169,12 @@ F: sound/soc/fsl/fsl*
|
|||
F: sound/soc/fsl/imx*
|
||||
F: sound/soc/fsl/mpc8610_hpcd.c
|
||||
|
||||
FREESCALE QORIQ MANAGEMENT COMPLEX DRIVER
|
||||
M: J. German Rivera <German.Rivera@freescale.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-mc/
|
||||
|
||||
FREEVXFS FILESYSTEM
|
||||
M: Christoph Hellwig <hch@infradead.org>
|
||||
W: ftp://ftp.openlinux.org/pub/people/hch/vxfs
|
||||
|
@ -4954,6 +4960,7 @@ S: Maintained
|
|||
F: drivers/iio/
|
||||
F: drivers/staging/iio/
|
||||
F: include/linux/iio/
|
||||
F: tools/iio/
|
||||
|
||||
IKANOS/ADI EAGLE ADSL USB DRIVER
|
||||
M: Matthieu Castet <castet.matthieu@free.fr>
|
||||
|
@ -9363,6 +9370,14 @@ L: linux-fbdev@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/staging/sm7xxfb/
|
||||
|
||||
STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
|
||||
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
|
||||
M: Teddy Wang <teddy.wang@siliconmotion.com>
|
||||
M: Sudip Mukherjee <sudip@vectorindia.org>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/sm750fb/
|
||||
|
||||
STAGING - SLICOSS
|
||||
M: Lior Dotan <liodot@gmail.com>
|
||||
M: Christopher Harrer <charrer@alacritech.com>
|
||||
|
|
|
@ -3548,7 +3548,7 @@ static int __init d40_probe(struct platform_device *pdev)
|
|||
|
||||
if (!plat_data) {
|
||||
if (np) {
|
||||
if(d40_of_probe(pdev, np)) {
|
||||
if (d40_of_probe(pdev, np)) {
|
||||
ret = -ENOMEM;
|
||||
goto failure;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1169,16 +1169,12 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0);
|
||||
gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
@ -418,17 +418,18 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev)
|
|||
struct device *dev = &data->client->dev;
|
||||
|
||||
for (i = 0; i < MMA9551_GPIO_COUNT; i++) {
|
||||
gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i);
|
||||
gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
ret = gpiod_to_irq(gpio);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->irqs[i] = gpiod_to_irq(gpio);
|
||||
data->irqs[i] = ret;
|
||||
ret = devm_request_threaded_irq(dev, data->irqs[i],
|
||||
NULL, mma9551_event_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
|
|
|
@ -1109,16 +1109,12 @@ static int mma9553_gpio_probe(struct i2c_client *client)
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
|
||||
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel"
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
#define LIS3DH_ACCEL_DEV_NAME "lis3dh"
|
||||
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
|
||||
|
|
|
@ -129,6 +129,30 @@
|
|||
#define ST_ACCEL_3_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_3_MULTIREAD_BIT false
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 4 */
|
||||
#define ST_ACCEL_4_WAI_EXP 0x3a
|
||||
#define ST_ACCEL_4_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_4_ODR_MASK 0x30 /* DF1 and DF0 */
|
||||
#define ST_ACCEL_4_ODR_AVL_40HZ_VAL 0x00
|
||||
#define ST_ACCEL_4_ODR_AVL_160HZ_VAL 0x01
|
||||
#define ST_ACCEL_4_ODR_AVL_640HZ_VAL 0x02
|
||||
#define ST_ACCEL_4_ODR_AVL_2560HZ_VAL 0x03
|
||||
#define ST_ACCEL_4_PW_ADDR 0x20
|
||||
#define ST_ACCEL_4_PW_MASK 0xc0
|
||||
#define ST_ACCEL_4_FS_ADDR 0x21
|
||||
#define ST_ACCEL_4_FS_MASK 0x80
|
||||
#define ST_ACCEL_4_FS_AVL_2_VAL 0X00
|
||||
#define ST_ACCEL_4_FS_AVL_6_VAL 0X01
|
||||
#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1024)
|
||||
#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(340)
|
||||
#define ST_ACCEL_4_BDU_ADDR 0x21
|
||||
#define ST_ACCEL_4_BDU_MASK 0x40
|
||||
#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21
|
||||
#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_ACCEL_4_IG1_EN_ADDR 0x21
|
||||
#define ST_ACCEL_4_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_4_MULTIREAD_BIT true
|
||||
|
||||
static const struct iio_chan_spec st_accel_12bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
@ -373,6 +397,63 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_4_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_4_ODR_ADDR,
|
||||
.mask = ST_ACCEL_4_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 40, ST_ACCEL_4_ODR_AVL_40HZ_VAL },
|
||||
{ 160, ST_ACCEL_4_ODR_AVL_160HZ_VAL, },
|
||||
{ 640, ST_ACCEL_4_ODR_AVL_640HZ_VAL, },
|
||||
{ 2560, ST_ACCEL_4_ODR_AVL_2560HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_4_PW_ADDR,
|
||||
.mask = ST_ACCEL_4_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_4_FS_ADDR,
|
||||
.mask = ST_ACCEL_4_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_4_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_4_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_6G,
|
||||
.value = ST_ACCEL_4_FS_AVL_6_VAL,
|
||||
.gain = ST_ACCEL_4_FS_AVL_6_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_4_BDU_ADDR,
|
||||
.mask = ST_ACCEL_4_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK,
|
||||
.ig1 = {
|
||||
.en_addr = ST_ACCEL_4_IG1_EN_ADDR,
|
||||
.en_mask = ST_ACCEL_4_IG1_EN_MASK,
|
||||
},
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
|
||||
.bootime = 2, /* guess */
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_accel_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lis3lv02dl-accel",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303dlh-accel",
|
||||
.data = LSM303DLH_ACCEL_DEV_NAME,
|
||||
|
|
|
@ -196,10 +196,11 @@ config MAX1363
|
|||
data via the iio dev interface.
|
||||
|
||||
config MCP320X
|
||||
tristate "Microchip Technology MCP3204/08"
|
||||
tristate "Microchip Technology MCP3x01/02/04/08"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's MCP3204 or
|
||||
Say yes here to build support for Microchip Technology's
|
||||
MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204 or
|
||||
MCP3208 analog to digital converter.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
|
|
|
@ -861,5 +861,5 @@ static struct spi_driver ad7793_driver = {
|
|||
module_spi_driver(ad7793_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -275,7 +275,6 @@ static void vf610_adc_cfg_post_set(struct vf610_adc *info)
|
|||
static void vf610_adc_calibration(struct vf610_adc *info)
|
||||
{
|
||||
int adc_gc, hc_cfg;
|
||||
int timeout;
|
||||
|
||||
if (!info->adc_feature.calibration)
|
||||
return;
|
||||
|
@ -287,9 +286,7 @@ static void vf610_adc_calibration(struct vf610_adc *info)
|
|||
adc_gc = readl(info->regs + VF610_REG_ADC_GC);
|
||||
writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (timeout == 0)
|
||||
if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT))
|
||||
dev_err(info->dev, "Timeout for adc calibration\n");
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
|
||||
|
|
|
@ -437,7 +437,7 @@ int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id ssp_of_match[] = {
|
||||
static const struct of_device_id ssp_of_match[] = {
|
||||
{
|
||||
.compatible = "samsung,sensorhub-rinato",
|
||||
.data = &ssp_rinato_info,
|
||||
|
|
|
@ -143,11 +143,16 @@ config AD7303
|
|||
ad7303.
|
||||
|
||||
config MAX517
|
||||
tristate "Maxim MAX517/518/519 DAC driver"
|
||||
tristate "Maxim MAX517/518/519/520/521 DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Maxim chips MAX517,
|
||||
MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs).
|
||||
If you say yes here you get support for the following Maxim chips
|
||||
(I2C 8-Bit DACs with rail-to-rail outputs):
|
||||
MAX517 - Single channel, single reference
|
||||
MAX518 - Dual channel, ref=Vdd
|
||||
MAX519 - Dual channel, dual reference
|
||||
MAX520 - Quad channel, quad reference
|
||||
MAX521 - Octal channel, independent ref for ch0-3, shared ref for ch4-7
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max517.
|
||||
|
|
|
@ -39,11 +39,13 @@ enum max517_device_ids {
|
|||
ID_MAX517,
|
||||
ID_MAX518,
|
||||
ID_MAX519,
|
||||
ID_MAX520,
|
||||
ID_MAX521,
|
||||
};
|
||||
|
||||
struct max517_data {
|
||||
struct i2c_client *client;
|
||||
unsigned short vref_mv[2];
|
||||
unsigned short vref_mv[8];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -149,7 +151,13 @@ static const struct iio_info max517_info = {
|
|||
|
||||
static const struct iio_chan_spec max517_channels[] = {
|
||||
MAX517_CHANNEL(0),
|
||||
MAX517_CHANNEL(1)
|
||||
MAX517_CHANNEL(1),
|
||||
MAX517_CHANNEL(2),
|
||||
MAX517_CHANNEL(3),
|
||||
MAX517_CHANNEL(4),
|
||||
MAX517_CHANNEL(5),
|
||||
MAX517_CHANNEL(6),
|
||||
MAX517_CHANNEL(7),
|
||||
};
|
||||
|
||||
static int max517_probe(struct i2c_client *client,
|
||||
|
@ -158,6 +166,7 @@ static int max517_probe(struct i2c_client *client,
|
|||
struct max517_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max517_platform_data *platform_data = client->dev.platform_data;
|
||||
int chan;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
|
@ -169,11 +178,21 @@ static int max517_probe(struct i2c_client *client,
|
|||
/* establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
/* reduced channel set for MAX517 */
|
||||
if (id->driver_data == ID_MAX517)
|
||||
indio_dev->num_channels = 1;
|
||||
else
|
||||
switch (id->driver_data) {
|
||||
case ID_MAX521:
|
||||
indio_dev->num_channels = 8;
|
||||
break;
|
||||
case ID_MAX520:
|
||||
indio_dev->num_channels = 4;
|
||||
break;
|
||||
case ID_MAX519:
|
||||
case ID_MAX518:
|
||||
indio_dev->num_channels = 2;
|
||||
break;
|
||||
default: /* single channel for MAX517 */
|
||||
indio_dev->num_channels = 1;
|
||||
break;
|
||||
}
|
||||
indio_dev->channels = max517_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &max517_info;
|
||||
|
@ -182,11 +201,11 @@ static int max517_probe(struct i2c_client *client,
|
|||
* Reference voltage on MAX518 and default is 5V, else take vref_mv
|
||||
* from platform_data
|
||||
*/
|
||||
if (id->driver_data == ID_MAX518 || !platform_data) {
|
||||
data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */
|
||||
} else {
|
||||
data->vref_mv[0] = platform_data->vref_mv[0];
|
||||
data->vref_mv[1] = platform_data->vref_mv[1];
|
||||
for (chan = 0; chan < indio_dev->num_channels; chan++) {
|
||||
if (id->driver_data == ID_MAX518 || !platform_data)
|
||||
data->vref_mv[chan] = 5000; /* mV */
|
||||
else
|
||||
data->vref_mv[chan] = platform_data->vref_mv[chan];
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
@ -202,6 +221,8 @@ static const struct i2c_device_id max517_id[] = {
|
|||
{ "max517", ID_MAX517 },
|
||||
{ "max518", ID_MAX518 },
|
||||
{ "max519", ID_MAX519 },
|
||||
{ "max520", ID_MAX520 },
|
||||
{ "max521", ID_MAX521 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max517_id);
|
||||
|
@ -218,5 +239,5 @@ static struct i2c_driver max517_driver = {
|
|||
module_i2c_driver(max517_driver);
|
||||
|
||||
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
|
||||
MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC");
|
||||
MODULE_DESCRIPTION("MAX517/518/519/520/521 8-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1001,16 +1001,12 @@ static int bmg160_gpio_probe(struct i2c_client *client,
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0);
|
||||
gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
@ -223,6 +223,10 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev)
|
|||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = itg3200_reset(indio_dev);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
@ -233,10 +237,6 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev)
|
|||
goto err_ret;
|
||||
}
|
||||
|
||||
ret = itg3200_reset(indio_dev);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
ret = itg3200_enable_full_scale(indio_dev);
|
||||
err_ret:
|
||||
return ret;
|
||||
|
@ -351,6 +351,26 @@ static int itg3200_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused itg3200_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&st->i2c->dev, "suspend device");
|
||||
|
||||
return itg3200_write_reg_8(indio_dev, ITG3200_REG_POWER_MANAGEMENT,
|
||||
ITG3200_SLEEP);
|
||||
}
|
||||
|
||||
static int __maybe_unused itg3200_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
return itg3200_initial_setup(indio_dev);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(itg3200_pm_ops, itg3200_suspend, itg3200_resume);
|
||||
|
||||
static const struct i2c_device_id itg3200_id[] = {
|
||||
{ "itg3200", 0 },
|
||||
{ }
|
||||
|
@ -361,6 +381,7 @@ static struct i2c_driver itg3200_driver = {
|
|||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "itg3200",
|
||||
.pm = &itg3200_pm_ops,
|
||||
},
|
||||
.id_table = itg3200_id,
|
||||
.probe = itg3200_probe,
|
||||
|
|
|
@ -87,6 +87,31 @@
|
|||
#define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08
|
||||
#define ST_GYRO_2_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 3 */
|
||||
#define ST_GYRO_3_WAI_EXP 0xd7
|
||||
#define ST_GYRO_3_ODR_ADDR 0x20
|
||||
#define ST_GYRO_3_ODR_MASK 0xc0
|
||||
#define ST_GYRO_3_ODR_AVL_95HZ_VAL 0x00
|
||||
#define ST_GYRO_3_ODR_AVL_190HZ_VAL 0x01
|
||||
#define ST_GYRO_3_ODR_AVL_380HZ_VAL 0x02
|
||||
#define ST_GYRO_3_ODR_AVL_760HZ_VAL 0x03
|
||||
#define ST_GYRO_3_PW_ADDR 0x20
|
||||
#define ST_GYRO_3_PW_MASK 0x08
|
||||
#define ST_GYRO_3_FS_ADDR 0x23
|
||||
#define ST_GYRO_3_FS_MASK 0x30
|
||||
#define ST_GYRO_3_FS_AVL_250_VAL 0x00
|
||||
#define ST_GYRO_3_FS_AVL_500_VAL 0x01
|
||||
#define ST_GYRO_3_FS_AVL_2000_VAL 0x02
|
||||
#define ST_GYRO_3_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
|
||||
#define ST_GYRO_3_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
|
||||
#define ST_GYRO_3_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
|
||||
#define ST_GYRO_3_BDU_ADDR 0x23
|
||||
#define ST_GYRO_3_BDU_MASK 0x80
|
||||
#define ST_GYRO_3_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_GYRO_3_DRDY_IRQ_INT2_MASK 0x08
|
||||
#define ST_GYRO_3_MULTIREAD_BIT true
|
||||
|
||||
|
||||
static const struct iio_chan_spec st_gyro_16bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
@ -225,6 +250,64 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_GYRO_3_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = L3GD20_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_GYRO_3_ODR_ADDR,
|
||||
.mask = ST_GYRO_3_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, },
|
||||
{ 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, },
|
||||
{ 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, },
|
||||
{ 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_GYRO_3_PW_ADDR,
|
||||
.mask = ST_GYRO_3_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_GYRO_3_FS_ADDR,
|
||||
.mask = ST_GYRO_3_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_GYRO_FS_AVL_250DPS,
|
||||
.value = ST_GYRO_3_FS_AVL_250_VAL,
|
||||
.gain = ST_GYRO_3_FS_AVL_250_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_GYRO_FS_AVL_500DPS,
|
||||
.value = ST_GYRO_3_FS_AVL_500_VAL,
|
||||
.gain = ST_GYRO_3_FS_AVL_500_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_GYRO_FS_AVL_2000DPS,
|
||||
.value = ST_GYRO_3_FS_AVL_2000_VAL,
|
||||
.gain = ST_GYRO_3_FS_AVL_2000_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_GYRO_3_BDU_ADDR,
|
||||
.mask = ST_GYRO_3_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_GYRO_3_DRDY_IRQ_ADDR,
|
||||
.mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_gyro_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
|
||||
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
|
||||
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o inv_mpu_acpi.o
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* inv_mpu_acpi: ACPI processing for creating client devices
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
enum inv_mpu_product_name {
|
||||
INV_MPU_NOT_MATCHED,
|
||||
INV_MPU_ASUS_T100TA,
|
||||
};
|
||||
|
||||
static enum inv_mpu_product_name matched_product_name;
|
||||
|
||||
static int __init asus_t100_matched(const struct dmi_system_id *d)
|
||||
{
|
||||
matched_product_name = INV_MPU_ASUS_T100TA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id inv_mpu_dev_list[] = {
|
||||
{
|
||||
.callback = asus_t100_matched,
|
||||
.ident = "Asus Transformer Book T100",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
|
||||
},
|
||||
},
|
||||
/* Add more matching tables here..*/
|
||||
{}
|
||||
};
|
||||
|
||||
static int asus_acpi_get_sensor_info(struct acpi_device *adev,
|
||||
struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
int i;
|
||||
acpi_status status;
|
||||
union acpi_object *cpm;
|
||||
|
||||
status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
cpm = buffer.pointer;
|
||||
for (i = 0; i < cpm->package.count; ++i) {
|
||||
union acpi_object *elem;
|
||||
int j;
|
||||
|
||||
elem = &(cpm->package.elements[i]);
|
||||
for (j = 0; j < elem->package.count; ++j) {
|
||||
union acpi_object *sub_elem;
|
||||
|
||||
sub_elem = &(elem->package.elements[j]);
|
||||
if (sub_elem->type == ACPI_TYPE_STRING)
|
||||
strlcpy(info->type, sub_elem->string.pointer,
|
||||
sizeof(info->type));
|
||||
else if (sub_elem->type == ACPI_TYPE_INTEGER) {
|
||||
if (sub_elem->integer.value != client->addr) {
|
||||
info->addr = sub_elem->integer.value;
|
||||
break; /* Not a MPU6500 primary */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kfree(buffer.pointer);
|
||||
|
||||
return cpm->package.count;
|
||||
}
|
||||
|
||||
static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
u32 *addr = data;
|
||||
|
||||
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||
struct acpi_resource_i2c_serialbus *sb;
|
||||
|
||||
sb = &ares->data.i2c_serial_bus;
|
||||
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||
if (*addr)
|
||||
*addr |= (sb->slave_address << 16);
|
||||
else
|
||||
*addr = sb->slave_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the ACPI core that we already copied this address */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int inv_mpu_process_acpi_config(struct i2c_client *client,
|
||||
unsigned short *primary_addr,
|
||||
unsigned short *secondary_addr)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
struct acpi_device *adev;
|
||||
u32 i2c_addr = 0;
|
||||
LIST_HEAD(resources);
|
||||
int ret;
|
||||
|
||||
id = acpi_match_device(client->dev.driver->acpi_match_table,
|
||||
&client->dev);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
adev = ACPI_COMPANION(&client->dev);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = acpi_dev_get_resources(adev, &resources,
|
||||
acpi_i2c_check_resource, &i2c_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
acpi_dev_free_resource_list(&resources);
|
||||
*primary_addr = i2c_addr & 0x0000ffff;
|
||||
*secondary_addr = (i2c_addr & 0xffff0000) >> 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
|
||||
st->mux_client = NULL;
|
||||
if (ACPI_HANDLE(&st->client->dev)) {
|
||||
struct i2c_board_info info;
|
||||
struct acpi_device *adev;
|
||||
int ret = -1;
|
||||
|
||||
adev = ACPI_COMPANION(&st->client->dev);
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
dmi_check_system(inv_mpu_dev_list);
|
||||
switch (matched_product_name) {
|
||||
case INV_MPU_ASUS_T100TA:
|
||||
ret = asus_acpi_get_sensor_info(adev, st->client,
|
||||
&info);
|
||||
break;
|
||||
/* Add more matched product processing here */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
/* No matching DMI, so create device on INV6XX type */
|
||||
unsigned short primary, secondary;
|
||||
|
||||
ret = inv_mpu_process_acpi_config(st->client, &primary,
|
||||
&secondary);
|
||||
if (!ret && secondary) {
|
||||
char *name;
|
||||
|
||||
info.addr = secondary;
|
||||
strlcpy(info.type, dev_name(&adev->dev),
|
||||
sizeof(info.type));
|
||||
name = strchr(info.type, ':');
|
||||
if (name)
|
||||
*name = '\0';
|
||||
strlcat(info.type, "-client",
|
||||
sizeof(info.type));
|
||||
} else
|
||||
return 0; /* no secondary addr, which is OK */
|
||||
}
|
||||
st->mux_client = i2c_new_device(st->mux_adapter, &info);
|
||||
if (!st->mux_client)
|
||||
return -ENODEV;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
if (st->mux_client)
|
||||
i2c_unregister_device(st->mux_client);
|
||||
}
|
||||
#else
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
}
|
||||
#endif
|
|
@ -829,8 +829,14 @@ static int inv_mpu_probe(struct i2c_client *client,
|
|||
goto out_unreg_device;
|
||||
}
|
||||
|
||||
result = inv_mpu_acpi_create_mux_client(st);
|
||||
if (result)
|
||||
goto out_del_mux;
|
||||
|
||||
return 0;
|
||||
|
||||
out_del_mux:
|
||||
i2c_del_mux_adapter(st->mux_adapter);
|
||||
out_unreg_device:
|
||||
iio_device_unregister(indio_dev);
|
||||
out_remove_trigger:
|
||||
|
@ -845,6 +851,7 @@ static int inv_mpu_remove(struct i2c_client *client)
|
|||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
inv_mpu_acpi_delete_mux_client(st);
|
||||
i2c_del_mux_adapter(st->mux_adapter);
|
||||
iio_device_unregister(indio_dev);
|
||||
inv_mpu6050_remove_trigger(st);
|
||||
|
|
|
@ -121,6 +121,7 @@ struct inv_mpu6050_state {
|
|||
spinlock_t time_stamp_lock;
|
||||
struct i2c_client *client;
|
||||
struct i2c_adapter *mux_adapter;
|
||||
struct i2c_client *mux_client;
|
||||
unsigned int powerup_count;
|
||||
struct inv_mpu6050_platform_data plat_data;
|
||||
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
|
||||
|
@ -251,3 +252,5 @@ int inv_reset_fifo(struct iio_dev *indio_dev);
|
|||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
|
||||
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
|
||||
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st);
|
||||
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st);
|
||||
|
|
|
@ -169,19 +169,18 @@ static const u16 kmx61_uscale_table[] = {9582, 19163, 38326};
|
|||
static const struct {
|
||||
int val;
|
||||
int val2;
|
||||
u8 odr_bits;
|
||||
} kmx61_samp_freq_table[] = { {12, 500000, 0x00},
|
||||
{25, 0, 0x01},
|
||||
{50, 0, 0x02},
|
||||
{100, 0, 0x03},
|
||||
{200, 0, 0x04},
|
||||
{400, 0, 0x05},
|
||||
{800, 0, 0x06},
|
||||
{1600, 0, 0x07},
|
||||
{0, 781000, 0x08},
|
||||
{1, 563000, 0x09},
|
||||
{3, 125000, 0x0A},
|
||||
{6, 250000, 0x0B} };
|
||||
} kmx61_samp_freq_table[] = { {12, 500000},
|
||||
{25, 0},
|
||||
{50, 0},
|
||||
{100, 0},
|
||||
{200, 0},
|
||||
{400, 0},
|
||||
{800, 0},
|
||||
{1600, 0},
|
||||
{0, 781000},
|
||||
{1, 563000},
|
||||
{3, 125000},
|
||||
{6, 250000} };
|
||||
|
||||
static const struct {
|
||||
int val;
|
||||
|
@ -302,24 +301,10 @@ static int kmx61_convert_freq_to_bit(int val, int val2)
|
|||
for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
|
||||
if (val == kmx61_samp_freq_table[i].val &&
|
||||
val2 == kmx61_samp_freq_table[i].val2)
|
||||
return kmx61_samp_freq_table[i].odr_bits;
|
||||
return i;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int kmx61_convert_bit_to_freq(u8 odr_bits, int *val, int *val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
|
||||
if (odr_bits == kmx61_samp_freq_table[i].odr_bits) {
|
||||
*val = kmx61_samp_freq_table[i].val;
|
||||
*val2 = kmx61_samp_freq_table[i].val2;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int kmx61_convert_wake_up_odr_to_bit(int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
@ -478,7 +463,7 @@ static int kmx61_set_odr(struct kmx61_data *data, int val, int val2, u8 device)
|
|||
|
||||
static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2,
|
||||
u8 device)
|
||||
{ int i;
|
||||
{
|
||||
u8 lodr_bits;
|
||||
|
||||
if (device & KMX61_ACC)
|
||||
|
@ -490,13 +475,13 @@ static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2,
|
|||
else
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
|
||||
if (lodr_bits == kmx61_samp_freq_table[i].odr_bits) {
|
||||
*val = kmx61_samp_freq_table[i].val;
|
||||
*val2 = kmx61_samp_freq_table[i].val2;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
if (lodr_bits >= ARRAY_SIZE(kmx61_samp_freq_table))
|
||||
return -EINVAL;
|
||||
|
||||
*val = kmx61_samp_freq_table[lodr_bits].val;
|
||||
*val2 = kmx61_samp_freq_table[lodr_bits].val2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kmx61_set_range(struct kmx61_data *data, u8 range)
|
||||
|
@ -580,8 +565,11 @@ static int kmx61_chip_init(struct kmx61_data *data)
|
|||
}
|
||||
data->odr_bits = ret;
|
||||
|
||||
/* set output data rate for wake up (motion detection) function */
|
||||
ret = kmx61_convert_bit_to_freq(data->odr_bits, &val, &val2);
|
||||
/*
|
||||
* set output data rate for wake up (motion detection) function
|
||||
* to match data rate for accelerometer sampling
|
||||
*/
|
||||
ret = kmx61_get_odr(data, &val, &val2, KMX61_ACC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -1267,16 +1255,12 @@ static int kmx61_gpio_probe(struct i2c_client *client, struct kmx61_data *data)
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0);
|
||||
gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
@ -37,11 +37,57 @@ static bool iio_buffer_is_active(struct iio_buffer *buf)
|
|||
return !list_empty(&buf->buffer_list);
|
||||
}
|
||||
|
||||
static bool iio_buffer_data_available(struct iio_buffer *buf)
|
||||
static size_t iio_buffer_data_available(struct iio_buffer *buf)
|
||||
{
|
||||
return buf->access->data_available(buf);
|
||||
}
|
||||
|
||||
static int iio_buffer_flush_hwfifo(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buf, size_t required)
|
||||
{
|
||||
if (!indio_dev->info->hwfifo_flush_to_buffer)
|
||||
return -ENODEV;
|
||||
|
||||
return indio_dev->info->hwfifo_flush_to_buffer(indio_dev, required);
|
||||
}
|
||||
|
||||
static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
|
||||
size_t to_wait, int to_flush)
|
||||
{
|
||||
size_t avail;
|
||||
int flushed = 0;
|
||||
|
||||
/* wakeup if the device was unregistered */
|
||||
if (!indio_dev->info)
|
||||
return true;
|
||||
|
||||
/* drain the buffer if it was disabled */
|
||||
if (!iio_buffer_is_active(buf)) {
|
||||
to_wait = min_t(size_t, to_wait, 1);
|
||||
to_flush = 0;
|
||||
}
|
||||
|
||||
avail = iio_buffer_data_available(buf);
|
||||
|
||||
if (avail >= to_wait) {
|
||||
/* force a flush for non-blocking reads */
|
||||
if (!to_wait && !avail && to_flush)
|
||||
iio_buffer_flush_hwfifo(indio_dev, buf, to_flush);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (to_flush)
|
||||
flushed = iio_buffer_flush_hwfifo(indio_dev, buf,
|
||||
to_wait - avail);
|
||||
if (flushed <= 0)
|
||||
return false;
|
||||
|
||||
if (avail + flushed >= to_wait)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_buffer_read_first_n_outer() - chrdev read for buffer access
|
||||
*
|
||||
|
@ -53,6 +99,9 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
|||
{
|
||||
struct iio_dev *indio_dev = filp->private_data;
|
||||
struct iio_buffer *rb = indio_dev->buffer;
|
||||
size_t datum_size;
|
||||
size_t to_wait = 0;
|
||||
size_t to_read;
|
||||
int ret;
|
||||
|
||||
if (!indio_dev->info)
|
||||
|
@ -61,19 +110,28 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
|||
if (!rb || !rb->access->read_first_n)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
if (!iio_buffer_data_available(rb)) {
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
datum_size = rb->bytes_per_datum;
|
||||
|
||||
ret = wait_event_interruptible(rb->pollq,
|
||||
iio_buffer_data_available(rb) ||
|
||||
indio_dev->info == NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (indio_dev->info == NULL)
|
||||
return -ENODEV;
|
||||
}
|
||||
/*
|
||||
* If datum_size is 0 there will never be anything to read from the
|
||||
* buffer, so signal end of file now.
|
||||
*/
|
||||
if (!datum_size)
|
||||
return 0;
|
||||
|
||||
to_read = min_t(size_t, n / datum_size, rb->watermark);
|
||||
|
||||
if (!(filp->f_flags & O_NONBLOCK))
|
||||
to_wait = to_read;
|
||||
|
||||
do {
|
||||
ret = wait_event_interruptible(rb->pollq,
|
||||
iio_buffer_ready(indio_dev, rb, to_wait, to_read));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = rb->access->read_first_n(rb, n, buf);
|
||||
if (ret == 0 && (filp->f_flags & O_NONBLOCK))
|
||||
|
@ -96,9 +154,8 @@ unsigned int iio_buffer_poll(struct file *filp,
|
|||
return -ENODEV;
|
||||
|
||||
poll_wait(filp, &rb->pollq, wait);
|
||||
if (iio_buffer_data_available(rb))
|
||||
if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0))
|
||||
return POLLIN | POLLRDNORM;
|
||||
/* need a way of knowing if there may be enough data... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -123,6 +180,7 @@ void iio_buffer_init(struct iio_buffer *buffer)
|
|||
INIT_LIST_HEAD(&buffer->buffer_list);
|
||||
init_waitqueue_head(&buffer->pollq);
|
||||
kref_init(&buffer->ref);
|
||||
buffer->watermark = 1;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_buffer_init);
|
||||
|
||||
|
@ -416,6 +474,11 @@ static ssize_t iio_buffer_write_length(struct device *dev,
|
|||
buffer->access->set_length(buffer, val);
|
||||
ret = 0;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
if (buffer->length && buffer->length < buffer->watermark)
|
||||
buffer->watermark = buffer->length;
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
|
@ -472,6 +535,7 @@ static void iio_buffer_activate(struct iio_dev *indio_dev,
|
|||
static void iio_buffer_deactivate(struct iio_buffer *buffer)
|
||||
{
|
||||
list_del_init(&buffer->buffer_list);
|
||||
wake_up_interruptible(&buffer->pollq);
|
||||
iio_buffer_put(buffer);
|
||||
}
|
||||
|
||||
|
@ -629,19 +693,16 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
/* Definitely possible for devices to support both of these. */
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
|
||||
if (!indio_dev->trig) {
|
||||
printk(KERN_INFO "Buffer not started: no trigger\n");
|
||||
ret = -EINVAL;
|
||||
/* Can only occur on first buffer */
|
||||
goto error_run_postdisable;
|
||||
}
|
||||
if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
|
||||
} else { /* Should never be reached */
|
||||
/* Can only occur on first buffer */
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
|
||||
pr_info("Buffer not started: no trigger\n");
|
||||
ret = -EINVAL;
|
||||
goto error_run_postdisable;
|
||||
}
|
||||
|
@ -754,12 +815,68 @@ done:
|
|||
|
||||
static const char * const iio_scan_elements_group_name = "scan_elements";
|
||||
|
||||
static ssize_t iio_buffer_show_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
|
||||
return sprintf(buf, "%u\n", buffer->watermark);
|
||||
}
|
||||
|
||||
static ssize_t iio_buffer_store_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (val > buffer->length) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iio_buffer_is_active(indio_dev->buffer)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer->watermark = val;
|
||||
|
||||
if (indio_dev->info->hwfifo_set_watermark)
|
||||
indio_dev->info->hwfifo_set_watermark(indio_dev, val);
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length,
|
||||
iio_buffer_write_length);
|
||||
static struct device_attribute dev_attr_length_ro = __ATTR(length,
|
||||
S_IRUGO, iio_buffer_read_length, NULL);
|
||||
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
|
||||
iio_buffer_show_enable, iio_buffer_store_enable);
|
||||
static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR,
|
||||
iio_buffer_show_watermark, iio_buffer_store_watermark);
|
||||
|
||||
static struct attribute *iio_buffer_attrs[] = {
|
||||
&dev_attr_length.attr,
|
||||
&dev_attr_enable.attr,
|
||||
&dev_attr_watermark.attr,
|
||||
};
|
||||
|
||||
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
|
||||
{
|
||||
|
@ -778,21 +895,23 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
|
|||
attrcount++;
|
||||
}
|
||||
|
||||
buffer->buffer_group.name = "buffer";
|
||||
buffer->buffer_group.attrs = kcalloc(attrcount + 3,
|
||||
sizeof(*buffer->buffer_group.attrs), GFP_KERNEL);
|
||||
if (!buffer->buffer_group.attrs)
|
||||
attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
|
||||
sizeof(struct attribute *), GFP_KERNEL);
|
||||
if (!attr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (buffer->access->set_length)
|
||||
buffer->buffer_group.attrs[0] = &dev_attr_length.attr;
|
||||
else
|
||||
buffer->buffer_group.attrs[0] = &dev_attr_length_ro.attr;
|
||||
buffer->buffer_group.attrs[1] = &dev_attr_enable.attr;
|
||||
memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs));
|
||||
if (!buffer->access->set_length)
|
||||
attr[0] = &dev_attr_length_ro.attr;
|
||||
|
||||
if (buffer->attrs)
|
||||
memcpy(&buffer->buffer_group.attrs[2], buffer->attrs,
|
||||
sizeof(*&buffer->buffer_group.attrs) * attrcount);
|
||||
buffer->buffer_group.attrs[attrcount+2] = NULL;
|
||||
memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
|
||||
sizeof(struct attribute *) * attrcount);
|
||||
|
||||
attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL;
|
||||
|
||||
buffer->buffer_group.name = "buffer";
|
||||
buffer->buffer_group.attrs = attr;
|
||||
|
||||
indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group;
|
||||
|
||||
|
@ -937,8 +1056,18 @@ static const void *iio_demux(struct iio_buffer *buffer,
|
|||
static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)
|
||||
{
|
||||
const void *dataout = iio_demux(buffer, data);
|
||||
int ret;
|
||||
|
||||
return buffer->access->store_to(buffer, dataout);
|
||||
ret = buffer->access->store_to(buffer, dataout);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We can't just test for watermark to decide if we wake the poll queue
|
||||
* because read may request less samples than the watermark.
|
||||
*/
|
||||
wake_up_interruptible_poll(&buffer->pollq, POLLIN | POLLRDNORM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iio_buffer_demux_free(struct iio_buffer *buffer)
|
||||
|
|
|
@ -83,9 +83,6 @@ static int iio_store_to_kfifo(struct iio_buffer *r,
|
|||
ret = kfifo_in(&kf->kf, data, 1);
|
||||
if (ret != 1)
|
||||
return -EBUSY;
|
||||
|
||||
wake_up_interruptible_poll(&r->pollq, POLLIN | POLLRDNORM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -109,16 +106,16 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
|
|||
return copied;
|
||||
}
|
||||
|
||||
static bool iio_kfifo_buf_data_available(struct iio_buffer *r)
|
||||
static size_t iio_kfifo_buf_data_available(struct iio_buffer *r)
|
||||
{
|
||||
struct iio_kfifo *kf = iio_to_kfifo(r);
|
||||
bool empty;
|
||||
size_t samples;
|
||||
|
||||
mutex_lock(&kf->user_lock);
|
||||
empty = kfifo_is_empty(&kf->kf);
|
||||
samples = kfifo_len(&kf->kf);
|
||||
mutex_unlock(&kf->user_lock);
|
||||
|
||||
return !empty;
|
||||
return samples;
|
||||
}
|
||||
|
||||
static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
|
||||
|
|
|
@ -59,6 +59,16 @@ config CM3232
|
|||
To compile this driver as a module, choose M here:
|
||||
the module will be called cm3232.
|
||||
|
||||
config CM3323
|
||||
depends on I2C
|
||||
tristate "Capella CM3323 color light sensor"
|
||||
help
|
||||
Say Y here if you want to build a driver for Capela CM3323
|
||||
color sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called cm3323.
|
||||
|
||||
config CM36651
|
||||
depends on I2C
|
||||
tristate "CM36651 driver"
|
||||
|
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_AL3320A) += al3320a.o
|
|||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
obj-$(CONFIG_CM3323) += cm3323.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
|
|
@ -378,6 +378,39 @@ static const struct i2c_device_id cm3232_id[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cm3232_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct cm3232_chip *chip = iio_priv(indio_dev);
|
||||
struct i2c_client *client = chip->client;
|
||||
int ret;
|
||||
|
||||
chip->regs_cmd |= CM3232_CMD_ALS_DISABLE;
|
||||
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
|
||||
chip->regs_cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm3232_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct cm3232_chip *chip = iio_priv(indio_dev);
|
||||
struct i2c_client *client = chip->client;
|
||||
int ret;
|
||||
|
||||
chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE;
|
||||
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
|
||||
chip->regs_cmd | CM3232_CMD_ALS_RESET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cm3232_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)};
|
||||
#endif
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, cm3232_id);
|
||||
|
||||
static const struct of_device_id cm3232_of_match[] = {
|
||||
|
@ -390,6 +423,9 @@ static struct i2c_driver cm3232_driver = {
|
|||
.name = "cm3232",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(cm3232_of_match),
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.pm = &cm3232_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.id_table = cm3232_id,
|
||||
.probe = cm3232_probe,
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* CM3323 - Capella Color Light Sensor
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for CM3323 (7-bit I2C slave address 0x10)
|
||||
*
|
||||
* TODO: calibscale to correct the lens factor
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define CM3323_DRV_NAME "cm3323"
|
||||
|
||||
#define CM3323_CMD_CONF 0x00
|
||||
#define CM3323_CMD_RED_DATA 0x08
|
||||
#define CM3323_CMD_GREEN_DATA 0x09
|
||||
#define CM3323_CMD_BLUE_DATA 0x0A
|
||||
#define CM3323_CMD_CLEAR_DATA 0x0B
|
||||
|
||||
#define CM3323_CONF_SD_BIT BIT(0) /* sensor disable */
|
||||
#define CM3323_CONF_AF_BIT BIT(1) /* auto/manual force mode */
|
||||
#define CM3323_CONF_IT_MASK (BIT(4) | BIT(5) | BIT(6))
|
||||
#define CM3323_CONF_IT_SHIFT 4
|
||||
|
||||
#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
|
||||
|
||||
static const struct {
|
||||
int val;
|
||||
int val2;
|
||||
} cm3323_int_time[] = {
|
||||
{0, 40000}, /* 40 ms */
|
||||
{0, 80000}, /* 80 ms */
|
||||
{0, 160000}, /* 160 ms */
|
||||
{0, 320000}, /* 320 ms */
|
||||
{0, 640000}, /* 640 ms */
|
||||
{1, 280000}, /* 1280 ms */
|
||||
};
|
||||
|
||||
struct cm3323_data {
|
||||
struct i2c_client *client;
|
||||
u16 reg_conf;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
#define CM3323_COLOR_CHANNEL(_color, _addr) { \
|
||||
.type = IIO_INTENSITY, \
|
||||
.modified = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
|
||||
.channel2 = IIO_MOD_LIGHT_##_color, \
|
||||
.address = _addr, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec cm3323_channels[] = {
|
||||
CM3323_COLOR_CHANNEL(RED, CM3323_CMD_RED_DATA),
|
||||
CM3323_COLOR_CHANNEL(GREEN, CM3323_CMD_GREEN_DATA),
|
||||
CM3323_COLOR_CHANNEL(BLUE, CM3323_CMD_BLUE_DATA),
|
||||
CM3323_COLOR_CHANNEL(CLEAR, CM3323_CMD_CLEAR_DATA),
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_INT_TIME_AVAIL(CM3323_INT_TIME_AVAILABLE);
|
||||
|
||||
static struct attribute *cm3323_attributes[] = {
|
||||
&iio_const_attr_integration_time_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group cm3323_attribute_group = {
|
||||
.attrs = cm3323_attributes,
|
||||
};
|
||||
|
||||
static int cm3323_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_conf\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable sensor and set auto force mode */
|
||||
ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_conf\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->reg_conf = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cm3323_disable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF,
|
||||
CM3323_CONF_SD_BIT);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev, "Error writing reg_conf\n");
|
||||
}
|
||||
|
||||
static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
|
||||
{
|
||||
int i, ret;
|
||||
u16 reg_conf;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cm3323_int_time); i++) {
|
||||
if (val == cm3323_int_time[i].val &&
|
||||
val2 == cm3323_int_time[i].val2) {
|
||||
reg_conf = data->reg_conf;
|
||||
reg_conf |= i << CM3323_CONF_IT_SHIFT;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
CM3323_CMD_CONF,
|
||||
reg_conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->reg_conf = reg_conf;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cm3323_get_it_bits(struct cm3323_data *data)
|
||||
{
|
||||
int bits;
|
||||
|
||||
bits = (data->reg_conf & CM3323_CONF_IT_MASK) >>
|
||||
CM3323_CONF_IT_SHIFT;
|
||||
|
||||
if (bits >= ARRAY_SIZE(cm3323_int_time))
|
||||
return -EINVAL;
|
||||
return bits;
|
||||
}
|
||||
|
||||
static int cm3323_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int i, ret;
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = i2c_smbus_read_word_data(data->client, chan->address);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
*val = ret;
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
mutex_lock(&data->mutex);
|
||||
i = cm3323_get_it_bits(data);
|
||||
if (i < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = cm3323_int_time[i].val;
|
||||
*val2 = cm3323_int_time[i].val2;
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cm3323_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = cm3323_set_it_bits(data, val, val2);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info cm3323_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = cm3323_read_raw,
|
||||
.write_raw = cm3323_write_raw,
|
||||
.attrs = &cm3323_attribute_group,
|
||||
};
|
||||
|
||||
static int cm3323_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct cm3323_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &cm3323_info;
|
||||
indio_dev->name = CM3323_DRV_NAME;
|
||||
indio_dev->channels = cm3323_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cm3323_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = cm3323_init(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cm3323 chip init failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to register iio dev\n");
|
||||
goto err_init;
|
||||
}
|
||||
return 0;
|
||||
err_init:
|
||||
cm3323_disable(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm3323_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
cm3323_disable(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cm3323_id[] = {
|
||||
{"cm3323", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cm3323_id);
|
||||
|
||||
static struct i2c_driver cm3323_driver = {
|
||||
.driver = {
|
||||
.name = CM3323_DRV_NAME,
|
||||
},
|
||||
.probe = cm3323_probe,
|
||||
.remove = cm3323_remove,
|
||||
.id_table = cm3323_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(cm3323_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
|
||||
MODULE_DESCRIPTION("Capella CM3323 Color Light Sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -46,6 +46,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -966,7 +967,6 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data)
|
|||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct gp2ap020a00f_data *priv = iio_priv(indio_dev);
|
||||
size_t d_size = 0;
|
||||
__le32 light_lux;
|
||||
int i, out_val, ret;
|
||||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask,
|
||||
|
@ -981,8 +981,8 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data)
|
|||
i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) {
|
||||
out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]);
|
||||
gp2ap020a00f_output_to_lux(priv, &out_val);
|
||||
light_lux = cpu_to_le32(out_val);
|
||||
memcpy(&priv->buffer[d_size], (u8 *)&light_lux, 4);
|
||||
|
||||
put_unaligned_le32(out_val, &priv->buffer[d_size]);
|
||||
d_size += 4;
|
||||
} else {
|
||||
d_size += 2;
|
||||
|
|
|
@ -308,7 +308,7 @@ static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg)
|
|||
}
|
||||
}
|
||||
|
||||
static struct regmap_config jsa1212_regmap_config = {
|
||||
static const struct regmap_config jsa1212_regmap_config = {
|
||||
.name = JSA1212_REGMAP_NAME,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
|
|
@ -333,6 +333,13 @@ static int ltr501_init(struct ltr501_data *data)
|
|||
data->ps_contr);
|
||||
}
|
||||
|
||||
static int ltr501_powerdown(struct ltr501_data *data)
|
||||
{
|
||||
return ltr501_write_contr(data->client,
|
||||
data->als_contr & ~LTR501_CONTR_ACTIVE,
|
||||
data->ps_contr & ~LTR501_CONTR_ACTIVE);
|
||||
}
|
||||
|
||||
static int ltr501_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -370,7 +377,7 @@ static int ltr501_probe(struct i2c_client *client,
|
|||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ltr501_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto powerdown_on_error;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
|
@ -380,16 +387,11 @@ static int ltr501_probe(struct i2c_client *client,
|
|||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
powerdown_on_error:
|
||||
ltr501_powerdown(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltr501_powerdown(struct ltr501_data *data)
|
||||
{
|
||||
return ltr501_write_contr(data->client,
|
||||
data->als_contr & ~LTR501_CONTR_ACTIVE,
|
||||
data->ps_contr & ~LTR501_CONTR_ACTIVE);
|
||||
}
|
||||
|
||||
static int ltr501_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
|
|
@ -321,6 +321,12 @@ static const struct iio_info mag3110_info = {
|
|||
|
||||
static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
|
||||
|
||||
static int mag3110_standby(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
|
||||
}
|
||||
|
||||
static int mag3110_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -360,12 +366,12 @@ static int mag3110_probe(struct i2c_client *client,
|
|||
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
|
||||
MAG3110_CTRL_AUTO_MRST_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto standby_on_error;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
mag3110_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto standby_on_error;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
|
@ -374,15 +380,11 @@ static int mag3110_probe(struct i2c_client *client,
|
|||
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
standby_on_error:
|
||||
mag3110_standby(iio_priv(indio_dev));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mag3110_standby(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
|
||||
}
|
||||
|
||||
static int mag3110_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
|
|
@ -52,6 +52,33 @@ config MPL3115
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl3115.
|
||||
|
||||
config MS5611
|
||||
tristate "Measurement Specialities MS5611 pressure sensor driver"
|
||||
help
|
||||
Say Y here to build support for the Measurement Specialities
|
||||
MS5611 pressure and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ms5611_core.
|
||||
|
||||
config MS5611_I2C
|
||||
tristate "support I2C bus connection"
|
||||
depends on I2C && MS5611
|
||||
help
|
||||
Say Y here to build I2C bus support for MS5611.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ms5611_i2c.
|
||||
|
||||
config MS5611_SPI
|
||||
tristate "support SPI bus connection"
|
||||
depends on SPI_MASTER && MS5611
|
||||
help
|
||||
Say Y here to build SPI bus support for MS5611.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ms5611_spi.
|
||||
|
||||
config IIO_ST_PRESS
|
||||
tristate "STMicroelectronics pressure sensor Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
|
|
|
@ -7,6 +7,9 @@ obj-$(CONFIG_BMP280) += bmp280.o
|
|||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
obj-$(CONFIG_MPL3115) += mpl3115.o
|
||||
obj-$(CONFIG_MS5611) += ms5611_core.o
|
||||
obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o
|
||||
obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o
|
||||
obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
|
||||
st_pressure-y := st_pressure_core.o
|
||||
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MS5611_H
|
||||
#define _MS5611_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define MS5611_RESET 0x1e
|
||||
#define MS5611_READ_ADC 0x00
|
||||
#define MS5611_READ_PROM_WORD 0xA0
|
||||
#define MS5611_START_TEMP_CONV 0x58
|
||||
#define MS5611_START_PRESSURE_CONV 0x48
|
||||
|
||||
#define MS5611_CONV_TIME_MIN 9040
|
||||
#define MS5611_CONV_TIME_MAX 10000
|
||||
|
||||
#define MS5611_PROM_WORDS_NB 8
|
||||
|
||||
struct ms5611_state {
|
||||
void *client;
|
||||
struct mutex lock;
|
||||
|
||||
int (*reset)(struct device *dev);
|
||||
int (*read_prom_word)(struct device *dev, int index, u16 *word);
|
||||
int (*read_adc_temp_and_pressure)(struct device *dev,
|
||||
s32 *temp, s32 *pressure);
|
||||
|
||||
u16 prom[MS5611_PROM_WORDS_NB];
|
||||
};
|
||||
|
||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev);
|
||||
|
||||
#endif /* _MS5611_H */
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Data sheet:
|
||||
* http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "ms5611.h"
|
||||
|
||||
static bool ms5611_prom_is_valid(u16 *prom, size_t len)
|
||||
{
|
||||
int i, j;
|
||||
uint16_t crc = 0, crc_orig = prom[7] & 0x000F;
|
||||
|
||||
prom[7] &= 0xFF00;
|
||||
|
||||
for (i = 0; i < len * 2; i++) {
|
||||
if (i % 2 == 1)
|
||||
crc ^= prom[i >> 1] & 0x00FF;
|
||||
else
|
||||
crc ^= prom[i >> 1] >> 8;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (crc & 0x8000)
|
||||
crc = (crc << 1) ^ 0x3000;
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
crc = (crc >> 12) & 0x000F;
|
||||
|
||||
return crc_orig != 0x0000 && crc == crc_orig;
|
||||
}
|
||||
|
||||
static int ms5611_read_prom(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret, i;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
|
||||
ret = st->read_prom_word(&indio_dev->dev, i, &st->prom[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to read prom at %d\n", i);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) {
|
||||
dev_err(&indio_dev->dev, "PROM integrity check failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
|
||||
s32 *temp, s32 *pressure)
|
||||
{
|
||||
int ret;
|
||||
s32 t, p;
|
||||
s64 off, sens, dt;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = st->read_adc_temp_and_pressure(&indio_dev->dev, &t, &p);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to read temperature and pressure\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dt = t - (st->prom[5] << 8);
|
||||
off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7);
|
||||
sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8);
|
||||
|
||||
t = 2000 + ((st->prom[6] * dt) >> 23);
|
||||
if (t < 2000) {
|
||||
s64 off2, sens2, t2;
|
||||
|
||||
t2 = (dt * dt) >> 31;
|
||||
off2 = (5 * (t - 2000) * (t - 2000)) >> 1;
|
||||
sens2 = off2 >> 1;
|
||||
|
||||
if (t < -1500) {
|
||||
s64 tmp = (t + 1500) * (t + 1500);
|
||||
|
||||
off2 += 7 * tmp;
|
||||
sens2 += (11 * tmp) >> 1;
|
||||
}
|
||||
|
||||
t -= t2;
|
||||
off -= off2;
|
||||
sens -= sens2;
|
||||
}
|
||||
|
||||
*temp = t;
|
||||
*pressure = (((p * sens) >> 21) - off) >> 15;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = st->reset(&indio_dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to reset device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(3000, 4000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
s32 temp, pressure;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&st->lock);
|
||||
ret = ms5611_read_temp_and_pressure(indio_dev,
|
||||
&temp, &pressure);
|
||||
mutex_unlock(&st->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = temp * 10;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PRESSURE:
|
||||
*val = pressure / 1000;
|
||||
*val2 = (pressure % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ms5611_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info ms5611_info = {
|
||||
.read_raw = &ms5611_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ms5611_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ms5611_reset(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ms5611_read_prom(indio_dev);
|
||||
}
|
||||
|
||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_init(&st->lock);
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = dev->driver->name;
|
||||
indio_dev->info = &ms5611_info;
|
||||
indio_dev->channels = ms5611_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = ms5611_init(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(ms5611_probe);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
|
||||
MODULE_DESCRIPTION("MS5611 core driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver (I2C bus)
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 7-bit I2C slave addresses:
|
||||
*
|
||||
* 0x77 (CSB pin low)
|
||||
* 0x76 (CSB pin high)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "ms5611.h"
|
||||
|
||||
static int ms5611_i2c_reset(struct device *dev)
|
||||
{
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return i2c_smbus_write_byte(st->client, MS5611_RESET);
|
||||
}
|
||||
|
||||
static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(st->client,
|
||||
MS5611_READ_PROM_WORD + (index << 1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*word = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[3];
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(st->client, MS5611_READ_ADC,
|
||||
3, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev,
|
||||
s32 *temp, s32 *pressure)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client, MS5611_START_TEMP_CONV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
ret = ms5611_i2c_read_adc(st, temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client, MS5611_START_PRESSURE_CONV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
return ms5611_i2c_read_adc(st, pressure);
|
||||
}
|
||||
|
||||
static int ms5611_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ms5611_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->reset = ms5611_i2c_reset;
|
||||
st->read_prom_word = ms5611_i2c_read_prom_word;
|
||||
st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure;
|
||||
st->client = client;
|
||||
|
||||
return ms5611_probe(indio_dev, &client->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ms5611_id[] = {
|
||||
{ "ms5611", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ms5611_id);
|
||||
|
||||
static struct i2c_driver ms5611_driver = {
|
||||
.driver = {
|
||||
.name = "ms5611",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = ms5611_id,
|
||||
.probe = ms5611_i2c_probe,
|
||||
};
|
||||
module_i2c_driver(ms5611_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
|
||||
MODULE_DESCRIPTION("MS5611 i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver (SPI bus)
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "ms5611.h"
|
||||
|
||||
static int ms5611_spi_reset(struct device *dev)
|
||||
{
|
||||
u8 cmd = MS5611_RESET;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return spi_write_then_read(st->client, &cmd, 1, NULL, 0);
|
||||
}
|
||||
|
||||
static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*word = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_spi_read_adc(struct device *dev, s32 *val)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[3] = { MS5611_READ_ADC };
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = spi_write_then_read(st->client, buf, 1, buf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev,
|
||||
s32 *temp, s32 *pressure)
|
||||
{
|
||||
u8 cmd;
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
cmd = MS5611_START_TEMP_CONV;
|
||||
ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
ret = ms5611_spi_read_adc(dev, temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cmd = MS5611_START_PRESSURE_CONV;
|
||||
ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
return ms5611_spi_read_adc(dev, pressure);
|
||||
}
|
||||
|
||||
static int ms5611_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi->max_speed_hz = 20000000;
|
||||
spi->bits_per_word = 8;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->reset = ms5611_spi_reset;
|
||||
st->read_prom_word = ms5611_spi_read_prom_word;
|
||||
st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure;
|
||||
st->client = spi;
|
||||
|
||||
return ms5611_probe(indio_dev, &spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ms5611_id[] = {
|
||||
{ "ms5611", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ms5611_id);
|
||||
|
||||
static struct spi_driver ms5611_driver = {
|
||||
.driver = {
|
||||
.name = "ms5611",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = ms5611_id,
|
||||
.probe = ms5611_spi_probe,
|
||||
};
|
||||
module_spi_driver(ms5611_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
|
||||
MODULE_DESCRIPTION("MS5611 spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -618,16 +618,12 @@ static int sx9500_gpio_probe(struct i2c_client *client,
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0);
|
||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
* Copyright (c) 2015 Essensium NV
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
|
@ -20,11 +21,35 @@
|
|||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define MLX90614_OP_RAM 0x00
|
||||
#define MLX90614_OP_RAM 0x00
|
||||
#define MLX90614_OP_EEPROM 0x20
|
||||
#define MLX90614_OP_SLEEP 0xff
|
||||
|
||||
/* RAM offsets with 16-bit data, MSB first */
|
||||
#define MLX90614_TA 0x06 /* ambient temperature */
|
||||
#define MLX90614_TOBJ1 0x07 /* object temperature */
|
||||
#define MLX90614_RAW1 (MLX90614_OP_RAM | 0x04) /* raw data IR channel 1 */
|
||||
#define MLX90614_RAW2 (MLX90614_OP_RAM | 0x05) /* raw data IR channel 2 */
|
||||
#define MLX90614_TA (MLX90614_OP_RAM | 0x06) /* ambient temperature */
|
||||
#define MLX90614_TOBJ1 (MLX90614_OP_RAM | 0x07) /* object 1 temperature */
|
||||
#define MLX90614_TOBJ2 (MLX90614_OP_RAM | 0x08) /* object 2 temperature */
|
||||
|
||||
/* EEPROM offsets with 16-bit data, MSB first */
|
||||
#define MLX90614_EMISSIVITY (MLX90614_OP_EEPROM | 0x04) /* emissivity correction coefficient */
|
||||
#define MLX90614_CONFIG (MLX90614_OP_EEPROM | 0x05) /* configuration register */
|
||||
|
||||
/* Control bits in configuration register */
|
||||
#define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */
|
||||
#define MLX90614_CONFIG_IIR_MASK (0x7 << MLX90614_CONFIG_IIR_SHIFT)
|
||||
#define MLX90614_CONFIG_DUAL_SHIFT 6 /* single (0) or dual (1) IR sensor */
|
||||
#define MLX90614_CONFIG_DUAL_MASK (1 << MLX90614_CONFIG_DUAL_SHIFT)
|
||||
#define MLX90614_CONFIG_FIR_SHIFT 8 /* FIR coefficient */
|
||||
#define MLX90614_CONFIG_FIR_MASK (0x7 << MLX90614_CONFIG_FIR_SHIFT)
|
||||
#define MLX90614_CONFIG_GAIN_SHIFT 11 /* gain */
|
||||
#define MLX90614_CONFIG_GAIN_MASK (0x7 << MLX90614_CONFIG_GAIN_SHIFT)
|
||||
|
||||
/* Timings (in ms) */
|
||||
#define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */
|
||||
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
|
||||
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
|
||||
|
||||
struct mlx90614_data {
|
||||
struct i2c_client *client;
|
||||
|
@ -35,26 +60,34 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
|||
int *val2, long mask)
|
||||
{
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
u8 cmd;
|
||||
s32 ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */
|
||||
switch (channel->channel2) {
|
||||
case IIO_MOD_TEMP_AMBIENT:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
MLX90614_OP_RAM | MLX90614_TA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
cmd = MLX90614_TA;
|
||||
break;
|
||||
case IIO_MOD_TEMP_OBJECT:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
MLX90614_OP_RAM | MLX90614_TOBJ1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (channel->channel) {
|
||||
case 0:
|
||||
cmd = MLX90614_TOBJ1;
|
||||
break;
|
||||
case 1:
|
||||
cmd = MLX90614_TOBJ2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
|
@ -86,6 +119,16 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.modified = 1,
|
||||
.channel = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info mlx90614_info = {
|
||||
|
@ -93,11 +136,25 @@ static const struct iio_info mlx90614_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
|
||||
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret & MLX90614_CONFIG_DUAL_MASK) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int mlx90614_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mlx90614_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
@ -115,8 +172,21 @@ static int mlx90614_probe(struct i2c_client *client,
|
|||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mlx90614_info;
|
||||
|
||||
indio_dev->channels = mlx90614_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mlx90614_channels);
|
||||
ret = mlx90614_probe_num_ir_sensors(client);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
dev_dbg(&client->dev, "Found single sensor");
|
||||
indio_dev->channels = mlx90614_channels;
|
||||
indio_dev->num_channels = 2;
|
||||
break;
|
||||
case 1:
|
||||
dev_dbg(&client->dev, "Found dual sensor");
|
||||
indio_dev->channels = mlx90614_channels;
|
||||
indio_dev->num_channels = 3;
|
||||
break;
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
@ -146,5 +216,6 @@ static struct i2c_driver mlx90614_driver = {
|
|||
module_i2c_driver(mlx90614_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_AUTHOR("Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>");
|
||||
MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -58,6 +58,8 @@ source "drivers/staging/iio/Kconfig"
|
|||
|
||||
source "drivers/staging/sm7xxfb/Kconfig"
|
||||
|
||||
source "drivers/staging/sm750fb/Kconfig"
|
||||
|
||||
source "drivers/staging/xgifb/Kconfig"
|
||||
|
||||
source "drivers/staging/emxx_udc/Kconfig"
|
||||
|
@ -108,4 +110,6 @@ source "drivers/staging/fbtft/Kconfig"
|
|||
|
||||
source "drivers/staging/i2o/Kconfig"
|
||||
|
||||
source "drivers/staging/fsl-mc/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_VT6656) += vt6656/
|
|||
obj-$(CONFIG_VME_BUS) += vme/
|
||||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
|
||||
obj-$(CONFIG_FB_SM7XX) += sm750fb/
|
||||
obj-$(CONFIG_FB_XGI) += xgifb/
|
||||
obj-$(CONFIG_USB_EMXX) += emxx_udc/
|
||||
obj-$(CONFIG_FT1000) += ft1000/
|
||||
|
@ -46,3 +47,4 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/
|
|||
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
|
||||
obj-$(CONFIG_FB_TFT) += fbtft/
|
||||
obj-$(CONFIG_I2O) += i2o/
|
||||
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
|
||||
|
|
|
@ -566,8 +566,8 @@ int ion_phys(struct ion_client *client, struct ion_handle *handle,
|
|||
buffer = handle->buffer;
|
||||
|
||||
if (!buffer->heap->ops->phys) {
|
||||
pr_err("%s: ion_phys is not implemented by this heap.\n",
|
||||
__func__);
|
||||
pr_err("%s: ion_phys is not implemented by this heap (name=%s, type=%d).\n",
|
||||
__func__, buffer->heap->name, buffer->heap->type);
|
||||
mutex_unlock(&client->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1395,7 +1395,7 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||
size_t total_size = 0;
|
||||
size_t total_orphaned_size = 0;
|
||||
|
||||
seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size");
|
||||
seq_printf(s, "%16s %16s %16s\n", "client", "pid", "size");
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
|
||||
for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
|
||||
|
@ -1409,10 +1409,10 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||
char task_comm[TASK_COMM_LEN];
|
||||
|
||||
get_task_comm(task_comm, client->task);
|
||||
seq_printf(s, "%16.s %16u %16zu\n", task_comm,
|
||||
seq_printf(s, "%16s %16u %16zu\n", task_comm,
|
||||
client->pid, size);
|
||||
} else {
|
||||
seq_printf(s, "%16.s %16u %16zu\n", client->name,
|
||||
seq_printf(s, "%16s %16u %16zu\n", client->name,
|
||||
client->pid, size);
|
||||
}
|
||||
}
|
||||
|
@ -1426,7 +1426,7 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||
continue;
|
||||
total_size += buffer->size;
|
||||
if (!buffer->handle_count) {
|
||||
seq_printf(s, "%16.s %16u %16zu %d %d\n",
|
||||
seq_printf(s, "%16s %16u %16zu %d %d\n",
|
||||
buffer->task_comm, buffer->pid,
|
||||
buffer->size, buffer->kmap_cnt,
|
||||
atomic_read(&buffer->ref.refcount));
|
||||
|
@ -1435,11 +1435,11 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||
}
|
||||
mutex_unlock(&dev->buffer_lock);
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
seq_printf(s, "%16.s %16zu\n", "total orphaned",
|
||||
seq_printf(s, "%16s %16zu\n", "total orphaned",
|
||||
total_orphaned_size);
|
||||
seq_printf(s, "%16.s %16zu\n", "total ", total_size);
|
||||
seq_printf(s, "%16s %16zu\n", "total ", total_size);
|
||||
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
|
||||
seq_printf(s, "%16.s %16zu\n", "deferred free",
|
||||
seq_printf(s, "%16s %16zu\n", "deferred free",
|
||||
heap->free_list_size);
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
|
||||
|
|
|
@ -280,3 +280,4 @@ static void __exit ion_test_exit(void)
|
|||
|
||||
module_init(ion_test_init);
|
||||
module_exit(ion_test_exit);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -114,7 +114,7 @@ void sync_timeline_signal(struct sync_timeline *obj)
|
|||
list_for_each_entry_safe(pt, next, &obj->active_list_head,
|
||||
active_list) {
|
||||
if (fence_is_signaled_locked(&pt->base))
|
||||
list_del(&pt->active_list);
|
||||
list_del_init(&pt->active_list);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&obj->child_list_lock, flags);
|
||||
|
|
|
@ -108,6 +108,7 @@ if COMEDI_ISA_DRIVERS
|
|||
|
||||
config COMEDI_PCL711
|
||||
tristate "Advantech PCL-711/711b and ADlink ACL-8112 ISA card support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Advantech PCL-711 and 711b, ADlink ACL-8112
|
||||
|
||||
|
@ -169,6 +170,7 @@ config COMEDI_PCL730
|
|||
config COMEDI_PCL812
|
||||
tristate "Advantech PCL-812/813 and ADlink ACL-8112/8113/8113/8216"
|
||||
select COMEDI_ISADMA if ISA_DMA_API
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Advantech PCL-812/PG, PCL-813/B, ADLink
|
||||
ACL-8112DG/HG/PG, ACL-8113, ACL-8216, ICP DAS A-821PGH/PGL/PGL-NDA,
|
||||
|
@ -180,6 +182,7 @@ config COMEDI_PCL812
|
|||
config COMEDI_PCL816
|
||||
tristate "Advantech PCL-814 and PCL-816 ISA card support"
|
||||
select COMEDI_ISADMA if ISA_DMA_API
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Advantech PCL-814 and PCL-816 ISA cards
|
||||
|
||||
|
@ -189,6 +192,7 @@ config COMEDI_PCL816
|
|||
config COMEDI_PCL818
|
||||
tristate "Advantech PCL-718 and PCL-818 ISA card support"
|
||||
select COMEDI_ISADMA if ISA_DMA_API
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Advantech PCL-818 ISA cards
|
||||
PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818 and PCL-718
|
||||
|
@ -259,6 +263,7 @@ config COMEDI_DAC02
|
|||
|
||||
config COMEDI_DAS16M1
|
||||
tristate "MeasurementComputing CIO-DAS16/M1DAS-16 ISA card support"
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
---help---
|
||||
Enable support for Measurement Computing CIO-DAS16/M1 ISA cards.
|
||||
|
@ -282,6 +287,7 @@ config COMEDI_DAS08_ISA
|
|||
config COMEDI_DAS16
|
||||
tristate "DAS-16 compatible ISA and PC/104 card support"
|
||||
select COMEDI_ISADMA if ISA_DMA_API
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
---help---
|
||||
Enable support for Keithley Metrabyte/ComputerBoards DAS16
|
||||
|
@ -298,6 +304,7 @@ config COMEDI_DAS16
|
|||
|
||||
config COMEDI_DAS800
|
||||
tristate "DAS800 and compatible ISA card support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Keithley Metrabyte DAS800 and compatible ISA cards
|
||||
Keithley Metrabyte DAS-800, DAS-801, DAS-802
|
||||
|
@ -310,6 +317,7 @@ config COMEDI_DAS800
|
|||
config COMEDI_DAS1800
|
||||
tristate "DAS1800 and compatible ISA card support"
|
||||
select COMEDI_ISADMA if ISA_DMA_API
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for DAS1800 and compatible ISA cards
|
||||
Keithley Metrabyte DAS-1701ST, DAS-1701ST-DA, DAS-1701/AO,
|
||||
|
@ -323,6 +331,7 @@ config COMEDI_DAS1800
|
|||
|
||||
config COMEDI_DAS6402
|
||||
tristate "DAS6402 and compatible ISA card support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for DAS6402 and compatible ISA cards
|
||||
Computerboards, Keithley Metrabyte DAS6402 and compatibles
|
||||
|
@ -463,6 +472,7 @@ config COMEDI_ADQ12B
|
|||
config COMEDI_NI_AT_A2150
|
||||
tristate "NI AT-A2150 ISA card support"
|
||||
select COMEDI_ISADMA if ISA_DMA_API
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for National Instruments AT-A2150 cards
|
||||
|
||||
|
@ -471,6 +481,7 @@ config COMEDI_NI_AT_A2150
|
|||
|
||||
config COMEDI_NI_AT_AO
|
||||
tristate "NI AT-AO-6/10 EISA card support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for National Instruments AT-AO-6/10 cards
|
||||
|
||||
|
@ -715,6 +726,7 @@ config COMEDI_ADL_PCI8164
|
|||
|
||||
config COMEDI_ADL_PCI9111
|
||||
tristate "ADLink PCI-9111HR support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for ADlink PCI9111 cards
|
||||
|
||||
|
@ -724,6 +736,7 @@ config COMEDI_ADL_PCI9111
|
|||
config COMEDI_ADL_PCI9118
|
||||
tristate "ADLink PCI-9118DG, PCI-9118HG, PCI-9118HR support"
|
||||
depends on HAS_DMA
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for ADlink PCI-9118DG, PCI-9118HG, PCI-9118HR cards
|
||||
|
||||
|
@ -732,6 +745,7 @@ config COMEDI_ADL_PCI9118
|
|||
|
||||
config COMEDI_ADV_PCI1710
|
||||
tristate "Advantech PCI-171x, PCI-1720 and PCI-1731 support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711,
|
||||
PCI-1713, PCI-1720 and PCI-1731
|
||||
|
@ -759,6 +773,7 @@ config COMEDI_ADV_PCI1724
|
|||
|
||||
config COMEDI_ADV_PCI_DIO
|
||||
tristate "Advantech PCI DIO card support"
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
---help---
|
||||
Enable support for Advantech PCI DIO cards
|
||||
|
@ -799,6 +814,7 @@ config COMEDI_AMPLC_PC263_PCI
|
|||
|
||||
config COMEDI_AMPLC_PCI224
|
||||
tristate "Amplicon PCI224 and PCI234 support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Amplicon PCI224 and PCI234 AO boards
|
||||
|
||||
|
@ -807,6 +823,7 @@ config COMEDI_AMPLC_PCI224
|
|||
|
||||
config COMEDI_AMPLC_PCI230
|
||||
tristate "Amplicon PCI230 and PCI260 support"
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
---help---
|
||||
Enable support for Amplicon PCI230 and PCI260 Multifunction I/O
|
||||
|
@ -912,6 +929,7 @@ config COMEDI_CB_PCIDAS64
|
|||
|
||||
config COMEDI_CB_PCIDAS
|
||||
tristate "MeasurementComputing PCI-DAS support"
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
---help---
|
||||
Enable support for ComputerBoards/MeasurementComputing PCI-DAS with
|
||||
|
@ -935,6 +953,7 @@ config COMEDI_CB_PCIDDA
|
|||
|
||||
config COMEDI_CB_PCIMDAS
|
||||
tristate "MeasurementComputing PCIM-DAS1602/16, PCIe-DAS1602/16 support"
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
---help---
|
||||
Enable support for ComputerBoards/MeasurementComputing PCI Migration
|
||||
|
@ -954,6 +973,7 @@ config COMEDI_CB_PCIMDDA
|
|||
|
||||
config COMEDI_ME4000
|
||||
tristate "Meilhaus ME-4000 support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for Meilhaus PCI data acquisition cards
|
||||
ME-4650, ME-4670i, ME-4680, ME-4680i and ME-4680is
|
||||
|
@ -1091,6 +1111,7 @@ if COMEDI_PCMCIA_DRIVERS
|
|||
|
||||
config COMEDI_CB_DAS16_CS
|
||||
tristate "CB DAS16 series PCMCIA support"
|
||||
select COMEDI_8254
|
||||
---help---
|
||||
Enable support for the ComputerBoards/MeasurementComputing PCMCIA
|
||||
cards DAS16/16, PCM-DAS16D/12 and PCM-DAS16s/16
|
||||
|
@ -1222,6 +1243,9 @@ config COMEDI_VMK80XX
|
|||
|
||||
endif # COMEDI_USB_DRIVERS
|
||||
|
||||
config COMEDI_8254
|
||||
tristate
|
||||
|
||||
config COMEDI_8255
|
||||
tristate "Generic 8255 support"
|
||||
---help---
|
||||
|
@ -1252,6 +1276,7 @@ config COMEDI_KCOMEDILIB
|
|||
called kcomedilib.
|
||||
|
||||
config COMEDI_AMPLC_DIO200
|
||||
select COMEDI_8254
|
||||
tristate
|
||||
|
||||
config COMEDI_AMPLC_PC236
|
||||
|
@ -1260,6 +1285,7 @@ config COMEDI_AMPLC_PC236
|
|||
|
||||
config COMEDI_DAS08
|
||||
tristate
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
|
||||
config COMEDI_ISADMA
|
||||
|
@ -1267,6 +1293,7 @@ config COMEDI_ISADMA
|
|||
|
||||
config COMEDI_NI_LABPC
|
||||
tristate
|
||||
select COMEDI_8254
|
||||
select COMEDI_8255
|
||||
|
||||
config COMEDI_NI_LABPC_ISADMA
|
||||
|
|
|
@ -144,7 +144,7 @@ static void comedi_device_cleanup(struct comedi_device *dev)
|
|||
{
|
||||
struct module *driver_module = NULL;
|
||||
|
||||
if (dev == NULL)
|
||||
if (!dev)
|
||||
return;
|
||||
mutex_lock(&dev->mutex);
|
||||
if (dev->attached)
|
||||
|
@ -260,7 +260,7 @@ comedi_read_subdevice(const struct comedi_device *dev, unsigned int minor)
|
|||
|
||||
if (minor >= COMEDI_NUM_BOARD_MINORS) {
|
||||
s = comedi_subdevice_from_minor(dev, minor);
|
||||
if (s == NULL || (s->subdev_flags & SDF_CMD_READ))
|
||||
if (!s || (s->subdev_flags & SDF_CMD_READ))
|
||||
return s;
|
||||
}
|
||||
return dev->read_subdev;
|
||||
|
@ -273,7 +273,7 @@ comedi_write_subdevice(const struct comedi_device *dev, unsigned int minor)
|
|||
|
||||
if (minor >= COMEDI_NUM_BOARD_MINORS) {
|
||||
s = comedi_subdevice_from_minor(dev, minor);
|
||||
if (s == NULL || (s->subdev_flags & SDF_CMD_WRITE))
|
||||
if (!s || (s->subdev_flags & SDF_CMD_WRITE))
|
||||
return s;
|
||||
}
|
||||
return dev->write_subdev;
|
||||
|
@ -290,9 +290,9 @@ static void comedi_file_reset(struct file *file)
|
|||
write_s = dev->write_subdev;
|
||||
if (minor >= COMEDI_NUM_BOARD_MINORS) {
|
||||
s = comedi_subdevice_from_minor(dev, minor);
|
||||
if (s == NULL || s->subdev_flags & SDF_CMD_READ)
|
||||
if (!s || s->subdev_flags & SDF_CMD_READ)
|
||||
read_s = s;
|
||||
if (s == NULL || s->subdev_flags & SDF_CMD_WRITE)
|
||||
if (!s || s->subdev_flags & SDF_CMD_WRITE)
|
||||
write_s = s;
|
||||
}
|
||||
cfp->last_attached = dev->attached;
|
||||
|
@ -601,28 +601,55 @@ static struct attribute *comedi_dev_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(comedi_dev);
|
||||
|
||||
static void comedi_set_subdevice_runflags(struct comedi_subdevice *s,
|
||||
unsigned mask, unsigned bits)
|
||||
static void __comedi_clear_subdevice_runflags(struct comedi_subdevice *s,
|
||||
unsigned bits)
|
||||
{
|
||||
s->runflags &= ~bits;
|
||||
}
|
||||
|
||||
static void __comedi_set_subdevice_runflags(struct comedi_subdevice *s,
|
||||
unsigned bits)
|
||||
{
|
||||
s->runflags |= bits;
|
||||
}
|
||||
|
||||
static void comedi_update_subdevice_runflags(struct comedi_subdevice *s,
|
||||
unsigned mask, unsigned bits)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
s->runflags &= ~mask;
|
||||
s->runflags |= (bits & mask);
|
||||
__comedi_clear_subdevice_runflags(s, mask);
|
||||
__comedi_set_subdevice_runflags(s, bits & mask);
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
}
|
||||
|
||||
static unsigned __comedi_get_subdevice_runflags(struct comedi_subdevice *s)
|
||||
{
|
||||
return s->runflags;
|
||||
}
|
||||
|
||||
static unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned runflags;
|
||||
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
runflags = s->runflags;
|
||||
runflags = __comedi_get_subdevice_runflags(s);
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
return runflags;
|
||||
}
|
||||
|
||||
static bool comedi_is_runflags_running(unsigned runflags)
|
||||
{
|
||||
return runflags & COMEDI_SRF_RUNNING;
|
||||
}
|
||||
|
||||
static bool comedi_is_runflags_in_error(unsigned runflags)
|
||||
{
|
||||
return runflags & COMEDI_SRF_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_is_subdevice_running - check if async command running on subdevice
|
||||
* @s: comedi_subdevice struct
|
||||
|
@ -634,22 +661,22 @@ bool comedi_is_subdevice_running(struct comedi_subdevice *s)
|
|||
{
|
||||
unsigned runflags = comedi_get_subdevice_runflags(s);
|
||||
|
||||
return (runflags & COMEDI_SRF_RUNNING) ? true : false;
|
||||
return comedi_is_runflags_running(runflags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_is_subdevice_running);
|
||||
|
||||
static bool comedi_is_subdevice_in_error(struct comedi_subdevice *s)
|
||||
static bool __comedi_is_subdevice_running(struct comedi_subdevice *s)
|
||||
{
|
||||
unsigned runflags = comedi_get_subdevice_runflags(s);
|
||||
unsigned runflags = __comedi_get_subdevice_runflags(s);
|
||||
|
||||
return (runflags & COMEDI_SRF_ERROR) ? true : false;
|
||||
return comedi_is_runflags_running(runflags);
|
||||
}
|
||||
|
||||
static bool comedi_is_subdevice_idle(struct comedi_subdevice *s)
|
||||
{
|
||||
unsigned runflags = comedi_get_subdevice_runflags(s);
|
||||
|
||||
return (runflags & COMEDI_SRF_BUSY_MASK) ? false : true;
|
||||
return !(runflags & COMEDI_SRF_BUSY_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -677,14 +704,14 @@ static void do_become_nonbusy(struct comedi_device *dev,
|
|||
{
|
||||
struct comedi_async *async = s->async;
|
||||
|
||||
comedi_set_subdevice_runflags(s, COMEDI_SRF_RUNNING, 0);
|
||||
comedi_update_subdevice_runflags(s, COMEDI_SRF_RUNNING, 0);
|
||||
if (async) {
|
||||
comedi_buf_reset(s);
|
||||
async->inttrig = NULL;
|
||||
kfree(async->cmd.chanlist);
|
||||
async->cmd.chanlist = NULL;
|
||||
s->busy = NULL;
|
||||
wake_up_interruptible_all(&s->async->wait_head);
|
||||
wake_up_interruptible_all(&async->wait_head);
|
||||
} else {
|
||||
dev_err(dev->class_dev,
|
||||
"BUG: (?) do_become_nonbusy called with async=NULL\n");
|
||||
|
@ -759,7 +786,7 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
|
|||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (arg == NULL) {
|
||||
if (!arg) {
|
||||
if (is_device_busy(dev))
|
||||
return -EBUSY;
|
||||
if (dev->attached) {
|
||||
|
@ -1678,8 +1705,8 @@ static int do_cmd_ioctl(struct comedi_device *dev,
|
|||
if (async->cmd.flags & CMDF_WAKE_EOS)
|
||||
async->cb_mask |= COMEDI_CB_EOS;
|
||||
|
||||
comedi_set_subdevice_runflags(s, COMEDI_SRF_BUSY_MASK,
|
||||
COMEDI_SRF_RUNNING);
|
||||
comedi_update_subdevice_runflags(s, COMEDI_SRF_BUSY_MASK,
|
||||
COMEDI_SRF_RUNNING);
|
||||
|
||||
/*
|
||||
* Set s->busy _after_ setting COMEDI_SRF_RUNNING flag to avoid
|
||||
|
@ -1840,7 +1867,7 @@ static int do_cancel_ioctl(struct comedi_device *dev, unsigned long arg,
|
|||
if (arg >= dev->n_subdevices)
|
||||
return -EINVAL;
|
||||
s = &dev->subdevices[arg];
|
||||
if (s->async == NULL)
|
||||
if (!s->async)
|
||||
return -EINVAL;
|
||||
|
||||
if (!s->busy)
|
||||
|
@ -2282,13 +2309,16 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
|
|||
add_wait_queue(&async->wait_head, &wait);
|
||||
on_wait_queue = true;
|
||||
while (nbytes > 0 && !retval) {
|
||||
unsigned runflags;
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
if (!comedi_is_subdevice_running(s)) {
|
||||
runflags = comedi_get_subdevice_runflags(s);
|
||||
if (!comedi_is_runflags_running(runflags)) {
|
||||
if (count == 0) {
|
||||
struct comedi_subdevice *new_s;
|
||||
|
||||
if (comedi_is_subdevice_in_error(s))
|
||||
if (comedi_is_runflags_in_error(runflags))
|
||||
retval = -EPIPE;
|
||||
else
|
||||
retval = 0;
|
||||
|
@ -2435,8 +2465,10 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
|
|||
n = m;
|
||||
|
||||
if (n == 0) {
|
||||
if (!comedi_is_subdevice_running(s)) {
|
||||
if (comedi_is_subdevice_in_error(s))
|
||||
unsigned runflags = comedi_get_subdevice_runflags(s);
|
||||
|
||||
if (!comedi_is_runflags_running(runflags)) {
|
||||
if (comedi_is_runflags_in_error(runflags))
|
||||
retval = -EPIPE;
|
||||
else
|
||||
retval = 0;
|
||||
|
@ -2638,39 +2670,38 @@ static const struct file_operations comedi_fops = {
|
|||
void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
{
|
||||
struct comedi_async *async = s->async;
|
||||
unsigned runflags = 0;
|
||||
unsigned runflags_mask = 0;
|
||||
unsigned int events;
|
||||
int si_code = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!comedi_is_subdevice_running(s))
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
|
||||
events = async->events;
|
||||
async->events = 0;
|
||||
if (!__comedi_is_subdevice_running(s)) {
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->async->events & COMEDI_CB_CANCEL_MASK)
|
||||
runflags_mask |= COMEDI_SRF_RUNNING;
|
||||
if (events & COMEDI_CB_CANCEL_MASK)
|
||||
__comedi_clear_subdevice_runflags(s, COMEDI_SRF_RUNNING);
|
||||
|
||||
/*
|
||||
* Remember if an error event has occurred, so an error
|
||||
* can be returned the next time the user does a read().
|
||||
* Remember if an error event has occurred, so an error can be
|
||||
* returned the next time the user does a read() or write().
|
||||
*/
|
||||
if (s->async->events & COMEDI_CB_ERROR_MASK) {
|
||||
runflags_mask |= COMEDI_SRF_ERROR;
|
||||
runflags |= COMEDI_SRF_ERROR;
|
||||
}
|
||||
if (runflags_mask) {
|
||||
/*
|
||||
* Sets COMEDI_SRF_ERROR and COMEDI_SRF_RUNNING together
|
||||
* atomically.
|
||||
*/
|
||||
comedi_set_subdevice_runflags(s, runflags_mask, runflags);
|
||||
if (events & COMEDI_CB_ERROR_MASK)
|
||||
__comedi_set_subdevice_runflags(s, COMEDI_SRF_ERROR);
|
||||
|
||||
if (async->cb_mask & events) {
|
||||
wake_up_interruptible(&async->wait_head);
|
||||
si_code = async->cmd.flags & CMDF_WRITE ? POLL_OUT : POLL_IN;
|
||||
}
|
||||
|
||||
if (async->cb_mask & s->async->events) {
|
||||
wake_up_interruptible(&async->wait_head);
|
||||
if (s->subdev_flags & SDF_CMD_READ)
|
||||
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
|
||||
if (s->subdev_flags & SDF_CMD_WRITE)
|
||||
kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
|
||||
}
|
||||
s->async->events = 0;
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
|
||||
if (si_code)
|
||||
kill_fasync(&dev->async_queue, SIGIO, si_code);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_event);
|
||||
|
||||
|
@ -2682,7 +2713,7 @@ struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device)
|
|||
unsigned i;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (dev == NULL)
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
comedi_device_init(dev);
|
||||
comedi_set_hw_dev(dev, hardware_device);
|
||||
|
@ -2690,7 +2721,7 @@ struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device)
|
|||
mutex_lock(&comedi_board_minor_table_lock);
|
||||
for (i = hardware_device ? comedi_num_legacy_minors : 0;
|
||||
i < COMEDI_NUM_BOARD_MINORS; ++i) {
|
||||
if (comedi_board_minor_table[i] == NULL) {
|
||||
if (!comedi_board_minor_table[i]) {
|
||||
comedi_board_minor_table[i] = dev;
|
||||
break;
|
||||
}
|
||||
|
@ -2700,7 +2731,8 @@ struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device)
|
|||
mutex_unlock(&dev->mutex);
|
||||
comedi_device_cleanup(dev);
|
||||
comedi_dev_put(dev);
|
||||
pr_err("ran out of minor numbers for board device files\n");
|
||||
dev_err(hardware_device,
|
||||
"ran out of minor numbers for board device files\n");
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
dev->minor = i;
|
||||
|
@ -2746,14 +2778,15 @@ int comedi_alloc_subdevice_minor(struct comedi_subdevice *s)
|
|||
|
||||
mutex_lock(&comedi_subdevice_minor_table_lock);
|
||||
for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i) {
|
||||
if (comedi_subdevice_minor_table[i] == NULL) {
|
||||
if (!comedi_subdevice_minor_table[i]) {
|
||||
comedi_subdevice_minor_table[i] = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&comedi_subdevice_minor_table_lock);
|
||||
if (i == COMEDI_NUM_SUBDEVICE_MINORS) {
|
||||
pr_err("ran out of minor numbers for subdevice files\n");
|
||||
dev_err(dev->class_dev,
|
||||
"ran out of minor numbers for subdevice files\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
i += COMEDI_NUM_BOARD_MINORS;
|
||||
|
@ -2771,7 +2804,7 @@ void comedi_free_subdevice_minor(struct comedi_subdevice *s)
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
if (s == NULL)
|
||||
if (!s)
|
||||
return;
|
||||
if (s->minor < 0)
|
||||
return;
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
#ifndef _COMEDI_INTERNAL_H
|
||||
#define _COMEDI_INTERNAL_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* various internal comedi stuff
|
||||
*/
|
||||
|
||||
struct comedi_buf_map;
|
||||
struct comedi_devconfig;
|
||||
struct comedi_device;
|
||||
struct comedi_insn;
|
||||
struct comedi_rangeinfo;
|
||||
struct comedi_subdevice;
|
||||
struct device;
|
||||
|
||||
int do_rangeinfo_ioctl(struct comedi_device *dev,
|
||||
struct comedi_rangeinfo __user *arg);
|
||||
struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device);
|
||||
|
|
|
@ -17,10 +17,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "comedidev.h"
|
||||
#include "comedi_pci.h"
|
||||
|
||||
/**
|
||||
* comedi_to_pci_dev() - comedi_device pointer to pci_dev pointer.
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* comedi_pci.h
|
||||
* header file for Comedi PCI drivers
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _COMEDI_PCI_H
|
||||
#define _COMEDI_PCI_H
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "comedidev.h"
|
||||
|
||||
/*
|
||||
* PCI Vendor IDs not in <linux/pci_ids.h>
|
||||
*/
|
||||
#define PCI_VENDOR_ID_KOLTER 0x1001
|
||||
#define PCI_VENDOR_ID_ICP 0x104c
|
||||
#define PCI_VENDOR_ID_DT 0x1116
|
||||
#define PCI_VENDOR_ID_IOTECH 0x1616
|
||||
#define PCI_VENDOR_ID_CONTEC 0x1221
|
||||
#define PCI_VENDOR_ID_RTD 0x1435
|
||||
#define PCI_VENDOR_ID_HUMUSOFT 0x186c
|
||||
|
||||
struct pci_dev *comedi_to_pci_dev(struct comedi_device *);
|
||||
|
||||
int comedi_pci_enable(struct comedi_device *);
|
||||
void comedi_pci_disable(struct comedi_device *);
|
||||
void comedi_pci_detach(struct comedi_device *);
|
||||
|
||||
int comedi_pci_auto_config(struct pci_dev *, struct comedi_driver *,
|
||||
unsigned long context);
|
||||
void comedi_pci_auto_unconfig(struct pci_dev *);
|
||||
|
||||
int comedi_pci_driver_register(struct comedi_driver *, struct pci_driver *);
|
||||
void comedi_pci_driver_unregister(struct comedi_driver *, struct pci_driver *);
|
||||
|
||||
/**
|
||||
* module_comedi_pci_driver() - Helper macro for registering a comedi PCI driver
|
||||
* @__comedi_driver: comedi_driver struct
|
||||
* @__pci_driver: pci_driver struct
|
||||
*
|
||||
* Helper macro for comedi PCI drivers which do not do anything special
|
||||
* in module init/exit. This eliminates a lot of boilerplate. Each
|
||||
* module may only use this macro once, and calling it replaces
|
||||
* module_init() and module_exit()
|
||||
*/
|
||||
#define module_comedi_pci_driver(__comedi_driver, __pci_driver) \
|
||||
module_driver(__comedi_driver, comedi_pci_driver_register, \
|
||||
comedi_pci_driver_unregister, &(__pci_driver))
|
||||
|
||||
#endif /* _COMEDI_PCI_H */
|
|
@ -256,6 +256,7 @@ struct comedi_driver {
|
|||
struct comedi_device {
|
||||
int use_count;
|
||||
struct comedi_driver *driver;
|
||||
struct comedi_8254 *pacer;
|
||||
void *private;
|
||||
|
||||
struct device *class_dev;
|
||||
|
@ -463,6 +464,84 @@ static inline unsigned int comedi_samples_to_bytes(struct comedi_subdevice *s,
|
|||
return nsamples << comedi_sample_shift(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_check_trigger_src() - trivially validate a comedi_cmd trigger source
|
||||
* @src: pointer to the trigger source to validate
|
||||
* @flags: bitmask of valid TRIG_* for the trigger
|
||||
*
|
||||
* This is used in "step 1" of the do_cmdtest functions of comedi drivers
|
||||
* to vaildate the comedi_cmd triggers. The mask of the @src against the
|
||||
* @flags allows the userspace comedilib to pass all the comedi_cmd
|
||||
* triggers as TRIG_ANY and get back a bitmask of the valid trigger sources.
|
||||
*/
|
||||
static inline int comedi_check_trigger_src(unsigned int *src,
|
||||
unsigned int flags)
|
||||
{
|
||||
unsigned int orig_src = *src;
|
||||
|
||||
*src = orig_src & flags;
|
||||
if (*src == TRIG_INVALID || *src != orig_src)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_check_trigger_is_unique() - make sure a trigger source is unique
|
||||
* @src: the trigger source to check
|
||||
*/
|
||||
static inline int comedi_check_trigger_is_unique(unsigned int src)
|
||||
{
|
||||
/* this test is true if more than one _src bit is set */
|
||||
if ((src & (src - 1)) != 0)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_check_trigger_arg_is() - trivially validate a trigger argument
|
||||
* @arg: pointer to the trigger arg to validate
|
||||
* @val: the value the argument should be
|
||||
*/
|
||||
static inline int comedi_check_trigger_arg_is(unsigned int *arg,
|
||||
unsigned int val)
|
||||
{
|
||||
if (*arg != val) {
|
||||
*arg = val;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_check_trigger_arg_min() - trivially validate a trigger argument
|
||||
* @arg: pointer to the trigger arg to validate
|
||||
* @val: the minimum value the argument should be
|
||||
*/
|
||||
static inline int comedi_check_trigger_arg_min(unsigned int *arg,
|
||||
unsigned int val)
|
||||
{
|
||||
if (*arg < val) {
|
||||
*arg = val;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_check_trigger_arg_max() - trivially validate a trigger argument
|
||||
* @arg: pointer to the trigger arg to validate
|
||||
* @val: the maximum value the argument should be
|
||||
*/
|
||||
static inline int comedi_check_trigger_arg_max(unsigned int *arg,
|
||||
unsigned int val)
|
||||
{
|
||||
if (*arg > val) {
|
||||
*arg = val;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must set dev->hw_dev if you wish to dma directly into comedi's buffer.
|
||||
* Also useful for retrieving a previously configured hardware device of
|
||||
|
@ -553,47 +632,4 @@ void comedi_driver_unregister(struct comedi_driver *);
|
|||
module_driver(__comedi_driver, comedi_driver_register, \
|
||||
comedi_driver_unregister)
|
||||
|
||||
/* comedi_pci.c - comedi PCI driver specific functions */
|
||||
|
||||
/*
|
||||
* PCI Vendor IDs not in <linux/pci_ids.h>
|
||||
*/
|
||||
#define PCI_VENDOR_ID_KOLTER 0x1001
|
||||
#define PCI_VENDOR_ID_ICP 0x104c
|
||||
#define PCI_VENDOR_ID_DT 0x1116
|
||||
#define PCI_VENDOR_ID_IOTECH 0x1616
|
||||
#define PCI_VENDOR_ID_CONTEC 0x1221
|
||||
#define PCI_VENDOR_ID_RTD 0x1435
|
||||
#define PCI_VENDOR_ID_HUMUSOFT 0x186c
|
||||
|
||||
struct pci_dev;
|
||||
struct pci_driver;
|
||||
|
||||
struct pci_dev *comedi_to_pci_dev(struct comedi_device *);
|
||||
|
||||
int comedi_pci_enable(struct comedi_device *);
|
||||
void comedi_pci_disable(struct comedi_device *);
|
||||
void comedi_pci_detach(struct comedi_device *);
|
||||
|
||||
int comedi_pci_auto_config(struct pci_dev *, struct comedi_driver *,
|
||||
unsigned long context);
|
||||
void comedi_pci_auto_unconfig(struct pci_dev *);
|
||||
|
||||
int comedi_pci_driver_register(struct comedi_driver *, struct pci_driver *);
|
||||
void comedi_pci_driver_unregister(struct comedi_driver *, struct pci_driver *);
|
||||
|
||||
/**
|
||||
* module_comedi_pci_driver() - Helper macro for registering a comedi PCI driver
|
||||
* @__comedi_driver: comedi_driver struct
|
||||
* @__pci_driver: pci_driver struct
|
||||
*
|
||||
* Helper macro for comedi PCI drivers which do not do anything special
|
||||
* in module init/exit. This eliminates a lot of boilerplate. Each
|
||||
* module may only use this macro once, and calling it replaces
|
||||
* module_init() and module_exit()
|
||||
*/
|
||||
#define module_comedi_pci_driver(__comedi_driver, __pci_driver) \
|
||||
module_driver(__comedi_driver, comedi_pci_driver_register, \
|
||||
comedi_pci_driver_unregister, &(__pci_driver))
|
||||
|
||||
#endif /* _COMEDIDEV_H */
|
||||
|
|
|
@ -46,7 +46,7 @@ int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev)
|
|||
{
|
||||
if (hw_dev == dev->hw_dev)
|
||||
return 0;
|
||||
if (dev->hw_dev != NULL)
|
||||
if (dev->hw_dev)
|
||||
return -EEXIST;
|
||||
dev->hw_dev = get_device(hw_dev);
|
||||
return 0;
|
||||
|
@ -139,7 +139,9 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
|
|||
dev->n_subdevices = 0;
|
||||
}
|
||||
kfree(dev->private);
|
||||
kfree(dev->pacer);
|
||||
dev->private = NULL;
|
||||
dev->pacer = NULL;
|
||||
dev->driver = NULL;
|
||||
dev->board_name = NULL;
|
||||
dev->board_ptr = NULL;
|
||||
|
@ -800,7 +802,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
|||
}
|
||||
module_put(driv->module);
|
||||
}
|
||||
if (driv == NULL) {
|
||||
if (!driv) {
|
||||
/* recognize has failed if we get here */
|
||||
/* report valid board names before returning error */
|
||||
for (driv = comedi_drivers; driv; driv = driv->next) {
|
||||
|
@ -812,7 +814,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
|||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (driv->attach == NULL) {
|
||||
if (!driv->attach) {
|
||||
/* driver does not support manual configuration */
|
||||
dev_warn(dev->class_dev,
|
||||
"driver '%s' does not support attach using comedi_config\n",
|
||||
|
@ -896,7 +898,7 @@ EXPORT_SYMBOL_GPL(comedi_auto_config);
|
|||
|
||||
void comedi_auto_unconfig(struct device *hardware_device)
|
||||
{
|
||||
if (hardware_device == NULL)
|
||||
if (!hardware_device)
|
||||
return;
|
||||
comedi_release_hardware_device(hardware_device);
|
||||
}
|
||||
|
|
|
@ -1,347 +0,0 @@
|
|||
/*
|
||||
* comedi/drivers/8253.h
|
||||
* Header file for 8253
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 2000 David A. Schleef <ds@schleef.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _8253_H
|
||||
#define _8253_H
|
||||
|
||||
#include "../comedi.h"
|
||||
|
||||
/*
|
||||
* Common oscillator base values in nanoseconds
|
||||
*/
|
||||
#define I8254_OSC_BASE_10MHZ 100
|
||||
#define I8254_OSC_BASE_5MHZ 200
|
||||
#define I8254_OSC_BASE_4MHZ 250
|
||||
#define I8254_OSC_BASE_2MHZ 500
|
||||
#define I8254_OSC_BASE_1MHZ 1000
|
||||
|
||||
static inline void i8253_cascade_ns_to_timer(int i8253_osc_base,
|
||||
unsigned int *d1,
|
||||
unsigned int *d2,
|
||||
unsigned int *nanosec,
|
||||
unsigned int flags)
|
||||
{
|
||||
unsigned int divider;
|
||||
unsigned int div1, div2;
|
||||
unsigned int div1_glb, div2_glb, ns_glb;
|
||||
unsigned int div1_lub, div2_lub, ns_lub;
|
||||
unsigned int ns;
|
||||
unsigned int start;
|
||||
unsigned int ns_low, ns_high;
|
||||
static const unsigned int max_count = 0x10000;
|
||||
/*
|
||||
* exit early if everything is already correct (this can save time
|
||||
* since this function may be called repeatedly during command tests
|
||||
* and execution)
|
||||
*/
|
||||
div1 = *d1 ? *d1 : max_count;
|
||||
div2 = *d2 ? *d2 : max_count;
|
||||
divider = div1 * div2;
|
||||
if (div1 * div2 * i8253_osc_base == *nanosec &&
|
||||
div1 > 1 && div1 <= max_count && div2 > 1 && div2 <= max_count &&
|
||||
/* check for overflow */
|
||||
divider > div1 && divider > div2 &&
|
||||
divider * i8253_osc_base > divider &&
|
||||
divider * i8253_osc_base > i8253_osc_base) {
|
||||
return;
|
||||
}
|
||||
|
||||
divider = *nanosec / i8253_osc_base;
|
||||
|
||||
div1_lub = div2_lub = 0;
|
||||
div1_glb = div2_glb = 0;
|
||||
|
||||
ns_glb = 0;
|
||||
ns_lub = 0xffffffff;
|
||||
|
||||
div2 = max_count;
|
||||
start = divider / div2;
|
||||
if (start < 2)
|
||||
start = 2;
|
||||
for (div1 = start; div1 <= divider / div1 + 1 && div1 <= max_count;
|
||||
div1++) {
|
||||
for (div2 = divider / div1;
|
||||
div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
|
||||
div2++) {
|
||||
ns = i8253_osc_base * div1 * div2;
|
||||
if (ns <= *nanosec && ns > ns_glb) {
|
||||
ns_glb = ns;
|
||||
div1_glb = div1;
|
||||
div2_glb = div2;
|
||||
}
|
||||
if (ns >= *nanosec && ns < ns_lub) {
|
||||
ns_lub = ns;
|
||||
div1_lub = div1;
|
||||
div2_lub = div2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (flags & CMDF_ROUND_MASK) {
|
||||
case CMDF_ROUND_NEAREST:
|
||||
default:
|
||||
ns_high = div1_lub * div2_lub * i8253_osc_base;
|
||||
ns_low = div1_glb * div2_glb * i8253_osc_base;
|
||||
if (ns_high - *nanosec < *nanosec - ns_low) {
|
||||
div1 = div1_lub;
|
||||
div2 = div2_lub;
|
||||
} else {
|
||||
div1 = div1_glb;
|
||||
div2 = div2_glb;
|
||||
}
|
||||
break;
|
||||
case CMDF_ROUND_UP:
|
||||
div1 = div1_lub;
|
||||
div2 = div2_lub;
|
||||
break;
|
||||
case CMDF_ROUND_DOWN:
|
||||
div1 = div1_glb;
|
||||
div2 = div2_glb;
|
||||
break;
|
||||
}
|
||||
|
||||
*nanosec = div1 * div2 * i8253_osc_base;
|
||||
/* masking is done since counter maps zero to 0x10000 */
|
||||
*d1 = div1 & 0xffff;
|
||||
*d2 = div2 & 0xffff;
|
||||
}
|
||||
|
||||
#ifndef CMDTEST
|
||||
/*
|
||||
* i8254_load programs 8254 counter chip. It should also work for the 8253.
|
||||
* base_address is the lowest io address
|
||||
* for the chip (the address of counter 0).
|
||||
* counter_number is the counter you want to load (0,1 or 2)
|
||||
* count is the number to load into the counter.
|
||||
*
|
||||
* You probably want to use mode 2.
|
||||
*
|
||||
* Use i8254_mm_load() if you board uses memory-mapped io, it is
|
||||
* the same as i8254_load() except it uses writeb() instead of outb().
|
||||
*
|
||||
* Neither i8254_load() or i8254_read() do their loading/reading
|
||||
* atomically. The 16 bit read/writes are performed with two successive
|
||||
* 8 bit read/writes. So if two parts of your driver do a load/read on
|
||||
* the same counter, it may be necessary to protect these functions
|
||||
* with a spinlock.
|
||||
*
|
||||
* FMH
|
||||
*/
|
||||
|
||||
#define i8254_control_reg 3
|
||||
|
||||
static inline int i8254_load(unsigned long base_address, unsigned int regshift,
|
||||
unsigned int counter_number, unsigned int count,
|
||||
unsigned int mode)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
if (count > 0xffff)
|
||||
return -1;
|
||||
if (mode > 5)
|
||||
return -1;
|
||||
if ((mode == 2 || mode == 3) && count == 1)
|
||||
return -1;
|
||||
|
||||
byte = counter_number << 6;
|
||||
byte |= 0x30; /* load low then high byte */
|
||||
byte |= (mode << 1); /* set counter mode */
|
||||
outb(byte, base_address + (i8254_control_reg << regshift));
|
||||
byte = count & 0xff; /* lsb of counter value */
|
||||
outb(byte, base_address + (counter_number << regshift));
|
||||
byte = (count >> 8) & 0xff; /* msb of counter value */
|
||||
outb(byte, base_address + (counter_number << regshift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int i8254_mm_load(void __iomem *base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number,
|
||||
unsigned int count,
|
||||
unsigned int mode)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
if (count > 0xffff)
|
||||
return -1;
|
||||
if (mode > 5)
|
||||
return -1;
|
||||
if ((mode == 2 || mode == 3) && count == 1)
|
||||
return -1;
|
||||
|
||||
byte = counter_number << 6;
|
||||
byte |= 0x30; /* load low then high byte */
|
||||
byte |= (mode << 1); /* set counter mode */
|
||||
writeb(byte, base_address + (i8254_control_reg << regshift));
|
||||
byte = count & 0xff; /* lsb of counter value */
|
||||
writeb(byte, base_address + (counter_number << regshift));
|
||||
byte = (count >> 8) & 0xff; /* msb of counter value */
|
||||
writeb(byte, base_address + (counter_number << regshift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 16 bit counter value, should work for 8253 also. */
|
||||
static inline int i8254_read(unsigned long base_address, unsigned int regshift,
|
||||
unsigned int counter_number)
|
||||
{
|
||||
unsigned int byte;
|
||||
int ret;
|
||||
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
|
||||
/* latch counter */
|
||||
byte = counter_number << 6;
|
||||
outb(byte, base_address + (i8254_control_reg << regshift));
|
||||
|
||||
/* read lsb */
|
||||
ret = inb(base_address + (counter_number << regshift));
|
||||
/* read msb */
|
||||
ret += inb(base_address + (counter_number << regshift)) << 8;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int i8254_mm_read(void __iomem *base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number)
|
||||
{
|
||||
unsigned int byte;
|
||||
int ret;
|
||||
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
|
||||
/* latch counter */
|
||||
byte = counter_number << 6;
|
||||
writeb(byte, base_address + (i8254_control_reg << regshift));
|
||||
|
||||
/* read lsb */
|
||||
ret = readb(base_address + (counter_number << regshift));
|
||||
/* read msb */
|
||||
ret += readb(base_address + (counter_number << regshift)) << 8;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Loads 16 bit initial counter value, should work for 8253 also. */
|
||||
static inline void i8254_write(unsigned long base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number, unsigned int count)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter_number > 2)
|
||||
return;
|
||||
|
||||
byte = count & 0xff; /* lsb of counter value */
|
||||
outb(byte, base_address + (counter_number << regshift));
|
||||
byte = (count >> 8) & 0xff; /* msb of counter value */
|
||||
outb(byte, base_address + (counter_number << regshift));
|
||||
}
|
||||
|
||||
static inline void i8254_mm_write(void __iomem *base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number,
|
||||
unsigned int count)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter_number > 2)
|
||||
return;
|
||||
|
||||
byte = count & 0xff; /* lsb of counter value */
|
||||
writeb(byte, base_address + (counter_number << regshift));
|
||||
byte = (count >> 8) & 0xff; /* msb of counter value */
|
||||
writeb(byte, base_address + (counter_number << regshift));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set counter mode, should work for 8253 also.
|
||||
* Note: the 'mode' value is different to that for i8254_load() and comes
|
||||
* from the INSN_CONFIG_8254_SET_MODE command:
|
||||
* I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
|
||||
* OR'ed with:
|
||||
* I8254_BCD, I8254_BINARY
|
||||
*/
|
||||
static inline int i8254_set_mode(unsigned long base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number, unsigned int mode)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
if (mode > (I8254_MODE5 | I8254_BCD))
|
||||
return -1;
|
||||
|
||||
byte = counter_number << 6;
|
||||
byte |= 0x30; /* load low then high byte */
|
||||
byte |= mode; /* set counter mode and BCD|binary */
|
||||
outb(byte, base_address + (i8254_control_reg << regshift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int i8254_mm_set_mode(void __iomem *base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number,
|
||||
unsigned int mode)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
if (mode > (I8254_MODE5 | I8254_BCD))
|
||||
return -1;
|
||||
|
||||
byte = counter_number << 6;
|
||||
byte |= 0x30; /* load low then high byte */
|
||||
byte |= mode; /* set counter mode and BCD|binary */
|
||||
writeb(byte, base_address + (i8254_control_reg << regshift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int i8254_status(unsigned long base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number)
|
||||
{
|
||||
outb(0xE0 | (2 << counter_number),
|
||||
base_address + (i8254_control_reg << regshift));
|
||||
return inb(base_address + (counter_number << regshift));
|
||||
}
|
||||
|
||||
static inline int i8254_mm_status(void __iomem *base_address,
|
||||
unsigned int regshift,
|
||||
unsigned int counter_number)
|
||||
{
|
||||
writeb(0xE0 | (2 << counter_number),
|
||||
base_address + (i8254_control_reg << regshift));
|
||||
return readb(base_address + (counter_number << regshift));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -51,7 +51,6 @@
|
|||
#include <linux/module.h>
|
||||
#include "../comedidev.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
#include "8255.h"
|
||||
|
||||
struct subdev_8255_private {
|
||||
|
|
|
@ -62,9 +62,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "8255.h"
|
||||
|
||||
|
@ -178,7 +177,7 @@ static const struct pci_8255_boardinfo pci_8255_boards[] = {
|
|||
},
|
||||
};
|
||||
|
||||
/* ripped from mite.h and mite_setup2() to avoid mite dependancy */
|
||||
/* ripped from mite.h and mite_setup2() to avoid mite dependency */
|
||||
#define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */
|
||||
#define WENAB (1 << 7) /* window enable */
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
ccflags-$(CONFIG_COMEDI_DEBUG) := -DDEBUG
|
||||
|
||||
# Comedi "helper" modules
|
||||
obj-$(CONFIG_COMEDI_8254) += comedi_8254.o
|
||||
obj-$(CONFIG_COMEDI_ISADMA) += comedi_isadma.o
|
||||
|
||||
# Comedi misc drivers
|
||||
|
|
|
@ -71,11 +71,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "amcc_s5933.h"
|
||||
|
||||
/*
|
||||
|
@ -196,11 +194,11 @@ static int apci1032_cos_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
@ -210,11 +208,12 @@ static int apci1032_cos_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -238,7 +237,7 @@ static int apci1032_cos_cmd(struct comedi_device *dev,
|
|||
|
||||
if (!devpriv->ctrl) {
|
||||
dev_warn(dev->class_dev,
|
||||
"Interrupts disabled due to mode configuration!\n");
|
||||
"Interrupts disabled due to mode configuration!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -296,7 +295,7 @@ static int apci1032_di_insn_bits(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
static int apci1032_auto_attach(struct comedi_device *dev,
|
||||
unsigned long context_unused)
|
||||
unsigned long context_unused)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
struct apci1032_private *devpriv;
|
||||
|
|
|
@ -22,11 +22,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "amcc_s5933.h"
|
||||
#include "z8536.h"
|
||||
|
||||
|
@ -249,8 +247,8 @@ static irqreturn_t apci1500_interrupt(int irq, void *d)
|
|||
*
|
||||
* Mask Meaning
|
||||
* ---------- ------------------------------------------
|
||||
* 0x00000001 Event 1 has occured
|
||||
* 0x00000010 Event 2 has occured
|
||||
* 0x00000001 Event 1 has occurred
|
||||
* 0x00000010 Event 2 has occurred
|
||||
* 0x00000100 Counter/timer 1 has run down (not implemented)
|
||||
* 0x00001000 Counter/timer 2 has run down (not implemented)
|
||||
* 0x00010000 Counter 3 has run down (not implemented)
|
||||
|
@ -386,11 +384,11 @@ static int apci1500_di_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
@ -415,12 +413,13 @@ static int apci1500_di_cmdtest(struct comedi_device *dev,
|
|||
* 3 OR mode for Port A (digital inputs 0-7)
|
||||
* OR mode for Port B (digital inputs 8-13 and internal signals)
|
||||
*/
|
||||
err |= cfc_check_trigger_arg_max(&cmd->start_arg, 3);
|
||||
err |= comedi_check_trigger_arg_max(&cmd->start_arg, 3);
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -514,11 +513,11 @@ static int apci1500_di_cfg_trig(struct comedi_device *dev,
|
|||
|
||||
src = pt & 0xff;
|
||||
if (src)
|
||||
ret |= cfc_check_trigger_is_unique(src);
|
||||
ret |= comedi_check_trigger_is_unique(src);
|
||||
|
||||
src = (pt >> 8) & 0xff;
|
||||
if (src)
|
||||
ret |= cfc_check_trigger_is_unique(src);
|
||||
ret |= comedi_check_trigger_is_unique(src);
|
||||
|
||||
if (ret) {
|
||||
dev_dbg(dev->class_dev,
|
||||
|
|
|
@ -23,11 +23,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "addi_watchdog.h"
|
||||
#include "comedi_fc.h"
|
||||
|
||||
/*
|
||||
* PCI bar 1 I/O Register map - Digital input/output
|
||||
|
|
|
@ -22,12 +22,10 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "addi_tcw.h"
|
||||
#include "addi_watchdog.h"
|
||||
|
||||
|
@ -107,12 +105,12 @@
|
|||
#define APCI1564_COUNTER(x) ((x) * 0x20)
|
||||
|
||||
struct apci1564_private {
|
||||
unsigned long eeprom; /* base address of EEPROM register */
|
||||
unsigned long timer; /* base address of 12-bit timer */
|
||||
unsigned long counters; /* base address of 32-bit counters */
|
||||
unsigned int mode1; /* riding-edge/high level channels */
|
||||
unsigned int mode2; /* falling-edge/low level channels */
|
||||
unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
|
||||
unsigned long eeprom; /* base address of EEPROM register */
|
||||
unsigned long timer; /* base address of 12-bit timer */
|
||||
unsigned long counters; /* base address of 32-bit counters */
|
||||
unsigned int mode1; /* riding-edge/high level channels */
|
||||
unsigned int mode2; /* falling-edge/low level channels */
|
||||
unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
|
||||
struct task_struct *tsk_current;
|
||||
};
|
||||
|
||||
|
@ -365,11 +363,11 @@ static int apci1564_cos_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
@ -379,11 +377,12 @@ static int apci1564_cos_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -407,7 +406,7 @@ static int apci1564_cos_cmd(struct comedi_device *dev,
|
|||
|
||||
if (!devpriv->ctrl) {
|
||||
dev_warn(dev->class_dev,
|
||||
"Interrupts disabled due to mode configuration!\n");
|
||||
"Interrupts disabled due to mode configuration!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -430,7 +429,7 @@ static int apci1564_cos_cancel(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
static int apci1564_auto_attach(struct comedi_device *dev,
|
||||
unsigned long context_unused)
|
||||
unsigned long context_unused)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
struct apci1564_private *devpriv;
|
||||
|
|
|
@ -23,9 +23,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
/*
|
||||
* Register I/O map
|
||||
|
|
|
@ -23,13 +23,11 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "addi_watchdog.h"
|
||||
#include "comedi_fc.h"
|
||||
|
||||
/*
|
||||
* PCI bar 1 I/O Register map
|
||||
|
@ -93,17 +91,17 @@ static int apci2032_int_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -112,14 +110,15 @@ static int apci2032_int_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
|
|
@ -23,9 +23,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "addi_watchdog.h"
|
||||
|
||||
/*
|
||||
|
|
|
@ -22,11 +22,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "amcc_s5933.h"
|
||||
|
||||
/*
|
||||
|
@ -612,21 +610,21 @@ static int apci3120_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src,
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_TIMER | TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -635,21 +633,24 @@ static int apci3120_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) /* Test Delay timing */
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, 100000);
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) { /* Test Delay timing */
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
100000);
|
||||
}
|
||||
|
||||
/* minimum conversion time per sample is 10us */
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
|
||||
|
||||
err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -659,7 +660,7 @@ static int apci3120_ai_cmdtest(struct comedi_device *dev,
|
|||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
/* scan begin must be larger than the scan time */
|
||||
arg = cmd->convert_arg * cmd->scan_end_arg;
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
|
|
@ -23,12 +23,10 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "../comedi_pci.h"
|
||||
#include "amcc_s5933.h"
|
||||
|
||||
/*
|
||||
|
@ -203,7 +201,7 @@ static unsigned short apci3501_eeprom_readw(unsigned long iobase,
|
|||
outb(NVCMD_LOAD_HIGH, iobase + AMCC_OP_REG_MCSR_NVCMD);
|
||||
apci3501_eeprom_wait(iobase);
|
||||
outb(((addr + i) >> 8) & 0xff,
|
||||
iobase + AMCC_OP_REG_MCSR_NVDATA);
|
||||
iobase + AMCC_OP_REG_MCSR_NVDATA);
|
||||
apci3501_eeprom_wait(iobase);
|
||||
|
||||
/* Read the eeprom data byte */
|
||||
|
@ -270,7 +268,7 @@ static irqreturn_t apci3501_interrupt(int irq, void *d)
|
|||
|
||||
/* Disable Interrupt */
|
||||
ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
|
||||
ul_Command1 = (ul_Command1 & 0xFFFFF9FDul);
|
||||
ul_Command1 = ul_Command1 & 0xFFFFF9FDul;
|
||||
outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
|
||||
|
||||
ui_Timer_AOWatchdog = inl(dev->iobase + APCI3501_TIMER_IRQ_REG) & 0x1;
|
||||
|
@ -282,7 +280,7 @@ static irqreturn_t apci3501_interrupt(int irq, void *d)
|
|||
/* Enable Interrupt Send a signal to from kernel to user space */
|
||||
send_sig(SIGIO, devpriv->tsk_Current, 0);
|
||||
ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
|
||||
ul_Command1 = ((ul_Command1 & 0xFFFFF9FDul) | 1 << 1);
|
||||
ul_Command1 = (ul_Command1 & 0xFFFFF9FDul) | 1 << 1;
|
||||
outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
|
||||
inl(dev->iobase + APCI3501_TIMER_STATUS_REG);
|
||||
|
||||
|
|
|
@ -23,12 +23,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#define CONV_UNIT_NS (1 << 0)
|
||||
#define CONV_UNIT_US (1 << 1)
|
||||
|
@ -529,18 +526,18 @@ static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -549,16 +546,17 @@ static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
|
||||
board->ai_min_acq_ns);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
|
||||
board->ai_min_acq_ns);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -567,7 +565,7 @@ static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
arg = cmd->convert_arg;
|
||||
err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
|
||||
if (err)
|
||||
return 4;
|
||||
|
|
|
@ -54,7 +54,7 @@ static int addi_watchdog_insn_config(struct comedi_device *dev,
|
|||
|
||||
/* Time base is 20ms, let the user know the timeout */
|
||||
dev_info(dev->class_dev, "watchdog enabled, timeout:%dms\n",
|
||||
20 * reload + 20);
|
||||
20 * reload + 20);
|
||||
break;
|
||||
case INSN_CONFIG_DISARM:
|
||||
spriv->wdog_ctrl = 0;
|
||||
|
|
|
@ -19,19 +19,22 @@
|
|||
/*
|
||||
* Driver: adl_pci6208
|
||||
* Description: ADLink PCI-6208/6216 Series Multi-channel Analog Output Cards
|
||||
* Devices: [ADLink] PCI-6208 (adl_pci6208), PCI-6216 (adl_pci6216)
|
||||
* Devices: [ADLink] PCI-6208 (adl_pci6208), PCI-6216
|
||||
* Author: nsyeow <nsyeow@pd.jaring.my>
|
||||
* Updated: Fri, 30 Jan 2004 14:44:27 +0800
|
||||
* Updated: Wed, 11 Feb 2015 11:37:18 +0000
|
||||
* Status: untested
|
||||
*
|
||||
* Configuration Options: not applicable, uses PCI auto config
|
||||
*
|
||||
* All supported devices share the same PCI device ID and are treated as a
|
||||
* PCI-6216 with 16 analog output channels. On a PCI-6208, the upper 8
|
||||
* channels exist in registers, but don't go to DAC chips.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
/*
|
||||
* PCI-6208/6216-GL register map
|
||||
|
@ -45,27 +48,6 @@
|
|||
#define PCI6208_DIO_DI_MASK (0xf0)
|
||||
#define PCI6208_DIO_DI_SHIFT (4)
|
||||
|
||||
enum pci6208_boardid {
|
||||
BOARD_PCI6208,
|
||||
BOARD_PCI6216,
|
||||
};
|
||||
|
||||
struct pci6208_board {
|
||||
const char *name;
|
||||
int ao_chans;
|
||||
};
|
||||
|
||||
static const struct pci6208_board pci6208_boards[] = {
|
||||
[BOARD_PCI6208] = {
|
||||
.name = "adl_pci6208",
|
||||
.ao_chans = 8,
|
||||
},
|
||||
[BOARD_PCI6216] = {
|
||||
.name = "adl_pci6216",
|
||||
.ao_chans = 16,
|
||||
},
|
||||
};
|
||||
|
||||
static int pci6208_ao_eoc(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
|
@ -136,21 +118,13 @@ static int pci6208_do_insn_bits(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
static int pci6208_auto_attach(struct comedi_device *dev,
|
||||
unsigned long context)
|
||||
unsigned long context_unused)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
const struct pci6208_board *boardinfo = NULL;
|
||||
struct comedi_subdevice *s;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (context < ARRAY_SIZE(pci6208_boards))
|
||||
boardinfo = &pci6208_boards[context];
|
||||
if (!boardinfo)
|
||||
return -ENODEV;
|
||||
dev->board_ptr = boardinfo;
|
||||
dev->board_name = boardinfo->name;
|
||||
|
||||
ret = comedi_pci_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -164,7 +138,7 @@ static int pci6208_auto_attach(struct comedi_device *dev,
|
|||
/* analog output subdevice */
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = boardinfo->ao_chans;
|
||||
s->n_chan = 16; /* Only 8 usable on PCI-6208 */
|
||||
s->maxdata = 0xffff;
|
||||
s->range_table = &range_bipolar10;
|
||||
s->insn_write = pci6208_ao_insn_write;
|
||||
|
@ -217,8 +191,9 @@ static int adl_pci6208_pci_probe(struct pci_dev *dev,
|
|||
}
|
||||
|
||||
static const struct pci_device_id adl_pci6208_pci_table[] = {
|
||||
{ PCI_VDEVICE(ADLINK, 0x6208), BOARD_PCI6208 },
|
||||
{ PCI_VDEVICE(ADLINK, 0x6216), BOARD_PCI6216 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) },
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
|
||||
0x9999, 0x6208) },
|
||||
{ 0 }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table);
|
||||
|
|
|
@ -53,9 +53,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
/*
|
||||
* Register I/O map (32-bit access only)
|
||||
|
|
|
@ -28,9 +28,8 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#define PCI8164_AXIS(x) ((x) * 0x08)
|
||||
#define PCI8164_CMD_MSTS_REG 0x00
|
||||
|
@ -69,7 +68,7 @@ static int adl_pci8164_insn_write(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
static int adl_pci8164_auto_attach(struct comedi_device *dev,
|
||||
unsigned long context_unused)
|
||||
unsigned long context_unused)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
struct comedi_subdevice *s;
|
||||
|
|
|
@ -65,15 +65,13 @@ TODO:
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "8253.h"
|
||||
#include "plx9052.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "comedi_8254.h"
|
||||
|
||||
#define PCI9111_FIFO_HALF_SIZE 512
|
||||
|
||||
|
@ -137,9 +135,6 @@ struct pci9111_private_data {
|
|||
unsigned int chunk_counter;
|
||||
unsigned int chunk_num_samples;
|
||||
|
||||
unsigned int div1;
|
||||
unsigned int div2;
|
||||
|
||||
unsigned short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
|
||||
};
|
||||
|
||||
|
@ -167,21 +162,6 @@ static void plx9050_interrupt_control(unsigned long io_base,
|
|||
outb(flags, io_base + PLX9052_INTCSR);
|
||||
}
|
||||
|
||||
static void pci9111_timer_set(struct comedi_device *dev)
|
||||
{
|
||||
struct pci9111_private_data *dev_private = dev->private;
|
||||
unsigned long timer_base = dev->iobase + PCI9111_8254_BASE_REG;
|
||||
|
||||
i8254_set_mode(timer_base, 1, 0, I8254_MODE0 | I8254_BINARY);
|
||||
i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
|
||||
i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
|
||||
|
||||
udelay(1);
|
||||
|
||||
i8254_write(timer_base, 1, 2, dev_private->div2);
|
||||
i8254_write(timer_base, 1, 1, dev_private->div1);
|
||||
}
|
||||
|
||||
enum pci9111_ISC0_sources {
|
||||
irq_on_eoc,
|
||||
irq_on_fifo_half_full
|
||||
|
@ -281,19 +261,18 @@ static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
|
|||
struct comedi_subdevice *s,
|
||||
struct comedi_cmd *cmd)
|
||||
{
|
||||
struct pci9111_private_data *dev_private = dev->private;
|
||||
int err = 0;
|
||||
unsigned int arg;
|
||||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src,
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src,
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src,
|
||||
TRIG_TIMER | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src,
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src,
|
||||
TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
|
@ -301,9 +280,9 @@ static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
|
|||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -317,26 +296,29 @@ static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
|
||||
if (cmd->convert_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
|
||||
PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
|
||||
else /* TRIG_EXT */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
} else { /* TRIG_EXT */
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
}
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
|
||||
else /* TRIG_FOLLOW || TRIG_EXT */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
} else { /* TRIG_FOLLOW || TRIG_EXT */
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -345,11 +327,8 @@ static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
|
|||
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
arg = cmd->convert_arg;
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_2MHZ,
|
||||
&dev_private->div1,
|
||||
&dev_private->div2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -362,7 +341,7 @@ static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
|
|||
if (arg < cmd->scan_begin_arg)
|
||||
arg *= (cmd->scan_begin_arg / arg);
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
@ -376,7 +355,6 @@ static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
|
|||
return 5;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int pci9111_ai_do_cmd(struct comedi_device *dev,
|
||||
|
@ -400,13 +378,14 @@ static int pci9111_ai_do_cmd(struct comedi_device *dev,
|
|||
/* This is the same gain on every channel */
|
||||
|
||||
outb(CR_RANGE(cmd->chanlist[0]) & PCI9111_AI_RANGE_MASK,
|
||||
dev->iobase + PCI9111_AI_RANGE_STAT_REG);
|
||||
dev->iobase + PCI9111_AI_RANGE_STAT_REG);
|
||||
|
||||
/* Set timer pacer */
|
||||
dev_private->scan_delay = 0;
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
trig |= PCI9111_AI_TRIG_CTRL_TPST;
|
||||
pci9111_timer_set(dev);
|
||||
comedi_8254_update_divisors(dev->pacer);
|
||||
comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
|
||||
pci9111_fifo_reset(dev);
|
||||
pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
|
||||
irq_on_timer_tick);
|
||||
|
@ -593,7 +572,7 @@ static int pci9111_ai_insn_read(struct comedi_device *dev,
|
|||
status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
|
||||
if ((status & PCI9111_AI_RANGE_MASK) != range) {
|
||||
outb(range & PCI9111_AI_RANGE_MASK,
|
||||
dev->iobase + PCI9111_AI_RANGE_STAT_REG);
|
||||
dev->iobase + PCI9111_AI_RANGE_STAT_REG);
|
||||
}
|
||||
|
||||
pci9111_fifo_reset(dev);
|
||||
|
@ -667,16 +646,11 @@ static int pci9111_reset(struct comedi_device *dev)
|
|||
/* disable A/D triggers (software trigger mode) and auto scan off */
|
||||
outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
|
||||
|
||||
/* Reset 8254 chip */
|
||||
dev_private->div1 = 0;
|
||||
dev_private->div2 = 0;
|
||||
pci9111_timer_set(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci9111_auto_attach(struct comedi_device *dev,
|
||||
unsigned long context_unused)
|
||||
unsigned long context_unused)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
struct pci9111_private_data *dev_private;
|
||||
|
@ -702,6 +676,11 @@ static int pci9111_auto_attach(struct comedi_device *dev,
|
|||
dev->irq = pcidev->irq;
|
||||
}
|
||||
|
||||
dev->pacer = comedi_8254_init(dev->iobase + PCI9111_8254_BASE_REG,
|
||||
I8254_OSC_BASE_2MHZ, I8254_IO16, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = comedi_alloc_subdevices(dev, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -73,17 +73,15 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "amcc_s5933.h"
|
||||
#include "8253.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "comedi_8254.h"
|
||||
|
||||
#define IORANGE_9118 64 /* I hope */
|
||||
#define PCI9118_CHANLEN 255 /*
|
||||
|
@ -94,8 +92,7 @@
|
|||
/*
|
||||
* PCI BAR2 Register map (dev->iobase)
|
||||
*/
|
||||
#define PCI9118_TIMER_REG(x) (0x00 + ((x) * 4))
|
||||
#define PCI9118_TIMER_CTRL_REG 0x0c
|
||||
#define PCI9118_TIMER_BASE 0x00
|
||||
#define PCI9118_AI_FIFO_REG 0x10
|
||||
#define PCI9118_AO_REG(x) (0x10 + ((x) * 4))
|
||||
#define PCI9118_AI_STATUS_REG 0x18
|
||||
|
@ -239,10 +236,6 @@ struct pci9118_private {
|
|||
* measure can start/stop
|
||||
* on external trigger
|
||||
*/
|
||||
unsigned int ai_divisor1, ai_divisor2; /*
|
||||
* divisors for start of measure
|
||||
* on external start
|
||||
*/
|
||||
unsigned int dma_actbuf; /* which buffer is used now */
|
||||
struct pci9118_dmabuf dmabuf[2];
|
||||
int softsshdelay; /*
|
||||
|
@ -297,24 +290,6 @@ static void pci9118_amcc_int_ena(struct comedi_device *dev, bool enable)
|
|||
outl(intcsr, devpriv->iobase_a + AMCC_OP_REG_INTCSR);
|
||||
}
|
||||
|
||||
static void pci9118_timer_write(struct comedi_device *dev,
|
||||
unsigned int timer, unsigned int val)
|
||||
{
|
||||
outl(val & 0xff, dev->iobase + PCI9118_TIMER_REG(timer));
|
||||
outl((val >> 8) & 0xff, dev->iobase + PCI9118_TIMER_REG(timer));
|
||||
}
|
||||
|
||||
static void pci9118_timer_set_mode(struct comedi_device *dev,
|
||||
unsigned int timer, unsigned int mode)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = timer << 6; /* select timer */
|
||||
val |= 0x30; /* load low then high byte */
|
||||
val |= mode; /* set timer mode and BCD|binary */
|
||||
outl(val, dev->iobase + PCI9118_TIMER_CTRL_REG);
|
||||
}
|
||||
|
||||
static void pci9118_ai_reset_fifo(struct comedi_device *dev)
|
||||
{
|
||||
/* writing any value resets the A/D FIFO */
|
||||
|
@ -440,8 +415,8 @@ static void interrupt_pci9118_ai_mode4_switch(struct comedi_device *dev,
|
|||
devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG |
|
||||
PCI9118_AI_CFG_AM;
|
||||
outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
|
||||
pci9118_timer_set_mode(dev, 0, I8254_MODE0);
|
||||
pci9118_timer_write(dev, 0, dmabuf->hw >> 1);
|
||||
comedi_8254_load(dev->pacer, 0, dmabuf->hw >> 1,
|
||||
I8254_MODE0 | I8254_BINARY);
|
||||
devpriv->ai_cfg |= PCI9118_AI_CFG_START;
|
||||
outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
|
||||
}
|
||||
|
@ -577,15 +552,16 @@ static void pci9118_calc_divisors(struct comedi_device *dev,
|
|||
unsigned int *div1, unsigned int *div2,
|
||||
unsigned int chnsshfront)
|
||||
{
|
||||
struct comedi_8254 *pacer = dev->pacer;
|
||||
struct comedi_cmd *cmd = &s->async->cmd;
|
||||
|
||||
*div1 = *tim2 / I8254_OSC_BASE_4MHZ; /* convert timer (burst) */
|
||||
*div2 = *tim1 / I8254_OSC_BASE_4MHZ; /* scan timer */
|
||||
*div1 = *tim2 / pacer->osc_base; /* convert timer (burst) */
|
||||
*div2 = *tim1 / pacer->osc_base; /* scan timer */
|
||||
*div2 = *div2 / *div1; /* major timer is c1*c2 */
|
||||
if (*div2 < chans)
|
||||
*div2 = chans;
|
||||
|
||||
*tim2 = *div1 * I8254_OSC_BASE_4MHZ; /* real convert timer */
|
||||
*tim2 = *div1 * pacer->osc_base; /* real convert timer */
|
||||
|
||||
if (cmd->convert_src == TRIG_NOW && !chnsshfront) {
|
||||
/* use BSSH signal */
|
||||
|
@ -593,21 +569,13 @@ static void pci9118_calc_divisors(struct comedi_device *dev,
|
|||
*div2 = chans + 2;
|
||||
}
|
||||
|
||||
*tim1 = *div1 * *div2 * I8254_OSC_BASE_4MHZ;
|
||||
*tim1 = *div1 * *div2 * pacer->osc_base;
|
||||
}
|
||||
|
||||
static void pci9118_start_pacer(struct comedi_device *dev, int mode)
|
||||
{
|
||||
struct pci9118_private *devpriv = dev->private;
|
||||
|
||||
pci9118_timer_set_mode(dev, 1, I8254_MODE2);
|
||||
pci9118_timer_set_mode(dev, 2, I8254_MODE2);
|
||||
udelay(1);
|
||||
|
||||
if ((mode == 1) || (mode == 2) || (mode == 4)) {
|
||||
pci9118_timer_write(dev, 2, devpriv->ai_divisor2);
|
||||
pci9118_timer_write(dev, 1, devpriv->ai_divisor1);
|
||||
}
|
||||
if (mode == 1 || mode == 2 || mode == 4)
|
||||
comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
|
||||
}
|
||||
|
||||
static int pci9118_ai_cancel(struct comedi_device *dev,
|
||||
|
@ -618,7 +586,7 @@ static int pci9118_ai_cancel(struct comedi_device *dev,
|
|||
if (devpriv->usedma)
|
||||
pci9118_amcc_dma_ena(dev, false);
|
||||
pci9118_exttrg_enable(dev, false);
|
||||
pci9118_start_pacer(dev, 0); /* stop 8254 counters */
|
||||
comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
|
||||
/* set default config (disable burst and triggers) */
|
||||
devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
|
||||
outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
|
||||
|
@ -663,7 +631,6 @@ static void pci9118_ai_munge(struct comedi_device *dev,
|
|||
array[i] ^= 0x8000;
|
||||
else
|
||||
array[i] = (array[i] >> 4) & 0x0fff;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -966,7 +933,7 @@ static int Compute_and_setup_dma(struct comedi_device *dev,
|
|||
/* outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); */
|
||||
pci9118_amcc_dma_ena(dev, true);
|
||||
outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS,
|
||||
devpriv->iobase_a + AMCC_OP_REG_INTCSR);
|
||||
devpriv->iobase_a + AMCC_OP_REG_INTCSR);
|
||||
/* allow bus mastering */
|
||||
|
||||
return 0;
|
||||
|
@ -975,6 +942,7 @@ static int Compute_and_setup_dma(struct comedi_device *dev,
|
|||
static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
{
|
||||
struct pci9118_private *devpriv = dev->private;
|
||||
struct comedi_8254 *pacer = dev->pacer;
|
||||
struct comedi_cmd *cmd = &s->async->cmd;
|
||||
unsigned int addchans = 0;
|
||||
|
||||
|
@ -1093,12 +1061,10 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
else
|
||||
devpriv->ai_do = 1;
|
||||
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_4MHZ,
|
||||
&devpriv->ai_divisor1,
|
||||
&devpriv->ai_divisor2,
|
||||
&cmd->convert_arg,
|
||||
devpriv->ai_flags &
|
||||
CMDF_ROUND_NEAREST);
|
||||
comedi_8254_cascade_ns_to_timer(pacer, &cmd->convert_arg,
|
||||
devpriv->ai_flags &
|
||||
CMDF_ROUND_NEAREST);
|
||||
comedi_8254_update_divisors(pacer);
|
||||
|
||||
devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;
|
||||
|
||||
|
@ -1112,8 +1078,8 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
|
||||
devpriv->ai_cfg |= PCI9118_AI_CFG_AM;
|
||||
outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
|
||||
pci9118_timer_set_mode(dev, 0, I8254_MODE0);
|
||||
pci9118_timer_write(dev, 0, dmabuf->hw >> 1);
|
||||
comedi_8254_load(pacer, 0, dmabuf->hw >> 1,
|
||||
I8254_MODE0 | I8254_BINARY);
|
||||
devpriv->ai_cfg |= PCI9118_AI_CFG_START;
|
||||
}
|
||||
}
|
||||
|
@ -1133,8 +1099,8 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
&cmd->scan_begin_arg, &cmd->convert_arg,
|
||||
devpriv->ai_flags,
|
||||
devpriv->ai_n_realscanlen,
|
||||
&devpriv->ai_divisor1,
|
||||
&devpriv->ai_divisor2,
|
||||
&pacer->divisor1,
|
||||
&pacer->divisor2,
|
||||
devpriv->ai_add_front);
|
||||
|
||||
devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;
|
||||
|
@ -1162,8 +1128,6 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
if (devpriv->usedma)
|
||||
devpriv->ai_ctrl |= PCI9118_AI_CTRL_DMA;
|
||||
|
||||
pci9118_start_pacer(dev, -1); /* stop pacer */
|
||||
|
||||
/* set default config (disable burst and triggers) */
|
||||
devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
|
||||
outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
|
||||
|
@ -1206,25 +1170,24 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|||
int err = 0;
|
||||
unsigned int flags;
|
||||
unsigned int arg;
|
||||
unsigned int divisor1 = 0, divisor2 = 0;
|
||||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src,
|
||||
err |= comedi_check_trigger_src(&cmd->start_src,
|
||||
TRIG_NOW | TRIG_EXT | TRIG_INT);
|
||||
|
||||
flags = TRIG_FOLLOW;
|
||||
if (devpriv->master)
|
||||
flags |= TRIG_TIMER | TRIG_EXT;
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, flags);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, flags);
|
||||
|
||||
flags = TRIG_TIMER | TRIG_EXT;
|
||||
if (devpriv->master)
|
||||
flags |= TRIG_NOW;
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, flags);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, flags);
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src,
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src,
|
||||
TRIG_COUNT | TRIG_NONE | TRIG_EXT);
|
||||
|
||||
if (err)
|
||||
|
@ -1232,10 +1195,10 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -1264,7 +1227,7 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|||
switch (cmd->start_src) {
|
||||
case TRIG_NOW:
|
||||
case TRIG_EXT:
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
break;
|
||||
case TRIG_INT:
|
||||
/* start_arg is the internal trigger (any value) */
|
||||
|
@ -1272,7 +1235,7 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT))
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
|
||||
if ((cmd->scan_begin_src == TRIG_TIMER) &&
|
||||
(cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) {
|
||||
|
@ -1281,34 +1244,37 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|||
cmd->scan_begin_arg = 0;
|
||||
}
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
devpriv->ai_ns_min);
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
devpriv->ai_ns_min);
|
||||
}
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_EXT)
|
||||
if (cmd->scan_begin_src == TRIG_EXT) {
|
||||
if (cmd->scan_begin_arg) {
|
||||
cmd->scan_begin_arg = 0;
|
||||
err |= -EINVAL;
|
||||
err |= cfc_check_trigger_arg_max(&cmd->scan_end_arg,
|
||||
65535);
|
||||
err |= comedi_check_trigger_arg_max(&cmd->scan_end_arg,
|
||||
65535);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW))
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
|
||||
devpriv->ai_ns_min);
|
||||
if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
|
||||
devpriv->ai_ns_min);
|
||||
}
|
||||
|
||||
if (cmd->convert_src == TRIG_EXT)
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if ((cmd->scan_end_arg % cmd->chanlist_len)) {
|
||||
cmd->scan_end_arg =
|
||||
|
@ -1323,18 +1289,14 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
arg = cmd->scan_begin_arg;
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_4MHZ,
|
||||
&divisor1, &divisor2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
}
|
||||
|
||||
if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
|
||||
arg = cmd->convert_arg;
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_4MHZ,
|
||||
&divisor1, &divisor2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER &&
|
||||
cmd->convert_src == TRIG_NOW) {
|
||||
|
@ -1344,8 +1306,9 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|||
} else {
|
||||
arg = cmd->convert_arg * cmd->chanlist_len;
|
||||
}
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
arg);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->
|
||||
scan_begin_arg,
|
||||
arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1482,10 +1445,6 @@ static void pci9118_reset(struct comedi_device *dev)
|
|||
inl(dev->iobase + PCI9118_INT_CTRL_REG);
|
||||
inl(dev->iobase + PCI9118_AI_STATUS_REG);
|
||||
|
||||
/* reset and stop counters */
|
||||
pci9118_timer_set_mode(dev, 0, I8254_MODE0);
|
||||
pci9118_start_pacer(dev, 0);
|
||||
|
||||
/* reset DMA and scan queue */
|
||||
outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG);
|
||||
outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
|
||||
|
@ -1590,6 +1549,11 @@ static int pci9118_common_attach(struct comedi_device *dev,
|
|||
devpriv->iobase_a = pci_resource_start(pcidev, 0);
|
||||
dev->iobase = pci_resource_start(pcidev, 2);
|
||||
|
||||
dev->pacer = comedi_8254_init(dev->iobase + PCI9118_TIMER_BASE,
|
||||
I8254_OSC_BASE_4MHZ, I8254_IO32, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
pci9118_reset(dev);
|
||||
|
||||
if (pcidev->irq) {
|
||||
|
|
|
@ -69,8 +69,6 @@ If you do not specify any options, they will default to
|
|||
|
||||
13-oct-2007
|
||||
+ first try
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -170,7 +168,6 @@ static int adq12b_di_insn_bits(struct comedi_device *dev,
|
|||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data)
|
||||
{
|
||||
|
||||
/* only bits 0-4 have information about digital inputs */
|
||||
data[1] = (inb(dev->iobase + ADQ12B_STINR) & ADQ12B_STINR_IN_MASK);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Author: Michal Dobes <dobes@tesnet.cz>
|
||||
*
|
||||
* Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
|
||||
* for testing and informations.
|
||||
* for testing and information.
|
||||
*
|
||||
* hardware driver for Advantech cards:
|
||||
* card: PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
|
||||
|
@ -42,13 +42,11 @@ Configuration options:
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
#include "8253.h"
|
||||
#include "comedi_8254.h"
|
||||
#include "amcc_s5933.h"
|
||||
|
||||
#define PCI171x_AD_DATA 0 /* R: A/D data */
|
||||
|
@ -67,11 +65,6 @@ Configuration options:
|
|||
|
||||
#define PCI171X_TIMER_BASE 0x18
|
||||
|
||||
#define PCI171x_CNT0 24 /* R/W: 8254 counter 0 */
|
||||
#define PCI171x_CNT1 26 /* R/W: 8254 counter 1 */
|
||||
#define PCI171x_CNT2 28 /* R/W: 8254 counter 2 */
|
||||
#define PCI171x_CNTCTRL 30 /* W: 8254 counter control */
|
||||
|
||||
/* upper bits from status register (PCI171x_STATUS) (lower is same with control
|
||||
* reg) */
|
||||
#define Status_FE 0x0100 /* 1=FIFO is empty */
|
||||
|
@ -87,16 +80,6 @@ Configuration options:
|
|||
#define Control_EXT 0x0004 /* 1=external trigger source */
|
||||
#define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
|
||||
#define Control_SW 0x0001 /* 1=enable software trigger source */
|
||||
/* bits from counter control register (PCI171x_CNTCTRL) */
|
||||
#define Counter_BCD 0x0001 /* 0 = binary counter, 1 = BCD counter */
|
||||
#define Counter_M0 0x0002 /* M0-M2 select modes 0-5 */
|
||||
#define Counter_M1 0x0004 /* 000 = mode 0, 010 = mode 2 ... */
|
||||
#define Counter_M2 0x0008
|
||||
#define Counter_RW0 0x0010 /* RW0/RW1 select read/write mode */
|
||||
#define Counter_RW1 0x0020
|
||||
#define Counter_SC0 0x0040 /* Select Counter. Only 00 or 11 may */
|
||||
#define Counter_SC1 0x0080 /* be used, 00 for CNT0,
|
||||
* 11 for read-back command */
|
||||
|
||||
#define PCI1720_DA0 0 /* W: D/A register 0 */
|
||||
#define PCI1720_DA1 2 /* W: D/A register 1 */
|
||||
|
@ -265,15 +248,9 @@ struct pci1710_private {
|
|||
unsigned char ai_et;
|
||||
unsigned int ai_et_CntrlReg;
|
||||
unsigned int ai_et_MuxVal;
|
||||
unsigned int next_divisor1;
|
||||
unsigned int next_divisor2;
|
||||
unsigned int divisor1;
|
||||
unsigned int divisor2;
|
||||
unsigned int act_chanlist[32]; /* list of scanned channel */
|
||||
unsigned char saved_seglen; /* len of the non-repeating chanlist */
|
||||
unsigned char da_ranges; /* copy of D/A outpit range register */
|
||||
unsigned int cnt0_write_wait; /* after a write, wait for update of the
|
||||
* internal state */
|
||||
};
|
||||
|
||||
static int pci171x_ai_check_chanlist(struct comedi_device *dev,
|
||||
|
@ -508,105 +485,6 @@ static int pci171x_do_insn_bits(struct comedi_device *dev,
|
|||
return insn->n;
|
||||
}
|
||||
|
||||
static void pci171x_start_pacer(struct comedi_device *dev,
|
||||
bool load_counters)
|
||||
{
|
||||
struct pci1710_private *devpriv = dev->private;
|
||||
unsigned long timer_base = dev->iobase + PCI171X_TIMER_BASE;
|
||||
|
||||
i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
|
||||
i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
|
||||
|
||||
if (load_counters) {
|
||||
i8254_write(timer_base, 1, 2, devpriv->divisor2);
|
||||
i8254_write(timer_base, 1, 1, devpriv->divisor1);
|
||||
}
|
||||
}
|
||||
|
||||
static int pci171x_counter_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
unsigned int msb, lsb, ccntrl;
|
||||
int i;
|
||||
|
||||
ccntrl = 0xD2; /* count only */
|
||||
for (i = 0; i < insn->n; i++) {
|
||||
outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
|
||||
|
||||
lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
|
||||
msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
|
||||
|
||||
data[0] = lsb | (msb << 8);
|
||||
}
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int pci171x_counter_insn_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct pci1710_private *devpriv = dev->private;
|
||||
uint msb, lsb, ccntrl, status;
|
||||
|
||||
lsb = data[0] & 0x00FF;
|
||||
msb = (data[0] & 0xFF00) >> 8;
|
||||
|
||||
/* write lsb, then msb */
|
||||
outw(lsb, dev->iobase + PCI171x_CNT0);
|
||||
outw(msb, dev->iobase + PCI171x_CNT0);
|
||||
|
||||
if (devpriv->cnt0_write_wait) {
|
||||
/* wait for the new count to be loaded */
|
||||
ccntrl = 0xE2;
|
||||
do {
|
||||
outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
|
||||
status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
|
||||
} while (status & 0x40);
|
||||
}
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int pci171x_counter_insn_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
#ifdef unused
|
||||
/* This doesn't work like a normal Comedi counter config */
|
||||
struct pci1710_private *devpriv = dev->private;
|
||||
uint ccntrl = 0;
|
||||
|
||||
devpriv->cnt0_write_wait = data[0] & 0x20;
|
||||
|
||||
/* internal or external clock? */
|
||||
if (!(data[0] & 0x10)) { /* internal */
|
||||
devpriv->CntrlReg &= ~Control_CNT0;
|
||||
} else {
|
||||
devpriv->CntrlReg |= Control_CNT0;
|
||||
}
|
||||
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
|
||||
|
||||
if (data[0] & 0x01)
|
||||
ccntrl |= Counter_M0;
|
||||
if (data[0] & 0x02)
|
||||
ccntrl |= Counter_M1;
|
||||
if (data[0] & 0x04)
|
||||
ccntrl |= Counter_M2;
|
||||
if (data[0] & 0x08)
|
||||
ccntrl |= Counter_BCD;
|
||||
ccntrl |= Counter_RW0; /* set read/write mode */
|
||||
ccntrl |= Counter_RW1;
|
||||
outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pci1720_ao_insn_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
|
@ -646,7 +524,7 @@ static int pci171x_ai_cancel(struct comedi_device *dev,
|
|||
devpriv->CntrlReg |= Control_SW;
|
||||
/* reset any operations */
|
||||
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
|
||||
pci171x_start_pacer(dev, false);
|
||||
comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
|
||||
outb(0, dev->iobase + PCI171x_CLRFIFO);
|
||||
outb(0, dev->iobase + PCI171x_CLRINT);
|
||||
|
||||
|
@ -767,7 +645,7 @@ static irqreturn_t interrupt_service_pci1710(int irq, void *d)
|
|||
outb(0, dev->iobase + PCI171x_CLRINT);
|
||||
outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
|
||||
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
|
||||
pci171x_start_pacer(dev, true);
|
||||
comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -786,8 +664,6 @@ static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
struct pci1710_private *devpriv = dev->private;
|
||||
struct comedi_cmd *cmd = &s->async->cmd;
|
||||
|
||||
pci171x_start_pacer(dev, false);
|
||||
|
||||
pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
|
||||
devpriv->saved_seglen);
|
||||
|
||||
|
@ -798,10 +674,9 @@ static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
if ((cmd->flags & CMDF_WAKE_EOS) == 0)
|
||||
devpriv->CntrlReg |= Control_ONEFH;
|
||||
|
||||
devpriv->divisor1 = devpriv->next_divisor1;
|
||||
devpriv->divisor2 = devpriv->next_divisor2;
|
||||
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
comedi_8254_update_divisors(dev->pacer);
|
||||
|
||||
devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
|
||||
if (cmd->start_src == TRIG_EXT) {
|
||||
devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
|
||||
|
@ -815,7 +690,7 @@ static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
|
||||
|
||||
if (cmd->start_src == TRIG_NOW)
|
||||
pci171x_start_pacer(dev, true);
|
||||
comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
|
||||
} else { /* TRIG_EXT */
|
||||
devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
|
||||
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
|
||||
|
@ -828,26 +703,25 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
|
|||
struct comedi_subdevice *s,
|
||||
struct comedi_cmd *cmd)
|
||||
{
|
||||
struct pci1710_private *devpriv = dev->private;
|
||||
int err = 0;
|
||||
unsigned int arg;
|
||||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src,
|
||||
TRIG_TIMER | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* step 2a: make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* step 2b: and mutually compatible */
|
||||
|
||||
|
@ -856,20 +730,21 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
|
||||
if (cmd->convert_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
|
||||
else /* TRIG_FOLLOW */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -877,12 +752,10 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
|
|||
/* step 4: fix up any arguments */
|
||||
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
arg = cmd->convert_arg;
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
|
||||
&devpriv->next_divisor1,
|
||||
&devpriv->next_divisor2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
unsigned int arg = cmd->convert_arg;
|
||||
|
||||
comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
@ -898,19 +771,54 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pci171x_insn_counter_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct pci1710_private *devpriv = dev->private;
|
||||
|
||||
switch (data[0]) {
|
||||
case INSN_CONFIG_SET_CLOCK_SRC:
|
||||
switch (data[1]) {
|
||||
case 0: /* internal */
|
||||
devpriv->ai_et_CntrlReg &= ~Control_CNT0;
|
||||
break;
|
||||
case 1: /* external */
|
||||
devpriv->ai_et_CntrlReg |= Control_CNT0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
outw(devpriv->ai_et_CntrlReg, dev->iobase + PCI171x_CONTROL);
|
||||
break;
|
||||
case INSN_CONFIG_GET_CLOCK_SRC:
|
||||
if (devpriv->ai_et_CntrlReg & Control_CNT0) {
|
||||
data[1] = 1;
|
||||
data[2] = 0;
|
||||
} else {
|
||||
data[1] = 0;
|
||||
data[2] = I8254_OSC_BASE_10MHZ;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int pci171x_reset(struct comedi_device *dev)
|
||||
{
|
||||
const struct boardtype *board = dev->board_ptr;
|
||||
struct pci1710_private *devpriv = dev->private;
|
||||
|
||||
outw(0x30, dev->iobase + PCI171x_CNTCTRL);
|
||||
/* Software trigger, CNT0=external */
|
||||
devpriv->CntrlReg = Control_SW | Control_CNT0;
|
||||
/* reset any operations */
|
||||
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
|
||||
outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
|
||||
outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
|
||||
pci171x_start_pacer(dev, false);
|
||||
devpriv->da_ranges = 0;
|
||||
if (board->has_ao) {
|
||||
/* set DACs to 0..5V */
|
||||
|
@ -977,6 +885,11 @@ static int pci1710_auto_attach(struct comedi_device *dev,
|
|||
return ret;
|
||||
dev->iobase = pci_resource_start(pcidev, 2);
|
||||
|
||||
dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE,
|
||||
I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
n_subdevices = 0;
|
||||
if (board->n_aichan)
|
||||
n_subdevices++;
|
||||
|
@ -1073,16 +986,17 @@ static int pci1710_auto_attach(struct comedi_device *dev,
|
|||
subdev++;
|
||||
}
|
||||
|
||||
/* Counter subdevice (8254) */
|
||||
if (board->has_counter) {
|
||||
s = &dev->subdevices[subdev];
|
||||
s->type = COMEDI_SUBD_COUNTER;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
s->n_chan = 1;
|
||||
s->maxdata = 0xffff;
|
||||
s->range_table = &range_unknown;
|
||||
s->insn_read = pci171x_counter_insn_read;
|
||||
s->insn_write = pci171x_counter_insn_write;
|
||||
s->insn_config = pci171x_counter_insn_config;
|
||||
comedi_8254_subdevice_init(s, dev->pacer);
|
||||
|
||||
dev->pacer->insn_config = pci171x_insn_counter_config;
|
||||
|
||||
/* counters 1 and 2 are used internally for the pacer */
|
||||
comedi_8254_set_busy(dev->pacer, 1, true);
|
||||
comedi_8254_set_busy(dev->pacer, 2, true);
|
||||
|
||||
subdev++;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
/*
|
||||
* PCI Bar 2 I/O Register map (dev->iobase)
|
||||
|
|
|
@ -47,9 +47,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
/*
|
||||
* PCI bar 2 Register I/O map (dev->iobase)
|
||||
|
|
|
@ -30,13 +30,12 @@ Configuration options:
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "8255.h"
|
||||
#include "8253.h"
|
||||
#include "comedi_8254.h"
|
||||
|
||||
/* hardware types of the cards */
|
||||
enum hw_cards_id {
|
||||
|
@ -60,12 +59,6 @@ enum hw_io_access {
|
|||
#define MAX_DO_SUBDEVS 2 /* max number of DO subdevices per card */
|
||||
#define MAX_DIO_SUBDEVG 2 /* max number of DIO subdevices group per
|
||||
* card */
|
||||
#define MAX_8254_SUBDEVS 1 /* max number of 8254 counter subdevs per
|
||||
* card */
|
||||
/* (could be more than one 8254 per
|
||||
* subdevice) */
|
||||
|
||||
#define SIZE_8254 4 /* 8254 IO space length */
|
||||
|
||||
#define PCIDIO_MAINREG 2 /* main I/O region for all Advantech cards? */
|
||||
|
||||
|
@ -230,7 +223,7 @@ struct diosubd_data {
|
|||
int chans; /* num of chans */
|
||||
int addr; /* PCI address ofset */
|
||||
int regs; /* number of registers to read or 8255
|
||||
subdevices or 8254 chips */
|
||||
subdevices */
|
||||
unsigned int specflags; /* addon subdevice flags */
|
||||
};
|
||||
|
||||
|
@ -243,7 +236,7 @@ struct dio_boardtype {
|
|||
struct diosubd_data sdo[MAX_DO_SUBDEVS]; /* DO chans */
|
||||
struct diosubd_data sdio[MAX_DIO_SUBDEVG]; /* DIO 8255 chans */
|
||||
struct diosubd_data boardid; /* card supports board ID switch */
|
||||
struct diosubd_data s8254[MAX_8254_SUBDEVS]; /* 8254 subdevices */
|
||||
unsigned long timer_regbase;
|
||||
enum hw_io_access io_access;
|
||||
};
|
||||
|
||||
|
@ -286,7 +279,7 @@ static const struct dio_boardtype boardtypes[] = {
|
|||
.sdi[0] = { 32, PCI1735_DI, 4, 0, },
|
||||
.sdo[0] = { 32, PCI1735_DO, 4, 0, },
|
||||
.boardid = { 4, PCI1735_BOARDID, 1, SDF_INTERNAL, },
|
||||
.s8254[0] = { 3, PCI1735_C8254, 1, 0, },
|
||||
.timer_regbase = PCI1735_C8254,
|
||||
.io_access = IO_8b,
|
||||
},
|
||||
[TYPE_PCI1736] = {
|
||||
|
@ -322,7 +315,7 @@ static const struct dio_boardtype boardtypes[] = {
|
|||
.cardtype = TYPE_PCI1751,
|
||||
.nsubdevs = 3,
|
||||
.sdio[0] = { 48, PCI1751_DIO, 2, 0, },
|
||||
.s8254[0] = { 3, PCI1751_CNT, 1, 0, },
|
||||
.timer_regbase = PCI1751_CNT,
|
||||
.io_access = IO_8b,
|
||||
},
|
||||
[TYPE_PCI1752] = {
|
||||
|
@ -425,7 +418,6 @@ static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
|
|||
for (i = 0; i < d->regs; i++)
|
||||
data[1] |= inb(dev->iobase + d->addr + i) << (8 * i);
|
||||
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
|
@ -484,83 +476,6 @@ static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
|
|||
return insn->n;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
*/
|
||||
static int pci_8254_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data)
|
||||
{
|
||||
const struct diosubd_data *d = (const struct diosubd_data *)s->private;
|
||||
unsigned int chan, chip, chipchan;
|
||||
unsigned long flags;
|
||||
|
||||
chan = CR_CHAN(insn->chanspec); /* channel on subdevice */
|
||||
chip = chan / 3; /* chip on subdevice */
|
||||
chipchan = chan - (3 * chip); /* channel on chip on subdevice */
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
data[0] = i8254_read(dev->iobase + d->addr + (SIZE_8254 * chip),
|
||||
0, chipchan);
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
*/
|
||||
static int pci_8254_insn_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data)
|
||||
{
|
||||
const struct diosubd_data *d = (const struct diosubd_data *)s->private;
|
||||
unsigned int chan, chip, chipchan;
|
||||
unsigned long flags;
|
||||
|
||||
chan = CR_CHAN(insn->chanspec); /* channel on subdevice */
|
||||
chip = chan / 3; /* chip on subdevice */
|
||||
chipchan = chan - (3 * chip); /* channel on chip on subdevice */
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
i8254_write(dev->iobase + d->addr + (SIZE_8254 * chip),
|
||||
0, chipchan, data[0]);
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
*/
|
||||
static int pci_8254_insn_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data)
|
||||
{
|
||||
const struct diosubd_data *d = (const struct diosubd_data *)s->private;
|
||||
unsigned int chan, chip, chipchan;
|
||||
unsigned long iobase;
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
chan = CR_CHAN(insn->chanspec); /* channel on subdevice */
|
||||
chip = chan / 3; /* chip on subdevice */
|
||||
chipchan = chan - (3 * chip); /* channel on chip on subdevice */
|
||||
iobase = dev->iobase + d->addr + (SIZE_8254 * chip);
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
switch (data[0]) {
|
||||
case INSN_CONFIG_SET_COUNTER_MODE:
|
||||
ret = i8254_set_mode(iobase, 0, chipchan, data[1]);
|
||||
if (ret < 0)
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
case INSN_CONFIG_8254_READ_STATUS:
|
||||
data[1] = i8254_status(iobase, 0, chipchan);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
return ret < 0 ? ret : insn->n;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
*/
|
||||
|
@ -845,9 +760,6 @@ static int pci_dio_reset(struct comedi_device *dev)
|
|||
outb(0, dev->iobase + PCI1735_DO + 1);
|
||||
outb(0, dev->iobase + PCI1735_DO + 2);
|
||||
outb(0, dev->iobase + PCI1735_DO + 3);
|
||||
i8254_set_mode(dev->iobase + PCI1735_C8254, 0, 0, I8254_MODE0);
|
||||
i8254_set_mode(dev->iobase + PCI1735_C8254, 0, 1, I8254_MODE0);
|
||||
i8254_set_mode(dev->iobase + PCI1735_C8254, 0, 2, I8254_MODE0);
|
||||
break;
|
||||
|
||||
case TYPE_PCI1736:
|
||||
|
@ -1029,26 +941,6 @@ static int pci_dio_add_do(struct comedi_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
*/
|
||||
static int pci_dio_add_8254(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
const struct diosubd_data *d)
|
||||
{
|
||||
s->type = COMEDI_SUBD_COUNTER;
|
||||
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
|
||||
s->n_chan = d->chans;
|
||||
s->maxdata = 65535;
|
||||
s->len_chanlist = d->chans;
|
||||
s->insn_read = pci_8254_insn_read;
|
||||
s->insn_write = pci_8254_insn_write;
|
||||
s->insn_config = pci_8254_insn_config;
|
||||
s->private = (void *)d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
|
||||
unsigned long cardtype)
|
||||
{
|
||||
|
@ -1144,12 +1036,19 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||
subdev++;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_8254_SUBDEVS; i++)
|
||||
if (this_board->s8254[i].chans) {
|
||||
s = &dev->subdevices[subdev];
|
||||
pci_dio_add_8254(dev, s, &this_board->s8254[i]);
|
||||
subdev++;
|
||||
}
|
||||
if (this_board->timer_regbase) {
|
||||
s = &dev->subdevices[subdev];
|
||||
|
||||
dev->pacer = comedi_8254_init(dev->iobase +
|
||||
this_board->timer_regbase,
|
||||
0, I8254_IO8, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
comedi_8254_subdevice_init(s, dev->pacer);
|
||||
|
||||
subdev++;
|
||||
}
|
||||
|
||||
if (this_board->cardtype == TYPE_PCI1760)
|
||||
pci1760_attach(dev);
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
|
||||
#include "../comedidev.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
|
||||
#define AIO_IIRO_16_RELAY_0_7 0x00
|
||||
#define AIO_IIRO_16_INPUT_0_7 0x01
|
||||
#define AIO_IIRO_16_IRQ 0x02
|
||||
|
@ -116,11 +114,11 @@ static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
@ -130,11 +128,12 @@ static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
|
|
@ -25,19 +25,14 @@
|
|||
#include "../comedidev.h"
|
||||
|
||||
#include "amplc_dio200.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "8253.h"
|
||||
#include "comedi_8254.h"
|
||||
#include "8255.h" /* only for register defines */
|
||||
|
||||
/* 200 series registers */
|
||||
#define DIO200_IO_SIZE 0x20
|
||||
#define DIO200_PCIE_IO_SIZE 0x4000
|
||||
#define DIO200_XCLK_SCE 0x18 /* Group X clock selection register */
|
||||
#define DIO200_YCLK_SCE 0x19 /* Group Y clock selection register */
|
||||
#define DIO200_ZCLK_SCE 0x1a /* Group Z clock selection register */
|
||||
#define DIO200_XGAT_SCE 0x1b /* Group X gate selection register */
|
||||
#define DIO200_YGAT_SCE 0x1c /* Group Y gate selection register */
|
||||
#define DIO200_ZGAT_SCE 0x1d /* Group Z gate selection register */
|
||||
#define DIO200_CLK_SCE(x) (0x18 + (x)) /* Group X/Y/Z clock sel reg */
|
||||
#define DIO200_GAT_SCE(x) (0x1b + (x)) /* Group X/Y/Z gate sel reg */
|
||||
#define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */
|
||||
/* Extra registers for new PCIe boards */
|
||||
#define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */
|
||||
|
@ -101,16 +96,6 @@ static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
|
|||
1000000, /* 1 millisecond. */
|
||||
};
|
||||
|
||||
struct dio200_subdev_8254 {
|
||||
unsigned int ofs; /* Counter base offset */
|
||||
unsigned int clk_sce_ofs; /* CLK_SCE base address */
|
||||
unsigned int gat_sce_ofs; /* GAT_SCE base address */
|
||||
int which; /* Bit 5 of CLK_SCE or GAT_SCE */
|
||||
unsigned int clock_src[3]; /* Current clock sources */
|
||||
unsigned int gate_src[3]; /* Current gate sources */
|
||||
spinlock_t spinlock;
|
||||
};
|
||||
|
||||
struct dio200_subdev_8255 {
|
||||
unsigned int ofs; /* DIO base offset */
|
||||
};
|
||||
|
@ -177,6 +162,27 @@ static void dio200_write32(struct comedi_device *dev,
|
|||
outl(val, dev->iobase + offset);
|
||||
}
|
||||
|
||||
static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s)
|
||||
{
|
||||
const struct dio200_board *board = dev->board_ptr;
|
||||
struct comedi_8254 *i8254 = s->private;
|
||||
unsigned int offset;
|
||||
|
||||
/* get the offset that was passed to comedi_8254_*_init() */
|
||||
if (dev->mmio)
|
||||
offset = i8254->mmio - dev->mmio;
|
||||
else
|
||||
offset = i8254->iobase - dev->iobase;
|
||||
|
||||
/* remove the shift that was added for PCIe boards */
|
||||
if (board->is_pcie)
|
||||
offset >>= 3;
|
||||
|
||||
/* this offset now works for the dio200_{read,write} helpers */
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
|
@ -366,19 +372,19 @@ static int dio200_subdev_intr_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -387,15 +393,16 @@ static int dio200_subdev_intr_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -482,175 +489,26 @@ static irqreturn_t dio200_interrupt(int irq, void *d)
|
|||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static unsigned int dio200_subdev_8254_read_chan(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int chan)
|
||||
{
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
unsigned int val;
|
||||
|
||||
/* latch counter */
|
||||
val = chan << 6;
|
||||
dio200_write8(dev, subpriv->ofs + i8254_control_reg, val);
|
||||
/* read lsb, msb */
|
||||
val = dio200_read8(dev, subpriv->ofs + chan);
|
||||
val += dio200_read8(dev, subpriv->ofs + chan) << 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void dio200_subdev_8254_write_chan(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int chan,
|
||||
unsigned int count)
|
||||
{
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
|
||||
/* write lsb, msb */
|
||||
dio200_write8(dev, subpriv->ofs + chan, count & 0xff);
|
||||
dio200_write8(dev, subpriv->ofs + chan, (count >> 8) & 0xff);
|
||||
}
|
||||
|
||||
static void dio200_subdev_8254_set_mode(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
unsigned int byte;
|
||||
|
||||
byte = chan << 6;
|
||||
byte |= 0x30; /* access order: lsb, msb */
|
||||
byte |= (mode & 0xf); /* counter mode and BCD|binary */
|
||||
dio200_write8(dev, subpriv->ofs + i8254_control_reg, byte);
|
||||
}
|
||||
|
||||
static unsigned int dio200_subdev_8254_status(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int chan)
|
||||
{
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
|
||||
/* latch status */
|
||||
dio200_write8(dev, subpriv->ofs + i8254_control_reg,
|
||||
0xe0 | (2 << chan));
|
||||
/* read status */
|
||||
return dio200_read8(dev, subpriv->ofs + chan);
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned int n;
|
||||
unsigned long flags;
|
||||
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
spin_lock_irqsave(&subpriv->spinlock, flags);
|
||||
data[n] = dio200_subdev_8254_read_chan(dev, s, chan);
|
||||
spin_unlock_irqrestore(&subpriv->spinlock, flags);
|
||||
}
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned int n;
|
||||
unsigned long flags;
|
||||
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
spin_lock_irqsave(&subpriv->spinlock, flags);
|
||||
dio200_subdev_8254_write_chan(dev, s, chan, data[n]);
|
||||
spin_unlock_irqrestore(&subpriv->spinlock, flags);
|
||||
}
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int counter_number,
|
||||
unsigned int gate_src)
|
||||
{
|
||||
const struct dio200_board *board = dev->board_ptr;
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
unsigned char byte;
|
||||
|
||||
if (!board->has_clk_gat_sce)
|
||||
return -1;
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
if (gate_src > (board->is_pcie ? 31 : 7))
|
||||
return -1;
|
||||
|
||||
subpriv->gate_src[counter_number] = gate_src;
|
||||
byte = gat_sce(subpriv->which, counter_number, gate_src);
|
||||
dio200_write8(dev, subpriv->gat_sce_ofs, byte);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_get_gate_src(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int counter_number)
|
||||
{
|
||||
const struct dio200_board *board = dev->board_ptr;
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
|
||||
if (!board->has_clk_gat_sce)
|
||||
return -1;
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
|
||||
return subpriv->gate_src[counter_number];
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
|
||||
static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int counter_number,
|
||||
unsigned int clock_src)
|
||||
unsigned int chan,
|
||||
unsigned int src)
|
||||
{
|
||||
const struct dio200_board *board = dev->board_ptr;
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
unsigned char byte;
|
||||
unsigned int offset = dio200_subdev_8254_offset(dev, s);
|
||||
|
||||
if (!board->has_clk_gat_sce)
|
||||
return -1;
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
if (clock_src > (board->is_pcie ? 31 : 7))
|
||||
return -1;
|
||||
|
||||
subpriv->clock_src[counter_number] = clock_src;
|
||||
byte = clk_sce(subpriv->which, counter_number, clock_src);
|
||||
dio200_write8(dev, subpriv->clk_sce_ofs, byte);
|
||||
|
||||
return 0;
|
||||
dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
|
||||
gat_sce((offset >> 2) & 1, chan, src));
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_get_clock_src(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int counter_number,
|
||||
unsigned int *period_ns)
|
||||
static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int chan,
|
||||
unsigned int src)
|
||||
{
|
||||
const struct dio200_board *board = dev->board_ptr;
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
unsigned clock_src;
|
||||
unsigned int offset = dio200_subdev_8254_offset(dev, s);
|
||||
|
||||
if (!board->has_clk_gat_sce)
|
||||
return -1;
|
||||
if (counter_number > 2)
|
||||
return -1;
|
||||
|
||||
clock_src = subpriv->clock_src[counter_number];
|
||||
*period_ns = clock_period[clock_src];
|
||||
return clock_src;
|
||||
dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
|
||||
clk_sce((offset >> 2) & 1, chan, src));
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_config(struct comedi_device *dev,
|
||||
|
@ -658,54 +516,44 @@ static int dio200_subdev_8254_config(struct comedi_device *dev,
|
|||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct dio200_subdev_8254 *subpriv = s->private;
|
||||
int ret = 0;
|
||||
int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned long flags;
|
||||
const struct dio200_board *board = dev->board_ptr;
|
||||
struct comedi_8254 *i8254 = s->private;
|
||||
unsigned int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned int max_src = board->is_pcie ? 31 : 7;
|
||||
unsigned int src;
|
||||
|
||||
if (!board->has_clk_gat_sce)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&subpriv->spinlock, flags);
|
||||
switch (data[0]) {
|
||||
case INSN_CONFIG_SET_COUNTER_MODE:
|
||||
if (data[1] > (I8254_MODE5 | I8254_BCD))
|
||||
ret = -EINVAL;
|
||||
else
|
||||
dio200_subdev_8254_set_mode(dev, s, chan, data[1]);
|
||||
break;
|
||||
case INSN_CONFIG_8254_READ_STATUS:
|
||||
data[1] = dio200_subdev_8254_status(dev, s, chan);
|
||||
break;
|
||||
case INSN_CONFIG_SET_GATE_SRC:
|
||||
ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]);
|
||||
if (ret < 0)
|
||||
ret = -EINVAL;
|
||||
src = data[2];
|
||||
if (src > max_src)
|
||||
return -EINVAL;
|
||||
|
||||
dio200_subdev_8254_set_gate_src(dev, s, chan, src);
|
||||
i8254->gate_src[chan] = src;
|
||||
break;
|
||||
case INSN_CONFIG_GET_GATE_SRC:
|
||||
ret = dio200_subdev_8254_get_gate_src(dev, s, chan);
|
||||
if (ret < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
data[2] = ret;
|
||||
data[2] = i8254->gate_src[chan];
|
||||
break;
|
||||
case INSN_CONFIG_SET_CLOCK_SRC:
|
||||
ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]);
|
||||
if (ret < 0)
|
||||
ret = -EINVAL;
|
||||
src = data[1];
|
||||
if (src > max_src)
|
||||
return -EINVAL;
|
||||
|
||||
dio200_subdev_8254_set_clock_src(dev, s, chan, src);
|
||||
i8254->clock_src[chan] = src;
|
||||
break;
|
||||
case INSN_CONFIG_GET_CLOCK_SRC:
|
||||
ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]);
|
||||
if (ret < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
data[1] = ret;
|
||||
data[1] = i8254->clock_src[chan];
|
||||
data[2] = clock_period[i8254->clock_src[chan]];
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_unlock_irqrestore(&subpriv->spinlock, flags);
|
||||
return ret < 0 ? ret : insn->n;
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_init(struct comedi_device *dev,
|
||||
|
@ -713,36 +561,46 @@ static int dio200_subdev_8254_init(struct comedi_device *dev,
|
|||
unsigned int offset)
|
||||
{
|
||||
const struct dio200_board *board = dev->board_ptr;
|
||||
struct dio200_subdev_8254 *subpriv;
|
||||
unsigned int chan;
|
||||
struct comedi_8254 *i8254;
|
||||
unsigned int regshift;
|
||||
int chan;
|
||||
|
||||
subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
|
||||
if (!subpriv)
|
||||
return -ENOMEM;
|
||||
|
||||
s->type = COMEDI_SUBD_COUNTER;
|
||||
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
|
||||
s->n_chan = 3;
|
||||
s->maxdata = 0xFFFF;
|
||||
s->insn_read = dio200_subdev_8254_read;
|
||||
s->insn_write = dio200_subdev_8254_write;
|
||||
s->insn_config = dio200_subdev_8254_config;
|
||||
|
||||
spin_lock_init(&subpriv->spinlock);
|
||||
subpriv->ofs = offset;
|
||||
if (board->has_clk_gat_sce) {
|
||||
/* Derive CLK_SCE and GAT_SCE register offsets from
|
||||
* 8254 offset. */
|
||||
subpriv->clk_sce_ofs = DIO200_XCLK_SCE + (offset >> 3);
|
||||
subpriv->gat_sce_ofs = DIO200_XGAT_SCE + (offset >> 3);
|
||||
subpriv->which = (offset >> 2) & 1;
|
||||
/*
|
||||
* PCIe boards need the offset shifted in order to get the
|
||||
* correct base address of the timer.
|
||||
*/
|
||||
if (board->is_pcie) {
|
||||
offset <<= 3;
|
||||
regshift = 3;
|
||||
} else {
|
||||
regshift = 0;
|
||||
}
|
||||
|
||||
if (dev->mmio)
|
||||
i8254 = comedi_8254_mm_init(dev->mmio + offset,
|
||||
0, I8254_IO8, regshift);
|
||||
else
|
||||
i8254 = comedi_8254_init(dev->iobase + offset,
|
||||
0, I8254_IO8, regshift);
|
||||
if (!i8254)
|
||||
return -ENOMEM;
|
||||
|
||||
comedi_8254_subdevice_init(s, i8254);
|
||||
|
||||
i8254->insn_config = dio200_subdev_8254_config;
|
||||
|
||||
/*
|
||||
* There could be multiple timers so this driver does not
|
||||
* use dev->pacer to save the i8254 pointer. Instead,
|
||||
* comedi_8254_subdevice_init() saved the i8254 pointer in
|
||||
* s->private. Set the runflag bit so that the core will
|
||||
* automatically free it when the driver is detached.
|
||||
*/
|
||||
s->runflags |= COMEDI_SRF_FREE_SPRIV;
|
||||
|
||||
/* Initialize channels. */
|
||||
for (chan = 0; chan < 3; chan++) {
|
||||
dio200_subdev_8254_set_mode(dev, s, chan,
|
||||
I8254_MODE0 | I8254_BINARY);
|
||||
if (board->has_clk_gat_sce) {
|
||||
if (board->has_clk_gat_sce) {
|
||||
for (chan = 0; chan < 3; chan++) {
|
||||
/* Gate source 0 is VCC (logic 1). */
|
||||
dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
|
||||
/* Clock source 0 is the dedicated clock input. */
|
||||
|
|
|
@ -221,10 +221,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "amplc_dio200.h"
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "../comedidev.h"
|
||||
|
||||
#include "amplc_pc236.h"
|
||||
#include "comedi_fc.h"
|
||||
#include "8255.h"
|
||||
|
||||
static void pc236_intr_update(struct comedi_device *dev, bool enable)
|
||||
|
@ -82,11 +81,11 @@ static int pc236_intr_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
@ -96,11 +95,12 @@ static int pc236_intr_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check it arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -195,7 +195,6 @@ static void __exit amplc_pc236_common_exit(void)
|
|||
}
|
||||
module_exit(amplc_pc236_common_exit);
|
||||
|
||||
|
||||
MODULE_AUTHOR("Comedi http://www.comedi.org");
|
||||
MODULE_DESCRIPTION("Comedi helper for amplc_pc236 and amplc_pci236");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -103,22 +103,17 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
#include "8253.h"
|
||||
#include "comedi_8254.h"
|
||||
|
||||
/*
|
||||
* PCI224/234 i/o space 1 (PCIBAR2) registers.
|
||||
*/
|
||||
#define PCI224_Z2_CT0 0x14 /* 82C54 counter/timer 0 */
|
||||
#define PCI224_Z2_CT1 0x15 /* 82C54 counter/timer 1 */
|
||||
#define PCI224_Z2_CT2 0x16 /* 82C54 counter/timer 2 */
|
||||
#define PCI224_Z2_CTC 0x17 /* 82C54 counter/timer control word */
|
||||
#define PCI224_Z2_BASE 0x14 /* 82C54 counter/timer */
|
||||
#define PCI224_ZCLK_SCE 0x1A /* Group Z Clock Configuration Register */
|
||||
#define PCI224_ZGAT_SCE 0x1D /* Group Z Gate Configuration Register */
|
||||
#define PCI224_INT_SCE 0x1E /* ISR Interrupt source mask register */
|
||||
|
@ -379,8 +374,6 @@ struct pci224_private {
|
|||
int intr_cpuid;
|
||||
short intr_running;
|
||||
unsigned short daccon;
|
||||
unsigned int cached_div1;
|
||||
unsigned int cached_div2;
|
||||
unsigned short ao_enab; /* max 16 channels so 'short' will do */
|
||||
unsigned char intsce;
|
||||
};
|
||||
|
@ -451,7 +444,6 @@ static void pci224_ao_stop(struct comedi_device *dev,
|
|||
if (!test_and_clear_bit(AO_CMD_STARTED, &devpriv->state))
|
||||
return;
|
||||
|
||||
|
||||
spin_lock_irqsave(&devpriv->ao_spinlock, flags);
|
||||
/* Kill the interrupts. */
|
||||
devpriv->intsce = 0;
|
||||
|
@ -668,28 +660,27 @@ static int
|
|||
pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
struct comedi_cmd *cmd)
|
||||
{
|
||||
struct pci224_private *devpriv = dev->private;
|
||||
int err = 0;
|
||||
unsigned int arg;
|
||||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_EXT | TRIG_TIMER);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src,
|
||||
TRIG_COUNT | TRIG_EXT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_EXT | TRIG_TIMER);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src,
|
||||
TRIG_COUNT | TRIG_EXT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -714,7 +705,7 @@ pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
|
||||
switch (cmd->start_src) {
|
||||
case TRIG_INT:
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
break;
|
||||
case TRIG_EXT:
|
||||
/* Force to external trigger 0. */
|
||||
|
@ -734,13 +725,13 @@ pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
|
||||
switch (cmd->scan_begin_src) {
|
||||
case TRIG_TIMER:
|
||||
err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg,
|
||||
MAX_SCAN_PERIOD);
|
||||
err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
|
||||
MAX_SCAN_PERIOD);
|
||||
|
||||
arg = cmd->chanlist_len * CONVERT_PERIOD;
|
||||
if (arg < MIN_SCAN_PERIOD)
|
||||
arg = MIN_SCAN_PERIOD;
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
|
||||
break;
|
||||
case TRIG_EXT:
|
||||
/* Force to external trigger 0. */
|
||||
|
@ -760,12 +751,13 @@ pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
break;
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
switch (cmd->stop_src) {
|
||||
case TRIG_COUNT:
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
break;
|
||||
case TRIG_EXT:
|
||||
/* Force to external trigger 0. */
|
||||
|
@ -781,7 +773,7 @@ pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
}
|
||||
break;
|
||||
case TRIG_NONE:
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -793,11 +785,8 @@ pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
arg = cmd->scan_begin_arg;
|
||||
/* Use two timers. */
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
|
||||
&devpriv->cached_div1,
|
||||
&devpriv->cached_div2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
@ -817,7 +806,6 @@ static void pci224_ao_start_pacer(struct comedi_device *dev,
|
|||
struct comedi_subdevice *s)
|
||||
{
|
||||
struct pci224_private *devpriv = dev->private;
|
||||
unsigned long timer_base = devpriv->iobase1 + PCI224_Z2_CT0;
|
||||
|
||||
/*
|
||||
* The output of timer Z2-0 will be used as the scan trigger
|
||||
|
@ -830,14 +818,10 @@ static void pci224_ao_start_pacer(struct comedi_device *dev,
|
|||
outb(GAT_CONFIG(2, GAT_VCC), devpriv->iobase1 + PCI224_ZGAT_SCE);
|
||||
/* Z2-2 needs 10 MHz clock. */
|
||||
outb(CLK_CONFIG(2, CLK_10MHZ), devpriv->iobase1 + PCI224_ZCLK_SCE);
|
||||
/* Load Z2-2 mode (2) and counter (div1). */
|
||||
i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
|
||||
i8254_write(timer_base, 0, 2, devpriv->cached_div1);
|
||||
/* Z2-0 is clocked from Z2-2's output. */
|
||||
outb(CLK_CONFIG(0, CLK_OUTNM1), devpriv->iobase1 + PCI224_ZCLK_SCE);
|
||||
/* Load Z2-0 mode (2) and counter (div2). */
|
||||
i8254_set_mode(timer_base, 0, 0, I8254_MODE2 | I8254_BINARY);
|
||||
i8254_write(timer_base, 0, 0, devpriv->cached_div2);
|
||||
|
||||
comedi_8254_pacer_enable(dev->pacer, 2, 0, false);
|
||||
}
|
||||
|
||||
static int pci224_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
|
@ -852,10 +836,9 @@ static int pci224_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
unsigned long flags;
|
||||
|
||||
/* Cannot handle null/empty chanlist. */
|
||||
if (cmd->chanlist == NULL || cmd->chanlist_len == 0)
|
||||
if (!cmd->chanlist || cmd->chanlist_len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
/* Determine which channels are enabled and their load order. */
|
||||
devpriv->ao_enab = 0;
|
||||
|
||||
|
@ -893,8 +876,10 @@ static int pci224_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
|
||||
dev->iobase + PCI224_DACCON);
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER)
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
comedi_8254_update_divisors(dev->pacer);
|
||||
pci224_ao_start_pacer(dev, s);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&devpriv->ao_spinlock, flags);
|
||||
if (cmd->start_src == TRIG_INT) {
|
||||
|
@ -1042,14 +1027,12 @@ pci224_auto_attach(struct comedi_device *dev, unsigned long context_model)
|
|||
if (!devpriv->ao_scan_vals)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
/* Allocate buffer to hold AO channel scan order. */
|
||||
devpriv->ao_scan_order = kmalloc(sizeof(devpriv->ao_scan_order[0]) *
|
||||
thisboard->ao_chans, GFP_KERNEL);
|
||||
if (!devpriv->ao_scan_order)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
/* Disable interrupt sources. */
|
||||
devpriv->intsce = 0;
|
||||
outb(0, devpriv->iobase1 + PCI224_INT_SCE);
|
||||
|
@ -1063,6 +1046,11 @@ pci224_auto_attach(struct comedi_device *dev, unsigned long context_model)
|
|||
outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
|
||||
dev->iobase + PCI224_DACCON);
|
||||
|
||||
dev->pacer = comedi_8254_init(devpriv->iobase1 + PCI224_Z2_BASE,
|
||||
I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = comedi_alloc_subdevices(dev, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -181,14 +181,12 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
#include "8253.h"
|
||||
#include "comedi_8254.h"
|
||||
#include "8255.h"
|
||||
|
||||
/*
|
||||
|
@ -206,10 +204,6 @@
|
|||
#define PCI230_PPI_X_C 0x02 /* User PPI (82C55) port C */
|
||||
#define PCI230_PPI_X_CMD 0x03 /* User PPI (82C55) control word */
|
||||
#define PCI230_Z2_CT_BASE 0x14 /* 82C54 counter/timer base */
|
||||
#define PCI230_Z2_CT0 0x14 /* 82C54 counter/timer 0 */
|
||||
#define PCI230_Z2_CT1 0x15 /* 82C54 counter/timer 1 */
|
||||
#define PCI230_Z2_CT2 0x16 /* 82C54 counter/timer 2 */
|
||||
#define PCI230_Z2_CTC 0x17 /* 82C54 counter/timer control word */
|
||||
#define PCI230_ZCLK_SCE 0x1A /* Group Z Clock Configuration */
|
||||
#define PCI230_ZGAT_SCE 0x1D /* Group Z Gate Configuration */
|
||||
#define PCI230_INT_SCE 0x1E /* Interrupt source mask (w) */
|
||||
|
@ -377,12 +371,6 @@
|
|||
#define CLK_EXT 7 /* external clock */
|
||||
/* Macro to construct clock input configuration register value. */
|
||||
#define CLK_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
|
||||
/* Timebases in ns. */
|
||||
#define TIMEBASE_10MHZ 100
|
||||
#define TIMEBASE_1MHZ 1000
|
||||
#define TIMEBASE_100KHZ 10000
|
||||
#define TIMEBASE_10KHZ 100000
|
||||
#define TIMEBASE_1KHZ 1000000
|
||||
|
||||
/*
|
||||
* Counter/timer gate input configuration sources.
|
||||
|
@ -507,11 +495,11 @@ struct pci230_private {
|
|||
|
||||
/* PCI230 clock source periods in ns */
|
||||
static const unsigned int pci230_timebase[8] = {
|
||||
[CLK_10MHZ] = TIMEBASE_10MHZ,
|
||||
[CLK_1MHZ] = TIMEBASE_1MHZ,
|
||||
[CLK_100KHZ] = TIMEBASE_100KHZ,
|
||||
[CLK_10KHZ] = TIMEBASE_10KHZ,
|
||||
[CLK_1KHZ] = TIMEBASE_1KHZ,
|
||||
[CLK_10MHZ] = I8254_OSC_BASE_10MHZ,
|
||||
[CLK_1MHZ] = I8254_OSC_BASE_1MHZ,
|
||||
[CLK_100KHZ] = I8254_OSC_BASE_100KHZ,
|
||||
[CLK_10KHZ] = I8254_OSC_BASE_10KHZ,
|
||||
[CLK_1KHZ] = I8254_OSC_BASE_1KHZ,
|
||||
};
|
||||
|
||||
/* PCI230 analogue input range table */
|
||||
|
@ -695,7 +683,7 @@ static void pci230_ct_setup_ns_mode(struct comedi_device *dev, unsigned int ct,
|
|||
unsigned int count;
|
||||
|
||||
/* Set mode. */
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0, ct, mode);
|
||||
comedi_8254_set_mode(dev->pacer, ct, mode);
|
||||
/* Determine clock source and count. */
|
||||
clk_src = pci230_choose_clk_count(ns, &count, flags);
|
||||
/* Program clock source. */
|
||||
|
@ -704,13 +692,13 @@ static void pci230_ct_setup_ns_mode(struct comedi_device *dev, unsigned int ct,
|
|||
if (count >= 65536)
|
||||
count = 0;
|
||||
|
||||
i8254_write(dev->iobase + PCI230_Z2_CT_BASE, 0, ct, count);
|
||||
comedi_8254_write(dev->pacer, ct, count);
|
||||
}
|
||||
|
||||
static void pci230_cancel_ct(struct comedi_device *dev, unsigned int ct)
|
||||
{
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0, ct, I8254_MODE1);
|
||||
/* Counter ct, 8254 mode 1, initial count not written. */
|
||||
comedi_8254_set_mode(dev->pacer, ct, I8254_MODE1);
|
||||
}
|
||||
|
||||
static int pci230_ai_eoc(struct comedi_device *dev,
|
||||
|
@ -760,7 +748,7 @@ static int pci230_ai_insn_read(struct comedi_device *dev,
|
|||
*/
|
||||
adccon = PCI230_ADC_TRIG_Z2CT2 | PCI230_ADC_FIFO_EN;
|
||||
/* Set Z2-CT2 output low to avoid any false triggers. */
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE0);
|
||||
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
|
||||
devpriv->ai_bipolar = comedi_range_is_bipolar(s, range);
|
||||
if (aref == AREF_DIFF) {
|
||||
/* Differential. */
|
||||
|
@ -811,10 +799,8 @@ static int pci230_ai_insn_read(struct comedi_device *dev,
|
|||
* Trigger conversion by toggling Z2-CT2 output
|
||||
* (finish with output high).
|
||||
*/
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0,
|
||||
2, I8254_MODE0);
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0,
|
||||
2, I8254_MODE1);
|
||||
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
|
||||
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1);
|
||||
|
||||
/* wait for conversion to end */
|
||||
ret = comedi_timeout(dev, s, insn, pci230_ai_eoc, 0);
|
||||
|
@ -898,7 +884,7 @@ static int pci230_ao_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
|
||||
|
||||
tmp = TRIG_TIMER | TRIG_INT;
|
||||
if (thisboard->min_hwver > 0 && devpriv->hwver >= 2) {
|
||||
|
@ -918,19 +904,19 @@ static int pci230_ao_cmdtest(struct comedi_device *dev,
|
|||
*/
|
||||
tmp |= TRIG_EXT;
|
||||
}
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, tmp);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, tmp);
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -939,7 +925,7 @@ static int pci230_ao_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
|
||||
#define MAX_SPEED_AO 8000 /* 8000 ns => 125 kHz */
|
||||
/*
|
||||
|
@ -950,10 +936,10 @@ static int pci230_ao_cmdtest(struct comedi_device *dev,
|
|||
|
||||
switch (cmd->scan_begin_src) {
|
||||
case TRIG_TIMER:
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
MAX_SPEED_AO);
|
||||
err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg,
|
||||
MIN_SPEED_AO);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
MAX_SPEED_AO);
|
||||
err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
|
||||
MIN_SPEED_AO);
|
||||
break;
|
||||
case TRIG_EXT:
|
||||
/*
|
||||
|
@ -978,16 +964,17 @@ static int pci230_ao_cmdtest(struct comedi_device *dev,
|
|||
}
|
||||
break;
|
||||
default:
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -1520,7 +1507,7 @@ static int pci230_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
|
||||
|
||||
tmp = TRIG_FOLLOW | TRIG_TIMER | TRIG_INT;
|
||||
if (thisboard->have_dio || thisboard->min_hwver > 0) {
|
||||
|
@ -1532,21 +1519,21 @@ static int pci230_ai_cmdtest(struct comedi_device *dev,
|
|||
*/
|
||||
tmp |= TRIG_EXT;
|
||||
}
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, tmp);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src,
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, tmp);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src,
|
||||
TRIG_TIMER | TRIG_INT | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -1563,7 +1550,7 @@ static int pci230_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
|
||||
#define MAX_SPEED_AI_SE 3200 /* PCI230 SE: 3200 ns => 312.5 kHz */
|
||||
#define MAX_SPEED_AI_DIFF 8000 /* PCI230 DIFF: 8000 ns => 125 kHz */
|
||||
|
@ -1598,10 +1585,10 @@ static int pci230_ai_cmdtest(struct comedi_device *dev,
|
|||
max_speed_ai = MAX_SPEED_AI_PLUS;
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
|
||||
max_speed_ai);
|
||||
err |= cfc_check_trigger_arg_max(&cmd->convert_arg,
|
||||
MIN_SPEED_AI);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
|
||||
max_speed_ai);
|
||||
err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
|
||||
MIN_SPEED_AI);
|
||||
} else if (cmd->convert_src == TRIG_EXT) {
|
||||
/*
|
||||
* external trigger
|
||||
|
@ -1636,18 +1623,20 @@ static int pci230_ai_cmdtest(struct comedi_device *dev,
|
|||
* convert_arg == 0 => trigger on -ve edge.
|
||||
* convert_arg == 1 => trigger on +ve edge.
|
||||
*/
|
||||
err |= cfc_check_trigger_arg_max(&cmd->convert_arg, 1);
|
||||
err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
|
||||
1);
|
||||
}
|
||||
} else {
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_EXT) {
|
||||
/*
|
||||
|
@ -1672,7 +1661,7 @@ static int pci230_ai_cmdtest(struct comedi_device *dev,
|
|||
err |= -EINVAL;
|
||||
|
||||
} else {
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
@ -1767,8 +1756,8 @@ static int pci230_ai_inttrig_convert(struct comedi_device *dev,
|
|||
* Trigger conversion by toggling Z2-CT2 output.
|
||||
* Finish with output high.
|
||||
*/
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE0);
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE1);
|
||||
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
|
||||
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1);
|
||||
/*
|
||||
* Delay. Should driver be responsible for this? An
|
||||
* alternative would be to wait until conversion is complete,
|
||||
|
@ -2189,7 +2178,7 @@ static int pci230_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
* Set counter/timer 2 output high for use as the initial start
|
||||
* conversion source.
|
||||
*/
|
||||
i8254_set_mode(dev->iobase + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE1);
|
||||
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1);
|
||||
|
||||
/*
|
||||
* Temporarily use CT2 output as conversion trigger source and
|
||||
|
@ -2393,7 +2382,7 @@ static int pci230_auto_attach(struct comedi_device *dev,
|
|||
spin_lock_init(&devpriv->ao_stop_spinlock);
|
||||
|
||||
dev->board_ptr = pci230_find_pci_board(pci_dev);
|
||||
if (dev->board_ptr == NULL) {
|
||||
if (!dev->board_ptr) {
|
||||
dev_err(dev->class_dev,
|
||||
"amplc_pci230: BUG! cannot determine board type!\n");
|
||||
return -EINVAL;
|
||||
|
@ -2481,6 +2470,11 @@ static int pci230_auto_attach(struct comedi_device *dev,
|
|||
dev->irq = pci_dev->irq;
|
||||
}
|
||||
|
||||
dev->pacer = comedi_8254_init(dev->iobase + PCI230_Z2_CT_BASE,
|
||||
0, I8254_IO8, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = comedi_alloc_subdevices(dev, 3);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
|
@ -42,10 +42,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "amplc_pc236.h"
|
||||
#include "plx9052.h"
|
||||
|
|
|
@ -33,9 +33,8 @@ The state of the outputs can be read.
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
static int pci263_do_insn_bits(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
|
|
|
@ -30,8 +30,6 @@ Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
|
|||
Author: ds
|
||||
Updated: Mon, 04 Nov 2002 20:04:21 -0800
|
||||
Status: experimental
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -40,17 +38,13 @@ Status: experimental
|
|||
|
||||
#include "../comedi_pcmcia.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
#include "8253.h"
|
||||
#include "comedi_8254.h"
|
||||
|
||||
#define DAS16CS_ADC_DATA 0
|
||||
#define DAS16CS_DIO_MUX 2
|
||||
#define DAS16CS_MISC1 4
|
||||
#define DAS16CS_MISC2 6
|
||||
#define DAS16CS_CTR0 8
|
||||
#define DAS16CS_CTR1 10
|
||||
#define DAS16CS_CTR2 12
|
||||
#define DAS16CS_CTR_CONTROL 14
|
||||
#define DAS16CS_TIMER_BASE 8
|
||||
#define DAS16CS_DIO 16
|
||||
|
||||
struct das16cs_board {
|
||||
|
@ -279,6 +273,11 @@ static int das16cs_auto_attach(struct comedi_device *dev,
|
|||
if (!devpriv)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->pacer = comedi_8254_init(dev->iobase + DAS16CS_TIMER_BASE,
|
||||
I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = comedi_alloc_subdevices(dev, 3);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -62,16 +62,14 @@ analog triggering on 1602 series
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "8253.h"
|
||||
#include "comedi_8254.h"
|
||||
#include "8255.h"
|
||||
#include "amcc_s5933.h"
|
||||
#include "comedi_fc.h"
|
||||
|
||||
#define AI_BUFFER_SIZE 1024 /* max ai fifo size */
|
||||
#define AO_BUFFER_SIZE 1024 /* max ao fifo size */
|
||||
|
@ -338,14 +336,12 @@ static const struct cb_pcidas_board cb_pcidas_boards[] = {
|
|||
};
|
||||
|
||||
struct cb_pcidas_private {
|
||||
struct comedi_8254 *ao_pacer;
|
||||
/* base addresses */
|
||||
unsigned long s5933_config;
|
||||
unsigned long control_status;
|
||||
unsigned long adc_fifo;
|
||||
unsigned long ao_registers;
|
||||
/* divisors of master clock for analog input pacing */
|
||||
unsigned int divisor1;
|
||||
unsigned int divisor2;
|
||||
/* bits to write to registers */
|
||||
unsigned int adc_fifo_bits;
|
||||
unsigned int s5933_intcsr_bits;
|
||||
|
@ -353,9 +349,6 @@ struct cb_pcidas_private {
|
|||
/* fifo buffers */
|
||||
unsigned short ai_buffer[AI_BUFFER_SIZE];
|
||||
unsigned short ao_buffer[AO_BUFFER_SIZE];
|
||||
/* divisors of master clock for analog output pacing */
|
||||
unsigned int ao_divisor1;
|
||||
unsigned int ao_divisor2;
|
||||
unsigned int calibration_source;
|
||||
};
|
||||
|
||||
|
@ -530,7 +523,7 @@ static int wait_for_nvram_ready(unsigned long s5933_base_addr)
|
|||
}
|
||||
|
||||
static int nvram_read(struct comedi_device *dev, unsigned int address,
|
||||
uint8_t *data)
|
||||
uint8_t *data)
|
||||
{
|
||||
struct cb_pcidas_private *devpriv = dev->private;
|
||||
unsigned long iobase = devpriv->s5933_config;
|
||||
|
@ -778,29 +771,28 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev,
|
|||
struct comedi_cmd *cmd)
|
||||
{
|
||||
const struct cb_pcidas_board *thisboard = dev->board_ptr;
|
||||
struct cb_pcidas_private *devpriv = dev->private;
|
||||
int err = 0;
|
||||
unsigned int arg;
|
||||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src,
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src,
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src,
|
||||
TRIG_TIMER | TRIG_NOW | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -819,7 +811,7 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
switch (cmd->start_src) {
|
||||
case TRIG_NOW:
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
break;
|
||||
case TRIG_EXT:
|
||||
/* External trigger, only CR_EDGE and CR_INVERT flags allowed */
|
||||
|
@ -836,20 +828,24 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev,
|
|||
break;
|
||||
}
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
thisboard->ai_speed * cmd->chanlist_len);
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
thisboard->ai_speed *
|
||||
cmd->chanlist_len);
|
||||
}
|
||||
|
||||
if (cmd->convert_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
|
||||
thisboard->ai_speed);
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
|
||||
thisboard->ai_speed);
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -858,19 +854,13 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev,
|
|||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
arg = cmd->scan_begin_arg;
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
|
||||
&devpriv->divisor1,
|
||||
&devpriv->divisor2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
}
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
arg = cmd->convert_arg;
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
|
||||
&devpriv->divisor1,
|
||||
&devpriv->divisor2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
@ -886,18 +876,6 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cb_pcidas_ai_load_counters(struct comedi_device *dev)
|
||||
{
|
||||
struct cb_pcidas_private *devpriv = dev->private;
|
||||
unsigned long timer_base = dev->iobase + ADC8254;
|
||||
|
||||
i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
|
||||
i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
|
||||
|
||||
i8254_write(timer_base, 0, 1, devpriv->divisor1);
|
||||
i8254_write(timer_base, 0, 2, devpriv->divisor2);
|
||||
}
|
||||
|
||||
static int cb_pcidas_ai_cmd(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s)
|
||||
{
|
||||
|
@ -933,8 +911,11 @@ static int cb_pcidas_ai_cmd(struct comedi_device *dev,
|
|||
outw(bits, devpriv->control_status + ADCMUX_CONT);
|
||||
|
||||
/* load counters */
|
||||
if (cmd->scan_begin_src == TRIG_TIMER || cmd->convert_src == TRIG_TIMER)
|
||||
cb_pcidas_ai_load_counters(dev);
|
||||
if (cmd->scan_begin_src == TRIG_TIMER ||
|
||||
cmd->convert_src == TRIG_TIMER) {
|
||||
comedi_8254_update_divisors(dev->pacer);
|
||||
comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
|
||||
}
|
||||
|
||||
/* enable interrupts */
|
||||
spin_lock_irqsave(&dev->spinlock, flags);
|
||||
|
@ -1004,24 +985,23 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev,
|
|||
const struct cb_pcidas_board *thisboard = dev->board_ptr;
|
||||
struct cb_pcidas_private *devpriv = dev->private;
|
||||
int err = 0;
|
||||
unsigned int arg;
|
||||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src,
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_TIMER | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -1030,18 +1010,20 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
thisboard->ao_scan_speed);
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
thisboard->ao_scan_speed);
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT)
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
else /* TRIG_NONE */
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -1049,12 +1031,11 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev,
|
|||
/* step 4: fix up any arguments */
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
arg = cmd->scan_begin_arg;
|
||||
i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
|
||||
&devpriv->ao_divisor1,
|
||||
&devpriv->ao_divisor2,
|
||||
&arg, cmd->flags);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
unsigned int arg = cmd->scan_begin_arg;
|
||||
|
||||
comedi_8254_cascade_ns_to_timer(devpriv->ao_pacer,
|
||||
&arg, cmd->flags);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
@ -1139,18 +1120,6 @@ static int cb_pcidas_ao_inttrig(struct comedi_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cb_pcidas_ao_load_counters(struct comedi_device *dev)
|
||||
{
|
||||
struct cb_pcidas_private *devpriv = dev->private;
|
||||
unsigned long timer_base = dev->iobase + DAC8254;
|
||||
|
||||
i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
|
||||
i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
|
||||
|
||||
i8254_write(timer_base, 0, 1, devpriv->ao_divisor1);
|
||||
i8254_write(timer_base, 0, 2, devpriv->ao_divisor2);
|
||||
}
|
||||
|
||||
static int cb_pcidas_ao_cmd(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s)
|
||||
{
|
||||
|
@ -1180,8 +1149,10 @@ static int cb_pcidas_ao_cmd(struct comedi_device *dev,
|
|||
outw(0, devpriv->ao_registers + DACFIFOCLR);
|
||||
|
||||
/* load counters */
|
||||
if (cmd->scan_begin_src == TRIG_TIMER)
|
||||
cb_pcidas_ao_load_counters(dev);
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
comedi_8254_update_divisors(devpriv->ao_pacer);
|
||||
comedi_8254_pacer_enable(devpriv->ao_pacer, 1, 2, true);
|
||||
}
|
||||
|
||||
/* set pacer source */
|
||||
spin_lock_irqsave(&dev->spinlock, flags);
|
||||
|
@ -1408,6 +1379,17 @@ static int cb_pcidas_auto_attach(struct comedi_device *dev,
|
|||
}
|
||||
dev->irq = pcidev->irq;
|
||||
|
||||
dev->pacer = comedi_8254_init(dev->iobase + ADC8254,
|
||||
I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
devpriv->ao_pacer = comedi_8254_init(dev->iobase + DAC8254,
|
||||
I8254_OSC_BASE_10MHZ,
|
||||
I8254_IO8, 0);
|
||||
if (!devpriv->ao_pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = comedi_alloc_subdevices(dev, 7);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1530,8 +1512,9 @@ static int cb_pcidas_auto_attach(struct comedi_device *dev,
|
|||
dac08_write(dev, s->maxdata / 2);
|
||||
s->readback[i] = s->maxdata / 2;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
s->type = COMEDI_SUBD_UNUSED;
|
||||
}
|
||||
|
||||
/* make sure mailbox 4 is empty */
|
||||
inl(devpriv->s5933_config + AMCC_OP_REG_IMB4);
|
||||
|
@ -1550,9 +1533,11 @@ static void cb_pcidas_detach(struct comedi_device *dev)
|
|||
{
|
||||
struct cb_pcidas_private *devpriv = dev->private;
|
||||
|
||||
if (devpriv && devpriv->s5933_config) {
|
||||
outl(INTCSR_INBOX_INTR_STATUS,
|
||||
devpriv->s5933_config + AMCC_OP_REG_INTCSR);
|
||||
if (devpriv) {
|
||||
if (devpriv->s5933_config)
|
||||
outl(INTCSR_INBOX_INTR_STATUS,
|
||||
devpriv->s5933_config + AMCC_OP_REG_INTCSR);
|
||||
kfree(devpriv->ao_pacer);
|
||||
}
|
||||
comedi_pci_detach(dev);
|
||||
}
|
||||
|
|
|
@ -81,16 +81,13 @@ TODO:
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "8253.h"
|
||||
#include "8255.h"
|
||||
#include "plx9080.h"
|
||||
#include "comedi_fc.h"
|
||||
|
||||
#define TIMER_BASE 25 /* 40MHz master clock */
|
||||
/* 100kHz 'prescaled' clock for slow acquisition,
|
||||
|
@ -1474,9 +1471,8 @@ static int alloc_and_init_dma_members(struct comedi_device *dev)
|
|||
devpriv->ai_buffer[i] =
|
||||
pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE,
|
||||
&devpriv->ai_buffer_bus_addr[i]);
|
||||
if (devpriv->ai_buffer[i] == NULL)
|
||||
if (!devpriv->ai_buffer[i])
|
||||
return -ENOMEM;
|
||||
|
||||
}
|
||||
for (i = 0; i < AO_DMA_RING_COUNT; i++) {
|
||||
if (ao_cmd_is_supported(thisboard)) {
|
||||
|
@ -1484,9 +1480,8 @@ static int alloc_and_init_dma_members(struct comedi_device *dev)
|
|||
pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE,
|
||||
&devpriv->
|
||||
ao_buffer_bus_addr[i]);
|
||||
if (devpriv->ao_buffer[i] == NULL)
|
||||
if (!devpriv->ao_buffer[i])
|
||||
return -ENOMEM;
|
||||
|
||||
}
|
||||
}
|
||||
/* allocate dma descriptors */
|
||||
|
@ -1494,7 +1489,7 @@ static int alloc_and_init_dma_members(struct comedi_device *dev)
|
|||
pci_alloc_consistent(pcidev, sizeof(struct plx_dma_desc) *
|
||||
ai_dma_ring_count(thisboard),
|
||||
&devpriv->ai_dma_desc_bus_addr);
|
||||
if (devpriv->ai_dma_desc == NULL)
|
||||
if (!devpriv->ai_dma_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ao_cmd_is_supported(thisboard)) {
|
||||
|
@ -1503,7 +1498,7 @@ static int alloc_and_init_dma_members(struct comedi_device *dev)
|
|||
sizeof(struct plx_dma_desc) *
|
||||
AO_DMA_RING_COUNT,
|
||||
&devpriv->ao_dma_desc_bus_addr);
|
||||
if (devpriv->ao_dma_desc == NULL)
|
||||
if (!devpriv->ao_dma_desc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* initialize dma descriptors */
|
||||
|
@ -1704,8 +1699,7 @@ static void i2c_write(struct comedi_device *dev, unsigned int address,
|
|||
|
||||
/* get acknowledge */
|
||||
if (i2c_read_ack(dev) != 0) {
|
||||
dev_err(dev->class_dev, "%s failed: no acknowledge\n",
|
||||
__func__);
|
||||
dev_err(dev->class_dev, "failed: no acknowledge\n");
|
||||
i2c_stop(dev);
|
||||
return;
|
||||
}
|
||||
|
@ -1713,8 +1707,7 @@ static void i2c_write(struct comedi_device *dev, unsigned int address,
|
|||
for (i = 0; i < length; i++) {
|
||||
i2c_write_byte(dev, data[i]);
|
||||
if (i2c_read_ack(dev) != 0) {
|
||||
dev_err(dev->class_dev, "%s failed: no acknowledge\n",
|
||||
__func__);
|
||||
dev_err(dev->class_dev, "failed: no acknowledge\n");
|
||||
i2c_stop(dev);
|
||||
return;
|
||||
}
|
||||
|
@ -1841,7 +1834,6 @@ static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
}
|
||||
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
|
||||
/* clear adc buffer (inside loop for 4020 sake) */
|
||||
writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
|
||||
|
||||
|
@ -1903,7 +1895,6 @@ static int ai_config_block_size(struct comedi_device *dev, unsigned int *data)
|
|||
retval = set_ai_fifo_size(dev, fifo_size);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
block_size = ai_fifo_size(dev) / fifo->num_segments * bytes_in_sample;
|
||||
|
@ -2001,7 +1992,8 @@ static unsigned int get_divisor(unsigned int ns, unsigned int flags)
|
|||
static void check_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
|
||||
{
|
||||
const struct pcidas64_board *thisboard = dev->board_ptr;
|
||||
unsigned int convert_divisor = 0, scan_divisor;
|
||||
unsigned long long convert_divisor = 0;
|
||||
unsigned int scan_divisor;
|
||||
static const int min_convert_divisor = 3;
|
||||
static const int max_convert_divisor =
|
||||
max_counter_value + min_convert_divisor;
|
||||
|
@ -2027,7 +2019,6 @@ static void check_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
|
|||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
scan_divisor = get_divisor(cmd->scan_begin_arg, cmd->flags);
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
/* XXX check for integer overflows */
|
||||
min_scan_divisor = convert_divisor * cmd->chanlist_len;
|
||||
max_scan_divisor =
|
||||
(convert_divisor * cmd->chanlist_len - 1) +
|
||||
|
@ -2094,34 +2085,34 @@ static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
|
||||
|
||||
triggers = TRIG_TIMER;
|
||||
if (thisboard->layout == LAYOUT_4020)
|
||||
triggers |= TRIG_OTHER;
|
||||
else
|
||||
triggers |= TRIG_FOLLOW;
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src, triggers);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src, triggers);
|
||||
|
||||
triggers = TRIG_TIMER;
|
||||
if (thisboard->layout == LAYOUT_4020)
|
||||
triggers |= TRIG_NOW;
|
||||
else
|
||||
triggers |= TRIG_EXT;
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, triggers);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src,
|
||||
TRIG_COUNT | TRIG_EXT | TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, triggers);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src,
|
||||
TRIG_COUNT | TRIG_EXT | TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->stop_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->convert_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->stop_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -2135,7 +2126,7 @@ static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
|
||||
switch (cmd->start_src) {
|
||||
case TRIG_NOW:
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
break;
|
||||
case TRIG_EXT:
|
||||
/*
|
||||
|
@ -2147,30 +2138,37 @@ static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
|
||||
if (cmd->convert_src == TRIG_TIMER) {
|
||||
if (thisboard->layout == LAYOUT_4020) {
|
||||
err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->convert_arg,
|
||||
0);
|
||||
} else {
|
||||
err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
|
||||
thisboard->ai_speed);
|
||||
/* if scans are timed faster than conversion rate allows */
|
||||
if (cmd->scan_begin_src == TRIG_TIMER)
|
||||
err |= cfc_check_trigger_arg_min(
|
||||
err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
|
||||
thisboard->
|
||||
ai_speed);
|
||||
/*
|
||||
* if scans are timed faster than conversion rate
|
||||
* allows
|
||||
*/
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
err |= comedi_check_trigger_arg_min(
|
||||
&cmd->scan_begin_arg,
|
||||
cmd->convert_arg *
|
||||
cmd->chanlist_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
switch (cmd->stop_src) {
|
||||
case TRIG_EXT:
|
||||
break;
|
||||
case TRIG_COUNT:
|
||||
err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
|
||||
break;
|
||||
case TRIG_NONE:
|
||||
err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2637,8 +2635,9 @@ static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|||
bits |= ADC_START_TRIG_EXT_BITS;
|
||||
if (cmd->start_arg & CR_INVERT)
|
||||
bits |= ADC_START_TRIG_FALLING_BIT;
|
||||
} else if (cmd->start_src == TRIG_NOW)
|
||||
} else if (cmd->start_src == TRIG_NOW) {
|
||||
bits |= ADC_START_TRIG_SOFT_BITS;
|
||||
}
|
||||
if (use_hw_sample_counter(cmd))
|
||||
bits |= ADC_SAMPLE_COUNTER_EN_BIT;
|
||||
writew(bits, devpriv->main_iobase + ADC_CONTROL0_REG);
|
||||
|
@ -2827,8 +2826,9 @@ static void handle_ai_interrupt(struct comedi_device *dev,
|
|||
if (devpriv->ai_cmd_running) {
|
||||
spin_unlock_irqrestore(&dev->spinlock, flags);
|
||||
pio_drain_ai_fifo(dev);
|
||||
} else
|
||||
} else {
|
||||
spin_unlock_irqrestore(&dev->spinlock, flags);
|
||||
}
|
||||
}
|
||||
/* if we are have all the data, then quit */
|
||||
if ((cmd->stop_src == TRIG_COUNT &&
|
||||
|
@ -2978,7 +2978,7 @@ static void handle_ao_interrupt(struct comedi_device *dev,
|
|||
unsigned long flags;
|
||||
|
||||
/* board might not support ao, in which case write_subdev is NULL */
|
||||
if (s == NULL)
|
||||
if (!s)
|
||||
return;
|
||||
async = s->async;
|
||||
cmd = &async->cmd;
|
||||
|
@ -3312,20 +3312,20 @@ static int ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
|
||||
/* Step 1 : check if triggers are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_TIMER | TRIG_EXT);
|
||||
err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_begin_src,
|
||||
TRIG_TIMER | TRIG_EXT);
|
||||
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
|
||||
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
/* Step 2a : make sure trigger sources are unique */
|
||||
|
||||
err |= cfc_check_trigger_is_unique(cmd->start_src);
|
||||
err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->start_src);
|
||||
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
|
||||
|
||||
/* Step 2b : and mutually compatible */
|
||||
|
||||
|
@ -3340,11 +3340,11 @@ static int ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
|
||||
/* Step 3: check if arguments are trivially valid */
|
||||
|
||||
err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||
|
||||
if (cmd->scan_begin_src == TRIG_TIMER) {
|
||||
err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
thisboard->ao_scan_speed);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
|
||||
thisboard->ao_scan_speed);
|
||||
if (get_ao_divisor(cmd->scan_begin_arg, cmd->flags) >
|
||||
max_counter_value) {
|
||||
cmd->scan_begin_arg = (max_counter_value + 2) *
|
||||
|
@ -3353,8 +3353,9 @@ static int ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
|||
}
|
||||
}
|
||||
|
||||
err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
|
||||
err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
|
||||
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||
cmd->chanlist_len);
|
||||
|
||||
if (err)
|
||||
return 3;
|
||||
|
@ -3817,8 +3818,9 @@ static int setup_subdevices(struct comedi_device *dev)
|
|||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_bits = di_rbits;
|
||||
} else
|
||||
} else {
|
||||
s->type = COMEDI_SUBD_UNUSED;
|
||||
}
|
||||
|
||||
/* digital output */
|
||||
if (thisboard->layout == LAYOUT_64XX) {
|
||||
|
@ -3829,8 +3831,9 @@ static int setup_subdevices(struct comedi_device *dev)
|
|||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_bits = do_wbits;
|
||||
} else
|
||||
} else {
|
||||
s->type = COMEDI_SUBD_UNUSED;
|
||||
}
|
||||
|
||||
/* 8255 */
|
||||
s = &dev->subdevices[4];
|
||||
|
@ -3858,8 +3861,9 @@ static int setup_subdevices(struct comedi_device *dev)
|
|||
s->range_table = &range_digital;
|
||||
s->insn_config = dio_60xx_config_insn;
|
||||
s->insn_bits = dio_60xx_wbits;
|
||||
} else
|
||||
} else {
|
||||
s->type = COMEDI_SUBD_UNUSED;
|
||||
}
|
||||
|
||||
/* caldac */
|
||||
s = &dev->subdevices[6];
|
||||
|
@ -3898,8 +3902,9 @@ static int setup_subdevices(struct comedi_device *dev)
|
|||
ad8402_write(dev, i, s->maxdata / 2);
|
||||
s->readback[i] = s->maxdata / 2;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
s->type = COMEDI_SUBD_UNUSED;
|
||||
}
|
||||
|
||||
/* serial EEPROM, if present */
|
||||
s = &dev->subdevices[8];
|
||||
|
@ -3909,8 +3914,9 @@ static int setup_subdevices(struct comedi_device *dev)
|
|||
s->n_chan = 128;
|
||||
s->maxdata = 0xffff;
|
||||
s->insn_read = eeprom_read_insn;
|
||||
} else
|
||||
} else {
|
||||
s->type = COMEDI_SUBD_UNUSED;
|
||||
}
|
||||
|
||||
/* user counter subd XXX */
|
||||
s = &dev->subdevices[9];
|
||||
|
|
|
@ -36,11 +36,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "comedi_fc.h"
|
||||
#include "8255.h"
|
||||
|
||||
#define EEPROM_SIZE 128 /* number of entries in eeprom */
|
||||
|
|
|
@ -42,37 +42,112 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "comedi_8254.h"
|
||||
#include "plx9052.h"
|
||||
#include "8255.h"
|
||||
|
||||
/* Registers for the PCIM-DAS1602/16 and PCIe-DAS1602/16 */
|
||||
/*
|
||||
* PCI Bar 1 Register map
|
||||
* see plx9052.h for register and bit defines
|
||||
*/
|
||||
|
||||
/* DAC Offsets */
|
||||
#define ADC_TRIG 0
|
||||
#define DAC0_OFFSET 2
|
||||
#define DAC1_OFFSET 4
|
||||
/*
|
||||
* PCI Bar 2 Register map (devpriv->daqio)
|
||||
*/
|
||||
#define PCIMDAS_AI_REG 0x00
|
||||
#define PCIMDAS_AI_SOFTTRIG_REG 0x00
|
||||
#define PCIMDAS_AO_REG(x) (0x02 + ((x) * 2))
|
||||
|
||||
/* AI and Counter Constants */
|
||||
#define MUX_LIMITS 0
|
||||
#define MAIN_CONN_DIO 1
|
||||
#define ADC_STAT 2
|
||||
#define ADC_CONV_STAT 3
|
||||
#define ADC_INT 4
|
||||
#define ADC_PACER 5
|
||||
#define BURST_MODE 6
|
||||
#define PROG_GAIN 7
|
||||
#define CLK8254_1_DATA 8
|
||||
#define CLK8254_2_DATA 9
|
||||
#define CLK8254_3_DATA 10
|
||||
#define CLK8254_CONTROL 11
|
||||
#define USER_COUNTER 12
|
||||
#define RESID_COUNT_H 13
|
||||
#define RESID_COUNT_L 14
|
||||
/*
|
||||
* PCI Bar 3 Register map (devpriv->BADR3)
|
||||
*/
|
||||
#define PCIMDAS_MUX_REG 0x00
|
||||
#define PCIMDAS_MUX(_lo, _hi) ((_lo) | ((_hi) << 4))
|
||||
#define PCIMDAS_DI_DO_REG 0x01
|
||||
#define PCIMDAS_STATUS_REG 0x02
|
||||
#define PCIMDAS_STATUS_EOC BIT(7)
|
||||
#define PCIMDAS_STATUS_UB BIT(6)
|
||||
#define PCIMDAS_STATUS_MUX BIT(5)
|
||||
#define PCIMDAS_STATUS_CLK BIT(4)
|
||||
#define PCIMDAS_STATUS_TO_CURR_MUX(x) ((x) & 0xf)
|
||||
#define PCIMDAS_CONV_STATUS_REG 0x03
|
||||
#define PCIMDAS_CONV_STATUS_EOC BIT(7)
|
||||
#define PCIMDAS_CONV_STATUS_EOB BIT(6)
|
||||
#define PCIMDAS_CONV_STATUS_EOA BIT(5)
|
||||
#define PCIMDAS_CONV_STATUS_FNE BIT(4)
|
||||
#define PCIMDAS_CONV_STATUS_FHF BIT(3)
|
||||
#define PCIMDAS_CONV_STATUS_OVERRUN BIT(2)
|
||||
#define PCIMDAS_IRQ_REG 0x04
|
||||
#define PCIMDAS_IRQ_INTE BIT(7)
|
||||
#define PCIMDAS_IRQ_INT BIT(6)
|
||||
#define PCIMDAS_IRQ_OVERRUN BIT(4)
|
||||
#define PCIMDAS_IRQ_EOA BIT(3)
|
||||
#define PCIMDAS_IRQ_EOA_INT_SEL BIT(2)
|
||||
#define PCIMDAS_IRQ_INTSEL(x) ((x) << 0)
|
||||
#define PCIMDAS_IRQ_INTSEL_EOC PCIMDAS_IRQ_INTSEL(0)
|
||||
#define PCIMDAS_IRQ_INTSEL_FNE PCIMDAS_IRQ_INTSEL(1)
|
||||
#define PCIMDAS_IRQ_INTSEL_EOB PCIMDAS_IRQ_INTSEL(2)
|
||||
#define PCIMDAS_IRQ_INTSEL_FHF_EOA PCIMDAS_IRQ_INTSEL(3)
|
||||
#define PCIMDAS_PACER_REG 0x05
|
||||
#define PCIMDAS_PACER_GATE_STATUS BIT(6)
|
||||
#define PCIMDAS_PACER_GATE_POL BIT(5)
|
||||
#define PCIMDAS_PACER_GATE_LATCH BIT(4)
|
||||
#define PCIMDAS_PACER_GATE_EN BIT(3)
|
||||
#define PCIMDAS_PACER_EXT_PACER_POL BIT(2)
|
||||
#define PCIMDAS_PACER_SRC(x) ((x) << 0)
|
||||
#define PCIMDAS_PACER_SRC_POLLED PCIMDAS_PACER_SRC(0)
|
||||
#define PCIMDAS_PACER_SRC_EXT PCIMDAS_PACER_SRC(2)
|
||||
#define PCIMDAS_PACER_SRC_INT PCIMDAS_PACER_SRC(3)
|
||||
#define PCIMDAS_PACER_SRC_MASK (3 << 0)
|
||||
#define PCIMDAS_BURST_REG 0x06
|
||||
#define PCIMDAS_BURST_BME BIT(1)
|
||||
#define PCIMDAS_BURST_CONV_EN BIT(0)
|
||||
#define PCIMDAS_GAIN_REG 0x07
|
||||
#define PCIMDAS_8254_BASE 0x08
|
||||
#define PCIMDAS_USER_CNTR_REG 0x0c
|
||||
#define PCIMDAS_USER_CNTR_CTR1_CLK_SEL BIT(0)
|
||||
#define PCIMDAS_RESIDUE_MSB_REG 0x0d
|
||||
#define PCIMDAS_RESIDUE_LSB_REG 0x0e
|
||||
|
||||
/*
|
||||
* PCI Bar 4 Register map (dev->iobase)
|
||||
*/
|
||||
#define PCIMDAS_8255_BASE 0x00
|
||||
|
||||
static const struct comedi_lrange cb_pcimdas_ai_bip_range = {
|
||||
4, {
|
||||
BIP_RANGE(10),
|
||||
BIP_RANGE(5),
|
||||
BIP_RANGE(2.5),
|
||||
BIP_RANGE(1.25)
|
||||
}
|
||||
};
|
||||
|
||||
static const struct comedi_lrange cb_pcimdas_ai_uni_range = {
|
||||
4, {
|
||||
UNI_RANGE(10),
|
||||
UNI_RANGE(5),
|
||||
UNI_RANGE(2.5),
|
||||
UNI_RANGE(1.25)
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* The Analog Output range is not programmable. The DAC ranges are
|
||||
* jumper-settable on the board. The settings are not software-readable.
|
||||
*/
|
||||
static const struct comedi_lrange cb_pcimdas_ao_range = {
|
||||
4, {
|
||||
BIP_RANGE(10),
|
||||
BIP_RANGE(5),
|
||||
UNI_RANGE(10),
|
||||
UNI_RANGE(5)
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* this structure is for data unique to this hardware driver. If
|
||||
|
@ -94,59 +169,47 @@ static int cb_pcimdas_ai_eoc(struct comedi_device *dev,
|
|||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int status;
|
||||
|
||||
status = inb(devpriv->BADR3 + 2);
|
||||
if ((status & 0x80) == 0)
|
||||
status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
|
||||
if ((status & PCIMDAS_STATUS_EOC) == 0)
|
||||
return 0;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data)
|
||||
static int cb_pcimdas_ai_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned int range = CR_RANGE(insn->chanspec);
|
||||
int n;
|
||||
unsigned int d;
|
||||
int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned short chanlims;
|
||||
int maxchans;
|
||||
int ret;
|
||||
|
||||
/* only support sw initiated reads from a single channel */
|
||||
|
||||
/* check channel number */
|
||||
if ((inb(devpriv->BADR3 + 2) & 0x20) == 0) /* differential mode */
|
||||
maxchans = s->n_chan / 2;
|
||||
else
|
||||
maxchans = s->n_chan;
|
||||
|
||||
if (chan > (maxchans - 1))
|
||||
return -ETIMEDOUT; /* *** Wrong error code. Fixme. */
|
||||
|
||||
/* configure for sw initiated read */
|
||||
d = inb(devpriv->BADR3 + 5);
|
||||
if ((d & 0x03) > 0) { /* only reset if needed. */
|
||||
d = d & 0xfd;
|
||||
outb(d, devpriv->BADR3 + 5);
|
||||
d = inb(devpriv->BADR3 + PCIMDAS_PACER_REG);
|
||||
if ((d & PCIMDAS_PACER_SRC_MASK) != PCIMDAS_PACER_SRC_POLLED) {
|
||||
d &= ~PCIMDAS_PACER_SRC_MASK;
|
||||
d |= PCIMDAS_PACER_SRC_POLLED;
|
||||
outb(d, devpriv->BADR3 + PCIMDAS_PACER_REG);
|
||||
}
|
||||
|
||||
/* set bursting off, conversions on */
|
||||
outb(0x01, devpriv->BADR3 + 6);
|
||||
outb(PCIMDAS_BURST_CONV_EN, devpriv->BADR3 + PCIMDAS_BURST_REG);
|
||||
|
||||
/* set range to 10V. UP/BP is controlled by a switch on the board */
|
||||
outb(0x00, devpriv->BADR3 + 7);
|
||||
/* set range */
|
||||
outb(range, devpriv->BADR3 + PCIMDAS_GAIN_REG);
|
||||
|
||||
/*
|
||||
* write channel limits to multiplexer, set Low (bits 0-3) and
|
||||
* High (bits 4-7) channels to chan.
|
||||
*/
|
||||
chanlims = chan | (chan << 4);
|
||||
outb(chanlims, devpriv->BADR3 + 0);
|
||||
/* set mux for single channel scan */
|
||||
outb(PCIMDAS_MUX(chan, chan), devpriv->BADR3 + PCIMDAS_MUX_REG);
|
||||
|
||||
/* convert n samples */
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
/* trigger conversion */
|
||||
outw(0, devpriv->daqio + 0);
|
||||
outw(0, devpriv->daqio + PCIMDAS_AI_SOFTTRIG_REG);
|
||||
|
||||
/* wait for conversion to end */
|
||||
ret = comedi_timeout(dev, s, insn, cb_pcimdas_ai_eoc, 0);
|
||||
|
@ -154,7 +217,7 @@ static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
|
|||
return ret;
|
||||
|
||||
/* read data */
|
||||
data[n] = inw(devpriv->daqio + 0);
|
||||
data[n] = inw(devpriv->daqio + PCIMDAS_AI_REG);
|
||||
}
|
||||
|
||||
/* return the number of samples read/written */
|
||||
|
@ -169,20 +232,128 @@ static int cb_pcimdas_ao_insn_write(struct comedi_device *dev,
|
|||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned int val = s->readback[chan];
|
||||
unsigned int reg = (chan) ? DAC1_OFFSET : DAC0_OFFSET;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < insn->n; i++) {
|
||||
val = data[i];
|
||||
outw(val, devpriv->daqio + reg);
|
||||
outw(val, devpriv->daqio + PCIMDAS_AO_REG(chan));
|
||||
}
|
||||
s->readback[chan] = val;
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int cb_pcimdas_di_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int val;
|
||||
|
||||
val = inb(devpriv->BADR3 + PCIMDAS_DI_DO_REG);
|
||||
|
||||
data[1] = val & 0x0f;
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int cb_pcimdas_do_insn_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
|
||||
if (comedi_dio_update_state(s, data))
|
||||
outb(s->state, devpriv->BADR3 + PCIMDAS_DI_DO_REG);
|
||||
|
||||
data[1] = s->state;
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int cb_pcimdas_counter_insn_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int ctrl;
|
||||
|
||||
switch (data[0]) {
|
||||
case INSN_CONFIG_SET_CLOCK_SRC:
|
||||
switch (data[1]) {
|
||||
case 0: /* internal 100 kHz clock */
|
||||
ctrl = PCIMDAS_USER_CNTR_CTR1_CLK_SEL;
|
||||
break;
|
||||
case 1: /* external clk on pin 21 */
|
||||
ctrl = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
outb(ctrl, devpriv->BADR3 + PCIMDAS_USER_CNTR_REG);
|
||||
break;
|
||||
case INSN_CONFIG_GET_CLOCK_SRC:
|
||||
ctrl = inb(devpriv->BADR3 + PCIMDAS_USER_CNTR_REG);
|
||||
if (ctrl & PCIMDAS_USER_CNTR_CTR1_CLK_SEL) {
|
||||
data[1] = 0;
|
||||
data[2] = I8254_OSC_BASE_100KHZ;
|
||||
} else {
|
||||
data[1] = 1;
|
||||
data[2] = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static unsigned int cb_pcimdas_pacer_clk(struct comedi_device *dev)
|
||||
{
|
||||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int status;
|
||||
|
||||
/* The Pacer Clock jumper selects a 10 MHz or 1 MHz clock */
|
||||
status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
|
||||
if (status & PCIMDAS_STATUS_CLK)
|
||||
return I8254_OSC_BASE_10MHZ;
|
||||
return I8254_OSC_BASE_1MHZ;
|
||||
}
|
||||
|
||||
static bool cb_pcimdas_is_ai_se(struct comedi_device *dev)
|
||||
{
|
||||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int status;
|
||||
|
||||
/*
|
||||
* The number of Analog Input channels is set with the
|
||||
* Analog Input Mode Switch on the board. The board can
|
||||
* have 16 single-ended or 8 differential channels.
|
||||
*/
|
||||
status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
|
||||
return status & PCIMDAS_STATUS_MUX;
|
||||
}
|
||||
|
||||
static bool cb_pcimdas_is_ai_uni(struct comedi_device *dev)
|
||||
{
|
||||
struct cb_pcimdas_private *devpriv = dev->private;
|
||||
unsigned int status;
|
||||
|
||||
/*
|
||||
* The Analog Input range polarity is set with the
|
||||
* Analog Input Polarity Switch on the board. The
|
||||
* inputs can be set to Unipolar or Bipolar ranges.
|
||||
*/
|
||||
status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
|
||||
return status & PCIMDAS_STATUS_UB;
|
||||
}
|
||||
|
||||
static int cb_pcimdas_auto_attach(struct comedi_device *dev,
|
||||
unsigned long context_unused)
|
||||
unsigned long context_unused)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
struct cb_pcimdas_private *devpriv;
|
||||
|
@ -201,42 +372,79 @@ static int cb_pcimdas_auto_attach(struct comedi_device *dev,
|
|||
devpriv->BADR3 = pci_resource_start(pcidev, 3);
|
||||
dev->iobase = pci_resource_start(pcidev, 4);
|
||||
|
||||
ret = comedi_alloc_subdevices(dev, 3);
|
||||
dev->pacer = comedi_8254_init(devpriv->BADR3 + PCIMDAS_8254_BASE,
|
||||
cb_pcimdas_pacer_clk(dev),
|
||||
I8254_IO8, 0);
|
||||
if (!dev->pacer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = comedi_alloc_subdevices(dev, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Analog Input subdevice */
|
||||
s = &dev->subdevices[0];
|
||||
/* dev->read_subdev=s; */
|
||||
/* analog input subdevice */
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->subdev_flags = SDF_READABLE | SDF_GROUND;
|
||||
s->n_chan = 16;
|
||||
s->maxdata = 0xffff;
|
||||
s->range_table = &range_unknown;
|
||||
s->len_chanlist = 1; /* This is the maximum chanlist length that */
|
||||
/* the board can handle */
|
||||
s->insn_read = cb_pcimdas_ai_rinsn;
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->subdev_flags = SDF_READABLE;
|
||||
if (cb_pcimdas_is_ai_se(dev)) {
|
||||
s->subdev_flags |= SDF_GROUND;
|
||||
s->n_chan = 16;
|
||||
} else {
|
||||
s->subdev_flags |= SDF_DIFF;
|
||||
s->n_chan = 8;
|
||||
}
|
||||
s->maxdata = 0xffff;
|
||||
s->range_table = cb_pcimdas_is_ai_uni(dev) ? &cb_pcimdas_ai_uni_range
|
||||
: &cb_pcimdas_ai_bip_range;
|
||||
s->insn_read = cb_pcimdas_ai_insn_read;
|
||||
|
||||
/* Analog Output subdevice */
|
||||
s = &dev->subdevices[1];
|
||||
/* analog output subdevice */
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 2;
|
||||
s->maxdata = 0xfff;
|
||||
/* ranges are hardware settable, but not software readable. */
|
||||
s->range_table = &range_unknown;
|
||||
s->insn_write = cb_pcimdas_ao_insn_write;
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 2;
|
||||
s->maxdata = 0xfff;
|
||||
s->range_table = &cb_pcimdas_ao_range;
|
||||
s->insn_write = cb_pcimdas_ao_insn_write;
|
||||
|
||||
ret = comedi_alloc_subdev_readback(s);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Digital I/O subdevice */
|
||||
s = &dev->subdevices[2];
|
||||
/* digital i/o subdevice */
|
||||
ret = subdev_8255_init(dev, s, NULL, 0x00);
|
||||
ret = subdev_8255_init(dev, s, NULL, PCIMDAS_8255_BASE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Digital Input subdevice (main connector) */
|
||||
s = &dev->subdevices[3];
|
||||
s->type = COMEDI_SUBD_DI;
|
||||
s->subdev_flags = SDF_READABLE;
|
||||
s->n_chan = 4;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_read = cb_pcimdas_di_insn_read;
|
||||
|
||||
/* Digital Output subdevice (main connector) */
|
||||
s = &dev->subdevices[4];
|
||||
s->type = COMEDI_SUBD_DO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 4;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_write = cb_pcimdas_do_insn_write;
|
||||
|
||||
/* Counter subdevice (8254) */
|
||||
s = &dev->subdevices[5];
|
||||
comedi_8254_subdevice_init(s, dev->pacer);
|
||||
|
||||
dev->pacer->insn_config = cb_pcimdas_counter_insn_config;
|
||||
|
||||
/* counters 1 and 2 are used internally for the pacer */
|
||||
comedi_8254_set_busy(dev->pacer, 1, true);
|
||||
comedi_8254_set_busy(dev->pacer, 2, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,9 +75,8 @@ Configuration Options: not applicable, uses PCI auto config
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "8255.h"
|
||||
|
||||
|
@ -134,7 +133,7 @@ static int cb_pcimdda_ao_insn_read(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
static int cb_pcimdda_auto_attach(struct comedi_device *dev,
|
||||
unsigned long context_unused)
|
||||
unsigned long context_unused)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
struct comedi_subdevice *s;
|
||||
|
|
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
* comedi_8254.c
|
||||
* Generic 8254 timer/counter support
|
||||
* Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
*
|
||||
* Based on 8253.h and various subdevice implementations in comedi drivers.
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 2000 David A. Schleef <ds@schleef.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Module: comedi_8254
|
||||
* Description: Generic 8254 timer/counter support
|
||||
* Author: H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
* Updated: Thu Jan 8 16:45:45 MST 2015
|
||||
* Status: works
|
||||
*
|
||||
* This module is not used directly by end-users. Rather, it is used by other
|
||||
* drivers to provide support for an 8254 Programmable Interval Timer. These
|
||||
* counters are typically used to generate the pacer clock used for data
|
||||
* acquisition. Some drivers also expose the counters for general purpose use.
|
||||
*
|
||||
* This module provides the following basic functions:
|
||||
*
|
||||
* comedi_8254_init() / comedi_8254_mm_init()
|
||||
* Initializes this module to access the 8254 registers. The _mm version
|
||||
* sets up the module for MMIO register access the other for PIO access.
|
||||
* The pointer returned from these functions is normally stored in the
|
||||
* comedi_device dev->pacer and will be freed by the comedi core during
|
||||
* the driver (*detach). If a driver has multiple 8254 devices, they need
|
||||
* to be stored in the drivers private data and freed when the driver is
|
||||
* detached.
|
||||
*
|
||||
* NOTE: The counters are reset by setting them to I8254_MODE0 as part of
|
||||
* this initialization.
|
||||
*
|
||||
* comedi_8254_set_mode()
|
||||
* Sets a counters operation mode:
|
||||
* I8254_MODE0 Interrupt on terminal count
|
||||
* I8254_MODE1 Hardware retriggerable one-shot
|
||||
* I8254_MODE2 Rate generator
|
||||
* I8254_MODE3 Square wave mode
|
||||
* I8254_MODE4 Software triggered strobe
|
||||
* I8254_MODE5 Hardware triggered strobe (retriggerable)
|
||||
*
|
||||
* In addition I8254_BCD and I8254_BINARY specify the counting mode:
|
||||
* I8254_BCD BCD counting
|
||||
* I8254_BINARY Binary counting
|
||||
*
|
||||
* comedi_8254_write()
|
||||
* Writes an initial value to a counter.
|
||||
*
|
||||
* The largest possible initial count is 0; this is equivalent to 2^16
|
||||
* for binary counting and 10^4 for BCD counting.
|
||||
*
|
||||
* NOTE: The counter does not stop when it reaches zero. In Mode 0, 1, 4,
|
||||
* and 5 the counter "wraps around" to the highest count, either 0xffff
|
||||
* for binary counting or 9999 for BCD counting, and continues counting.
|
||||
* Modes 2 and 3 are periodic; the counter reloads itself with the initial
|
||||
* count and continues counting from there.
|
||||
*
|
||||
* comedi_8254_read()
|
||||
* Reads the current value from a counter.
|
||||
*
|
||||
* comedi_8254_status()
|
||||
* Reads the status of a counter.
|
||||
*
|
||||
* comedi_8254_load()
|
||||
* Sets a counters operation mode and writes the initial value.
|
||||
*
|
||||
* Typically the pacer clock is created by cascading two of the 16-bit counters
|
||||
* to create a 32-bit rate generator (I8254_MODE2). These functions are
|
||||
* provided to handle the cascaded counters:
|
||||
*
|
||||
* comedi_8254_ns_to_timer()
|
||||
* Calculates the divisor value needed for a single counter to generate
|
||||
* ns timing.
|
||||
*
|
||||
* comedi_8254_cascade_ns_to_timer()
|
||||
* Calculates the two divisor values needed to the generate the pacer
|
||||
* clock (in ns).
|
||||
*
|
||||
* comedi_8254_update_divisors()
|
||||
* Transfers the intermediate divisor values to the current divisors.
|
||||
*
|
||||
* comedi_8254_pacer_enable()
|
||||
* Programs the mode of the cascaded counters and writes the current
|
||||
* divisor values.
|
||||
*
|
||||
* To expose the counters as a subdevice for general purpose use the following
|
||||
* functions a provided:
|
||||
*
|
||||
* comedi_8254_subdevice_init()
|
||||
* Initializes a comedi_subdevice to use the 8254 timer.
|
||||
*
|
||||
* comedi_8254_set_busy()
|
||||
* Internally flags a counter as "busy". This is done to protect the
|
||||
* counters that are used for the cascaded 32-bit pacer.
|
||||
*
|
||||
* The subdevice provides (*insn_read) and (*insn_write) operations to read
|
||||
* the current value and write an initial value to a counter. A (*insn_config)
|
||||
* operation is also provided to handle the following comedi instructions:
|
||||
*
|
||||
* INSN_CONFIG_SET_COUNTER_MODE calls comedi_8254_set_mode()
|
||||
* INSN_CONFIG_8254_READ_STATUS calls comedi_8254_status()
|
||||
*
|
||||
* The (*insn_config) member of comedi_8254 can be initialized by the external
|
||||
* driver to handle any additional instructions.
|
||||
*
|
||||
* NOTE: Gate control, clock routing, and any interrupt handling for the
|
||||
* counters is not handled by this module. These features are driver dependent.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "../comedidev.h"
|
||||
|
||||
#include "comedi_8254.h"
|
||||
|
||||
static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg)
|
||||
{
|
||||
unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
|
||||
unsigned int val;
|
||||
|
||||
switch (i8254->iosize) {
|
||||
default:
|
||||
case I8254_IO8:
|
||||
if (i8254->mmio)
|
||||
val = readb(i8254->mmio + reg_offset);
|
||||
else
|
||||
val = inb(i8254->iobase + reg_offset);
|
||||
break;
|
||||
case I8254_IO16:
|
||||
if (i8254->mmio)
|
||||
val = readw(i8254->mmio + reg_offset);
|
||||
else
|
||||
val = inw(i8254->iobase + reg_offset);
|
||||
break;
|
||||
case I8254_IO32:
|
||||
if (i8254->mmio)
|
||||
val = readl(i8254->mmio + reg_offset);
|
||||
else
|
||||
val = inl(i8254->iobase + reg_offset);
|
||||
break;
|
||||
}
|
||||
return val & 0xff;
|
||||
}
|
||||
|
||||
static void __i8254_write(struct comedi_8254 *i8254,
|
||||
unsigned int val, unsigned int reg)
|
||||
{
|
||||
unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
|
||||
|
||||
switch (i8254->iosize) {
|
||||
default:
|
||||
case I8254_IO8:
|
||||
if (i8254->mmio)
|
||||
writeb(val, i8254->mmio + reg_offset);
|
||||
else
|
||||
outb(val, i8254->iobase + reg_offset);
|
||||
break;
|
||||
case I8254_IO16:
|
||||
if (i8254->mmio)
|
||||
writew(val, i8254->mmio + reg_offset);
|
||||
else
|
||||
outw(val, i8254->iobase + reg_offset);
|
||||
break;
|
||||
case I8254_IO32:
|
||||
if (i8254->mmio)
|
||||
writel(val, i8254->mmio + reg_offset);
|
||||
else
|
||||
outl(val, i8254->iobase + reg_offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_8254_status - return the status of a counter
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @counter: the counter number
|
||||
*/
|
||||
unsigned int comedi_8254_status(struct comedi_8254 *i8254, unsigned int counter)
|
||||
{
|
||||
unsigned int cmd;
|
||||
|
||||
if (counter > 2)
|
||||
return 0;
|
||||
|
||||
cmd = I8254_CTRL_READBACK_STATUS | I8254_CTRL_READBACK_SEL_CTR(counter);
|
||||
__i8254_write(i8254, cmd, I8254_CTRL_REG);
|
||||
|
||||
return __i8254_read(i8254, counter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_status);
|
||||
|
||||
/**
|
||||
* comedi_8254_read - read the current counter value
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @counter: the counter number
|
||||
*/
|
||||
unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (counter > 2)
|
||||
return 0;
|
||||
|
||||
/* latch counter */
|
||||
__i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
|
||||
I8254_CTRL_REG);
|
||||
|
||||
/* read LSB then MSB */
|
||||
val = __i8254_read(i8254, counter);
|
||||
val |= (__i8254_read(i8254, counter) << 8);
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_read);
|
||||
|
||||
/**
|
||||
* comedi_8254_write - load a 16-bit initial counter value
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @counter: the counter number
|
||||
* @val: the initial value
|
||||
*/
|
||||
void comedi_8254_write(struct comedi_8254 *i8254,
|
||||
unsigned int counter, unsigned int val)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter > 2)
|
||||
return;
|
||||
if (val > 0xffff)
|
||||
return;
|
||||
|
||||
/* load LSB then MSB */
|
||||
byte = val & 0xff;
|
||||
__i8254_write(i8254, byte, counter);
|
||||
byte = (val >> 8) & 0xff;
|
||||
__i8254_write(i8254, byte, counter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_write);
|
||||
|
||||
/**
|
||||
* comedi_8254_set_mode - set the mode of a counter
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @counter: the counter number
|
||||
* @mode: the I8254_MODEx and I8254_BCD|I8254_BINARY
|
||||
*/
|
||||
int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter,
|
||||
unsigned int mode)
|
||||
{
|
||||
unsigned int byte;
|
||||
|
||||
if (counter > 2)
|
||||
return -EINVAL;
|
||||
if (mode > (I8254_MODE5 | I8254_BCD))
|
||||
return -EINVAL;
|
||||
|
||||
byte = I8254_CTRL_SEL_CTR(counter) | /* select counter */
|
||||
I8254_CTRL_LSB_MSB | /* load LSB then MSB */
|
||||
mode; /* mode and BCD|binary */
|
||||
__i8254_write(i8254, byte, I8254_CTRL_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
|
||||
|
||||
/**
|
||||
* comedi_8254_load - program the mode and initial count of a counter
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @counter: the counter number
|
||||
* @mode: the I8254_MODEx and I8254_BCD|I8254_BINARY
|
||||
* @val: the initial value
|
||||
*/
|
||||
int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter,
|
||||
unsigned int val, unsigned int mode)
|
||||
{
|
||||
if (counter > 2)
|
||||
return -EINVAL;
|
||||
if (val > 0xffff)
|
||||
return -EINVAL;
|
||||
if (mode > (I8254_MODE5 | I8254_BCD))
|
||||
return -EINVAL;
|
||||
|
||||
comedi_8254_set_mode(i8254, counter, mode);
|
||||
comedi_8254_write(i8254, counter, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_load);
|
||||
|
||||
/**
|
||||
* comedi_8254_pacer_enable - set the mode and load the cascaded counters
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @counter1: the counter number for the first divisor
|
||||
* @counter2: the counter number for the second divisor
|
||||
* @enable: flag to enable (load) the counters
|
||||
*/
|
||||
void comedi_8254_pacer_enable(struct comedi_8254 *i8254,
|
||||
unsigned int counter1,
|
||||
unsigned int counter2,
|
||||
bool enable)
|
||||
{
|
||||
unsigned int mode;
|
||||
|
||||
if (counter1 > 2 || counter2 > 2 || counter1 == counter2)
|
||||
return;
|
||||
|
||||
if (enable)
|
||||
mode = I8254_MODE2 | I8254_BINARY;
|
||||
else
|
||||
mode = I8254_MODE0 | I8254_BINARY;
|
||||
|
||||
comedi_8254_set_mode(i8254, counter1, mode);
|
||||
comedi_8254_set_mode(i8254, counter2, mode);
|
||||
|
||||
if (enable) {
|
||||
/*
|
||||
* Divisors are loaded second counter then first counter to
|
||||
* avoid possible issues with the first counter expiring
|
||||
* before the second counter is loaded.
|
||||
*/
|
||||
comedi_8254_write(i8254, counter2, i8254->divisor2);
|
||||
comedi_8254_write(i8254, counter1, i8254->divisor1);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
|
||||
|
||||
/**
|
||||
* comedi_8254_update_divisors - update the divisors for the cascaded counters
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
*/
|
||||
void comedi_8254_update_divisors(struct comedi_8254 *i8254)
|
||||
{
|
||||
/* masking is done since counter maps zero to 0x10000 */
|
||||
i8254->divisor = i8254->next_div & 0xffff;
|
||||
i8254->divisor1 = i8254->next_div1 & 0xffff;
|
||||
i8254->divisor2 = i8254->next_div2 & 0xffff;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
|
||||
|
||||
/**
|
||||
* comedi_8254_cascade_ns_to_timer - calculate the cascaded divisor values
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @nanosec: the desired ns time
|
||||
* @flags: comedi_cmd flags
|
||||
*/
|
||||
void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254,
|
||||
unsigned int *nanosec,
|
||||
unsigned int flags)
|
||||
{
|
||||
unsigned int d1 = i8254->next_div1 ? i8254->next_div1 : I8254_MAX_COUNT;
|
||||
unsigned int d2 = i8254->next_div2 ? i8254->next_div2 : I8254_MAX_COUNT;
|
||||
unsigned int div = d1 * d2;
|
||||
unsigned int ns_lub = 0xffffffff;
|
||||
unsigned int ns_glb = 0;
|
||||
unsigned int d1_lub = 0;
|
||||
unsigned int d1_glb = 0;
|
||||
unsigned int d2_lub = 0;
|
||||
unsigned int d2_glb = 0;
|
||||
unsigned int start;
|
||||
unsigned int ns;
|
||||
unsigned int ns_low;
|
||||
unsigned int ns_high;
|
||||
|
||||
/* exit early if everything is already correct */
|
||||
if (div * i8254->osc_base == *nanosec &&
|
||||
d1 > 1 && d1 <= I8254_MAX_COUNT &&
|
||||
d2 > 1 && d2 <= I8254_MAX_COUNT &&
|
||||
/* check for overflow */
|
||||
div > d1 && div > d2 &&
|
||||
div * i8254->osc_base > div &&
|
||||
div * i8254->osc_base > i8254->osc_base)
|
||||
return;
|
||||
|
||||
div = *nanosec / i8254->osc_base;
|
||||
d2 = I8254_MAX_COUNT;
|
||||
start = div / d2;
|
||||
if (start < 2)
|
||||
start = 2;
|
||||
for (d1 = start; d1 <= div / d1 + 1 && d1 <= I8254_MAX_COUNT; d1++) {
|
||||
for (d2 = div / d1;
|
||||
d1 * d2 <= div + d1 + 1 && d2 <= I8254_MAX_COUNT; d2++) {
|
||||
ns = i8254->osc_base * d1 * d2;
|
||||
if (ns <= *nanosec && ns > ns_glb) {
|
||||
ns_glb = ns;
|
||||
d1_glb = d1;
|
||||
d2_glb = d2;
|
||||
}
|
||||
if (ns >= *nanosec && ns < ns_lub) {
|
||||
ns_lub = ns;
|
||||
d1_lub = d1;
|
||||
d2_lub = d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (flags & CMDF_ROUND_MASK) {
|
||||
case CMDF_ROUND_NEAREST:
|
||||
default:
|
||||
ns_high = d1_lub * d2_lub * i8254->osc_base;
|
||||
ns_low = d1_glb * d2_glb * i8254->osc_base;
|
||||
if (ns_high - *nanosec < *nanosec - ns_low) {
|
||||
d1 = d1_lub;
|
||||
d2 = d2_lub;
|
||||
} else {
|
||||
d1 = d1_glb;
|
||||
d2 = d2_glb;
|
||||
}
|
||||
break;
|
||||
case CMDF_ROUND_UP:
|
||||
d1 = d1_lub;
|
||||
d2 = d2_lub;
|
||||
break;
|
||||
case CMDF_ROUND_DOWN:
|
||||
d1 = d1_glb;
|
||||
d2 = d2_glb;
|
||||
break;
|
||||
}
|
||||
|
||||
*nanosec = d1 * d2 * i8254->osc_base;
|
||||
i8254->next_div1 = d1;
|
||||
i8254->next_div2 = d2;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
|
||||
|
||||
/**
|
||||
* comedi_8254_ns_to_timer - calculate the divisor value for nanosec timing
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @nanosec: the desired ns time
|
||||
* @flags: comedi_cmd flags
|
||||
*/
|
||||
void comedi_8254_ns_to_timer(struct comedi_8254 *i8254,
|
||||
unsigned int *nanosec, unsigned int flags)
|
||||
{
|
||||
unsigned int divisor;
|
||||
|
||||
switch (flags & CMDF_ROUND_MASK) {
|
||||
default:
|
||||
case CMDF_ROUND_NEAREST:
|
||||
divisor = DIV_ROUND_CLOSEST(*nanosec, i8254->osc_base);
|
||||
break;
|
||||
case CMDF_ROUND_UP:
|
||||
divisor = DIV_ROUND_UP(*nanosec, i8254->osc_base);
|
||||
break;
|
||||
case CMDF_ROUND_DOWN:
|
||||
divisor = *nanosec / i8254->osc_base;
|
||||
break;
|
||||
}
|
||||
if (divisor < 2)
|
||||
divisor = 2;
|
||||
if (divisor > I8254_MAX_COUNT)
|
||||
divisor = I8254_MAX_COUNT;
|
||||
|
||||
*nanosec = divisor * i8254->osc_base;
|
||||
i8254->next_div = divisor;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
|
||||
|
||||
/**
|
||||
* comedi_8254_set_busy - set/clear the "busy" flag for a given counter
|
||||
* @i8254: comedi_8254 struct for the timer
|
||||
* @counter: the counter number
|
||||
* @busy: set/clear flag
|
||||
*/
|
||||
void comedi_8254_set_busy(struct comedi_8254 *i8254,
|
||||
unsigned int counter, bool busy)
|
||||
{
|
||||
if (counter < 3)
|
||||
i8254->busy[counter] = busy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
|
||||
|
||||
static int comedi_8254_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct comedi_8254 *i8254 = s->private;
|
||||
unsigned int chan = CR_CHAN(insn->chanspec);
|
||||
int i;
|
||||
|
||||
if (i8254->busy[chan])
|
||||
return -EBUSY;
|
||||
|
||||
for (i = 0; i < insn->n; i++)
|
||||
data[i] = comedi_8254_read(i8254, chan);
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int comedi_8254_insn_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct comedi_8254 *i8254 = s->private;
|
||||
unsigned int chan = CR_CHAN(insn->chanspec);
|
||||
|
||||
if (i8254->busy[chan])
|
||||
return -EBUSY;
|
||||
|
||||
if (insn->n)
|
||||
comedi_8254_write(i8254, chan, data[insn->n - 1]);
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int comedi_8254_insn_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct comedi_8254 *i8254 = s->private;
|
||||
unsigned int chan = CR_CHAN(insn->chanspec);
|
||||
int ret;
|
||||
|
||||
if (i8254->busy[chan])
|
||||
return -EBUSY;
|
||||
|
||||
switch (data[0]) {
|
||||
case INSN_CONFIG_RESET:
|
||||
ret = comedi_8254_set_mode(i8254, chan,
|
||||
I8254_MODE0 | I8254_BINARY);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case INSN_CONFIG_SET_COUNTER_MODE:
|
||||
ret = comedi_8254_set_mode(i8254, chan, data[1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case INSN_CONFIG_8254_READ_STATUS:
|
||||
data[1] = comedi_8254_status(i8254, chan);
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* If available, call the driver provided (*insn_config)
|
||||
* to handle any driver implemented instructions.
|
||||
*/
|
||||
if (i8254->insn_config)
|
||||
return i8254->insn_config(dev, s, insn, data);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
|
||||
* @s: comedi_subdevice struct
|
||||
*/
|
||||
void comedi_8254_subdevice_init(struct comedi_subdevice *s,
|
||||
struct comedi_8254 *i8254)
|
||||
{
|
||||
s->type = COMEDI_SUBD_COUNTER;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
s->n_chan = 3;
|
||||
s->maxdata = 0xffff;
|
||||
s->range_table = &range_unknown;
|
||||
s->insn_read = comedi_8254_insn_read;
|
||||
s->insn_write = comedi_8254_insn_write;
|
||||
s->insn_config = comedi_8254_insn_config;
|
||||
|
||||
s->private = i8254;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
|
||||
|
||||
static struct comedi_8254 *__i8254_init(unsigned long iobase,
|
||||
void __iomem *mmio,
|
||||
unsigned int osc_base,
|
||||
unsigned int iosize,
|
||||
unsigned int regshift)
|
||||
{
|
||||
struct comedi_8254 *i8254;
|
||||
int i;
|
||||
|
||||
/* sanity check that the iosize is valid */
|
||||
if (!(iosize == I8254_IO8 || iosize == I8254_IO16 ||
|
||||
iosize == I8254_IO32))
|
||||
return NULL;
|
||||
|
||||
i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL);
|
||||
if (!i8254)
|
||||
return NULL;
|
||||
|
||||
i8254->iobase = iobase;
|
||||
i8254->mmio = mmio;
|
||||
i8254->iosize = iosize;
|
||||
i8254->regshift = regshift;
|
||||
|
||||
/* default osc_base to the max speed of a generic 8254 timer */
|
||||
i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
|
||||
|
||||
/* reset all the counters by setting them to I8254_MODE0 */
|
||||
for (i = 0; i < 3; i++)
|
||||
comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY);
|
||||
|
||||
return i8254;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_8254_init - allocate and initialize the 8254 device for pio access
|
||||
* @mmio: port I/O base address
|
||||
* @osc_base: base time of the counter in ns
|
||||
* OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
|
||||
* @iosize: I/O register size
|
||||
* @regshift: register gap shift
|
||||
*/
|
||||
struct comedi_8254 *comedi_8254_init(unsigned long iobase,
|
||||
unsigned int osc_base,
|
||||
unsigned int iosize,
|
||||
unsigned int regshift)
|
||||
{
|
||||
return __i8254_init(iobase, NULL, osc_base, iosize, regshift);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_init);
|
||||
|
||||
/**
|
||||
* comedi_8254_mm_init - allocate and initialize the 8254 device for mmio access
|
||||
* @mmio: memory mapped I/O base address
|
||||
* @osc_base: base time of the counter in ns
|
||||
* OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
|
||||
* @iosize: I/O register size
|
||||
* @regshift: register gap shift
|
||||
*/
|
||||
struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
|
||||
unsigned int osc_base,
|
||||
unsigned int iosize,
|
||||
unsigned int regshift)
|
||||
{
|
||||
return __i8254_init(0, mmio, osc_base, iosize, regshift);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_8254_mm_init);
|
||||
|
||||
static int __init comedi_8254_module_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
module_init(comedi_8254_module_init);
|
||||
|
||||
static void __exit comedi_8254_module_exit(void)
|
||||
{
|
||||
}
|
||||
module_exit(comedi_8254_module_exit);
|
||||
|
||||
MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
|
||||
MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* comedi_8254.h
|
||||
* Generic 8254 timer/counter support
|
||||
* Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 2000 David A. Schleef <ds@schleef.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _COMEDI_8254_H
|
||||
#define _COMEDI_8254_H
|
||||
|
||||
/*
|
||||
* Common oscillator base values in nanoseconds
|
||||
*/
|
||||
#define I8254_OSC_BASE_10MHZ 100
|
||||
#define I8254_OSC_BASE_5MHZ 200
|
||||
#define I8254_OSC_BASE_4MHZ 250
|
||||
#define I8254_OSC_BASE_2MHZ 500
|
||||
#define I8254_OSC_BASE_1MHZ 1000
|
||||
#define I8254_OSC_BASE_100KHZ 10000
|
||||
#define I8254_OSC_BASE_10KHZ 100000
|
||||
#define I8254_OSC_BASE_1KHZ 1000000
|
||||
|
||||
/*
|
||||
* I/O access size used to read/write registers
|
||||
*/
|
||||
#define I8254_IO8 1
|
||||
#define I8254_IO16 2
|
||||
#define I8254_IO32 4
|
||||
|
||||
/*
|
||||
* Register map for generic 8254 timer (I8254_IO8 with 0 regshift)
|
||||
*/
|
||||
#define I8254_COUNTER0_REG 0x00
|
||||
#define I8254_COUNTER1_REG 0x01
|
||||
#define I8254_COUNTER2_REG 0x02
|
||||
#define I8254_CTRL_REG 0x03
|
||||
#define I8254_CTRL_SEL_CTR(x) ((x) << 6)
|
||||
#define I8254_CTRL_READBACK_COUNT ((3 << 6) | (1 << 4))
|
||||
#define I8254_CTRL_READBACK_STATUS ((3 << 6) | (1 << 5))
|
||||
#define I8254_CTRL_READBACK_SEL_CTR(x) (2 << (x))
|
||||
#define I8254_CTRL_LATCH (0 << 4)
|
||||
#define I8254_CTRL_LSB_ONLY (1 << 4)
|
||||
#define I8254_CTRL_MSB_ONLY (2 << 4)
|
||||
#define I8254_CTRL_LSB_MSB (3 << 4)
|
||||
|
||||
/* counter maps zero to 0x10000 */
|
||||
#define I8254_MAX_COUNT 0x10000
|
||||
|
||||
/**
|
||||
* struct comedi_8254 - private data used by this module
|
||||
* @iobase: PIO base address of the registers (in/out)
|
||||
* @mmio: MMIO base address of the registers (read/write)
|
||||
* @iosize: I/O size used to access the registers (b/w/l)
|
||||
* @regshift: register gap shift
|
||||
* @osc_base: cascaded oscillator speed in ns
|
||||
* @divisor: divisor for single counter
|
||||
* @divisor1: divisor loaded into first cascaded counter
|
||||
* @divisor2: divisor loaded into second cascaded counter
|
||||
* #next_div: next divisor for single counter
|
||||
* @next_div1: next divisor to use for first cascaded counter
|
||||
* @next_div2: next divisor to use for second cascaded counter
|
||||
* @clock_src; current clock source for each counter (driver specific)
|
||||
* @gate_src; current gate source for each counter (driver specific)
|
||||
* @busy: flags used to indicate that a counter is "busy"
|
||||
* @insn_config: driver specific (*insn_config) callback
|
||||
*/
|
||||
struct comedi_8254 {
|
||||
unsigned long iobase;
|
||||
void __iomem *mmio;
|
||||
unsigned int iosize;
|
||||
unsigned int regshift;
|
||||
unsigned int osc_base;
|
||||
unsigned int divisor;
|
||||
unsigned int divisor1;
|
||||
unsigned int divisor2;
|
||||
unsigned int next_div;
|
||||
unsigned int next_div1;
|
||||
unsigned int next_div2;
|
||||
unsigned int clock_src[3];
|
||||
unsigned int gate_src[3];
|
||||
bool busy[3];
|
||||
|
||||
int (*insn_config)(struct comedi_device *, struct comedi_subdevice *s,
|
||||
struct comedi_insn *, unsigned int *data);
|
||||
};
|
||||
|
||||
unsigned int comedi_8254_status(struct comedi_8254 *, unsigned int counter);
|
||||
unsigned int comedi_8254_read(struct comedi_8254 *, unsigned int counter);
|
||||
void comedi_8254_write(struct comedi_8254 *,
|
||||
unsigned int counter, unsigned int val);
|
||||
|
||||
int comedi_8254_set_mode(struct comedi_8254 *,
|
||||
unsigned int counter, unsigned int mode);
|
||||
int comedi_8254_load(struct comedi_8254 *,
|
||||
unsigned int counter, unsigned int val, unsigned int mode);
|
||||
|
||||
void comedi_8254_pacer_enable(struct comedi_8254 *,
|
||||
unsigned int counter1, unsigned int counter2,
|
||||
bool enable);
|
||||
void comedi_8254_update_divisors(struct comedi_8254 *);
|
||||
void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *,
|
||||
unsigned int *nanosec, unsigned int flags);
|
||||
void comedi_8254_ns_to_timer(struct comedi_8254 *,
|
||||
unsigned int *nanosec, unsigned int flags);
|
||||
|
||||
void comedi_8254_set_busy(struct comedi_8254 *,
|
||||
unsigned int counter, bool busy);
|
||||
|
||||
void comedi_8254_subdevice_init(struct comedi_subdevice *,
|
||||
struct comedi_8254 *);
|
||||
|
||||
struct comedi_8254 *comedi_8254_init(unsigned long iobase,
|
||||
unsigned int osc_base,
|
||||
unsigned int iosize,
|
||||
unsigned int regshift);
|
||||
struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
|
||||
unsigned int osc_base,
|
||||
unsigned int iosize,
|
||||
unsigned int regshift);
|
||||
|
||||
#endif /* _COMEDI_8254_H */
|
|
@ -267,7 +267,6 @@ static int do_dev_config(struct comedi_device *dev, struct comedi_devconfig *it)
|
|||
strlcat(devpriv->name, buf,
|
||||
sizeof(devpriv->name));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,9 +312,9 @@ static int bonding_attach(struct comedi_device *dev,
|
|||
s->insn_config = bonding_dio_insn_config;
|
||||
|
||||
dev_info(dev->class_dev,
|
||||
"%s: %s attached, %u channels from %u devices\n",
|
||||
dev->driver->driver_name, dev->board_name,
|
||||
devpriv->nchans, devpriv->ndevs);
|
||||
"%s: %s attached, %u channels from %u devices\n",
|
||||
dev->driver->driver_name, dev->board_name,
|
||||
devpriv->nchans, devpriv->ndevs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче