diff --git a/CREDITS b/CREDITS index a3887b59b9f9..4312cd076b5b 100644 --- a/CREDITS +++ b/CREDITS @@ -3054,6 +3054,7 @@ D: PLX USB338x driver D: PCA9634 driver D: Option GTM671WFS D: Fintek F81216A +D: AD5761 iio driver D: Various kernel hacks S: Qtechnology A/S S: Valby Langgade 142 diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 0439c2aaf741..3c6624881375 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -496,8 +496,11 @@ Description: 1kohm_to_gnd: connected to ground via an 1kOhm resistor, 6kohm_to_gnd: connected to ground via a 6kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor, + 90kohm_to_gnd: connected to ground via a 90kOhm resistor, 100kohm_to_gnd: connected to ground via an 100kOhm resistor, + 125kohm_to_gnd: connected to ground via an 125kOhm resistor, 500kohm_to_gnd: connected to ground via a 500kOhm resistor, + 640kohm_to_gnd: connected to ground via a 640kOhm resistor, three_state: left floating. For a list of available output power down options read outX_powerdown_mode_available. If Y is not present the @@ -1491,3 +1494,10 @@ Description: This ABI is especially applicable for humidity sensors to heatup the device and get rid of any condensation in some humidity environment + +What: /sys/bus/iio/devices/iio:deviceX/in_ph_raw +KernelVersion: 4.5 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no offset etc.) pH reading of a substance as a negative + base-10 logarithm of hydrodium ions in a litre of water. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x new file mode 100644 index 000000000000..3740f253d406 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -0,0 +1,54 @@ +What: /sys/bus/iio/devices/iio:deviceX/tia_resistanceY + /sys/bus/iio/devices/iio:deviceX/tia_capacitanceY +Date: December 2015 +KernelVersion: +Contact: Andrew F. Davis +Description: + Get and set the resistance and the capacitance settings for the + Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for + Rf2 and Cf2 values. + +What: /sys/bus/iio/devices/iio:deviceX/tia_separate_en +Date: December 2015 +KernelVersion: +Contact: Andrew F. Davis +Description: + Enable or disable separate settings for the TransImpedance + Amplifier above, when disabled both values are set by the + first channel. + +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_raw + /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_ambient_raw +Date: December 2015 +KernelVersion: +Contact: Andrew F. Davis +Description: + Get measured values from the ADC for these stages. Y is the + specific LED number. The values are expressed in 24-bit twos + complement. + +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY-ledY_ambient_raw +Date: December 2015 +KernelVersion: +Contact: Andrew F. Davis +Description: + Get differential values from the ADC for these stages. Y is the + specific LED number. The values are expressed in 24-bit twos + complement for the specified LEDs. + +What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_offset + /sys/bus/iio/devices/iio:deviceX/out_current_ledY_ambient_offset +Date: December 2015 +KernelVersion: +Contact: Andrew F. Davis +Description: + Get and set the offset cancellation DAC setting for these + stages. The values are expressed in 5-bit sign-magnitude. + +What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_raw +Date: December 2015 +KernelVersion: +Contact: Andrew F. Davis +Description: + Get and set the LED current for the specified LED. Y is the + specific LED number. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-magnetometer-hmc5843 b/Documentation/ABI/testing/sysfs-bus-iio-magnetometer-hmc5843 new file mode 100644 index 000000000000..6275e9f56e6c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-magnetometer-hmc5843 @@ -0,0 +1,15 @@ +What: /sys/bus/iio/devices/iio:deviceX/meas_conf +What: /sys/bus/iio/devices/iio:deviceX/meas_conf_available +KernelVersion: 4.5 +Contact: linux-iio@vger.kernel.org +Description: + Current configuration and available configurations + for the bias current. + normal - Normal measurement configurations (default) + positivebias - Positive bias configuration + negativebias - Negative bias configuration + disabled - Only available on HMC5983. Disables magnetic + sensor and enables temperature sensor. + Note: The effect of this configuration may vary + according to the device. For exact documentation + check the device's datasheet. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-vf610 b/Documentation/ABI/testing/sysfs-bus-iio-vf610 index ecbc1f4af921..308a6756d3bf 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-vf610 +++ b/Documentation/ABI/testing/sysfs-bus-iio-vf610 @@ -5,3 +5,12 @@ Description: Specifies the hardware conversion mode used. The three available modes are "normal", "high-speed" and "low-power", where the last is the default mode. + + +What: /sys/bus/iio/devices/iio:deviceX/out_conversion_mode +KernelVersion: 4.6 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the hardware conversion mode used within DAC. + The two available modes are "high-power" and "low-power", + where "low-power" mode is the default mode. diff --git a/Documentation/devicetree/bindings/goldfish/audio.txt b/Documentation/devicetree/bindings/goldfish/audio.txt new file mode 100644 index 000000000000..d043fda433ba --- /dev/null +++ b/Documentation/devicetree/bindings/goldfish/audio.txt @@ -0,0 +1,17 @@ +Android Goldfish Audio + +Android goldfish audio device generated by android emulator. + +Required properties: + +- compatible : should contain "google,goldfish-audio" to match emulator +- reg : +- interrupts : + +Example: + + goldfish_audio@9030000 { + compatible = "google,goldfish-audio"; + reg = <0x9030000 0x100>; + interrupts = <0x4>; + }; diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt index 3c10e8581144..165937e1ac1c 100644 --- a/Documentation/devicetree/bindings/iio/accel/mma8452.txt +++ b/Documentation/devicetree/bindings/iio/accel/mma8452.txt @@ -1,8 +1,10 @@ -Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer +Freescale MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC +triaxial accelerometer Required properties: - compatible: should contain one of + * "fsl,mma8451" * "fsl,mma8452" * "fsl,mma8453" * "fsl,mma8652" diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt new file mode 100644 index 000000000000..3223684a643b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt @@ -0,0 +1,28 @@ +* AT91 SAMA5D2 Analog to Digital Converter (ADC) + +Required properties: + - compatible: Should be "atmel,sama5d2-adc". + - reg: Should contain ADC registers location and length. + - interrupts: Should contain the IRQ line for the ADC. + - clocks: phandle to device clock. + - clock-names: Must be "adc_clk". + - vref-supply: Supply used as reference for conversions. + - vddana-supply: Supply for the adc device. + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC. + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC. + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC. + +Example: + +adc: adc@fc030000 { + compatible = "atmel,sama5d2-adc"; + reg = <0xfc030000 0x100>; + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&adc_clk>; + clock-names = "adc_clk"; + atmel,min-sample-rate-hz = <200000>; + atmel,max-sample-rate-hz = <20000000>; + atmel,startup-time-ms = <4>; + vddana-supply = <&vdd_3v3_lp_reg>; + vref-supply = <&vdd_3v3_lp_reg>; +} diff --git a/Documentation/devicetree/bindings/iio/adc/mcp3422.txt b/Documentation/devicetree/bindings/iio/adc/mcp3422.txt index dcae4ccfcc52..82bcce07255d 100644 --- a/Documentation/devicetree/bindings/iio/adc/mcp3422.txt +++ b/Documentation/devicetree/bindings/iio/adc/mcp3422.txt @@ -6,6 +6,7 @@ Required properties: "microchip,mcp3422" or "microchip,mcp3423" or "microchip,mcp3424" or + "microchip,mcp3425" or "microchip,mcp3426" or "microchip,mcp3427" or "microchip,mcp3428" diff --git a/Documentation/devicetree/bindings/iio/adc/ti-adc0832.txt b/Documentation/devicetree/bindings/iio/adc/ti-adc0832.txt new file mode 100644 index 000000000000..d91130587d01 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti-adc0832.txt @@ -0,0 +1,19 @@ +* Texas Instruments' ADC0831/ADC0832/ADC0832/ADC0838 + +Required properties: + - compatible: Should be one of + * "ti,adc0831" + * "ti,adc0832" + * "ti,adc0834" + * "ti,adc0838" + - reg: spi chip select number for the device + - vref-supply: The regulator supply for ADC reference voltage + - spi-max-frequency: Max SPI frequency to use (< 400000) + +Example: +adc@0 { + compatible = "ti,adc0832"; + reg = <0>; + vref-supply = <&vdd_supply>; + spi-max-frequency = <200000>; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt new file mode 100644 index 000000000000..cffa1907463a --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt @@ -0,0 +1,22 @@ +* Atlas Scientific pH-SM OEM sensor + +http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf + +Required properties: + + - compatible: must be "atlas,ph-sm" + - reg: the I2C address of the sensor + - interrupt-parent: should be the phandle for the interrupt controller + - interrupts: the sole interrupt generated by the device + + Refer to interrupt-controller/interrupts.txt for generic interrupt client + node bindings. + +Example: + +atlas@65 { + compatible = "atlas,ph-sm"; + reg = <0x65>; + interrupt-parent = <&gpio1>; + interrupts = <16 2>; +}; diff --git a/Documentation/devicetree/bindings/iio/dac/vf610-dac.txt b/Documentation/devicetree/bindings/iio/dac/vf610-dac.txt new file mode 100644 index 000000000000..20c6c7ae9687 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/vf610-dac.txt @@ -0,0 +1,20 @@ +Freescale vf610 Digital to Analog Converter bindings + +The devicetree bindings are for the new DAC driver written for +vf610 SoCs from Freescale. + +Required properties: +- compatible: Should contain "fsl,vf610-dac" +- reg: Offset and length of the register set for the device +- interrupts: Should contain the interrupt for the device +- clocks: The clock is needed by the DAC controller +- clock-names: Must contain "dac" matching entry in the clocks property. + +Example: +dac0: dac@400cc000 { + compatible = "fsl,vf610-dac"; + reg = <0x400cc000 0x1000>; + interrupts = <55 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "dac"; + clocks = <&clks VF610_CLK_DAC0>; +}; diff --git a/Documentation/devicetree/bindings/iio/health/afe4403.txt b/Documentation/devicetree/bindings/iio/health/afe4403.txt new file mode 100644 index 000000000000..2fffd70336ba --- /dev/null +++ b/Documentation/devicetree/bindings/iio/health/afe4403.txt @@ -0,0 +1,34 @@ +Texas Instruments AFE4403 Heart rate and Pulse Oximeter + +Required properties: + - compatible : Should be "ti,afe4403". + - reg : SPI chip select address of device. + - tx-supply : Regulator supply to transmitting LEDs. + - interrupt-parent : Phandle to he parent interrupt controller. + - interrupts : The interrupt line the device ADC_RDY pin is + connected to. For details refer to, + ../../interrupt-controller/interrupts.txt. + +Optional properties: + - reset-gpios : GPIO used to reset the device. + For details refer to, ../../gpio/gpio.txt. + +For other required and optional properties of SPI slave nodes +please refer to ../../spi/spi-bus.txt. + +Example: + +&spi0 { + heart_mon@0 { + compatible = "ti,afe4403"; + reg = <0>; + spi-max-frequency = <10000000>; + + tx-supply = <&vbat>; + + interrupt-parent = <&gpio1>; + interrupts = <28 IRQ_TYPE_EDGE_RISING>; + + reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>; + }; +}; diff --git a/Documentation/devicetree/bindings/iio/health/afe4404.txt b/Documentation/devicetree/bindings/iio/health/afe4404.txt new file mode 100644 index 000000000000..de69f203edfa --- /dev/null +++ b/Documentation/devicetree/bindings/iio/health/afe4404.txt @@ -0,0 +1,30 @@ +Texas Instruments AFE4404 Heart rate and Pulse Oximeter + +Required properties: + - compatible : Should be "ti,afe4404". + - reg : I2C address of the device. + - tx-supply : Regulator supply to transmitting LEDs. + - interrupt-parent : Phandle to he parent interrupt controller. + - interrupts : The interrupt line the device ADC_RDY pin is + connected to. For details refer to, + ../interrupt-controller/interrupts.txt. + +Optional properties: + - reset-gpios : GPIO used to reset the device. + For details refer to, ../gpio/gpio.txt. + +Example: + +&i2c2 { + heart_mon@58 { + compatible = "ti,afe4404"; + reg = <0x58>; + + tx-supply = <&vbat>; + + interrupt-parent = <&gpio1>; + interrupts = <28 IRQ_TYPE_EDGE_RISING>; + + reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>; + }; +}; diff --git a/Documentation/devicetree/bindings/iio/health/max30100.txt b/Documentation/devicetree/bindings/iio/health/max30100.txt index f6fbac66ad06..295a9edfa4fd 100644 --- a/Documentation/devicetree/bindings/iio/health/max30100.txt +++ b/Documentation/devicetree/bindings/iio/health/max30100.txt @@ -11,11 +11,19 @@ Required properties: Refer to interrupt-controller/interrupts.txt for generic interrupt client node bindings. +Optional properties: + - maxim,led-current-microamp: configuration for LED current in microamperes + while the engine is running. First indexed value is the configuration for + the RED LED, and second value is for the IR LED. + + Refer to the datasheet for the allowed current values. + Example: max30100@057 { compatible = "maxim,max30100"; reg = <57>; + maxim,led-current-microamp = <24000 50000>; interrupt-parent = <&gpio1>; interrupts = <16 2>; }; diff --git a/Documentation/devicetree/bindings/iio/light/opt3001.txt b/Documentation/devicetree/bindings/iio/light/opt3001.txt new file mode 100644 index 000000000000..eac30d508849 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/opt3001.txt @@ -0,0 +1,26 @@ +* Texas Instruments OPT3001 Ambient Light Sensor + +The driver supports interrupt-driven and interrupt-less operation, depending +on whether an interrupt property has been populated into the DT. Note that +the optional generation of IIO events on rising/falling light threshold changes +requires the use of interrupts. Without interrupts, only the simple reading +of the current light value is supported through the IIO API. + +http://www.ti.com/product/opt3001 + +Required properties: + - compatible: should be "ti,opt3001" + - reg: the I2C address of the sensor + +Optional properties: + - interrupt-parent: should be the phandle for the interrupt controller + - interrupts: interrupt mapping for GPIO IRQ (configure for falling edge) + +Example: + +opt3001@44 { + compatible = "ti,opt3001"; + reg = <0x44>; + interrupt-parent = <&gpio1>; + interrupts = <28 IRQ_TYPE_EDGE_FALLING>; +}; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index bd33a268fd1b..ee66defcdd8b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -28,6 +28,7 @@ arm ARM Ltd. armadeus ARMadeus Systems SARL artesyn Artesyn Embedded Technologies Inc. asahi-kasei Asahi Kasei Corp. +atlas Atlas Scientific LLC atmel Atmel Corporation auo AU Optronics Corporation avago Avago Technologies diff --git a/Documentation/isdn/00-INDEX b/Documentation/isdn/00-INDEX index e87e336f590e..2d1889b6c1fa 100644 --- a/Documentation/isdn/00-INDEX +++ b/Documentation/isdn/00-INDEX @@ -16,8 +16,6 @@ README.FAQ - general info for FAQ. README.HiSax - info on the HiSax driver which replaces the old teles. -README.act2000 - - info on driver for IBM ACT-2000 card. README.audio - info for running audio over ISDN. README.avmb1 @@ -34,14 +32,8 @@ README.hfc-pci - info on hfc-pci based cards. README.hysdn - info on driver for Hypercope active HYSDN cards -README.icn - - info on the ICN-ISDN-card and its driver. README.mISDN - info on the Modular ISDN subsystem (mISDN) -README.pcbit - - info on the PCBIT-D ISDN adapter and driver. -README.sc - - info on driver for Spellcaster cards. README.syncppp - info on running Sync PPP over ISDN. README.x25 diff --git a/drivers/staging/panel/lcd-panel-cgram.txt b/Documentation/misc-devices/lcd-panel-cgram.txt similarity index 100% rename from drivers/staging/panel/lcd-panel-cgram.txt rename to Documentation/misc-devices/lcd-panel-cgram.txt diff --git a/MAINTAINERS b/MAINTAINERS index 145289e3eb7a..15b4c417211f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -775,6 +775,12 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: sound/aoa/ +APEX EMBEDDED SYSTEMS STX104 DAC DRIVER +M: William Breathitt Gray +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/dac/stx104.c + APM DRIVER M: Jiri Kosina S: Odd fixes @@ -1962,6 +1968,12 @@ M: Nicolas Ferre S: Supported F: drivers/tty/serial/atmel_serial.c +ATMEL SAMA5D2 ADC DRIVER +M: Ludovic Desroches +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/adc/at91-sama5d2_adc.c + ATMEL Audio ALSA driver M: Nicolas Ferre L: alsa-devel@alsa-project.org (moderated for non-subscribers) @@ -3548,13 +3560,6 @@ L: driverdev-devel@linuxdriverproject.org S: Maintained F: drivers/staging/dgnc/ -DIGI EPCA PCI PRODUCTS -M: Lidza Louina -M: Daeseok Youn -L: driverdev-devel@linuxdriverproject.org -S: Maintained -F: drivers/staging/dgap/ - DIOLAN U2C-12 I2C DRIVER M: Guenter Roeck L: linux-i2c@vger.kernel.org @@ -8202,6 +8207,13 @@ S: Maintained F: Documentation/mn10300/ F: arch/mn10300/ +PARALLEL LCD/KEYPAD PANEL DRIVER +M: Willy Tarreau +M: Ksenija Stanojevic +S: Odd Fixes +F: Documentation/misc-devices/lcd-panel-cgram.txt +F: drivers/misc/panel.c + PARALLEL PORT SUBSYSTEM M: Sudip Mukherjee M: Sudip Mukherjee @@ -10455,19 +10467,6 @@ L: linux-tegra@vger.kernel.org S: Maintained F: drivers/staging/nvec/ -STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON) -M: Jens Frederich -M: Daniel Drake -M: Jon Nettleton -W: http://wiki.laptop.org/go/DCON -S: Maintained -F: drivers/staging/olpc_dcon/ - -STAGING - PARALLEL LCD/KEYPAD PANEL DRIVER -M: Willy Tarreau -S: Odd Fixes -F: drivers/staging/panel/ - STAGING - REALTEK RTL8712U DRIVERS M: Larry Finger M: Florian Schilhabel . diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 57f52a2afa35..16288e777ec3 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2744,6 +2744,10 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) /*pr_info("binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ + if (unlikely(current->mm != proc->vma_vm_mm)) { + pr_err("current mm mismatch proc mm\n"); + return -EINVAL; + } trace_binder_ioctl(cmd, arg); ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); @@ -2958,6 +2962,7 @@ static int binder_open(struct inode *nodp, struct file *filp) return -ENOMEM; get_task_struct(current); proc->tsk = current; + proc->vma_vm_mm = current->mm; INIT_LIST_HEAD(&proc->todo); init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 833ea9dd4464..b0d3ecf3318b 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -143,7 +143,7 @@ config MMA8452 select IIO_TRIGGERED_BUFFER help Say yes here to build support for the following Freescale 3-axis - accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC. + accelerometers: MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC. To compile this driver as a module, choose M here: the module will be called mma8452. diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index ccc632a7cf01..7f4994f32a90 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1,6 +1,7 @@ /* * mma8452.c - Support for following Freescale 3-axis accelerometers: * + * MMA8451Q (14 bit) * MMA8452Q (12 bit) * MMA8453Q (10 bit) * MMA8652FC (12 bit) @@ -15,7 +16,7 @@ * * 7-bit I2C slave address 0x1c/0x1d (pin selectable) * - * TODO: orientation / freefall events, autosleep + * TODO: orientation events, autosleep */ #include @@ -85,8 +86,9 @@ #define MMA8452_INT_FF_MT BIT(2) #define MMA8452_INT_TRANS BIT(5) -#define MMA8452_DEVICE_ID 0x2a -#define MMA8453_DEVICE_ID 0x3a +#define MMA8451_DEVICE_ID 0x1a +#define MMA8452_DEVICE_ID 0x2a +#define MMA8453_DEVICE_ID 0x3a #define MMA8652_DEVICE_ID 0x4a #define MMA8653_DEVICE_ID 0x5a @@ -416,6 +418,51 @@ fail: return ret; } +/* returns >0 if in freefall mode, 0 if not or <0 if an error occured */ +static int mma8452_freefall_mode_enabled(struct mma8452_data *data) +{ + int val; + const struct mma_chip_info *chip = data->chip_info; + + val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg); + if (val < 0) + return val; + + return !(val & MMA8452_FF_MT_CFG_OAE); +} + +static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state) +{ + int val; + const struct mma_chip_info *chip = data->chip_info; + + if ((state && mma8452_freefall_mode_enabled(data)) || + (!state && !(mma8452_freefall_mode_enabled(data)))) + return 0; + + val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg); + if (val < 0) + return val; + + if (state) { + val |= BIT(idx_x + chip->ev_cfg_chan_shift); + val |= BIT(idx_y + chip->ev_cfg_chan_shift); + val |= BIT(idx_z + chip->ev_cfg_chan_shift); + val &= ~MMA8452_FF_MT_CFG_OAE; + } else { + val &= ~BIT(idx_x + chip->ev_cfg_chan_shift); + val &= ~BIT(idx_y + chip->ev_cfg_chan_shift); + val &= ~BIT(idx_z + chip->ev_cfg_chan_shift); + val |= MMA8452_FF_MT_CFG_OAE; + } + + val = mma8452_change_config(data, chip->ev_cfg, val); + if (val) + return val; + + return 0; +} + static int mma8452_set_hp_filter_frequency(struct mma8452_data *data, int val, int val2) { @@ -609,12 +656,22 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev, const struct mma_chip_info *chip = data->chip_info; int ret; - ret = i2c_smbus_read_byte_data(data->client, - data->chip_info->ev_cfg); - if (ret < 0) - return ret; + switch (dir) { + case IIO_EV_DIR_FALLING: + return mma8452_freefall_mode_enabled(data); + case IIO_EV_DIR_RISING: + if (mma8452_freefall_mode_enabled(data)) + return 0; - return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift)); + ret = i2c_smbus_read_byte_data(data->client, + data->chip_info->ev_cfg); + if (ret < 0) + return ret; + + return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift)); + default: + return -EINVAL; + } } static int mma8452_write_event_config(struct iio_dev *indio_dev, @@ -627,19 +684,35 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev, const struct mma_chip_info *chip = data->chip_info; int val; - val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg); - if (val < 0) - return val; + switch (dir) { + case IIO_EV_DIR_FALLING: + return mma8452_set_freefall_mode(data, state); + case IIO_EV_DIR_RISING: + val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg); + if (val < 0) + return val; - if (state) - val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift); - else - val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift); + if (state) { + if (mma8452_freefall_mode_enabled(data)) { + val &= ~BIT(idx_x + chip->ev_cfg_chan_shift); + val &= ~BIT(idx_y + chip->ev_cfg_chan_shift); + val &= ~BIT(idx_z + chip->ev_cfg_chan_shift); + val |= MMA8452_FF_MT_CFG_OAE; + } + val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift); + } else { + if (mma8452_freefall_mode_enabled(data)) + return 0; - val |= chip->ev_cfg_ele; - val |= MMA8452_FF_MT_CFG_OAE; + val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift); + } - return mma8452_change_config(data, chip->ev_cfg, val); + val |= chip->ev_cfg_ele; + + return mma8452_change_config(data, chip->ev_cfg, val); + default: + return -EINVAL; + } } static void mma8452_transient_interrupt(struct iio_dev *indio_dev) @@ -652,6 +725,16 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev) if (src < 0) return; + if (mma8452_freefall_mode_enabled(data)) { + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_AND_Y_AND_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_FALLING), + ts); + return; + } + if (src & data->chip_info->ev_src_xe) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, @@ -745,6 +828,27 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev, return 0; } +static const struct iio_event_spec mma8452_freefall_event[] = { + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB) + }, +}; + +static const struct iio_event_spec mma8652_freefall_event[] = { + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) + }, +}; + static const struct iio_event_spec mma8452_transient_event[] = { { .type = IIO_EV_TYPE_MAG, @@ -781,6 +885,24 @@ static struct attribute_group mma8452_event_attribute_group = { .attrs = mma8452_event_attributes, }; +#define MMA8452_FREEFALL_CHANNEL(modifier) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = modifier, \ + .scan_index = -1, \ + .event_spec = mma8452_freefall_event, \ + .num_event_specs = ARRAY_SIZE(mma8452_freefall_event), \ +} + +#define MMA8652_FREEFALL_CHANNEL(modifier) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = modifier, \ + .scan_index = -1, \ + .event_spec = mma8652_freefall_event, \ + .num_event_specs = ARRAY_SIZE(mma8652_freefall_event), \ +} + #define MMA8452_CHANNEL(axis, idx, bits) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -822,11 +944,20 @@ static struct attribute_group mma8452_event_attribute_group = { .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \ } +static const struct iio_chan_spec mma8451_channels[] = { + MMA8452_CHANNEL(X, idx_x, 14), + MMA8452_CHANNEL(Y, idx_y, 14), + MMA8452_CHANNEL(Z, idx_z, 14), + IIO_CHAN_SOFT_TIMESTAMP(idx_ts), + MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z), +}; + static const struct iio_chan_spec mma8452_channels[] = { MMA8452_CHANNEL(X, idx_x, 12), MMA8452_CHANNEL(Y, idx_y, 12), MMA8452_CHANNEL(Z, idx_z, 12), IIO_CHAN_SOFT_TIMESTAMP(idx_ts), + MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z), }; static const struct iio_chan_spec mma8453_channels[] = { @@ -834,6 +965,7 @@ static const struct iio_chan_spec mma8453_channels[] = { MMA8452_CHANNEL(Y, idx_y, 10), MMA8452_CHANNEL(Z, idx_z, 10), IIO_CHAN_SOFT_TIMESTAMP(idx_ts), + MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z), }; static const struct iio_chan_spec mma8652_channels[] = { @@ -841,6 +973,7 @@ static const struct iio_chan_spec mma8652_channels[] = { MMA8652_CHANNEL(Y, idx_y, 12), MMA8652_CHANNEL(Z, idx_z, 12), IIO_CHAN_SOFT_TIMESTAMP(idx_ts), + MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z), }; static const struct iio_chan_spec mma8653_channels[] = { @@ -848,9 +981,11 @@ static const struct iio_chan_spec mma8653_channels[] = { MMA8652_CHANNEL(Y, idx_y, 10), MMA8652_CHANNEL(Z, idx_z, 10), IIO_CHAN_SOFT_TIMESTAMP(idx_ts), + MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z), }; enum { + mma8451, mma8452, mma8453, mma8652, @@ -858,17 +993,34 @@ enum { }; static const struct mma_chip_info mma_chip_info_table[] = { + [mma8451] = { + .chip_id = MMA8451_DEVICE_ID, + .channels = mma8451_channels, + .num_channels = ARRAY_SIZE(mma8451_channels), + /* + * Hardware has fullscale of -2G, -4G, -8G corresponding to + * raw value -8192 for 14 bit, -2048 for 12 bit or -512 for 10 + * bit. + * The userspace interface uses m/s^2 and we declare micro units + * So scale factor for 12 bit here is given by: + * g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665 + */ + .mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} }, + .ev_cfg = MMA8452_TRANSIENT_CFG, + .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, + .ev_cfg_chan_shift = 1, + .ev_src = MMA8452_TRANSIENT_SRC, + .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE, + .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE, + .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE, + .ev_ths = MMA8452_TRANSIENT_THS, + .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, + .ev_count = MMA8452_TRANSIENT_COUNT, + }, [mma8452] = { .chip_id = MMA8452_DEVICE_ID, .channels = mma8452_channels, .num_channels = ARRAY_SIZE(mma8452_channels), - /* - * Hardware has fullscale of -2G, -4G, -8G corresponding to - * raw value -2048 for 12 bit or -512 for 10 bit. - * The userspace interface uses m/s^2 and we declare micro units - * So scale factor for 12 bit here is given by: - * g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665 - */ .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} }, .ev_cfg = MMA8452_TRANSIENT_CFG, .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, @@ -1049,6 +1201,7 @@ static int mma8452_reset(struct i2c_client *client) } static const struct of_device_id mma8452_dt_ids[] = { + { .compatible = "fsl,mma8451", .data = &mma_chip_info_table[mma8451] }, { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] }, { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] }, { .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] }, @@ -1085,6 +1238,7 @@ static int mma8452_probe(struct i2c_client *client, return ret; switch (ret) { + case MMA8451_DEVICE_ID: case MMA8452_DEVICE_ID: case MMA8453_DEVICE_ID: case MMA8652_DEVICE_ID: @@ -1190,6 +1344,10 @@ static int mma8452_probe(struct i2c_client *client, if (ret < 0) goto buffer_cleanup; + ret = mma8452_set_freefall_mode(data, false); + if (ret) + return ret; + return 0; buffer_cleanup: diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 70f042797f15..a03a1417dd63 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -67,6 +67,8 @@ #define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22 #define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10 #define ST_ACCEL_1_DRDY_IRQ_INT2_MASK 0x08 +#define ST_ACCEL_1_IHL_IRQ_ADDR 0x25 +#define ST_ACCEL_1_IHL_IRQ_MASK 0x02 #define ST_ACCEL_1_MULTIREAD_BIT true /* CUSTOM VALUES FOR SENSOR 2 */ @@ -92,6 +94,8 @@ #define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 #define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02 #define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10 +#define ST_ACCEL_2_IHL_IRQ_ADDR 0x22 +#define ST_ACCEL_2_IHL_IRQ_MASK 0x80 #define ST_ACCEL_2_MULTIREAD_BIT true /* CUSTOM VALUES FOR SENSOR 3 */ @@ -125,6 +129,8 @@ #define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23 #define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80 #define ST_ACCEL_3_DRDY_IRQ_INT2_MASK 0x00 +#define ST_ACCEL_3_IHL_IRQ_ADDR 0x23 +#define ST_ACCEL_3_IHL_IRQ_MASK 0x40 #define ST_ACCEL_3_IG1_EN_ADDR 0x23 #define ST_ACCEL_3_IG1_EN_MASK 0x08 #define ST_ACCEL_3_MULTIREAD_BIT false @@ -169,6 +175,8 @@ #define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22 #define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04 #define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20 +#define ST_ACCEL_5_IHL_IRQ_ADDR 0x22 +#define ST_ACCEL_5_IHL_IRQ_MASK 0x80 #define ST_ACCEL_5_IG1_EN_ADDR 0x21 #define ST_ACCEL_5_IG1_EN_MASK 0x08 #define ST_ACCEL_5_MULTIREAD_BIT false @@ -292,6 +300,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .addr = ST_ACCEL_1_DRDY_IRQ_ADDR, .mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK, .mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR, + .mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK, }, .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT, .bootime = 2, @@ -355,6 +365,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .addr = ST_ACCEL_2_DRDY_IRQ_ADDR, .mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK, .mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR, + .mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK, }, .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, .bootime = 2, @@ -430,6 +442,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .addr = ST_ACCEL_3_DRDY_IRQ_ADDR, .mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK, .mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR, + .mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK, .ig1 = { .en_addr = ST_ACCEL_3_IG1_EN_ADDR, .en_mask = ST_ACCEL_3_IG1_EN_MASK, @@ -537,6 +551,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .addr = ST_ACCEL_5_DRDY_IRQ_ADDR, .mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK, .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR, + .mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK, }, .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT, .bootime = 2, /* guess */ diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 283ded7747a9..932de1f9d1e7 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -131,6 +131,16 @@ config AT91_ADC To compile this driver as a module, choose M here: the module will be called at91_adc. +config AT91_SAMA5D2_ADC + tristate "Atmel AT91 SAMA5D2 ADC" + depends on ARCH_AT91 || COMPILE_TEST + help + Say yes here to build support for Atmel SAMA5D2 ADC which is + available on SAMA5D2 SoC family. + + To compile this driver as a module, choose M here: the module will be + called at91-sama5d2_adc. + config AXP288_ADC tristate "X-Powers AXP288 ADC driver" depends on MFD_AXP20X @@ -267,11 +277,11 @@ config MCP320X called mcp320x. config MCP3422 - tristate "Microchip Technology MCP3422/3/4/6/7/8 driver" + tristate "Microchip Technology MCP3421/2/3/4/5/6/7/8 driver" depends on I2C help - Say yes here to build support for Microchip Technology's - MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428 + Say yes here to build support for Microchip Technology's MCP3421 + MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428 analog to digital converters. This driver can also be built as a module. If so, the module will be @@ -287,6 +297,20 @@ config MEN_Z188_ADC This driver can also be built as a module. If so, the module will be called men_z188_adc. +config MXS_LRADC + tristate "Freescale i.MX23/i.MX28 LRADC" + depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM + depends on INPUT + select STMP_DEVICE + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for i.MX23/i.MX28 LRADC convertor + built into these chips. + + To compile this driver as a module, choose M here: the + module will be called mxs-lradc. + config NAU7802 tristate "Nuvoton NAU7802 ADC driver" depends on I2C @@ -352,6 +376,16 @@ config TI_ADC081C This driver can also be built as a module. If so, the module will be called ti-adc081c. +config TI_ADC0832 + tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838" + depends on SPI + help + If you say yes here you get support for Texas Instruments ADC0831, + ADC0832, ADC0834, ADC0838 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc0832. + config TI_ADC128S052 tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021" depends on SPI @@ -362,6 +396,19 @@ config TI_ADC128S052 This driver can also be built as a module. If so, the module will be called ti-adc128s052. +config TI_ADS1015 + tristate "Texas Instruments ADS1015 ADC" + depends on I2C && !SENSORS_ADS1015 + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS1015 + ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads1015. + config TI_ADS8688 tristate "Texas Instruments ADS8688" depends on SPI && OF diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 6435780e9b71..b1aa456e6af3 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o @@ -28,13 +29,16 @@ obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o +obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o +obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o +obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c new file mode 100644 index 000000000000..dbee13ad33a3 --- /dev/null +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -0,0 +1,508 @@ +/* + * Atmel ADC driver for SAMA5D2 devices and compatible. + * + * Copyright (C) 2015 Atmel, + * 2015 Ludovic Desroches + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control Register */ +#define AT91_SAMA5D2_CR 0x00 +/* Software Reset */ +#define AT91_SAMA5D2_CR_SWRST BIT(0) +/* Start Conversion */ +#define AT91_SAMA5D2_CR_START BIT(1) +/* Touchscreen Calibration */ +#define AT91_SAMA5D2_CR_TSCALIB BIT(2) +/* Comparison Restart */ +#define AT91_SAMA5D2_CR_CMPRST BIT(4) + +/* Mode Register */ +#define AT91_SAMA5D2_MR 0x04 +/* Trigger Selection */ +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1) +/* ADTRG */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0 +/* TIOA0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1 +/* TIOA1 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2 +/* TIOA2 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3 +/* PWM event line 0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4 +/* PWM event line 1 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5 +/* TIOA3 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6 +/* RTCOUT0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7 +/* Sleep Mode */ +#define AT91_SAMA5D2_MR_SLEEP BIT(5) +/* Fast Wake Up */ +#define AT91_SAMA5D2_MR_FWUP BIT(6) +/* Prescaler Rate Selection */ +#define AT91_SAMA5D2_MR_PRESCAL(v) ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET) +#define AT91_SAMA5D2_MR_PRESCAL_OFFSET 8 +#define AT91_SAMA5D2_MR_PRESCAL_MAX 0xff +/* Startup Time */ +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16) +/* Analog Change */ +#define AT91_SAMA5D2_MR_ANACH BIT(23) +/* Tracking Time */ +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24) +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff +/* Transfer Time */ +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28) +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3 +/* Use Sequence Enable */ +#define AT91_SAMA5D2_MR_USEQ BIT(31) + +/* Channel Sequence Register 1 */ +#define AT91_SAMA5D2_SEQR1 0x08 +/* Channel Sequence Register 2 */ +#define AT91_SAMA5D2_SEQR2 0x0c +/* Channel Enable Register */ +#define AT91_SAMA5D2_CHER 0x10 +/* Channel Disable Register */ +#define AT91_SAMA5D2_CHDR 0x14 +/* Channel Status Register */ +#define AT91_SAMA5D2_CHSR 0x18 +/* Last Converted Data Register */ +#define AT91_SAMA5D2_LCDR 0x20 +/* Interrupt Enable Register */ +#define AT91_SAMA5D2_IER 0x24 +/* Interrupt Disable Register */ +#define AT91_SAMA5D2_IDR 0x28 +/* Interrupt Mask Register */ +#define AT91_SAMA5D2_IMR 0x2c +/* Interrupt Status Register */ +#define AT91_SAMA5D2_ISR 0x30 +/* Last Channel Trigger Mode Register */ +#define AT91_SAMA5D2_LCTMR 0x34 +/* Last Channel Compare Window Register */ +#define AT91_SAMA5D2_LCCWR 0x38 +/* Overrun Status Register */ +#define AT91_SAMA5D2_OVER 0x3c +/* Extended Mode Register */ +#define AT91_SAMA5D2_EMR 0x40 +/* Compare Window Register */ +#define AT91_SAMA5D2_CWR 0x44 +/* Channel Gain Register */ +#define AT91_SAMA5D2_CGR 0x48 +/* Channel Offset Register */ +#define AT91_SAMA5D2_COR 0x4c +/* Channel Data Register 0 */ +#define AT91_SAMA5D2_CDR0 0x50 +/* Analog Control Register */ +#define AT91_SAMA5D2_ACR 0x94 +/* Touchscreen Mode Register */ +#define AT91_SAMA5D2_TSMR 0xb0 +/* Touchscreen X Position Register */ +#define AT91_SAMA5D2_XPOSR 0xb4 +/* Touchscreen Y Position Register */ +#define AT91_SAMA5D2_YPOSR 0xb8 +/* Touchscreen Pressure Register */ +#define AT91_SAMA5D2_PRESSR 0xbc +/* Trigger Register */ +#define AT91_SAMA5D2_TRGR 0xc0 +/* Correction Select Register */ +#define AT91_SAMA5D2_COSR 0xd0 +/* Correction Value Register */ +#define AT91_SAMA5D2_CVR 0xd4 +/* Channel Error Correction Register */ +#define AT91_SAMA5D2_CECR 0xd8 +/* Write Protection Mode Register */ +#define AT91_SAMA5D2_WPMR 0xe4 +/* Write Protection Status Register */ +#define AT91_SAMA5D2_WPSR 0xe8 +/* Version Register */ +#define AT91_SAMA5D2_VERSION 0xfc + +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \ + { \ + .type = IIO_VOLTAGE, \ + .channel = num, \ + .address = addr, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .datasheet_name = "CH"#num, \ + .indexed = 1, \ + } + +#define at91_adc_readl(st, reg) readl_relaxed(st->base + reg) +#define at91_adc_writel(st, reg, val) writel_relaxed(val, st->base + reg) + +struct at91_adc_soc_info { + unsigned startup_time; + unsigned min_sample_rate; + unsigned max_sample_rate; +}; + +struct at91_adc_state { + void __iomem *base; + int irq; + struct clk *per_clk; + struct regulator *reg; + struct regulator *vref; + int vref_uv; + const struct iio_chan_spec *chan; + bool conversion_done; + u32 conversion_value; + struct at91_adc_soc_info soc_info; + wait_queue_head_t wq_data_available; + /* + * lock to prevent concurrent 'single conversion' requests through + * sysfs. + */ + struct mutex lock; +}; + +static const struct iio_chan_spec at91_adc_channels[] = { + AT91_AT91_SAMA5D2_CHAN(0, 0x50), + AT91_AT91_SAMA5D2_CHAN(1, 0x54), + AT91_AT91_SAMA5D2_CHAN(2, 0x58), + AT91_AT91_SAMA5D2_CHAN(3, 0x5c), + AT91_AT91_SAMA5D2_CHAN(4, 0x60), + AT91_AT91_SAMA5D2_CHAN(5, 0x64), + AT91_AT91_SAMA5D2_CHAN(6, 0x68), + AT91_AT91_SAMA5D2_CHAN(7, 0x6c), + AT91_AT91_SAMA5D2_CHAN(8, 0x70), + AT91_AT91_SAMA5D2_CHAN(9, 0x74), + AT91_AT91_SAMA5D2_CHAN(10, 0x78), + AT91_AT91_SAMA5D2_CHAN(11, 0x7c), +}; + +static unsigned at91_adc_startup_time(unsigned startup_time_min, + unsigned adc_clk_khz) +{ + const unsigned startup_lookup[] = { + 0, 8, 16, 24, + 64, 80, 96, 112, + 512, 576, 640, 704, + 768, 832, 896, 960 + }; + unsigned ticks_min, i; + + /* + * Since the adc frequency is checked before, there is no reason + * to not meet the startup time constraint. + */ + + ticks_min = startup_time_min * adc_clk_khz / 1000; + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++) + if (startup_lookup[i] > ticks_min) + break; + + return i; +} + +static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(st); + unsigned f_per, prescal, startup; + + f_per = clk_get_rate(st->per_clk); + prescal = (f_per / (2 * freq)) - 1; + + startup = at91_adc_startup_time(st->soc_info.startup_time, + freq / 1000); + + at91_adc_writel(st, AT91_SAMA5D2_MR, + AT91_SAMA5D2_MR_TRANSFER(2) + | AT91_SAMA5D2_MR_STARTUP(startup) + | AT91_SAMA5D2_MR_PRESCAL(prescal)); + + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n", + freq, startup, prescal); +} + +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st) +{ + unsigned f_adc, f_per = clk_get_rate(st->per_clk); + unsigned mr, prescal; + + mr = at91_adc_readl(st, AT91_SAMA5D2_MR); + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET) + & AT91_SAMA5D2_MR_PRESCAL_MAX; + f_adc = f_per / (2 * (prescal + 1)); + + return f_adc; +} + +static irqreturn_t at91_adc_interrupt(int irq, void *private) +{ + struct iio_dev *indio = private; + struct at91_adc_state *st = iio_priv(indio); + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR); + + if (status & imr) { + st->conversion_value = at91_adc_readl(st, st->chan->address); + st->conversion_done = true; + wake_up_interruptible(&st->wq_data_available); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int at91_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + + st->chan = chan; + + at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START); + + ret = wait_event_interruptible_timeout(st->wq_data_available, + st->conversion_done, + msecs_to_jiffies(1000)); + if (ret == 0) + ret = -ETIMEDOUT; + + if (ret > 0) { + *val = st->conversion_value; + ret = IIO_VAL_INT; + st->conversion_done = false; + } + + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + + mutex_unlock(&st->lock); + return ret; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uv / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = at91_adc_get_sample_freq(st); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int at91_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + if (val < st->soc_info.min_sample_rate || + val > st->soc_info.max_sample_rate) + return -EINVAL; + + at91_adc_setup_samp_freq(st, val); + + return 0; +} + +static const struct iio_info at91_adc_info = { + .read_raw = &at91_adc_read_raw, + .write_raw = &at91_adc_write_raw, + .driver_module = THIS_MODULE, +}; + +static int at91_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct at91_adc_state *st; + struct resource *res; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &at91_adc_info; + indio_dev->channels = at91_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels); + + st = iio_priv(indio_dev); + + ret = of_property_read_u32(pdev->dev.of_node, + "atmel,min-sample-rate-hz", + &st->soc_info.min_sample_rate); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,min-sample-rate-hz\n"); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "atmel,max-sample-rate-hz", + &st->soc_info.max_sample_rate); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,max-sample-rate-hz\n"); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms", + &st->soc_info.startup_time); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,startup-time-ms\n"); + return ret; + } + + init_waitqueue_head(&st->wq_data_available); + mutex_init(&st->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + st->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(st->base)) + return PTR_ERR(st->base); + + st->irq = platform_get_irq(pdev, 0); + if (st->irq <= 0) { + if (!st->irq) + st->irq = -ENXIO; + + return st->irq; + } + + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->per_clk)) + return PTR_ERR(st->per_clk); + + st->reg = devm_regulator_get(&pdev->dev, "vddana"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + st->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(st->vref)) + return PTR_ERR(st->vref); + + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0, + pdev->dev.driver->name, indio_dev); + if (ret) + return ret; + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = regulator_enable(st->vref); + if (ret) + goto reg_disable; + + st->vref_uv = regulator_get_voltage(st->vref); + if (st->vref_uv <= 0) { + ret = -EINVAL; + goto vref_disable; + } + + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff); + + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate); + + ret = clk_prepare_enable(st->per_clk); + if (ret) + goto vref_disable; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto per_clk_disable_unprepare; + + dev_info(&pdev->dev, "version: %x\n", + readl_relaxed(st->base + AT91_SAMA5D2_VERSION)); + + return 0; + +per_clk_disable_unprepare: + clk_disable_unprepare(st->per_clk); +vref_disable: + regulator_disable(st->vref); +reg_disable: + regulator_disable(st->reg); + return ret; +} + +static int at91_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + clk_disable_unprepare(st->per_clk); + + regulator_disable(st->vref); + regulator_disable(st->reg); + + return 0; +} + +static const struct of_device_id at91_adc_dt_match[] = { + { + .compatible = "atmel,sama5d2-adc", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, at91_adc_dt_match); + +static struct platform_driver at91_adc_driver = { + .probe = at91_adc_probe, + .remove = at91_adc_remove, + .driver = { + .name = "at91-sama5d2_adc", + .of_match_table = at91_adc_dt_match, + }, +}; +module_platform_driver(at91_adc_driver) + +MODULE_AUTHOR("Ludovic Desroches "); +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 0c904edd6c00..7fd24949c0c1 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -46,7 +46,7 @@ struct axp288_adc_info { struct regmap *regmap; }; -static const struct iio_chan_spec const axp288_adc_channels[] = { +static const struct iio_chan_spec axp288_adc_channels[] = { { .indexed = 1, .type = IIO_TEMP, diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index d803e5018a42..65909d5858b1 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -19,17 +19,18 @@ * * Configurable 7-bit I2C slave address from 0x40 to 0x4F */ -#include -#include + #include +#include #include #include -#include +#include +#include #include -#include - #include +#include + /* INA2XX registers definition */ #define INA2XX_CONFIG 0x00 #define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ @@ -38,7 +39,7 @@ #define INA2XX_CURRENT 0x04 /* readonly */ #define INA2XX_CALIBRATION 0x05 -#define INA226_ALERT_MASK 0x06 +#define INA226_ALERT_MASK GENMASK(2, 1) #define INA266_CVRF BIT(3) #define INA2XX_MAX_REGISTERS 8 @@ -113,7 +114,7 @@ struct ina2xx_chip_info { struct mutex state_lock; unsigned int shunt_resistor; int avg; - s64 prev_ns; /* track buffer capture time, check for underruns*/ + s64 prev_ns; /* track buffer capture time, check for underruns */ int int_time_vbus; /* Bus voltage integration time uS */ int int_time_vshunt; /* Shunt voltage integration time uS */ bool allow_async_readout; @@ -121,21 +122,21 @@ struct ina2xx_chip_info { static const struct ina2xx_config ina2xx_config[] = { [ina219] = { - .config_default = INA219_CONFIG_DEFAULT, - .calibration_factor = 40960000, - .shunt_div = 100, - .bus_voltage_shift = 3, - .bus_voltage_lsb = 4000, - .power_lsb = 20000, - }, + .config_default = INA219_CONFIG_DEFAULT, + .calibration_factor = 40960000, + .shunt_div = 100, + .bus_voltage_shift = 3, + .bus_voltage_lsb = 4000, + .power_lsb = 20000, + }, [ina226] = { - .config_default = INA226_CONFIG_DEFAULT, - .calibration_factor = 5120000, - .shunt_div = 400, - .bus_voltage_shift = 0, - .bus_voltage_lsb = 1250, - .power_lsb = 25000, - }, + .config_default = INA226_CONFIG_DEFAULT, + .calibration_factor = 5120000, + .shunt_div = 400, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb = 25000, + }, }; static int ina2xx_read_raw(struct iio_dev *indio_dev, @@ -149,7 +150,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: ret = regmap_read(chip->regmap, chan->address, ®val); - if (ret < 0) + if (ret) return ret; if (is_signed_reg(chan->address)) @@ -251,7 +252,7 @@ static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip, return -EINVAL; bits = find_closest(val_us, ina226_conv_time_tab, - ARRAY_SIZE(ina226_conv_time_tab)); + ARRAY_SIZE(ina226_conv_time_tab)); chip->int_time_vbus = ina226_conv_time_tab[bits]; @@ -270,7 +271,7 @@ static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip, return -EINVAL; bits = find_closest(val_us, ina226_conv_time_tab, - ARRAY_SIZE(ina226_conv_time_tab)); + ARRAY_SIZE(ina226_conv_time_tab)); chip->int_time_vshunt = ina226_conv_time_tab[bits]; @@ -285,8 +286,8 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct ina2xx_chip_info *chip = iio_priv(indio_dev); - int ret; unsigned int config, tmp; + int ret; if (iio_buffer_enabled(indio_dev)) return -EBUSY; @@ -294,8 +295,8 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev, mutex_lock(&chip->state_lock); ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config); - if (ret < 0) - goto _err; + if (ret) + goto err; tmp = config; @@ -310,19 +311,19 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev, else ret = ina226_set_int_time_vbus(chip, val2, &tmp); break; + default: ret = -EINVAL; } if (!ret && (tmp != config)) ret = regmap_write(chip->regmap, INA2XX_CONFIG, tmp); -_err: +err: mutex_unlock(&chip->state_lock); return ret; } - static ssize_t ina2xx_allow_async_readout_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -355,6 +356,7 @@ static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val) return -EINVAL; chip->shunt_resistor = val; + return 0; } @@ -438,7 +440,6 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) struct ina2xx_chip_info *chip = iio_priv(indio_dev); unsigned short data[8]; int bit, ret, i = 0; - unsigned long buffer_us, elapsed_us; s64 time_a, time_b; unsigned int alert; @@ -462,8 +463,6 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) return ret; alert &= INA266_CVRF; - trace_printk("Conversion ready: %d\n", !!alert); - } while (!alert); /* @@ -488,19 +487,14 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) iio_push_to_buffers_with_timestamp(indio_dev, (unsigned int *)data, time_a); - buffer_us = (unsigned long)(time_b - time_a) / 1000; - elapsed_us = (unsigned long)(time_a - chip->prev_ns) / 1000; - - trace_printk("uS: elapsed: %lu, buf: %lu\n", elapsed_us, buffer_us); - chip->prev_ns = time_a; - return buffer_us; + return (unsigned long)(time_b - time_a) / 1000; }; static int ina2xx_capture_thread(void *data) { - struct iio_dev *indio_dev = (struct iio_dev *)data; + struct iio_dev *indio_dev = data; struct ina2xx_chip_info *chip = iio_priv(indio_dev); unsigned int sampling_us = SAMPLING_PERIOD(chip); int buffer_us; @@ -530,12 +524,13 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) struct ina2xx_chip_info *chip = iio_priv(indio_dev); unsigned int sampling_us = SAMPLING_PERIOD(chip); - trace_printk("Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n", - (unsigned int)(*indio_dev->active_scan_mask), - 1000000/sampling_us, chip->avg); + dev_dbg(&indio_dev->dev, "Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n", + (unsigned int)(*indio_dev->active_scan_mask), + 1000000 / sampling_us, chip->avg); - trace_printk("Expected work period: %u us\n", sampling_us); - trace_printk("Async readout mode: %d\n", chip->allow_async_readout); + dev_dbg(&indio_dev->dev, "Expected work period: %u us\n", sampling_us); + dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", + chip->allow_async_readout); chip->prev_ns = iio_get_time_ns(); @@ -575,8 +570,7 @@ static int ina2xx_debug_reg(struct iio_dev *indio_dev, } /* Possible integration times for vshunt and vbus */ -static IIO_CONST_ATTR_INT_TIME_AVAIL \ - ("0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244"); +static IIO_CONST_ATTR_INT_TIME_AVAIL("0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244"); static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR, ina2xx_allow_async_readout_show, @@ -598,21 +592,23 @@ static const struct attribute_group ina2xx_attribute_group = { }; static const struct iio_info ina2xx_info = { - .debugfs_reg_access = &ina2xx_debug_reg, - .read_raw = &ina2xx_read_raw, - .write_raw = &ina2xx_write_raw, - .attrs = &ina2xx_attribute_group, .driver_module = THIS_MODULE, + .attrs = &ina2xx_attribute_group, + .read_raw = ina2xx_read_raw, + .write_raw = ina2xx_write_raw, + .debugfs_reg_access = ina2xx_debug_reg, }; /* Initialize the configuration and calibration registers. */ static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config) { u16 regval; - int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config); + int ret; - if (ret < 0) + ret = regmap_write(chip->regmap, INA2XX_CONFIG, config); + if (ret) return ret; + /* * Set current LSB to 1mA, shunt is in uOhms * (equation 13 in datasheet). We hardcode a Current_LSB @@ -621,7 +617,7 @@ static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config) * to the user for now. */ regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor, - chip->shunt_resistor); + chip->shunt_resistor); return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval); } @@ -632,8 +628,8 @@ static int ina2xx_probe(struct i2c_client *client, struct ina2xx_chip_info *chip; struct iio_dev *indio_dev; struct iio_buffer *buffer; - int ret; unsigned int val; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); if (!indio_dev) @@ -641,8 +637,19 @@ static int ina2xx_probe(struct i2c_client *client, chip = iio_priv(indio_dev); + /* This is only used for device removal purposes. */ + i2c_set_clientdata(client, indio_dev); + + chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); + if (IS_ERR(chip->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(chip->regmap); + } + chip->config = &ina2xx_config[id->driver_data]; + mutex_init(&chip->state_lock); + if (of_property_read_u32(client->dev.of_node, "shunt-resistor", &val) < 0) { struct ina2xx_platform_data *pdata = @@ -658,25 +665,6 @@ static int ina2xx_probe(struct i2c_client *client, if (ret) return ret; - mutex_init(&chip->state_lock); - - /* This is only used for device removal purposes. */ - i2c_set_clientdata(client, indio_dev); - - indio_dev->name = id->name; - indio_dev->channels = ina2xx_channels; - indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels); - - indio_dev->dev.parent = &client->dev; - indio_dev->info = &ina2xx_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; - - chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); - if (IS_ERR(chip->regmap)) { - dev_err(&client->dev, "failed to allocate register map\n"); - return PTR_ERR(chip->regmap); - } - /* Patch the current config register with default. */ val = chip->config->config_default; @@ -687,24 +675,28 @@ static int ina2xx_probe(struct i2c_client *client, } ret = ina2xx_init(chip, val); - if (ret < 0) { - dev_err(&client->dev, "error configuring the device: %d\n", - ret); - return -ENODEV; + if (ret) { + dev_err(&client->dev, "error configuring the device\n"); + return ret; } + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->dev.parent = &client->dev; + indio_dev->channels = ina2xx_channels; + indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels); + indio_dev->name = id->name; + indio_dev->info = &ina2xx_info; + indio_dev->setup_ops = &ina2xx_setup_ops; + buffer = devm_iio_kfifo_allocate(&indio_dev->dev); if (!buffer) return -ENOMEM; - indio_dev->setup_ops = &ina2xx_setup_ops; - iio_device_attach_buffer(indio_dev, buffer); return iio_device_register(indio_dev); } - static int ina2xx_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); @@ -717,7 +709,6 @@ static int ina2xx_remove(struct i2c_client *client) INA2XX_MODE_MASK, 0); } - static const struct i2c_device_id ina2xx_id[] = { {"ina219", ina219}, {"ina220", ina219}, @@ -726,7 +717,6 @@ static const struct i2c_device_id ina2xx_id[] = { {"ina231", ina226}, {} }; - MODULE_DEVICE_TABLE(i2c, ina2xx_id); static struct i2c_driver ina2xx_driver = { @@ -737,7 +727,6 @@ static struct i2c_driver ina2xx_driver = { .remove = ina2xx_remove, .id_table = ina2xx_id, }; - module_i2c_driver(ina2xx_driver); MODULE_AUTHOR("Marc Titinger "); diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index d1c05f6eed18..a850ca7d1eda 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -187,26 +187,27 @@ out: .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ } -#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \ +#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ - .channel = (num * 2), \ - .channel2 = (num * 2 + 1), \ - .address = (num * 2), \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .address = (chan1), \ .differential = 1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ } static const struct iio_chan_spec mcp3201_channels[] = { - MCP320X_VOLTAGE_CHANNEL_DIFF(0), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), }; static const struct iio_chan_spec mcp3202_channels[] = { MCP320X_VOLTAGE_CHANNEL(0), MCP320X_VOLTAGE_CHANNEL(1), - MCP320X_VOLTAGE_CHANNEL_DIFF(0), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), }; static const struct iio_chan_spec mcp3204_channels[] = { @@ -214,8 +215,10 @@ static const struct iio_chan_spec mcp3204_channels[] = { MCP320X_VOLTAGE_CHANNEL(1), MCP320X_VOLTAGE_CHANNEL(2), MCP320X_VOLTAGE_CHANNEL(3), - MCP320X_VOLTAGE_CHANNEL_DIFF(0), - MCP320X_VOLTAGE_CHANNEL_DIFF(1), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), + MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3), + MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2), }; static const struct iio_chan_spec mcp3208_channels[] = { @@ -227,10 +230,14 @@ static const struct iio_chan_spec mcp3208_channels[] = { MCP320X_VOLTAGE_CHANNEL(5), MCP320X_VOLTAGE_CHANNEL(6), MCP320X_VOLTAGE_CHANNEL(7), - MCP320X_VOLTAGE_CHANNEL_DIFF(0), - MCP320X_VOLTAGE_CHANNEL_DIFF(1), - MCP320X_VOLTAGE_CHANNEL_DIFF(2), - MCP320X_VOLTAGE_CHANNEL_DIFF(3), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), + MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3), + MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2), + MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5), + MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4), + MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7), + MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6), }; static const struct iio_info mcp320x_info = { diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 6eca7aea8a37..d7b36efd2f3c 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -1,11 +1,12 @@ /* - * mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family + * mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family * * Copyright (C) 2013, Angelo Compagnucci * Author: Angelo Compagnucci * * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf * http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf + * http://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf * * This driver exports the value of analog input voltage to sysfs, the * voltage unit is nV. @@ -338,7 +339,7 @@ static int mcp3422_probe(struct i2c_client *client, u8 config; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -ENODEV; + return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc)); if (!indio_dev) @@ -357,6 +358,7 @@ static int mcp3422_probe(struct i2c_client *client, switch (adc->id) { case 1: + case 5: indio_dev->channels = mcp3421_channels; indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels); break; @@ -395,6 +397,7 @@ static const struct i2c_device_id mcp3422_id[] = { { "mcp3422", 2 }, { "mcp3423", 3 }, { "mcp3424", 4 }, + { "mcp3425", 5 }, { "mcp3426", 6 }, { "mcp3427", 7 }, { "mcp3428", 8 }, @@ -421,5 +424,5 @@ static struct i2c_driver mcp3422_driver = { module_i2c_driver(mcp3422_driver); MODULE_AUTHOR("Angelo Compagnucci "); -MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver"); +MODULE_DESCRIPTION("Microchip mcp3421/2/3/4/5/6/7/8 driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c similarity index 99% rename from drivers/staging/iio/adc/mxs-lradc.c rename to drivers/iio/adc/mxs-lradc.c index bb1f15224ac8..33051b87aac2 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/iio/adc/mxs-lradc.c @@ -443,7 +443,8 @@ static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1), LRADC_CH(ch)); - /* from the datasheet: + /* + * from the datasheet: * "Software must clear this register in preparation for a * multi-cycle accumulation. */ @@ -504,7 +505,8 @@ static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1, mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1)); mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2)); - /* from the datasheet: + /* + * from the datasheet: * "Software must clear this register in preparation for a * multi-cycle accumulation. */ @@ -914,7 +916,8 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_TEMP) { - /* From the datasheet, we have to multiply by 1.012 and + /* + * From the datasheet, we have to multiply by 1.012 and * divide by 4 */ *val = 0; @@ -929,7 +932,8 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_OFFSET: if (chan->type == IIO_TEMP) { - /* The calculated value from the ADC is in Kelvin, we + /* + * The calculated value from the ADC is in Kelvin, we * want Celsius for hwmon so the offset is -273.15 * The offset is applied before scaling so it is * actually -213.15 * 4 / 1.012 = -1079.644268 @@ -1750,6 +1754,7 @@ static int mxs_lradc_remove(struct platform_device *pdev) iio_triggered_buffer_cleanup(iio); clk_disable_unprepare(lradc->clk); + return 0; } diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index f42eb8a7d21f..2bbf0c521beb 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -534,7 +534,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev) } ret = request_threaded_irq(adc->irq, NULL, palmas_gpadc_irq, - IRQF_ONESHOT | IRQF_EARLY_RESUME, dev_name(adc->dev), + IRQF_ONESHOT, dev_name(adc->dev), adc); if (ret < 0) { dev_err(adc->dev, @@ -549,7 +549,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->irq_auto_0 = platform_get_irq(pdev, 1); ret = request_threaded_irq(adc->irq_auto_0, NULL, palmas_gpadc_irq_auto, - IRQF_ONESHOT | IRQF_EARLY_RESUME, + IRQF_ONESHOT, "palmas-adc-auto-0", adc); if (ret < 0) { dev_err(adc->dev, "request auto0 irq %d failed: %d\n", @@ -565,7 +565,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->irq_auto_1 = platform_get_irq(pdev, 2); ret = request_threaded_irq(adc->irq_auto_1, NULL, palmas_gpadc_irq_auto, - IRQF_ONESHOT | IRQF_EARLY_RESUME, + IRQF_ONESHOT, "palmas-adc-auto-1", adc); if (ret < 0) { dev_err(adc->dev, "request auto1 irq %d failed: %d\n", diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 2c8374f86252..ecbc12138d58 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -73,7 +73,7 @@ static int adc081c_probe(struct i2c_client *client, int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; + return -EOPNOTSUPP; iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); if (!iio) diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c new file mode 100644 index 000000000000..0afeac0c9bad --- /dev/null +++ b/drivers/iio/adc/ti-adc0832.c @@ -0,0 +1,288 @@ +/* + * ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver + * + * Copyright (c) 2016 Akinobu Mita + * + * 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. + * + * Datasheet: http://www.ti.com/lit/ds/symlink/adc0832-n.pdf + */ + +#include +#include +#include +#include + +enum { + adc0831, + adc0832, + adc0834, + adc0838, +}; + +struct adc0832 { + struct spi_device *spi; + struct regulator *reg; + struct mutex lock; + u8 mux_bits; + + u8 tx_buf[2] ____cacheline_aligned; + u8 rx_buf[2]; +}; + +#define ADC0832_VOLTAGE_CHANNEL(chan) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +static const struct iio_chan_spec adc0831_channels[] = { + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1), +}; + +static const struct iio_chan_spec adc0832_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0), +}; + +static const struct iio_chan_spec adc0834_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL(2), + ADC0832_VOLTAGE_CHANNEL(3), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0), + ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3), + ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2), +}; + +static const struct iio_chan_spec adc0838_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL(2), + ADC0832_VOLTAGE_CHANNEL(3), + ADC0832_VOLTAGE_CHANNEL(4), + ADC0832_VOLTAGE_CHANNEL(5), + ADC0832_VOLTAGE_CHANNEL(6), + ADC0832_VOLTAGE_CHANNEL(7), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0), + ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3), + ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2), + ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5), + ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4), + ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7), + ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6), +}; + +static int adc0831_adc_conversion(struct adc0832 *adc) +{ + struct spi_device *spi = adc->spi; + int ret; + + ret = spi_read(spi, &adc->rx_buf, 2); + if (ret) + return ret; + + /* + * Skip TRI-STATE and a leading zero + */ + return (adc->rx_buf[0] << 2 & 0xff) | (adc->rx_buf[1] >> 6); +} + +static int adc0832_adc_conversion(struct adc0832 *adc, int channel, + bool differential) +{ + struct spi_device *spi = adc->spi; + struct spi_transfer xfer = { + .tx_buf = adc->tx_buf, + .rx_buf = adc->rx_buf, + .len = 2, + }; + int ret; + + if (!adc->mux_bits) + return adc0831_adc_conversion(adc); + + /* start bit */ + adc->tx_buf[0] = 1 << (adc->mux_bits + 1); + /* single-ended or differential */ + adc->tx_buf[0] |= differential ? 0 : (1 << adc->mux_bits); + /* odd / sign */ + adc->tx_buf[0] |= (channel % 2) << (adc->mux_bits - 1); + /* select */ + if (adc->mux_bits > 1) + adc->tx_buf[0] |= channel / 2; + + /* align Data output BIT7 (MSB) to 8-bit boundary */ + adc->tx_buf[0] <<= 1; + + ret = spi_sync_transfer(spi, &xfer, 1); + if (ret) + return ret; + + return adc->rx_buf[1]; +} + +static int adc0832_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc0832 *adc = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *value = adc0832_adc_conversion(adc, channel->channel, + channel->differential); + mutex_unlock(&adc->lock); + if (*value < 0) + return *value; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *value = regulator_get_voltage(adc->reg); + if (*value < 0) + return *value; + + /* convert regulator output voltage to mV */ + *value /= 1000; + *shift = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info adc0832_info = { + .read_raw = adc0832_read_raw, + .driver_module = THIS_MODULE, +}; + +static int adc0832_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc0832 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adc0832_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (spi_get_device_id(spi)->driver_data) { + case adc0831: + adc->mux_bits = 0; + indio_dev->channels = adc0831_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0831_channels); + break; + case adc0832: + adc->mux_bits = 1; + indio_dev->channels = adc0832_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0832_channels); + break; + case adc0834: + adc->mux_bits = 2; + indio_dev->channels = adc0834_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0834_channels); + break; + case adc0838: + adc->mux_bits = 3; + indio_dev->channels = adc0838_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0838_channels); + break; + default: + return -EINVAL; + } + + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return PTR_ERR(adc->reg); + + ret = regulator_enable(adc->reg); + if (ret) + return ret; + + spi_set_drvdata(spi, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret) + regulator_disable(adc->reg); + + return ret; +} + +static int adc0832_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adc0832 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(adc->reg); + + return 0; +} + +#ifdef CONFIG_OF + +static const struct of_device_id adc0832_dt_ids[] = { + { .compatible = "ti,adc0831", }, + { .compatible = "ti,adc0832", }, + { .compatible = "ti,adc0834", }, + { .compatible = "ti,adc0838", }, + {} +}; +MODULE_DEVICE_TABLE(of, adc0832_dt_ids); + +#endif + +static const struct spi_device_id adc0832_id[] = { + { "adc0831", adc0831 }, + { "adc0832", adc0832 }, + { "adc0834", adc0834 }, + { "adc0838", adc0838 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adc0832_id); + +static struct spi_driver adc0832_driver = { + .driver = { + .name = "adc0832", + .of_match_table = of_match_ptr(adc0832_dt_ids), + }, + .probe = adc0832_probe, + .remove = adc0832_remove, + .id_table = adc0832_id, +}; +module_spi_driver(adc0832_driver); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_DESCRIPTION("ADC0831/ADC0832/ADC0834/ADC0838 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c new file mode 100644 index 000000000000..73cbf0b54e54 --- /dev/null +++ b/drivers/iio/adc/ti-ads1015.c @@ -0,0 +1,612 @@ +/* + * ADS1015 - Texas Instruments Analog-to-Digital Converter + * + * Copyright (c) 2016, 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 ADS1015 ADC 7-bit I2C slave address: + * * 0x48 - ADDR connected to Ground + * * 0x49 - ADDR connected to Vdd + * * 0x4A - ADDR connected to SDA + * * 0x4B - ADDR connected to SCL + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define ADS1015_DRV_NAME "ads1015" + +#define ADS1015_CONV_REG 0x00 +#define ADS1015_CFG_REG 0x01 + +#define ADS1015_CFG_DR_SHIFT 5 +#define ADS1015_CFG_MOD_SHIFT 8 +#define ADS1015_CFG_PGA_SHIFT 9 +#define ADS1015_CFG_MUX_SHIFT 12 + +#define ADS1015_CFG_DR_MASK GENMASK(7, 5) +#define ADS1015_CFG_MOD_MASK BIT(8) +#define ADS1015_CFG_PGA_MASK GENMASK(11, 9) +#define ADS1015_CFG_MUX_MASK GENMASK(14, 12) + +/* device operating modes */ +#define ADS1015_CONTINUOUS 0 +#define ADS1015_SINGLESHOT 1 + +#define ADS1015_SLEEP_DELAY_MS 2000 +#define ADS1015_DEFAULT_PGA 2 +#define ADS1015_DEFAULT_DATA_RATE 4 +#define ADS1015_DEFAULT_CHAN 0 + +enum ads1015_channels { + ADS1015_AIN0_AIN1 = 0, + ADS1015_AIN0_AIN3, + ADS1015_AIN1_AIN3, + ADS1015_AIN2_AIN3, + ADS1015_AIN0, + ADS1015_AIN1, + ADS1015_AIN2, + ADS1015_AIN3, + ADS1015_TIMESTAMP, +}; + +static const unsigned int ads1015_data_rate[] = { + 128, 250, 490, 920, 1600, 2400, 3300, 3300 +}; + +static const struct { + int scale; + int uscale; +} ads1015_scale[] = { + {3, 0}, + {2, 0}, + {1, 0}, + {0, 500000}, + {0, 250000}, + {0, 125000}, + {0, 125000}, + {0, 125000}, +}; + +#define ADS1015_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ +} + +struct ads1015_data { + struct regmap *regmap; + /* + * Protects ADC ops, e.g: concurrent sysfs/buffered + * data reads, configuration updates + */ + struct mutex lock; + struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; +}; + +static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return (reg == ADS1015_CFG_REG); +} + +static const struct regmap_config ads1015_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = ADS1015_CFG_REG, + .writeable_reg = ads1015_is_writeable_reg, +}; + +static const struct iio_chan_spec ads1015_channels[] = { + ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1), + ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3), + ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3), + ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3), + ADS1015_V_CHAN(0, ADS1015_AIN0), + ADS1015_V_CHAN(1, ADS1015_AIN1), + ADS1015_V_CHAN(2, ADS1015_AIN2), + ADS1015_V_CHAN(3, ADS1015_AIN3), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + +static int ads1015_set_power_state(struct ads1015_data *data, bool on) +{ + int ret; + struct device *dev = regmap_get_device(data->regmap); + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret; +} + +static +int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val) +{ + int ret, pga, dr, conv_time; + bool change; + + if (chan < 0 || chan >= ADS1015_CHANNELS) + return -EINVAL; + + pga = data->channel_data[chan].pga; + dr = data->channel_data[chan].data_rate; + + ret = regmap_update_bits_check(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_MUX_MASK | + ADS1015_CFG_PGA_MASK, + chan << ADS1015_CFG_MUX_SHIFT | + pga << ADS1015_CFG_PGA_SHIFT, + &change); + if (ret < 0) + return ret; + + if (change) { + conv_time = DIV_ROUND_UP(USEC_PER_SEC, ads1015_data_rate[dr]); + usleep_range(conv_time, conv_time + 1); + } + + return regmap_read(data->regmap, ADS1015_CONV_REG, val); +} + +static irqreturn_t ads1015_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads1015_data *data = iio_priv(indio_dev); + s16 buf[8]; /* 1x s16 ADC val + 3x s16 padding + 4x s16 timestamp */ + int chan, ret, res; + + memset(buf, 0, sizeof(buf)); + + mutex_lock(&data->lock); + chan = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + ret = ads1015_get_adc_result(data, chan, &res); + if (ret < 0) { + mutex_unlock(&data->lock); + goto err; + } + + buf[0] = res; + mutex_unlock(&data->lock); + + iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ads1015_set_scale(struct ads1015_data *data, int chan, + int scale, int uscale) +{ + int i, ret, rindex = -1; + + for (i = 0; i < ARRAY_SIZE(ads1015_scale); i++) + if (ads1015_scale[i].scale == scale && + ads1015_scale[i].uscale == uscale) { + rindex = i; + break; + } + if (rindex < 0) + return -EINVAL; + + ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_PGA_MASK, + rindex << ADS1015_CFG_PGA_SHIFT); + if (ret < 0) + return ret; + + data->channel_data[chan].pga = rindex; + + return 0; +} + +static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate) +{ + int i, ret, rindex = -1; + + for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++) + if (ads1015_data_rate[i] == rate) { + rindex = i; + break; + } + if (rindex < 0) + return -EINVAL; + + ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_DR_MASK, + rindex << ADS1015_CFG_DR_SHIFT); + if (ret < 0) + return ret; + + data->channel_data[chan].data_rate = rindex; + + return 0; +} + +static int ads1015_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret, idx; + struct ads1015_data *data = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) { + ret = -EBUSY; + break; + } + + ret = ads1015_set_power_state(data, true); + if (ret < 0) + break; + + ret = ads1015_get_adc_result(data, chan->address, val); + if (ret < 0) { + ads1015_set_power_state(data, false); + break; + } + + /* 12 bit res, D0 is bit 4 in conversion register */ + *val = sign_extend32(*val >> 4, 11); + + ret = ads1015_set_power_state(data, false); + if (ret < 0) + break; + + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + idx = data->channel_data[chan->address].pga; + *val = ads1015_scale[idx].scale; + *val2 = ads1015_scale[idx].uscale; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + idx = data->channel_data[chan->address].data_rate; + *val = ads1015_data_rate[idx]; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ads1015_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1015_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = ads1015_set_scale(data, chan->address, val, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ads1015_set_data_rate(data, chan->address, val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1015_buffer_preenable(struct iio_dev *indio_dev) +{ + return ads1015_set_power_state(iio_priv(indio_dev), true); +} + +static int ads1015_buffer_postdisable(struct iio_dev *indio_dev) +{ + return ads1015_set_power_state(iio_priv(indio_dev), false); +} + +static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = { + .preenable = ads1015_buffer_preenable, + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, + .postdisable = ads1015_buffer_postdisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125"); +static IIO_CONST_ATTR(sampling_frequency_available, + "128 250 490 920 1600 2400 3300"); + +static struct attribute *ads1015_attributes[] = { + &iio_const_attr_scale_available.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ads1015_attribute_group = { + .attrs = ads1015_attributes, +}; + +static const struct iio_info ads1015_info = { + .driver_module = THIS_MODULE, + .read_raw = ads1015_read_raw, + .write_raw = ads1015_write_raw, + .attrs = &ads1015_attribute_group, +}; + +#ifdef CONFIG_OF +static int ads1015_get_channels_config_of(struct i2c_client *client) +{ + struct ads1015_data *data = i2c_get_clientdata(client); + struct device_node *node; + + if (!client->dev.of_node || + !of_get_next_child(client->dev.of_node, NULL)) + return -EINVAL; + + for_each_child_of_node(client->dev.of_node, node) { + u32 pval; + unsigned int channel; + unsigned int pga = ADS1015_DEFAULT_PGA; + unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; + + if (of_property_read_u32(node, "reg", &pval)) { + dev_err(&client->dev, "invalid reg on %s\n", + node->full_name); + continue; + } + + channel = pval; + if (channel >= ADS1015_CHANNELS) { + dev_err(&client->dev, + "invalid channel index %d on %s\n", + channel, node->full_name); + continue; + } + + if (!of_property_read_u32(node, "ti,gain", &pval)) { + pga = pval; + if (pga > 6) { + dev_err(&client->dev, "invalid gain on %s\n", + node->full_name); + return -EINVAL; + } + } + + if (!of_property_read_u32(node, "ti,datarate", &pval)) { + data_rate = pval; + if (data_rate > 7) { + dev_err(&client->dev, + "invalid data_rate on %s\n", + node->full_name); + return -EINVAL; + } + } + + data->channel_data[channel].pga = pga; + data->channel_data[channel].data_rate = data_rate; + } + + return 0; +} +#endif + +static void ads1015_get_channels_config(struct i2c_client *client) +{ + unsigned int k; + + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); + struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); + + /* prefer platform data */ + if (pdata) { + memcpy(data->channel_data, pdata->channel_data, + sizeof(data->channel_data)); + return; + } + +#ifdef CONFIG_OF + if (!ads1015_get_channels_config_of(client)) + return; +#endif + /* fallback on default configuration */ + for (k = 0; k < ADS1015_CHANNELS; ++k) { + data->channel_data[k].pga = ADS1015_DEFAULT_PGA; + data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE; + } +} + +static int ads1015_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ads1015_data *data; + 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); + + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &ads1015_info; + indio_dev->name = ADS1015_DRV_NAME; + indio_dev->channels = ads1015_channels; + indio_dev->num_channels = ARRAY_SIZE(ads1015_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + /* we need to keep this ABI the same as used by hwmon ADS1015 driver */ + ads1015_get_channels_config(client); + + data->regmap = devm_regmap_init_i2c(client, &ads1015_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "Failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ads1015_trigger_handler, + &ads1015_buffer_setup_ops); + if (ret < 0) { + dev_err(&client->dev, "iio triggered buffer setup failed\n"); + return ret; + } + ret = pm_runtime_set_active(&client->dev); + if (ret) + goto err_buffer_cleanup; + pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_enable(&client->dev); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "Failed to register IIO device\n"); + goto err_buffer_cleanup; + } + + return 0; + +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; +} + +static int ads1015_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + iio_triggered_buffer_cleanup(indio_dev); + + /* power down single shot mode */ + return regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_MOD_MASK, + ADS1015_SINGLESHOT << ADS1015_CFG_MOD_SHIFT); +} + +#ifdef CONFIG_PM +static int ads1015_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ads1015_data *data = iio_priv(indio_dev); + + return regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_MOD_MASK, + ADS1015_SINGLESHOT << ADS1015_CFG_MOD_SHIFT); +} + +static int ads1015_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ads1015_data *data = iio_priv(indio_dev); + + return regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_MOD_MASK, + ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT); +} +#endif + +static const struct dev_pm_ops ads1015_pm_ops = { + SET_RUNTIME_PM_OPS(ads1015_runtime_suspend, + ads1015_runtime_resume, NULL) +}; + +static const struct i2c_device_id ads1015_id[] = { + {"ads1015", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ads1015_id); + +static struct i2c_driver ads1015_driver = { + .driver = { + .name = ADS1015_DRV_NAME, + .pm = &ads1015_pm_ops, + }, + .probe = ads1015_probe, + .remove = ads1015_remove, + .id_table = ads1015_id, +}; + +module_i2c_driver(ads1015_driver); + +MODULE_AUTHOR("Daniel Baluta "); +MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index ebdb838d3a1c..9fabed47053d 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -93,12 +93,7 @@ static void iio_dmaengine_buffer_abort(struct iio_dma_buffer_queue *queue) struct dmaengine_buffer *dmaengine_buffer = iio_buffer_to_dmaengine_buffer(&queue->buffer); - dmaengine_terminate_all(dmaengine_buffer->chan); - /* FIXME: There is a slight chance of a race condition here. - * dmaengine_terminate_all() does not guarantee that all transfer - * callbacks have finished running. Need to introduce a - * dmaengine_terminate_all_sync(). - */ + dmaengine_terminate_sync(dmaengine_buffer->chan); iio_dma_buffer_block_list_abort(queue, &dmaengine_buffer->active); } diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index f16de61be46d..f73290f84c90 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -4,6 +4,20 @@ menu "Chemical Sensors" +config ATLAS_PH_SENSOR + tristate "Atlas Scientific OEM pH-SM sensor" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select IRQ_WORK + help + Say Y here to build I2C interface support for the Atlas + Scientific OEM pH-SM sensor. + + To compile this driver as module, choose M here: the + module will be called atlas-ph-sensor. + config IAQCORE tristate "AMS iAQ-Core VOC sensors" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index 167861fadfab..b02202b41289 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -3,5 +3,6 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o obj-$(CONFIG_IAQCORE) += ams-iaq-core.o obj-$(CONFIG_VZ89X) += vz89x.o diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c new file mode 100644 index 000000000000..62b37cd8fb56 --- /dev/null +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -0,0 +1,509 @@ +/* + * atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor + * + * Copyright (C) 2015 Matt Ranostay + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ATLAS_REGMAP_NAME "atlas_ph_regmap" +#define ATLAS_DRV_NAME "atlas_ph" + +#define ATLAS_REG_DEV_TYPE 0x00 +#define ATLAS_REG_DEV_VERSION 0x01 + +#define ATLAS_REG_INT_CONTROL 0x04 +#define ATLAS_REG_INT_CONTROL_EN BIT(3) + +#define ATLAS_REG_PWR_CONTROL 0x06 + +#define ATLAS_REG_CALIB_STATUS 0x0d +#define ATLAS_REG_CALIB_STATUS_MASK 0x07 +#define ATLAS_REG_CALIB_STATUS_LOW BIT(0) +#define ATLAS_REG_CALIB_STATUS_MID BIT(1) +#define ATLAS_REG_CALIB_STATUS_HIGH BIT(2) + +#define ATLAS_REG_TEMP_DATA 0x0e +#define ATLAS_REG_PH_DATA 0x16 + +#define ATLAS_PH_INT_TIME_IN_US 450000 + +struct atlas_data { + struct i2c_client *client; + struct iio_trigger *trig; + struct regmap *regmap; + struct irq_work work; + + __be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */ +}; + +static const struct regmap_range atlas_volatile_ranges[] = { + regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL), + regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4), +}; + +static const struct regmap_access_table atlas_volatile_table = { + .yes_ranges = atlas_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges), +}; + +static const struct regmap_config atlas_regmap_config = { + .name = ATLAS_REGMAP_NAME, + + .reg_bits = 8, + .val_bits = 8, + + .volatile_table = &atlas_volatile_table, + .max_register = ATLAS_REG_PH_DATA + 4, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct iio_chan_spec atlas_channels[] = { + { + .type = IIO_PH, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), + { + .type = IIO_TEMP, + .address = ATLAS_REG_TEMP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .output = 1, + .scan_index = -1 + }, +}; + +static int atlas_set_powermode(struct atlas_data *data, int on) +{ + return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on); +} + +static int atlas_set_interrupt(struct atlas_data *data, bool state) +{ + return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL, + ATLAS_REG_INT_CONTROL_EN, + state ? ATLAS_REG_INT_CONTROL_EN : 0); +} + +static int atlas_buffer_postenable(struct iio_dev *indio_dev) +{ + struct atlas_data *data = iio_priv(indio_dev); + int ret; + + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret) + return ret; + + ret = pm_runtime_get_sync(&data->client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&data->client->dev); + return ret; + } + + return atlas_set_interrupt(data, true); +} + +static int atlas_buffer_predisable(struct iio_dev *indio_dev) +{ + struct atlas_data *data = iio_priv(indio_dev); + int ret; + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret) + return ret; + + ret = atlas_set_interrupt(data, false); + if (ret) + return ret; + + pm_runtime_mark_last_busy(&data->client->dev); + return pm_runtime_put_autosuspend(&data->client->dev); +} + +static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { + .owner = THIS_MODULE, +}; + +static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { + .postenable = atlas_buffer_postenable, + .predisable = atlas_buffer_predisable, +}; + +static void atlas_work_handler(struct irq_work *work) +{ + struct atlas_data *data = container_of(work, struct atlas_data, work); + + iio_trigger_poll(data->trig); +} + +static irqreturn_t atlas_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct atlas_data *data = iio_priv(indio_dev); + int ret; + + ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA, + (u8 *) &data->buffer, sizeof(data->buffer[0])); + + if (!ret) + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_get_time_ns()); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t atlas_interrupt_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct atlas_data *data = iio_priv(indio_dev); + + irq_work_queue(&data->work); + + return IRQ_HANDLED; +} + +static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val) +{ + struct device *dev = &data->client->dev; + int suspended = pm_runtime_suspended(dev); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + if (suspended) + usleep_range(ATLAS_PH_INT_TIME_IN_US, + ATLAS_PH_INT_TIME_IN_US + 100000); + + ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA, + (u8 *) val, sizeof(*val)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int atlas_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct atlas_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + int ret; + __be32 reg; + + switch (chan->type) { + case IIO_TEMP: + ret = regmap_bulk_read(data->regmap, chan->address, + (u8 *) ®, sizeof(reg)); + break; + case IIO_PH: + mutex_lock(&indio_dev->mlock); + + if (iio_buffer_enabled(indio_dev)) + ret = -EBUSY; + else + ret = atlas_read_ph_measurement(data, ®); + + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + if (!ret) { + *val = be32_to_cpu(reg); + ret = IIO_VAL_INT; + } + return ret; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = 1; /* 0.01 */ + *val2 = 100; + break; + case IIO_PH: + *val = 1; /* 0.001 */ + *val2 = 1000; + break; + default: + return -EINVAL; + } + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int atlas_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct atlas_data *data = iio_priv(indio_dev); + __be32 reg = cpu_to_be32(val); + + if (val2 != 0 || val < 0 || val > 20000) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_TEMP) + return -EINVAL; + + return regmap_bulk_write(data->regmap, chan->address, + ®, sizeof(reg)); +} + +static const struct iio_info atlas_info = { + .driver_module = THIS_MODULE, + .read_raw = atlas_read_raw, + .write_raw = atlas_write_raw, +}; + +static int atlas_check_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_CALIB_STATUS_MID)) + dev_warn(dev, "device missing mid point calibration\n"); + + if (!(val & ATLAS_REG_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + + return 0; +}; + +static int atlas_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct atlas_data *data; + struct iio_trigger *trig; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &atlas_info; + indio_dev->name = ATLAS_DRV_NAME; + indio_dev->channels = atlas_channels; + indio_dev->num_channels = ARRAY_SIZE(atlas_channels); + indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; + indio_dev->dev.parent = &client->dev; + + trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + + if (!trig) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + data->trig = trig; + trig->dev.parent = indio_dev->dev.parent; + trig->ops = &atlas_interrupt_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + + i2c_set_clientdata(client, indio_dev); + + data->regmap = devm_regmap_init_i2c(client, &atlas_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "regmap initialization failed\n"); + return PTR_ERR(data->regmap); + } + + ret = pm_runtime_set_active(&client->dev); + if (ret) + return ret; + + if (client->irq <= 0) { + dev_err(&client->dev, "no valid irq defined\n"); + return -EINVAL; + } + + ret = atlas_check_calibration(data); + if (ret) + return ret; + + ret = iio_trigger_register(trig); + if (ret) { + dev_err(&client->dev, "failed to register trigger\n"); + return ret; + } + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &atlas_trigger_handler, &atlas_buffer_setup_ops); + if (ret) { + dev_err(&client->dev, "cannot setup iio trigger\n"); + goto unregister_trigger; + } + + init_irq_work(&data->work, atlas_work_handler); + + /* interrupt pin toggles on new conversion */ + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, atlas_interrupt_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "atlas_irq", + indio_dev); + if (ret) { + dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + goto unregister_buffer; + } + + ret = atlas_set_powermode(data, 1); + if (ret) { + dev_err(&client->dev, "cannot power device on"); + goto unregister_buffer; + } + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 2500); + pm_runtime_use_autosuspend(&client->dev); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&client->dev, "unable to register device\n"); + goto unregister_pm; + } + + return 0; + +unregister_pm: + pm_runtime_disable(&client->dev); + atlas_set_powermode(data, 0); + +unregister_buffer: + iio_triggered_buffer_cleanup(indio_dev); + +unregister_trigger: + iio_trigger_unregister(data->trig); + + return ret; +} + +static int atlas_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct atlas_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + iio_trigger_unregister(data->trig); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + return atlas_set_powermode(data, 0); +} + +#ifdef CONFIG_PM +static int atlas_runtime_suspend(struct device *dev) +{ + struct atlas_data *data = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return atlas_set_powermode(data, 0); +} + +static int atlas_runtime_resume(struct device *dev) +{ + struct atlas_data *data = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return atlas_set_powermode(data, 1); +} +#endif + +static const struct dev_pm_ops atlas_pm_ops = { + SET_RUNTIME_PM_OPS(atlas_runtime_suspend, + atlas_runtime_resume, NULL) +}; + +static const struct i2c_device_id atlas_id[] = { + { "atlas-ph-sm", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, atlas_id); + +static const struct of_device_id atlas_dt_ids[] = { + { .compatible = "atlas,ph-sm" }, + { } +}; +MODULE_DEVICE_TABLE(of, atlas_dt_ids); + +static struct i2c_driver atlas_driver = { + .driver = { + .name = ATLAS_DRV_NAME, + .of_match_table = of_match_ptr(atlas_dt_ids), + .pm = &atlas_pm_ops, + }, + .probe = atlas_probe, + .remove = atlas_remove, + .id_table = atlas_id, +}; +module_i2c_driver(atlas_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index b8b804923230..652649da500f 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -249,7 +249,7 @@ static int vz89x_probe(struct i2c_client *client, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) data->xfer = vz89x_smbus_xfer; else - return -ENOTSUPP; + return -EOPNOTSUPP; i2c_set_clientdata(client, indio_dev); data->client = client; diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 8447c31e27f2..f5a2d445d0c0 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -18,13 +18,15 @@ #include #include +#include "st_sensors_core.h" + static inline u32 st_sensors_get_unaligned_le24(const u8 *p) { return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8; } -static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, - u8 reg_addr, u8 mask, u8 data) +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, + u8 reg_addr, u8 mask, u8 data) { int err; u8 new_data; diff --git a/drivers/iio/common/st_sensors/st_sensors_core.h b/drivers/iio/common/st_sensors/st_sensors_core.h new file mode 100644 index 000000000000..cd88098ff6f1 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_core.h @@ -0,0 +1,8 @@ +/* + * Local functions in the ST Sensors core + */ +#ifndef __ST_SENSORS_CORE_H +#define __ST_SENSORS_CORE_H +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, + u8 reg_addr, u8 mask, u8 data); +#endif diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 3e907040c2c7..6a8c98327945 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -14,32 +14,65 @@ #include #include #include - #include - +#include "st_sensors_core.h" int st_sensors_allocate_trigger(struct iio_dev *indio_dev, const struct iio_trigger_ops *trigger_ops) { - int err; + int err, irq; struct st_sensor_data *sdata = iio_priv(indio_dev); + unsigned long irq_trig; sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); if (sdata->trig == NULL) { - err = -ENOMEM; dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); - goto iio_trigger_alloc_error; + return -ENOMEM; } - err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), + irq = sdata->get_irq_data_ready(indio_dev); + irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + /* + * If the IRQ is triggered on falling edge, we need to mark the + * interrupt as active low, if the hardware supports this. + */ + if (irq_trig == IRQF_TRIGGER_FALLING) { + if (!sdata->sensor_settings->drdy_irq.addr_ihl) { + dev_err(&indio_dev->dev, + "falling edge specified for IRQ but hardware " + "only support rising edge, will request " + "rising edge\n"); + irq_trig = IRQF_TRIGGER_RISING; + } else { + /* Set up INT active low i.e. falling edge */ + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor_settings->drdy_irq.addr_ihl, + sdata->sensor_settings->drdy_irq.mask_ihl, 1); + if (err < 0) + goto iio_trigger_free; + dev_info(&indio_dev->dev, + "interrupts on the falling edge\n"); + } + } else if (irq_trig == IRQF_TRIGGER_RISING) { + dev_info(&indio_dev->dev, + "interrupts on the rising edge\n"); + + } else { + dev_err(&indio_dev->dev, + "unsupported IRQ trigger specified (%lx), only " + "rising and falling edges supported, enforce " + "rising edge\n", irq_trig); + irq_trig = IRQF_TRIGGER_RISING; + } + err = request_threaded_irq(irq, iio_trigger_generic_data_rdy_poll, NULL, - IRQF_TRIGGER_RISING, + irq_trig, sdata->trig->name, sdata->trig); if (err) { dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n"); - goto request_irq_error; + goto iio_trigger_free; } iio_trigger_set_drvdata(sdata->trig, indio_dev); @@ -57,9 +90,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, iio_trigger_register_error: free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); -request_irq_error: +iio_trigger_free: iio_trigger_free(sdata->trig); -iio_trigger_alloc_error: return err; } EXPORT_SYMBOL(st_sensors_allocate_trigger); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index e701e28fb1cd..a995139f907c 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -10,8 +10,10 @@ config AD5064 depends on (SPI_MASTER && I2C!=m) || I2C help Say yes here to build support for Analog Devices AD5024, AD5025, AD5044, - AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, AD5648, AD5666, AD5668, - AD5669R Digital to Analog Converter. + AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R, AD5627, AD5627R, + AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R, AD5666, + AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616, + LTC2617, LTC2619, LTC2626, LTC2627, LTC2629 Digital to Analog Converter. To compile this driver as a module, choose M here: the module will be called ad5064. @@ -111,6 +113,16 @@ config AD5755 To compile this driver as a module, choose M here: the module will be called ad5755. +config AD5761 + tristate "Analog Devices AD5761/61R/21/21R DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5761, AD5761R, AD5721, + AD5721R Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5761. + config AD5764 tristate "Analog Devices AD5764/64R/44/44R DAC driver" depends on SPI_MASTER @@ -176,11 +188,11 @@ config MAX5821 10 bits DAC. config MCP4725 - tristate "MCP4725 DAC driver" + tristate "MCP4725/6 DAC driver" depends on I2C ---help--- Say Y here if you want to build a driver for the Microchip - MCP 4725 12-bit digital-to-analog converter (DAC) with I2C + MCP 4725/6 12-bit digital-to-analog converter (DAC) with I2C interface. To compile this driver as a module, choose M here: the module @@ -196,4 +208,23 @@ config MCP4922 To compile this driver as a module, choose M here: the module will be called mcp4922. +config STX104 + tristate "Apex Embedded Systems STX104 DAC driver" + depends on ISA + help + Say yes here to build support for the 2-channel DAC on the Apex + Embedded Systems STX104 integrated analog PC/104 card. The base port + addresses for the devices may be configured via the "base" module + parameter array. + +config VF610_DAC + tristate "Vybrid vf610 DAC driver" + depends on OF + depends on HAS_IOMEM + help + Say yes here to support Vybrid board digital-to-analog converter. + + This driver can also be built as a module. If so, the module will + be called vf610_dac. + endmenu diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 63ae05633e0c..67b48429686d 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_AD5504) += ad5504.o obj-$(CONFIG_AD5446) += ad5446.o obj-$(CONFIG_AD5449) += ad5449.o obj-$(CONFIG_AD5755) += ad5755.o +obj-$(CONFIG_AD5761) += ad5761.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o @@ -21,3 +22,5 @@ obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4922) += mcp4922.o +obj-$(CONFIG_STX104) += stx104.o +obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index 81ca0081a019..6803e4a137cd 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -1,6 +1,9 @@ /* - * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, - * AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver + * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R, + * AD5627, AD5627R, AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R, + * AD5666, AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616, + * LTC2617, LTC2619, LTC2626, LTC2627, LTC2629 Digital to analog converters + * driver * * Copyright 2011 Analog Devices Inc. * @@ -39,6 +42,9 @@ #define AD5064_CMD_RESET 0x7 #define AD5064_CMD_CONFIG 0x8 +#define AD5064_CMD_RESET_V2 0x5 +#define AD5064_CMD_CONFIG_V2 0x7 + #define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1) #define AD5064_CONFIG_INT_VREF_ENABLE BIT(0) @@ -47,13 +53,26 @@ #define AD5064_LDAC_PWRDN_100K 0x2 #define AD5064_LDAC_PWRDN_3STATE 0x3 +/** + * enum ad5064_regmap_type - Register layout variant + * @AD5064_REGMAP_ADI: Old Analog Devices register map layout + * @AD5064_REGMAP_ADI2: New Analog Devices register map layout + * @AD5064_REGMAP_LTC: LTC register map layout + */ +enum ad5064_regmap_type { + AD5064_REGMAP_ADI, + AD5064_REGMAP_ADI2, + AD5064_REGMAP_LTC, +}; + /** * struct ad5064_chip_info - chip specific information * @shared_vref: whether the vref supply is shared between channels - * @internal_vref: internal reference voltage. 0 if the chip has no internal - * vref. + * @internal_vref: internal reference voltage. 0 if the chip has no + internal vref. * @channel: channel specification * @num_channels: number of channels + * @regmap_type: register map layout variant */ struct ad5064_chip_info { @@ -61,6 +80,7 @@ struct ad5064_chip_info { unsigned long internal_vref; const struct iio_chan_spec *channels; unsigned int num_channels; + enum ad5064_regmap_type regmap_type; }; struct ad5064_state; @@ -111,18 +131,43 @@ enum ad5064_type { ID_AD5064, ID_AD5064_1, ID_AD5065, + ID_AD5625, + ID_AD5625R_1V25, + ID_AD5625R_2V5, + ID_AD5627, + ID_AD5627R_1V25, + ID_AD5627R_2V5, ID_AD5628_1, ID_AD5628_2, ID_AD5629_1, ID_AD5629_2, + ID_AD5645R_1V25, + ID_AD5645R_2V5, + ID_AD5647R_1V25, + ID_AD5647R_2V5, ID_AD5648_1, ID_AD5648_2, + ID_AD5665, + ID_AD5665R_1V25, + ID_AD5665R_2V5, ID_AD5666_1, ID_AD5666_2, + ID_AD5667, + ID_AD5667R_1V25, + ID_AD5667R_2V5, ID_AD5668_1, ID_AD5668_2, ID_AD5669_1, ID_AD5669_2, + ID_LTC2606, + ID_LTC2607, + ID_LTC2609, + ID_LTC2616, + ID_LTC2617, + ID_LTC2619, + ID_LTC2626, + ID_LTC2627, + ID_LTC2629, }; static int ad5064_write(struct ad5064_state *st, unsigned int cmd, @@ -136,15 +181,27 @@ static int ad5064_write(struct ad5064_state *st, unsigned int cmd, static int ad5064_sync_powerdown_mode(struct ad5064_state *st, const struct iio_chan_spec *chan) { - unsigned int val; + unsigned int val, address; + unsigned int shift; int ret; - val = (0x1 << chan->address); + if (st->chip_info->regmap_type == AD5064_REGMAP_LTC) { + val = 0; + address = chan->address; + } else { + if (st->chip_info->regmap_type == AD5064_REGMAP_ADI2) + shift = 4; + else + shift = 8; - if (st->pwr_down[chan->channel]) - val |= st->pwr_down_mode[chan->channel] << 8; + val = (0x1 << chan->address); + address = 0; - ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0); + if (st->pwr_down[chan->channel]) + val |= st->pwr_down_mode[chan->channel] << shift; + } + + ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, address, val, 0); return ret; } @@ -155,6 +212,10 @@ static const char * const ad5064_powerdown_modes[] = { "three_state", }; +static const char * const ltc2617_powerdown_modes[] = { + "90kohm_to_gnd", +}; + static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -185,6 +246,13 @@ static const struct iio_enum ad5064_powerdown_mode_enum = { .set = ad5064_set_powerdown_mode, }; +static const struct iio_enum ltc2617_powerdown_mode_enum = { + .items = ltc2617_powerdown_modes, + .num_items = ARRAY_SIZE(ltc2617_powerdown_modes), + .get = ad5064_get_powerdown_mode, + .set = ad5064_set_powerdown_mode, +}; + static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { @@ -295,7 +363,19 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { { }, }; -#define AD5064_CHANNEL(chan, addr, bits, _shift) { \ +static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = { + { + .name = "powerdown", + .read = ad5064_read_dac_powerdown, + .write = ad5064_write_dac_powerdown, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, <c2617_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", <c2617_powerdown_mode_enum), + { }, +}; + +#define AD5064_CHANNEL(chan, addr, bits, _shift, _ext_info) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .output = 1, \ @@ -309,145 +389,340 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { .storagebits = 16, \ .shift = (_shift), \ }, \ - .ext_info = ad5064_ext_info, \ + .ext_info = (_ext_info), \ } -#define DECLARE_AD5064_CHANNELS(name, bits, shift) \ +#define DECLARE_AD5064_CHANNELS(name, bits, shift, ext_info) \ const struct iio_chan_spec name[] = { \ - AD5064_CHANNEL(0, 0, bits, shift), \ - AD5064_CHANNEL(1, 1, bits, shift), \ - AD5064_CHANNEL(2, 2, bits, shift), \ - AD5064_CHANNEL(3, 3, bits, shift), \ - AD5064_CHANNEL(4, 4, bits, shift), \ - AD5064_CHANNEL(5, 5, bits, shift), \ - AD5064_CHANNEL(6, 6, bits, shift), \ - AD5064_CHANNEL(7, 7, bits, shift), \ + AD5064_CHANNEL(0, 0, bits, shift, ext_info), \ + AD5064_CHANNEL(1, 1, bits, shift, ext_info), \ + AD5064_CHANNEL(2, 2, bits, shift, ext_info), \ + AD5064_CHANNEL(3, 3, bits, shift, ext_info), \ + AD5064_CHANNEL(4, 4, bits, shift, ext_info), \ + AD5064_CHANNEL(5, 5, bits, shift, ext_info), \ + AD5064_CHANNEL(6, 6, bits, shift, ext_info), \ + AD5064_CHANNEL(7, 7, bits, shift, ext_info), \ } -#define DECLARE_AD5065_CHANNELS(name, bits, shift) \ +#define DECLARE_AD5065_CHANNELS(name, bits, shift, ext_info) \ const struct iio_chan_spec name[] = { \ - AD5064_CHANNEL(0, 0, bits, shift), \ - AD5064_CHANNEL(1, 3, bits, shift), \ + AD5064_CHANNEL(0, 0, bits, shift, ext_info), \ + AD5064_CHANNEL(1, 3, bits, shift, ext_info), \ } -static DECLARE_AD5064_CHANNELS(ad5024_channels, 12, 8); -static DECLARE_AD5064_CHANNELS(ad5044_channels, 14, 6); -static DECLARE_AD5064_CHANNELS(ad5064_channels, 16, 4); +static DECLARE_AD5064_CHANNELS(ad5024_channels, 12, 8, ad5064_ext_info); +static DECLARE_AD5064_CHANNELS(ad5044_channels, 14, 6, ad5064_ext_info); +static DECLARE_AD5064_CHANNELS(ad5064_channels, 16, 4, ad5064_ext_info); -static DECLARE_AD5065_CHANNELS(ad5025_channels, 12, 8); -static DECLARE_AD5065_CHANNELS(ad5045_channels, 14, 6); -static DECLARE_AD5065_CHANNELS(ad5065_channels, 16, 4); +static DECLARE_AD5065_CHANNELS(ad5025_channels, 12, 8, ad5064_ext_info); +static DECLARE_AD5065_CHANNELS(ad5045_channels, 14, 6, ad5064_ext_info); +static DECLARE_AD5065_CHANNELS(ad5065_channels, 16, 4, ad5064_ext_info); -static DECLARE_AD5064_CHANNELS(ad5629_channels, 12, 4); -static DECLARE_AD5064_CHANNELS(ad5669_channels, 16, 0); +static DECLARE_AD5064_CHANNELS(ad5629_channels, 12, 4, ad5064_ext_info); +static DECLARE_AD5064_CHANNELS(ad5645_channels, 14, 2, ad5064_ext_info); +static DECLARE_AD5064_CHANNELS(ad5669_channels, 16, 0, ad5064_ext_info); + +static DECLARE_AD5064_CHANNELS(ltc2607_channels, 16, 0, ltc2617_ext_info); +static DECLARE_AD5064_CHANNELS(ltc2617_channels, 14, 2, ltc2617_ext_info); +static DECLARE_AD5064_CHANNELS(ltc2627_channels, 12, 4, ltc2617_ext_info); static const struct ad5064_chip_info ad5064_chip_info_tbl[] = { [ID_AD5024] = { .shared_vref = false, .channels = ad5024_channels, .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5025] = { .shared_vref = false, .channels = ad5025_channels, .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5044] = { .shared_vref = false, .channels = ad5044_channels, .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5045] = { .shared_vref = false, .channels = ad5045_channels, .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5064] = { .shared_vref = false, .channels = ad5064_channels, .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5064_1] = { .shared_vref = true, .channels = ad5064_channels, .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5065] = { .shared_vref = false, .channels = ad5065_channels, .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI, + }, + [ID_AD5625] = { + .shared_vref = true, + .channels = ad5629_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5625R_1V25] = { + .shared_vref = true, + .internal_vref = 1250000, + .channels = ad5629_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5625R_2V5] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5629_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5627] = { + .shared_vref = true, + .channels = ad5629_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5627R_1V25] = { + .shared_vref = true, + .internal_vref = 1250000, + .channels = ad5629_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5627R_2V5] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5629_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 }, [ID_AD5628_1] = { .shared_vref = true, .internal_vref = 2500000, .channels = ad5024_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5628_2] = { .shared_vref = true, .internal_vref = 5000000, .channels = ad5024_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5629_1] = { .shared_vref = true, .internal_vref = 2500000, .channels = ad5629_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5629_2] = { .shared_vref = true, .internal_vref = 5000000, .channels = ad5629_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, + }, + [ID_AD5645R_1V25] = { + .shared_vref = true, + .internal_vref = 1250000, + .channels = ad5645_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5645R_2V5] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5645_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5647R_1V25] = { + .shared_vref = true, + .internal_vref = 1250000, + .channels = ad5645_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5647R_2V5] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5645_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 }, [ID_AD5648_1] = { .shared_vref = true, .internal_vref = 2500000, .channels = ad5044_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5648_2] = { .shared_vref = true, .internal_vref = 5000000, .channels = ad5044_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, + }, + [ID_AD5665] = { + .shared_vref = true, + .channels = ad5669_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5665R_1V25] = { + .shared_vref = true, + .internal_vref = 1250000, + .channels = ad5669_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5665R_2V5] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5669_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI2 }, [ID_AD5666_1] = { .shared_vref = true, .internal_vref = 2500000, .channels = ad5064_channels, .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5666_2] = { .shared_vref = true, .internal_vref = 5000000, .channels = ad5064_channels, .num_channels = 4, + .regmap_type = AD5064_REGMAP_ADI, + }, + [ID_AD5667] = { + .shared_vref = true, + .channels = ad5669_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5667R_1V25] = { + .shared_vref = true, + .internal_vref = 1250000, + .channels = ad5669_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 + }, + [ID_AD5667R_2V5] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5669_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_ADI2 }, [ID_AD5668_1] = { .shared_vref = true, .internal_vref = 2500000, .channels = ad5064_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5668_2] = { .shared_vref = true, .internal_vref = 5000000, .channels = ad5064_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5669_1] = { .shared_vref = true, .internal_vref = 2500000, .channels = ad5669_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, }, [ID_AD5669_2] = { .shared_vref = true, .internal_vref = 5000000, .channels = ad5669_channels, .num_channels = 8, + .regmap_type = AD5064_REGMAP_ADI, + }, + [ID_LTC2606] = { + .shared_vref = true, + .internal_vref = 0, + .channels = ltc2607_channels, + .num_channels = 1, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2607] = { + .shared_vref = true, + .internal_vref = 0, + .channels = ltc2607_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2609] = { + .shared_vref = false, + .internal_vref = 0, + .channels = ltc2607_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2616] = { + .shared_vref = true, + .internal_vref = 0, + .channels = ltc2617_channels, + .num_channels = 1, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2617] = { + .shared_vref = true, + .internal_vref = 0, + .channels = ltc2617_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2619] = { + .shared_vref = false, + .internal_vref = 0, + .channels = ltc2617_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2626] = { + .shared_vref = true, + .internal_vref = 0, + .channels = ltc2627_channels, + .num_channels = 1, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2627] = { + .shared_vref = true, + .internal_vref = 0, + .channels = ltc2627_channels, + .num_channels = 2, + .regmap_type = AD5064_REGMAP_LTC, + }, + [ID_LTC2629] = { + .shared_vref = false, + .internal_vref = 0, + .channels = ltc2627_channels, + .num_channels = 4, + .regmap_type = AD5064_REGMAP_LTC, }, }; @@ -469,6 +744,22 @@ static const char * const ad5064_vref_name(struct ad5064_state *st, return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref]; } +static int ad5064_set_config(struct ad5064_state *st, unsigned int val) +{ + unsigned int cmd; + + switch (st->chip_info->regmap_type) { + case AD5064_REGMAP_ADI2: + cmd = AD5064_CMD_CONFIG_V2; + break; + default: + cmd = AD5064_CMD_CONFIG; + break; + } + + return ad5064_write(st, cmd, 0, val, 0); +} + static int ad5064_probe(struct device *dev, enum ad5064_type type, const char *name, ad5064_write_func write) { @@ -498,8 +789,7 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, if (!st->chip_info->internal_vref) return ret; st->use_internal_vref = true; - ret = ad5064_write(st, AD5064_CMD_CONFIG, 0, - AD5064_CONFIG_INT_VREF_ENABLE, 0); + ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE); if (ret) { dev_err(dev, "Failed to enable internal vref: %d\n", ret); @@ -628,9 +918,19 @@ static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd, unsigned int addr, unsigned int val) { struct i2c_client *i2c = to_i2c_client(st->dev); + unsigned int cmd_shift; int ret; - st->data.i2c[0] = (cmd << 4) | addr; + switch (st->chip_info->regmap_type) { + case AD5064_REGMAP_ADI2: + cmd_shift = 3; + break; + default: + cmd_shift = 4; + break; + } + + st->data.i2c[0] = (cmd << cmd_shift) | addr; put_unaligned_be16(val, &st->data.i2c[1]); ret = i2c_master_send(i2c, st->data.i2c, 3); @@ -653,12 +953,35 @@ static int ad5064_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id ad5064_i2c_ids[] = { + {"ad5625", ID_AD5625 }, + {"ad5625r-1v25", ID_AD5625R_1V25 }, + {"ad5625r-2v5", ID_AD5625R_2V5 }, + {"ad5627", ID_AD5627 }, + {"ad5627r-1v25", ID_AD5627R_1V25 }, + {"ad5627r-2v5", ID_AD5627R_2V5 }, {"ad5629-1", ID_AD5629_1}, {"ad5629-2", ID_AD5629_2}, {"ad5629-3", ID_AD5629_2}, /* similar enough to ad5629-2 */ + {"ad5645r-1v25", ID_AD5645R_1V25 }, + {"ad5645r-2v5", ID_AD5645R_2V5 }, + {"ad5665", ID_AD5665 }, + {"ad5665r-1v25", ID_AD5665R_1V25 }, + {"ad5665r-2v5", ID_AD5665R_2V5 }, + {"ad5667", ID_AD5667 }, + {"ad5667r-1v25", ID_AD5667R_1V25 }, + {"ad5667r-2v5", ID_AD5667R_2V5 }, {"ad5669-1", ID_AD5669_1}, {"ad5669-2", ID_AD5669_2}, {"ad5669-3", ID_AD5669_2}, /* similar enough to ad5669-2 */ + {"ltc2606", ID_LTC2606}, + {"ltc2607", ID_LTC2607}, + {"ltc2609", ID_LTC2609}, + {"ltc2616", ID_LTC2616}, + {"ltc2617", ID_LTC2617}, + {"ltc2619", ID_LTC2619}, + {"ltc2626", ID_LTC2626}, + {"ltc2627", ID_LTC2627}, + {"ltc2629", ID_LTC2629}, {} }; MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids); diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c new file mode 100644 index 000000000000..d6510d6928b3 --- /dev/null +++ b/drivers/iio/dac/ad5761.c @@ -0,0 +1,430 @@ +/* + * AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter + * + * Copyright 2016 Qtechnology A/S + * 2016 Ricardo Ribalda + * + * Licensed under the GPL-2. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define AD5761_ADDR(addr) ((addr & 0xf) << 16) +#define AD5761_ADDR_NOOP 0x0 +#define AD5761_ADDR_DAC_WRITE 0x3 +#define AD5761_ADDR_CTRL_WRITE_REG 0x4 +#define AD5761_ADDR_SW_DATA_RESET 0x7 +#define AD5761_ADDR_DAC_READ 0xb +#define AD5761_ADDR_CTRL_READ_REG 0xc +#define AD5761_ADDR_SW_FULL_RESET 0xf + +#define AD5761_CTRL_USE_INTVREF BIT(5) +#define AD5761_CTRL_ETS BIT(6) + +/** + * struct ad5761_chip_info - chip specific information + * @int_vref: Value of the internal reference voltage in mV - 0 if external + * reference voltage is used + * @channel: channel specification +*/ + +struct ad5761_chip_info { + unsigned long int_vref; + const struct iio_chan_spec channel; +}; + +struct ad5761_range_params { + int m; + int c; +}; + +enum ad5761_supported_device_ids { + ID_AD5721, + ID_AD5721R, + ID_AD5761, + ID_AD5761R, +}; + +/** + * struct ad5761_state - driver instance specific data + * @spi: spi_device + * @vref_reg: reference voltage regulator + * @use_intref: true when the internal voltage reference is used + * @vref: actual voltage reference in mVolts + * @range: output range mode used + * @data: cache aligned spi buffer + */ +struct ad5761_state { + struct spi_device *spi; + struct regulator *vref_reg; + + bool use_intref; + int vref; + enum ad5761_voltage_range range; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[4]; + } data[3] ____cacheline_aligned; +}; + +static const struct ad5761_range_params ad5761_range_params[] = { + [AD5761_VOLTAGE_RANGE_M10V_10V] = { + .m = 80, + .c = 40, + }, + [AD5761_VOLTAGE_RANGE_0V_10V] = { + .m = 40, + .c = 0, + }, + [AD5761_VOLTAGE_RANGE_M5V_5V] = { + .m = 40, + .c = 20, + }, + [AD5761_VOLTAGE_RANGE_0V_5V] = { + .m = 20, + .c = 0, + }, + [AD5761_VOLTAGE_RANGE_M2V5_7V5] = { + .m = 40, + .c = 10, + }, + [AD5761_VOLTAGE_RANGE_M3V_3V] = { + .m = 24, + .c = 12, + }, + [AD5761_VOLTAGE_RANGE_0V_16V] = { + .m = 64, + .c = 0, + }, + [AD5761_VOLTAGE_RANGE_0V_20V] = { + .m = 80, + .c = 0, + }, +}; + +static int _ad5761_spi_write(struct ad5761_state *st, u8 addr, u16 val) +{ + st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr) | val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5761_spi_write(struct iio_dev *indio_dev, u8 addr, u16 val) +{ + struct ad5761_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + ret = _ad5761_spi_write(st, addr, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int _ad5761_spi_read(struct ad5761_state *st, u8 addr, u16 *val) +{ + int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = &st->data[0].d8[1], + .bits_per_word = 8, + .len = 3, + .cs_change = true, + }, { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[2].d8[1], + .bits_per_word = 8, + .len = 3, + }, + }; + + st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr)); + st->data[1].d32 = cpu_to_be32(AD5761_ADDR(AD5761_ADDR_NOOP)); + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + + *val = be32_to_cpu(st->data[2].d32); + + return ret; +} + +static int ad5761_spi_read(struct iio_dev *indio_dev, u8 addr, u16 *val) +{ + struct ad5761_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + ret = _ad5761_spi_read(st, addr, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5761_spi_set_range(struct ad5761_state *st, + enum ad5761_voltage_range range) +{ + u16 aux; + int ret; + + aux = (range & 0x7) | AD5761_CTRL_ETS; + + if (st->use_intref) + aux |= AD5761_CTRL_USE_INTVREF; + + ret = _ad5761_spi_write(st, AD5761_ADDR_SW_FULL_RESET, 0); + if (ret) + return ret; + + ret = _ad5761_spi_write(st, AD5761_ADDR_CTRL_WRITE_REG, aux); + if (ret) + return ret; + + st->range = range; + + return 0; +} + +static int ad5761_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad5761_state *st; + int ret; + u16 aux; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ad5761_spi_read(indio_dev, AD5761_ADDR_DAC_READ, &aux); + if (ret) + return ret; + *val = aux >> chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + st = iio_priv(indio_dev); + *val = st->vref * ad5761_range_params[st->range].m; + *val /= 10; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + st = iio_priv(indio_dev); + *val = -(1 << chan->scan_type.realbits); + *val *= ad5761_range_params[st->range].c; + *val /= ad5761_range_params[st->range].m; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad5761_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + u16 aux; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + if (val2 || (val << chan->scan_type.shift) > 0xffff || val < 0) + return -EINVAL; + + aux = val << chan->scan_type.shift; + + return ad5761_spi_write(indio_dev, AD5761_ADDR_DAC_WRITE, aux); +} + +static const struct iio_info ad5761_info = { + .read_raw = &ad5761_read_raw, + .write_raw = &ad5761_write_raw, + .driver_module = THIS_MODULE, +}; + +#define AD5761_CHAN(_bits) { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 16, \ + .shift = 16 - (_bits), \ + }, \ +} + +static const struct ad5761_chip_info ad5761_chip_infos[] = { + [ID_AD5721] = { + .int_vref = 0, + .channel = AD5761_CHAN(12), + }, + [ID_AD5721R] = { + .int_vref = 2500, + .channel = AD5761_CHAN(12), + }, + [ID_AD5761] = { + .int_vref = 0, + .channel = AD5761_CHAN(16), + }, + [ID_AD5761R] = { + .int_vref = 2500, + .channel = AD5761_CHAN(16), + }, +}; + +static int ad5761_get_vref(struct ad5761_state *st, + const struct ad5761_chip_info *chip_info) +{ + int ret; + + st->vref_reg = devm_regulator_get_optional(&st->spi->dev, "vref"); + if (PTR_ERR(st->vref_reg) == -ENODEV) { + /* Use Internal regulator */ + if (!chip_info->int_vref) { + dev_err(&st->spi->dev, + "Voltage reference not found\n"); + return -EIO; + } + + st->use_intref = true; + st->vref = chip_info->int_vref; + return 0; + } + + if (IS_ERR(st->vref_reg)) { + dev_err(&st->spi->dev, + "Error getting voltage reference regulator\n"); + return PTR_ERR(st->vref_reg); + } + + ret = regulator_enable(st->vref_reg); + if (ret) { + dev_err(&st->spi->dev, + "Failed to enable voltage reference\n"); + return ret; + } + + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) { + dev_err(&st->spi->dev, + "Failed to get voltage reference value\n"); + goto disable_regulator_vref; + } + + if (ret < 2000000 || ret > 3000000) { + dev_warn(&st->spi->dev, + "Invalid external voltage ref. value %d uV\n", ret); + ret = -EIO; + goto disable_regulator_vref; + } + + st->vref = ret / 1000; + st->use_intref = false; + + return 0; + +disable_regulator_vref: + regulator_disable(st->vref_reg); + st->vref_reg = NULL; + return ret; +} + +static int ad5761_probe(struct spi_device *spi) +{ + struct iio_dev *iio_dev; + struct ad5761_state *st; + int ret; + const struct ad5761_chip_info *chip_info = + &ad5761_chip_infos[spi_get_device_id(spi)->driver_data]; + enum ad5761_voltage_range voltage_range = AD5761_VOLTAGE_RANGE_0V_5V; + struct ad5761_platform_data *pdata = dev_get_platdata(&spi->dev); + + iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!iio_dev) + return -ENOMEM; + + st = iio_priv(iio_dev); + + st->spi = spi; + spi_set_drvdata(spi, iio_dev); + + ret = ad5761_get_vref(st, chip_info); + if (ret) + return ret; + + if (pdata) + voltage_range = pdata->voltage_range; + + ret = ad5761_spi_set_range(st, voltage_range); + if (ret) + goto disable_regulator_err; + + iio_dev->dev.parent = &spi->dev; + iio_dev->info = &ad5761_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = &chip_info->channel; + iio_dev->num_channels = 1; + iio_dev->name = spi_get_device_id(st->spi)->name; + ret = iio_device_register(iio_dev); + if (ret) + goto disable_regulator_err; + + return 0; + +disable_regulator_err: + if (!IS_ERR_OR_NULL(st->vref_reg)) + regulator_disable(st->vref_reg); + + return ret; +} + +static int ad5761_remove(struct spi_device *spi) +{ + struct iio_dev *iio_dev = spi_get_drvdata(spi); + struct ad5761_state *st = iio_priv(iio_dev); + + iio_device_unregister(iio_dev); + + if (!IS_ERR_OR_NULL(st->vref_reg)) + regulator_disable(st->vref_reg); + + return 0; +} + +static const struct spi_device_id ad5761_id[] = { + {"ad5721", ID_AD5721}, + {"ad5721r", ID_AD5721R}, + {"ad5761", ID_AD5761}, + {"ad5761r", ID_AD5761R}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5761_id); + +static struct spi_driver ad5761_driver = { + .driver = { + .name = "ad5761", + }, + .probe = ad5761_probe, + .remove = ad5761_remove, + .id_table = ad5761_id, +}; +module_spi_driver(ad5761_driver); + +MODULE_AUTHOR("Ricardo Ribalda "); +MODULE_DESCRIPTION("Analog Devices AD5721, AD5721R, AD5761, AD5761R driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index b4dde8315210..cca935c06f2b 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -1,5 +1,5 @@ /* - * mcp4725.c - Support for Microchip MCP4725 + * mcp4725.c - Support for Microchip MCP4725/6 * * Copyright (C) 2012 Peter Meerwald * @@ -134,6 +134,12 @@ static const char * const mcp4725_powerdown_modes[] = { "500kohm_to_gnd" }; +static const char * const mcp4726_powerdown_modes[] = { + "1kohm_to_gnd", + "125kohm_to_gnd", + "640kohm_to_gnd" +}; + static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -182,11 +188,24 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev, return len; } -static const struct iio_enum mcp4725_powerdown_mode_enum = { - .items = mcp4725_powerdown_modes, - .num_items = ARRAY_SIZE(mcp4725_powerdown_modes), - .get = mcp4725_get_powerdown_mode, - .set = mcp4725_set_powerdown_mode, +enum { + MCP4725, + MCP4726, +}; + +static const struct iio_enum mcp472x_powerdown_mode_enum[] = { + [MCP4725] = { + .items = mcp4725_powerdown_modes, + .num_items = ARRAY_SIZE(mcp4725_powerdown_modes), + .get = mcp4725_get_powerdown_mode, + .set = mcp4725_set_powerdown_mode, + }, + [MCP4726] = { + .items = mcp4726_powerdown_modes, + .num_items = ARRAY_SIZE(mcp4726_powerdown_modes), + .get = mcp4725_get_powerdown_mode, + .set = mcp4725_set_powerdown_mode, + }, }; static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = { @@ -196,19 +215,46 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = { .write = mcp4725_write_powerdown, .shared = IIO_SEPARATE, }, - IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum), + IIO_ENUM("powerdown_mode", IIO_SEPARATE, + &mcp472x_powerdown_mode_enum[MCP4725]), + IIO_ENUM_AVAILABLE("powerdown_mode", + &mcp472x_powerdown_mode_enum[MCP4725]), { }, }; -static const struct iio_chan_spec mcp4725_channel = { - .type = IIO_VOLTAGE, - .indexed = 1, - .output = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .ext_info = mcp4725_ext_info, +static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { + { + .name = "powerdown", + .read = mcp4725_read_powerdown, + .write = mcp4725_write_powerdown, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, + &mcp472x_powerdown_mode_enum[MCP4726]), + IIO_ENUM_AVAILABLE("powerdown_mode", + &mcp472x_powerdown_mode_enum[MCP4726]), + { }, +}; + +static const struct iio_chan_spec mcp472x_channel[] = { + [MCP4725] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .ext_info = mcp4725_ext_info, + }, + [MCP4726] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .ext_info = mcp4726_ext_info, + }, }; static int mcp4725_set_value(struct iio_dev *indio_dev, int val) @@ -302,7 +348,7 @@ static int mcp4725_probe(struct i2c_client *client, indio_dev->dev.parent = &client->dev; indio_dev->name = id->name; indio_dev->info = &mcp4725_info; - indio_dev->channels = &mcp4725_channel; + indio_dev->channels = &mcp472x_channel[id->driver_data]; indio_dev->num_channels = 1; indio_dev->modes = INDIO_DIRECT_MODE; @@ -316,7 +362,7 @@ static int mcp4725_probe(struct i2c_client *client, } pd = (inbuf[0] >> 1) & 0x3; data->powerdown = pd > 0 ? true : false; - data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */ + data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); return iio_device_register(indio_dev); @@ -329,7 +375,8 @@ static int mcp4725_remove(struct i2c_client *client) } static const struct i2c_device_id mcp4725_id[] = { - { "mcp4725", 0 }, + { "mcp4725", MCP4725 }, + { "mcp4726", MCP4726 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp4725_id); @@ -346,5 +393,5 @@ static struct i2c_driver mcp4725_driver = { module_i2c_driver(mcp4725_driver); MODULE_AUTHOR("Peter Meerwald "); -MODULE_DESCRIPTION("MCP4725 12-bit DAC"); +MODULE_DESCRIPTION("MCP4725/6 12-bit DAC"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/stx104.c b/drivers/iio/dac/stx104.c new file mode 100644 index 000000000000..174f4b75ceed --- /dev/null +++ b/drivers/iio/dac/stx104.c @@ -0,0 +1,152 @@ +/* + * DAC driver for the Apex Embedded Systems STX104 + * Copyright (C) 2016 William Breathitt Gray + * + * 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. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STX104_NUM_CHAN 2 + +#define STX104_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .output = 1 \ +} + +#define STX104_EXTENT 16 +/** + * The highest base address possible for an ISA device is 0x3FF; this results in + * 1024 possible base addresses. Dividing the number of possible base addresses + * by the address extent taken by each device results in the maximum number of + * devices on a system. + */ +#define MAX_NUM_STX104 (1024 / STX104_EXTENT) + +static unsigned base[MAX_NUM_STX104]; +static unsigned num_stx104; +module_param_array(base, uint, &num_stx104, 0); +MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); + +/** + * struct stx104_iio - IIO device private data structure + * @chan_out_states: channels' output states + * @base: base port address of the IIO device + */ +struct stx104_iio { + unsigned chan_out_states[STX104_NUM_CHAN]; + unsigned base; +}; + +static int stx104_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + *val = priv->chan_out_states[chan->channel]; + + return IIO_VAL_INT; +} + +static int stx104_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + const unsigned chan_addr_offset = 2 * chan->channel; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + priv->chan_out_states[chan->channel] = val; + outw(val, priv->base + 4 + chan_addr_offset); + + return 0; +} + +static const struct iio_info stx104_info = { + .driver_module = THIS_MODULE, + .read_raw = stx104_read_raw, + .write_raw = stx104_write_raw +}; + +static const struct iio_chan_spec stx104_channels[STX104_NUM_CHAN] = { + STX104_CHAN(0), + STX104_CHAN(1) +}; + +static int stx104_probe(struct device *dev, unsigned int id) +{ + struct iio_dev *indio_dev; + struct stx104_iio *priv; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + if (!devm_request_region(dev, base[id], STX104_EXTENT, + dev_name(dev))) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base[id], base[id] + STX104_EXTENT); + return -EBUSY; + } + + indio_dev->info = &stx104_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = stx104_channels; + indio_dev->num_channels = STX104_NUM_CHAN; + indio_dev->name = dev_name(dev); + + priv = iio_priv(indio_dev); + priv->base = base[id]; + + /* initialize DAC output to 0V */ + outw(0, base[id] + 4); + outw(0, base[id] + 6); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct isa_driver stx104_driver = { + .probe = stx104_probe, + .driver = { + .name = "stx104" + } +}; + +static void __exit stx104_exit(void) +{ + isa_unregister_driver(&stx104_driver); +} + +static int __init stx104_init(void) +{ + return isa_register_driver(&stx104_driver, num_stx104); +} + +module_init(stx104_init); +module_exit(stx104_exit); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c new file mode 100644 index 000000000000..c4ec7779b394 --- /dev/null +++ b/drivers/iio/dac/vf610_dac.c @@ -0,0 +1,298 @@ +/* + * Freescale Vybrid vf610 DAC driver + * + * Copyright 2016 Toradex AG + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define VF610_DACx_STATCTRL 0x20 + +#define VF610_DAC_DACEN BIT(15) +#define VF610_DAC_DACRFS BIT(14) +#define VF610_DAC_LPEN BIT(11) + +#define VF610_DAC_DAT0(x) ((x) & 0xFFF) + +enum vf610_conversion_mode_sel { + VF610_DAC_CONV_HIGH_POWER, + VF610_DAC_CONV_LOW_POWER, +}; + +struct vf610_dac { + struct clk *clk; + struct device *dev; + enum vf610_conversion_mode_sel conv_mode; + void __iomem *regs; +}; + +static void vf610_dac_init(struct vf610_dac *info) +{ + int val; + + info->conv_mode = VF610_DAC_CONV_LOW_POWER; + val = VF610_DAC_DACEN | VF610_DAC_DACRFS | + VF610_DAC_LPEN; + writel(val, info->regs + VF610_DACx_STATCTRL); +} + +static void vf610_dac_exit(struct vf610_dac *info) +{ + int val; + + val = readl(info->regs + VF610_DACx_STATCTRL); + val &= ~VF610_DAC_DACEN; + writel(val, info->regs + VF610_DACx_STATCTRL); +} + +static int vf610_set_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct vf610_dac *info = iio_priv(indio_dev); + int val; + + mutex_lock(&indio_dev->mlock); + info->conv_mode = mode; + val = readl(info->regs + VF610_DACx_STATCTRL); + if (mode) + val |= VF610_DAC_LPEN; + else + val &= ~VF610_DAC_LPEN; + writel(val, info->regs + VF610_DACx_STATCTRL); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int vf610_get_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct vf610_dac *info = iio_priv(indio_dev); + + return info->conv_mode; +} + +static const char * const vf610_conv_modes[] = { "high-power", "low-power" }; + +static const struct iio_enum vf610_conversion_mode = { + .items = vf610_conv_modes, + .num_items = ARRAY_SIZE(vf610_conv_modes), + .get = vf610_get_conversion_mode, + .set = vf610_set_conversion_mode, +}; + +static const struct iio_chan_spec_ext_info vf610_ext_info[] = { + IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, + &vf610_conversion_mode), + {}, +}; + +#define VF610_DAC_CHAN(_chan_type) { \ + .type = (_chan_type), \ + .output = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = vf610_ext_info, \ +} + +static const struct iio_chan_spec vf610_dac_iio_channels[] = { + VF610_DAC_CHAN(IIO_VOLTAGE), +}; + +static int vf610_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct vf610_dac *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = VF610_DAC_DAT0(readl(info->regs)); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * DACRFS is always 1 for valid reference and typical + * reference voltage as per Vybrid datasheet is 3.3V + * from section 9.1.2.1 of Vybrid datasheet + */ + *val = 3300 /* mV */; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int vf610_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, + long mask) +{ + struct vf610_dac *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + writel(VF610_DAC_DAT0(val), info->regs); + mutex_unlock(&indio_dev->mlock); + return 0; + + default: + return -EINVAL; + } +} + +static const struct iio_info vf610_dac_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = &vf610_read_raw, + .write_raw = &vf610_write_raw, +}; + +static const struct of_device_id vf610_dac_match[] = { + { .compatible = "fsl,vf610-dac", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_dac_match); + +static int vf610_dac_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct vf610_dac *info; + struct resource *mem; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(struct vf610_dac)); + if (!indio_dev) { + dev_err(&pdev->dev, "Failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + info->dev = &pdev->dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + info->clk = devm_clk_get(&pdev->dev, "dac"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "Failed getting clock, err = %ld\n", + PTR_ERR(info->clk)); + return PTR_ERR(info->clk); + } + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &vf610_dac_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vf610_dac_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels); + + ret = clk_prepare_enable(info->clk); + if (ret) { + dev_err(&pdev->dev, + "Could not prepare or enable the clock\n"); + return ret; + } + + vf610_dac_init(info); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device\n"); + goto error_iio_device_register; + } + + return 0; + +error_iio_device_register: + clk_disable_unprepare(info->clk); + + return ret; +} + +static int vf610_dac_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct vf610_dac *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + vf610_dac_exit(info); + clk_disable_unprepare(info->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vf610_dac_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_dac *info = iio_priv(indio_dev); + + vf610_dac_exit(info); + clk_disable_unprepare(info->clk); + + return 0; +} + +static int vf610_dac_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_dac *info = iio_priv(indio_dev); + int ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + return ret; + + vf610_dac_init(info); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume); + +static struct platform_driver vf610_dac_driver = { + .probe = vf610_dac_probe, + .remove = vf610_dac_remove, + .driver = { + .name = "vf610-dac", + .of_match_table = vf610_dac_match, + .pm = &vf610_dac_pm_ops, + }, +}; +module_platform_driver(vf610_dac_driver); + +MODULE_AUTHOR("Sanchayan Maity "); +MODULE_DESCRIPTION("Freescale VF610 DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 02eddcebeea3..110f95b6e52f 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -185,6 +185,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .drdy_irq = { .addr = ST_GYRO_1_DRDY_IRQ_ADDR, .mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK, + /* + * The sensor has IHL (active low) and open + * drain settings, but only for INT1 and not + * for the DRDY line on INT2. + */ }, .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT, .bootime = 2, @@ -248,6 +253,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .drdy_irq = { .addr = ST_GYRO_2_DRDY_IRQ_ADDR, .mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK, + /* + * The sensor has IHL (active low) and open + * drain settings, but only for INT1 and not + * for the DRDY line on INT2. + */ }, .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, .bootime = 2, @@ -307,6 +317,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .drdy_irq = { .addr = ST_GYRO_3_DRDY_IRQ_ADDR, .mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK, + /* + * The sensor has IHL (active low) and open + * drain settings, but only for INT1 and not + * for the DRDY line on INT2. + */ }, .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT, .bootime = 2, diff --git a/drivers/iio/health/Kconfig b/drivers/iio/health/Kconfig index a647679da805..c5f004a8e447 100644 --- a/drivers/iio/health/Kconfig +++ b/drivers/iio/health/Kconfig @@ -3,7 +3,35 @@ # # When adding new entries keep the list in alphabetical order -menu "Health sensors" +menu "Health Sensors" + +menu "Heart Rate Monitors" + +config AFE4403 + tristate "TI AFE4403 Heart Rate Monitor" + depends on SPI_MASTER + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes to choose the Texas Instruments AFE4403 + heart rate monitor and low-cost pulse oximeter. + + To compile this driver as a module, choose M here: the + module will be called afe4403. + +config AFE4404 + tristate "TI AFE4404 heart rate and pulse oximeter sensor" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes to choose the Texas Instruments AFE4404 + heart rate monitor and low-cost pulse oximeter. + + To compile this driver as a module, choose M here: the + module will be called afe4404. config MAX30100 tristate "MAX30100 heart rate and pulse oximeter sensor" @@ -19,3 +47,5 @@ config MAX30100 module will be called max30100. endmenu + +endmenu diff --git a/drivers/iio/health/Makefile b/drivers/iio/health/Makefile index 7c475d7faad8..9955a2ae8df1 100644 --- a/drivers/iio/health/Makefile +++ b/drivers/iio/health/Makefile @@ -4,4 +4,6 @@ # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AFE4403) += afe4403.o +obj-$(CONFIG_AFE4404) += afe4404.o obj-$(CONFIG_MAX30100) += max30100.o diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c new file mode 100644 index 000000000000..88e43f87b926 --- /dev/null +++ b/drivers/iio/health/afe4403.c @@ -0,0 +1,708 @@ +/* + * AFE4403 Heart Rate Monitors and Low-Cost Pulse Oximeters + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "afe440x.h" + +#define AFE4403_DRIVER_NAME "afe4403" + +/* AFE4403 Registers */ +#define AFE4403_TIAGAIN 0x20 +#define AFE4403_TIA_AMB_GAIN 0x21 + +/* AFE4403 GAIN register fields */ +#define AFE4403_TIAGAIN_RES_MASK GENMASK(2, 0) +#define AFE4403_TIAGAIN_RES_SHIFT 0 +#define AFE4403_TIAGAIN_CAP_MASK GENMASK(7, 3) +#define AFE4403_TIAGAIN_CAP_SHIFT 3 + +/* AFE4403 LEDCNTRL register fields */ +#define AFE440X_LEDCNTRL_LED1_MASK GENMASK(15, 8) +#define AFE440X_LEDCNTRL_LED1_SHIFT 8 +#define AFE440X_LEDCNTRL_LED2_MASK GENMASK(7, 0) +#define AFE440X_LEDCNTRL_LED2_SHIFT 0 +#define AFE440X_LEDCNTRL_LED_RANGE_MASK GENMASK(17, 16) +#define AFE440X_LEDCNTRL_LED_RANGE_SHIFT 16 + +/* AFE4403 CONTROL2 register fields */ +#define AFE440X_CONTROL2_PWR_DWN_TX BIT(2) +#define AFE440X_CONTROL2_EN_SLOW_DIAG BIT(8) +#define AFE440X_CONTROL2_DIAG_OUT_TRI BIT(10) +#define AFE440X_CONTROL2_TX_BRDG_MOD BIT(11) +#define AFE440X_CONTROL2_TX_REF_MASK GENMASK(18, 17) +#define AFE440X_CONTROL2_TX_REF_SHIFT 17 + +/* AFE4404 NULL fields */ +#define NULL_MASK 0 +#define NULL_SHIFT 0 + +/* AFE4403 LEDCNTRL values */ +#define AFE440X_LEDCNTRL_RANGE_TX_HALF 0x1 +#define AFE440X_LEDCNTRL_RANGE_TX_FULL 0x2 +#define AFE440X_LEDCNTRL_RANGE_TX_OFF 0x3 + +/* AFE4403 CONTROL2 values */ +#define AFE440X_CONTROL2_TX_REF_025 0x0 +#define AFE440X_CONTROL2_TX_REF_050 0x1 +#define AFE440X_CONTROL2_TX_REF_100 0x2 +#define AFE440X_CONTROL2_TX_REF_075 0x3 + +/* AFE4403 CONTROL3 values */ +#define AFE440X_CONTROL3_CLK_DIV_2 0x0 +#define AFE440X_CONTROL3_CLK_DIV_4 0x2 +#define AFE440X_CONTROL3_CLK_DIV_6 0x3 +#define AFE440X_CONTROL3_CLK_DIV_8 0x4 +#define AFE440X_CONTROL3_CLK_DIV_12 0x5 +#define AFE440X_CONTROL3_CLK_DIV_1 0x7 + +/* AFE4403 TIAGAIN_CAP values */ +#define AFE4403_TIAGAIN_CAP_5_P 0x0 +#define AFE4403_TIAGAIN_CAP_10_P 0x1 +#define AFE4403_TIAGAIN_CAP_20_P 0x2 +#define AFE4403_TIAGAIN_CAP_30_P 0x3 +#define AFE4403_TIAGAIN_CAP_55_P 0x8 +#define AFE4403_TIAGAIN_CAP_155_P 0x10 + +/* AFE4403 TIAGAIN_RES values */ +#define AFE4403_TIAGAIN_RES_500_K 0x0 +#define AFE4403_TIAGAIN_RES_250_K 0x1 +#define AFE4403_TIAGAIN_RES_100_K 0x2 +#define AFE4403_TIAGAIN_RES_50_K 0x3 +#define AFE4403_TIAGAIN_RES_25_K 0x4 +#define AFE4403_TIAGAIN_RES_10_K 0x5 +#define AFE4403_TIAGAIN_RES_1_M 0x6 +#define AFE4403_TIAGAIN_RES_NONE 0x7 + +/** + * struct afe4403_data + * @dev - Device structure + * @spi - SPI device handle + * @regmap - Register map of the device + * @regulator - Pointer to the regulator for the IC + * @trig - IIO trigger for this device + * @irq - ADC_RDY line interrupt number + */ +struct afe4403_data { + struct device *dev; + struct spi_device *spi; + struct regmap *regmap; + struct regulator *regulator; + struct iio_trigger *trig; + int irq; +}; + +enum afe4403_chan_id { + LED1, + ALED1, + LED2, + ALED2, + LED1_ALED1, + LED2_ALED2, + ILED1, + ILED2, +}; + +static const struct afe440x_reg_info afe4403_reg_info[] = { + [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL), + [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL), + [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, 0, NULL), + [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), + [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), + [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), + [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED1), + [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED2), +}; + +static const struct iio_chan_spec afe4403_channels[] = { + /* ADC values */ + AFE440X_INTENSITY_CHAN(LED1, "led1", 0), + AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2, "led2", 0), + AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", 0), + AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + /* LED current */ + AFE440X_CURRENT_CHAN(ILED1, "led1"), + AFE440X_CURRENT_CHAN(ILED2, "led2"), +}; + +static const struct afe440x_val_table afe4403_res_table[] = { + { 500000 }, { 250000 }, { 100000 }, { 50000 }, + { 25000 }, { 10000 }, { 1000000 }, { 0 }, +}; +AFE440X_TABLE_ATTR(tia_resistance_available, afe4403_res_table); + +static const struct afe440x_val_table afe4403_cap_table[] = { + { 0, 5000 }, { 0, 10000 }, { 0, 20000 }, { 0, 25000 }, + { 0, 30000 }, { 0, 35000 }, { 0, 45000 }, { 0, 50000 }, + { 0, 55000 }, { 0, 60000 }, { 0, 70000 }, { 0, 75000 }, + { 0, 80000 }, { 0, 85000 }, { 0, 95000 }, { 0, 100000 }, + { 0, 155000 }, { 0, 160000 }, { 0, 170000 }, { 0, 175000 }, + { 0, 180000 }, { 0, 185000 }, { 0, 195000 }, { 0, 200000 }, + { 0, 205000 }, { 0, 210000 }, { 0, 220000 }, { 0, 225000 }, + { 0, 230000 }, { 0, 235000 }, { 0, 245000 }, { 0, 250000 }, +}; +AFE440X_TABLE_ATTR(tia_capacitance_available, afe4403_cap_table); + +static ssize_t afe440x_show_register(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4403_data *afe = iio_priv(indio_dev); + struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); + unsigned int reg_val, type; + int vals[2]; + int ret, val_len; + + ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); + if (ret) + return ret; + + reg_val &= afe440x_attr->mask; + reg_val >>= afe440x_attr->shift; + + switch (afe440x_attr->type) { + case SIMPLE: + type = IIO_VAL_INT; + val_len = 1; + vals[0] = reg_val; + break; + case RESISTANCE: + case CAPACITANCE: + type = IIO_VAL_INT_PLUS_MICRO; + val_len = 2; + if (reg_val < afe440x_attr->table_size) { + vals[0] = afe440x_attr->val_table[reg_val].integer; + vals[1] = afe440x_attr->val_table[reg_val].fract; + break; + } + return -EINVAL; + default: + return -EINVAL; + } + + return iio_format_value(buf, type, val_len, vals); +} + +static ssize_t afe440x_store_register(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4403_data *afe = iio_priv(indio_dev); + struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); + int val, integer, fract, ret; + + ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract); + if (ret) + return ret; + + switch (afe440x_attr->type) { + case SIMPLE: + val = integer; + break; + case RESISTANCE: + case CAPACITANCE: + for (val = 0; val < afe440x_attr->table_size; val++) + if (afe440x_attr->val_table[val].integer == integer && + afe440x_attr->val_table[val].fract == fract) + break; + if (val == afe440x_attr->table_size) + return -EINVAL; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, + afe440x_attr->mask, + (val << afe440x_attr->shift)); + if (ret) + return ret; + + return count; +} + +static AFE440X_ATTR(tia_separate_en, AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0); + +static AFE440X_ATTR(tia_resistance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table)); +static AFE440X_ATTR(tia_capacitance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_CAP, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table)); + +static AFE440X_ATTR(tia_resistance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table)); +static AFE440X_ATTR(tia_capacitance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table)); + +static struct attribute *afe440x_attributes[] = { + &afe440x_attr_tia_separate_en.dev_attr.attr, + &afe440x_attr_tia_resistance1.dev_attr.attr, + &afe440x_attr_tia_capacitance1.dev_attr.attr, + &afe440x_attr_tia_resistance2.dev_attr.attr, + &afe440x_attr_tia_capacitance2.dev_attr.attr, + &dev_attr_tia_resistance_available.attr, + &dev_attr_tia_capacitance_available.attr, + NULL +}; + +static const struct attribute_group afe440x_attribute_group = { + .attrs = afe440x_attributes +}; + +static int afe4403_read(struct afe4403_data *afe, unsigned int reg, u32 *val) +{ + u8 tx[4] = {AFE440X_CONTROL0, 0x0, 0x0, AFE440X_CONTROL0_READ}; + u8 rx[3]; + int ret; + + /* Enable reading from the device */ + ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0); + if (ret) + return ret; + + ret = spi_write_then_read(afe->spi, ®, 1, rx, 3); + if (ret) + return ret; + + *val = (rx[0] << 16) | + (rx[1] << 8) | + (rx[2]); + + /* Disable reading from the device */ + tx[3] = AFE440X_CONTROL0_WRITE; + ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0); + if (ret) + return ret; + + return 0; +} + +static int afe4403_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct afe4403_data *afe = iio_priv(indio_dev); + const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address]; + int ret; + + switch (chan->type) { + case IIO_INTENSITY: + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = afe4403_read(afe, reg_info.reg, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + ret = regmap_read(afe->regmap, reg_info.offreg, + val); + if (ret) + return ret; + *val &= reg_info.mask; + *val >>= reg_info.shift; + return IIO_VAL_INT; + } + break; + case IIO_CURRENT: + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(afe->regmap, reg_info.reg, val); + if (ret) + return ret; + *val &= reg_info.mask; + *val >>= reg_info.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + } + break; + default: + break; + } + + return -EINVAL; +} + +static int afe4403_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct afe4403_data *afe = iio_priv(indio_dev); + const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address]; + + switch (chan->type) { + case IIO_INTENSITY: + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + return regmap_update_bits(afe->regmap, + reg_info.offreg, + reg_info.mask, + (val << reg_info.shift)); + } + break; + case IIO_CURRENT: + switch (mask) { + case IIO_CHAN_INFO_RAW: + return regmap_update_bits(afe->regmap, + reg_info.reg, + reg_info.mask, + (val << reg_info.shift)); + } + break; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_info afe4403_iio_info = { + .attrs = &afe440x_attribute_group, + .read_raw = afe4403_read_raw, + .write_raw = afe4403_write_raw, + .driver_module = THIS_MODULE, +}; + +static irqreturn_t afe4403_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct afe4403_data *afe = iio_priv(indio_dev); + int ret, bit, i = 0; + s32 buffer[8]; + u8 tx[4] = {AFE440X_CONTROL0, 0x0, 0x0, AFE440X_CONTROL0_READ}; + u8 rx[3]; + + /* Enable reading from the device */ + ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0); + if (ret) + goto err; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = spi_write_then_read(afe->spi, + &afe4403_reg_info[bit].reg, 1, + rx, 3); + if (ret) + goto err; + + buffer[i++] = (rx[0] << 16) | + (rx[1] << 8) | + (rx[2]); + } + + /* Disable reading from the device */ + tx[3] = AFE440X_CONTROL0_WRITE; + ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0); + if (ret) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops afe4403_trigger_ops = { + .owner = THIS_MODULE, +}; + +#define AFE4403_TIMING_PAIRS \ + { AFE440X_LED2STC, 0x000050 }, \ + { AFE440X_LED2ENDC, 0x0003e7 }, \ + { AFE440X_LED1LEDSTC, 0x0007d0 }, \ + { AFE440X_LED1LEDENDC, 0x000bb7 }, \ + { AFE440X_ALED2STC, 0x000438 }, \ + { AFE440X_ALED2ENDC, 0x0007cf }, \ + { AFE440X_LED1STC, 0x000820 }, \ + { AFE440X_LED1ENDC, 0x000bb7 }, \ + { AFE440X_LED2LEDSTC, 0x000000 }, \ + { AFE440X_LED2LEDENDC, 0x0003e7 }, \ + { AFE440X_ALED1STC, 0x000c08 }, \ + { AFE440X_ALED1ENDC, 0x000f9f }, \ + { AFE440X_LED2CONVST, 0x0003ef }, \ + { AFE440X_LED2CONVEND, 0x0007cf }, \ + { AFE440X_ALED2CONVST, 0x0007d7 }, \ + { AFE440X_ALED2CONVEND, 0x000bb7 }, \ + { AFE440X_LED1CONVST, 0x000bbf }, \ + { AFE440X_LED1CONVEND, 0x009c3f }, \ + { AFE440X_ALED1CONVST, 0x000fa7 }, \ + { AFE440X_ALED1CONVEND, 0x001387 }, \ + { AFE440X_ADCRSTSTCT0, 0x0003e8 }, \ + { AFE440X_ADCRSTENDCT0, 0x0003eb }, \ + { AFE440X_ADCRSTSTCT1, 0x0007d0 }, \ + { AFE440X_ADCRSTENDCT1, 0x0007d3 }, \ + { AFE440X_ADCRSTSTCT2, 0x000bb8 }, \ + { AFE440X_ADCRSTENDCT2, 0x000bbb }, \ + { AFE440X_ADCRSTSTCT3, 0x000fa0 }, \ + { AFE440X_ADCRSTENDCT3, 0x000fa3 }, \ + { AFE440X_PRPCOUNT, 0x009c3f }, \ + { AFE440X_PDNCYCLESTC, 0x001518 }, \ + { AFE440X_PDNCYCLEENDC, 0x00991f } + +static const struct reg_sequence afe4403_reg_sequences[] = { + AFE4403_TIMING_PAIRS, + { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN | 0x000007}, + { AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES_1_M }, + { AFE440X_LEDCNTRL, (0x14 << AFE440X_LEDCNTRL_LED1_SHIFT) | + (0x14 << AFE440X_LEDCNTRL_LED2_SHIFT) }, + { AFE440X_CONTROL2, AFE440X_CONTROL2_TX_REF_050 << + AFE440X_CONTROL2_TX_REF_SHIFT }, +}; + +static const struct regmap_range afe4403_yes_ranges[] = { + regmap_reg_range(AFE440X_LED2VAL, AFE440X_LED1_ALED1VAL), +}; + +static const struct regmap_access_table afe4403_volatile_table = { + .yes_ranges = afe4403_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(afe4403_yes_ranges), +}; + +static const struct regmap_config afe4403_regmap_config = { + .reg_bits = 8, + .val_bits = 24, + + .max_register = AFE440X_PDNCYCLEENDC, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &afe4403_volatile_table, +}; + +#ifdef CONFIG_OF +static const struct of_device_id afe4403_of_match[] = { + { .compatible = "ti,afe4403", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, afe4403_of_match); +#endif + +static int __maybe_unused afe4403_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4403_data *afe = iio_priv(indio_dev); + int ret; + + ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2, + AFE440X_CONTROL2_PDN_AFE, + AFE440X_CONTROL2_PDN_AFE); + if (ret) + return ret; + + ret = regulator_disable(afe->regulator); + if (ret) { + dev_err(dev, "Unable to disable regulator\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused afe4403_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4403_data *afe = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(afe->regulator); + if (ret) { + dev_err(dev, "Unable to enable regulator\n"); + return ret; + } + + ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2, + AFE440X_CONTROL2_PDN_AFE, 0); + if (ret) + return ret; + + return 0; +} + +static SIMPLE_DEV_PM_OPS(afe4403_pm_ops, afe4403_suspend, afe4403_resume); + +static int afe4403_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct afe4403_data *afe; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*afe)); + if (!indio_dev) + return -ENOMEM; + + afe = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + afe->dev = &spi->dev; + afe->spi = spi; + afe->irq = spi->irq; + + afe->regmap = devm_regmap_init_spi(spi, &afe4403_regmap_config); + if (IS_ERR(afe->regmap)) { + dev_err(afe->dev, "Unable to allocate register map\n"); + return PTR_ERR(afe->regmap); + } + + afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); + if (IS_ERR(afe->regulator)) { + dev_err(afe->dev, "Unable to get regulator\n"); + return PTR_ERR(afe->regulator); + } + ret = regulator_enable(afe->regulator); + if (ret) { + dev_err(afe->dev, "Unable to enable regulator\n"); + return ret; + } + + ret = regmap_write(afe->regmap, AFE440X_CONTROL0, + AFE440X_CONTROL0_SW_RESET); + if (ret) { + dev_err(afe->dev, "Unable to reset device\n"); + goto err_disable_reg; + } + + ret = regmap_multi_reg_write(afe->regmap, afe4403_reg_sequences, + ARRAY_SIZE(afe4403_reg_sequences)); + if (ret) { + dev_err(afe->dev, "Unable to set register defaults\n"); + goto err_disable_reg; + } + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->dev.parent = afe->dev; + indio_dev->channels = afe4403_channels; + indio_dev->num_channels = ARRAY_SIZE(afe4403_channels); + indio_dev->name = AFE4403_DRIVER_NAME; + indio_dev->info = &afe4403_iio_info; + + if (afe->irq > 0) { + afe->trig = devm_iio_trigger_alloc(afe->dev, + "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (!afe->trig) { + dev_err(afe->dev, "Unable to allocate IIO trigger\n"); + ret = -ENOMEM; + goto err_disable_reg; + } + + iio_trigger_set_drvdata(afe->trig, indio_dev); + + afe->trig->ops = &afe4403_trigger_ops; + afe->trig->dev.parent = afe->dev; + + ret = iio_trigger_register(afe->trig); + if (ret) { + dev_err(afe->dev, "Unable to register IIO trigger\n"); + goto err_disable_reg; + } + + ret = devm_request_threaded_irq(afe->dev, afe->irq, + iio_trigger_generic_data_rdy_poll, + NULL, IRQF_ONESHOT, + AFE4403_DRIVER_NAME, + afe->trig); + if (ret) { + dev_err(afe->dev, "Unable to request IRQ\n"); + goto err_trig; + } + } + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + afe4403_trigger_handler, NULL); + if (ret) { + dev_err(afe->dev, "Unable to setup buffer\n"); + goto err_trig; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(afe->dev, "Unable to register IIO device\n"); + goto err_buff; + } + + return 0; + +err_buff: + iio_triggered_buffer_cleanup(indio_dev); +err_trig: + if (afe->irq > 0) + iio_trigger_unregister(afe->trig); +err_disable_reg: + regulator_disable(afe->regulator); + + return ret; +} + +static int afe4403_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct afe4403_data *afe = iio_priv(indio_dev); + int ret; + + iio_device_unregister(indio_dev); + + iio_triggered_buffer_cleanup(indio_dev); + + if (afe->irq > 0) + iio_trigger_unregister(afe->trig); + + ret = regulator_disable(afe->regulator); + if (ret) { + dev_err(afe->dev, "Unable to disable regulator\n"); + return ret; + } + + return 0; +} + +static const struct spi_device_id afe4403_ids[] = { + { "afe4403", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, afe4403_ids); + +static struct spi_driver afe4403_spi_driver = { + .driver = { + .name = AFE4403_DRIVER_NAME, + .of_match_table = of_match_ptr(afe4403_of_match), + .pm = &afe4403_pm_ops, + }, + .probe = afe4403_probe, + .remove = afe4403_remove, + .id_table = afe4403_ids, +}; +module_spi_driver(afe4403_spi_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TI AFE4403 Heart Rate and Pulse Oximeter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c new file mode 100644 index 000000000000..5096a4643784 --- /dev/null +++ b/drivers/iio/health/afe4404.c @@ -0,0 +1,679 @@ +/* + * AFE4404 Heart Rate Monitors and Low-Cost Pulse Oximeters + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "afe440x.h" + +#define AFE4404_DRIVER_NAME "afe4404" + +/* AFE4404 registers */ +#define AFE4404_TIA_GAIN_SEP 0x20 +#define AFE4404_TIA_GAIN 0x21 +#define AFE4404_PROG_TG_STC 0x34 +#define AFE4404_PROG_TG_ENDC 0x35 +#define AFE4404_LED3LEDSTC 0x36 +#define AFE4404_LED3LEDENDC 0x37 +#define AFE4404_CLKDIV_PRF 0x39 +#define AFE4404_OFFDAC 0x3a +#define AFE4404_DEC 0x3d +#define AFE4404_AVG_LED2_ALED2VAL 0x3f +#define AFE4404_AVG_LED1_ALED1VAL 0x40 + +/* AFE4404 GAIN register fields */ +#define AFE4404_TIA_GAIN_RES_MASK GENMASK(2, 0) +#define AFE4404_TIA_GAIN_RES_SHIFT 0 +#define AFE4404_TIA_GAIN_CAP_MASK GENMASK(5, 3) +#define AFE4404_TIA_GAIN_CAP_SHIFT 3 + +/* AFE4404 LEDCNTRL register fields */ +#define AFE4404_LEDCNTRL_ILED1_MASK GENMASK(5, 0) +#define AFE4404_LEDCNTRL_ILED1_SHIFT 0 +#define AFE4404_LEDCNTRL_ILED2_MASK GENMASK(11, 6) +#define AFE4404_LEDCNTRL_ILED2_SHIFT 6 +#define AFE4404_LEDCNTRL_ILED3_MASK GENMASK(17, 12) +#define AFE4404_LEDCNTRL_ILED3_SHIFT 12 + +/* AFE4404 CONTROL2 register fields */ +#define AFE440X_CONTROL2_ILED_2X_MASK BIT(17) +#define AFE440X_CONTROL2_ILED_2X_SHIFT 17 + +/* AFE4404 CONTROL3 register fields */ +#define AFE440X_CONTROL3_OSC_ENABLE BIT(9) + +/* AFE4404 OFFDAC register current fields */ +#define AFE4404_OFFDAC_CURR_LED1_MASK GENMASK(9, 5) +#define AFE4404_OFFDAC_CURR_LED1_SHIFT 5 +#define AFE4404_OFFDAC_CURR_LED2_MASK GENMASK(19, 15) +#define AFE4404_OFFDAC_CURR_LED2_SHIFT 15 +#define AFE4404_OFFDAC_CURR_LED3_MASK GENMASK(4, 0) +#define AFE4404_OFFDAC_CURR_LED3_SHIFT 0 +#define AFE4404_OFFDAC_CURR_ALED1_MASK GENMASK(14, 10) +#define AFE4404_OFFDAC_CURR_ALED1_SHIFT 10 +#define AFE4404_OFFDAC_CURR_ALED2_MASK GENMASK(4, 0) +#define AFE4404_OFFDAC_CURR_ALED2_SHIFT 0 + +/* AFE4404 NULL fields */ +#define NULL_MASK 0 +#define NULL_SHIFT 0 + +/* AFE4404 TIA_GAIN_CAP values */ +#define AFE4404_TIA_GAIN_CAP_5_P 0x0 +#define AFE4404_TIA_GAIN_CAP_2_5_P 0x1 +#define AFE4404_TIA_GAIN_CAP_10_P 0x2 +#define AFE4404_TIA_GAIN_CAP_7_5_P 0x3 +#define AFE4404_TIA_GAIN_CAP_20_P 0x4 +#define AFE4404_TIA_GAIN_CAP_17_5_P 0x5 +#define AFE4404_TIA_GAIN_CAP_25_P 0x6 +#define AFE4404_TIA_GAIN_CAP_22_5_P 0x7 + +/* AFE4404 TIA_GAIN_RES values */ +#define AFE4404_TIA_GAIN_RES_500_K 0x0 +#define AFE4404_TIA_GAIN_RES_250_K 0x1 +#define AFE4404_TIA_GAIN_RES_100_K 0x2 +#define AFE4404_TIA_GAIN_RES_50_K 0x3 +#define AFE4404_TIA_GAIN_RES_25_K 0x4 +#define AFE4404_TIA_GAIN_RES_10_K 0x5 +#define AFE4404_TIA_GAIN_RES_1_M 0x6 +#define AFE4404_TIA_GAIN_RES_2_M 0x7 + +/** + * struct afe4404_data + * @dev - Device structure + * @regmap - Register map of the device + * @regulator - Pointer to the regulator for the IC + * @trig - IIO trigger for this device + * @irq - ADC_RDY line interrupt number + */ +struct afe4404_data { + struct device *dev; + struct regmap *regmap; + struct regulator *regulator; + struct iio_trigger *trig; + int irq; +}; + +enum afe4404_chan_id { + LED1, + ALED1, + LED2, + ALED2, + LED3, + LED1_ALED1, + LED2_ALED2, + ILED1, + ILED2, + ILED3, +}; + +static const struct afe440x_reg_info afe4404_reg_info[] = { + [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1), + [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), + [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2), + [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2), + [LED3] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), + [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), + [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), + [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1), + [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED2), + [ILED3] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED3), +}; + +static const struct iio_chan_spec afe4404_channels[] = { + /* ADC values */ + AFE440X_INTENSITY_CHAN(LED1, "led1", BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED2, "led2", BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED3, "led3", BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + /* LED current */ + AFE440X_CURRENT_CHAN(ILED1, "led1"), + AFE440X_CURRENT_CHAN(ILED2, "led2"), + AFE440X_CURRENT_CHAN(ILED3, "led3"), +}; + +static const struct afe440x_val_table afe4404_res_table[] = { + { .integer = 500000, .fract = 0 }, + { .integer = 250000, .fract = 0 }, + { .integer = 100000, .fract = 0 }, + { .integer = 50000, .fract = 0 }, + { .integer = 25000, .fract = 0 }, + { .integer = 10000, .fract = 0 }, + { .integer = 1000000, .fract = 0 }, + { .integer = 2000000, .fract = 0 }, +}; +AFE440X_TABLE_ATTR(tia_resistance_available, afe4404_res_table); + +static const struct afe440x_val_table afe4404_cap_table[] = { + { .integer = 0, .fract = 5000 }, + { .integer = 0, .fract = 2500 }, + { .integer = 0, .fract = 10000 }, + { .integer = 0, .fract = 7500 }, + { .integer = 0, .fract = 20000 }, + { .integer = 0, .fract = 17500 }, + { .integer = 0, .fract = 25000 }, + { .integer = 0, .fract = 22500 }, +}; +AFE440X_TABLE_ATTR(tia_capacitance_available, afe4404_cap_table); + +static ssize_t afe440x_show_register(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4404_data *afe = iio_priv(indio_dev); + struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); + unsigned int reg_val, type; + int vals[2]; + int ret, val_len; + + ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); + if (ret) + return ret; + + reg_val &= afe440x_attr->mask; + reg_val >>= afe440x_attr->shift; + + switch (afe440x_attr->type) { + case SIMPLE: + type = IIO_VAL_INT; + val_len = 1; + vals[0] = reg_val; + break; + case RESISTANCE: + case CAPACITANCE: + type = IIO_VAL_INT_PLUS_MICRO; + val_len = 2; + if (reg_val < afe440x_attr->table_size) { + vals[0] = afe440x_attr->val_table[reg_val].integer; + vals[1] = afe440x_attr->val_table[reg_val].fract; + break; + } + return -EINVAL; + default: + return -EINVAL; + } + + return iio_format_value(buf, type, val_len, vals); +} + +static ssize_t afe440x_store_register(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4404_data *afe = iio_priv(indio_dev); + struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); + int val, integer, fract, ret; + + ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract); + if (ret) + return ret; + + switch (afe440x_attr->type) { + case SIMPLE: + val = integer; + break; + case RESISTANCE: + case CAPACITANCE: + for (val = 0; val < afe440x_attr->table_size; val++) + if (afe440x_attr->val_table[val].integer == integer && + afe440x_attr->val_table[val].fract == fract) + break; + if (val == afe440x_attr->table_size) + return -EINVAL; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, + afe440x_attr->mask, + (val << afe440x_attr->shift)); + if (ret) + return ret; + + return count; +} + +static AFE440X_ATTR(tia_separate_en, AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0); + +static AFE440X_ATTR(tia_resistance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table)); +static AFE440X_ATTR(tia_capacitance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table)); + +static AFE440X_ATTR(tia_resistance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table)); +static AFE440X_ATTR(tia_capacitance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table)); + +static struct attribute *afe440x_attributes[] = { + &afe440x_attr_tia_separate_en.dev_attr.attr, + &afe440x_attr_tia_resistance1.dev_attr.attr, + &afe440x_attr_tia_capacitance1.dev_attr.attr, + &afe440x_attr_tia_resistance2.dev_attr.attr, + &afe440x_attr_tia_capacitance2.dev_attr.attr, + &dev_attr_tia_resistance_available.attr, + &dev_attr_tia_capacitance_available.attr, + NULL +}; + +static const struct attribute_group afe440x_attribute_group = { + .attrs = afe440x_attributes +}; + +static int afe4404_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct afe4404_data *afe = iio_priv(indio_dev); + const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address]; + int ret; + + switch (chan->type) { + case IIO_INTENSITY: + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(afe->regmap, reg_info.reg, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + ret = regmap_read(afe->regmap, reg_info.offreg, + val); + if (ret) + return ret; + *val &= reg_info.mask; + *val >>= reg_info.shift; + return IIO_VAL_INT; + } + break; + case IIO_CURRENT: + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(afe->regmap, reg_info.reg, val); + if (ret) + return ret; + *val &= reg_info.mask; + *val >>= reg_info.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + } + break; + default: + break; + } + + return -EINVAL; +} + +static int afe4404_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct afe4404_data *afe = iio_priv(indio_dev); + const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address]; + + switch (chan->type) { + case IIO_INTENSITY: + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + return regmap_update_bits(afe->regmap, + reg_info.offreg, + reg_info.mask, + (val << reg_info.shift)); + } + break; + case IIO_CURRENT: + switch (mask) { + case IIO_CHAN_INFO_RAW: + return regmap_update_bits(afe->regmap, + reg_info.reg, + reg_info.mask, + (val << reg_info.shift)); + } + break; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_info afe4404_iio_info = { + .attrs = &afe440x_attribute_group, + .read_raw = afe4404_read_raw, + .write_raw = afe4404_write_raw, + .driver_module = THIS_MODULE, +}; + +static irqreturn_t afe4404_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct afe4404_data *afe = iio_priv(indio_dev); + int ret, bit, i = 0; + s32 buffer[10]; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = regmap_read(afe->regmap, afe4404_reg_info[bit].reg, + &buffer[i++]); + if (ret) + goto err; + } + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops afe4404_trigger_ops = { + .owner = THIS_MODULE, +}; + +/* Default timings from data-sheet */ +#define AFE4404_TIMING_PAIRS \ + { AFE440X_PRPCOUNT, 39999 }, \ + { AFE440X_LED2LEDSTC, 0 }, \ + { AFE440X_LED2LEDENDC, 398 }, \ + { AFE440X_LED2STC, 80 }, \ + { AFE440X_LED2ENDC, 398 }, \ + { AFE440X_ADCRSTSTCT0, 5600 }, \ + { AFE440X_ADCRSTENDCT0, 5606 }, \ + { AFE440X_LED2CONVST, 5607 }, \ + { AFE440X_LED2CONVEND, 6066 }, \ + { AFE4404_LED3LEDSTC, 400 }, \ + { AFE4404_LED3LEDENDC, 798 }, \ + { AFE440X_ALED2STC, 480 }, \ + { AFE440X_ALED2ENDC, 798 }, \ + { AFE440X_ADCRSTSTCT1, 6068 }, \ + { AFE440X_ADCRSTENDCT1, 6074 }, \ + { AFE440X_ALED2CONVST, 6075 }, \ + { AFE440X_ALED2CONVEND, 6534 }, \ + { AFE440X_LED1LEDSTC, 800 }, \ + { AFE440X_LED1LEDENDC, 1198 }, \ + { AFE440X_LED1STC, 880 }, \ + { AFE440X_LED1ENDC, 1198 }, \ + { AFE440X_ADCRSTSTCT2, 6536 }, \ + { AFE440X_ADCRSTENDCT2, 6542 }, \ + { AFE440X_LED1CONVST, 6543 }, \ + { AFE440X_LED1CONVEND, 7003 }, \ + { AFE440X_ALED1STC, 1280 }, \ + { AFE440X_ALED1ENDC, 1598 }, \ + { AFE440X_ADCRSTSTCT3, 7005 }, \ + { AFE440X_ADCRSTENDCT3, 7011 }, \ + { AFE440X_ALED1CONVST, 7012 }, \ + { AFE440X_ALED1CONVEND, 7471 }, \ + { AFE440X_PDNCYCLESTC, 7671 }, \ + { AFE440X_PDNCYCLEENDC, 39199 } + +static const struct reg_sequence afe4404_reg_sequences[] = { + AFE4404_TIMING_PAIRS, + { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, + { AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES_50_K }, + { AFE440X_LEDCNTRL, (0xf << AFE4404_LEDCNTRL_ILED1_SHIFT) | + (0x3 << AFE4404_LEDCNTRL_ILED2_SHIFT) | + (0x3 << AFE4404_LEDCNTRL_ILED3_SHIFT) }, + { AFE440X_CONTROL2, AFE440X_CONTROL3_OSC_ENABLE }, +}; + +static const struct regmap_range afe4404_yes_ranges[] = { + regmap_reg_range(AFE440X_LED2VAL, AFE440X_LED1_ALED1VAL), + regmap_reg_range(AFE4404_AVG_LED2_ALED2VAL, AFE4404_AVG_LED1_ALED1VAL), +}; + +static const struct regmap_access_table afe4404_volatile_table = { + .yes_ranges = afe4404_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(afe4404_yes_ranges), +}; + +static const struct regmap_config afe4404_regmap_config = { + .reg_bits = 8, + .val_bits = 24, + + .max_register = AFE4404_AVG_LED1_ALED1VAL, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &afe4404_volatile_table, +}; + +#ifdef CONFIG_OF +static const struct of_device_id afe4404_of_match[] = { + { .compatible = "ti,afe4404", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, afe4404_of_match); +#endif + +static int __maybe_unused afe4404_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4404_data *afe = iio_priv(indio_dev); + int ret; + + ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2, + AFE440X_CONTROL2_PDN_AFE, + AFE440X_CONTROL2_PDN_AFE); + if (ret) + return ret; + + ret = regulator_disable(afe->regulator); + if (ret) { + dev_err(dev, "Unable to disable regulator\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused afe4404_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct afe4404_data *afe = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(afe->regulator); + if (ret) { + dev_err(dev, "Unable to enable regulator\n"); + return ret; + } + + ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2, + AFE440X_CONTROL2_PDN_AFE, 0); + if (ret) + return ret; + + return 0; +} + +static SIMPLE_DEV_PM_OPS(afe4404_pm_ops, afe4404_suspend, afe4404_resume); + +static int afe4404_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct afe4404_data *afe; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*afe)); + if (!indio_dev) + return -ENOMEM; + + afe = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + afe->dev = &client->dev; + afe->irq = client->irq; + + afe->regmap = devm_regmap_init_i2c(client, &afe4404_regmap_config); + if (IS_ERR(afe->regmap)) { + dev_err(afe->dev, "Unable to allocate register map\n"); + return PTR_ERR(afe->regmap); + } + + afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); + if (IS_ERR(afe->regulator)) { + dev_err(afe->dev, "Unable to get regulator\n"); + return PTR_ERR(afe->regulator); + } + ret = regulator_enable(afe->regulator); + if (ret) { + dev_err(afe->dev, "Unable to enable regulator\n"); + return ret; + } + + ret = regmap_write(afe->regmap, AFE440X_CONTROL0, + AFE440X_CONTROL0_SW_RESET); + if (ret) { + dev_err(afe->dev, "Unable to reset device\n"); + goto disable_reg; + } + + ret = regmap_multi_reg_write(afe->regmap, afe4404_reg_sequences, + ARRAY_SIZE(afe4404_reg_sequences)); + if (ret) { + dev_err(afe->dev, "Unable to set register defaults\n"); + goto disable_reg; + } + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->dev.parent = afe->dev; + indio_dev->channels = afe4404_channels; + indio_dev->num_channels = ARRAY_SIZE(afe4404_channels); + indio_dev->name = AFE4404_DRIVER_NAME; + indio_dev->info = &afe4404_iio_info; + + if (afe->irq > 0) { + afe->trig = devm_iio_trigger_alloc(afe->dev, + "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (!afe->trig) { + dev_err(afe->dev, "Unable to allocate IIO trigger\n"); + ret = -ENOMEM; + goto disable_reg; + } + + iio_trigger_set_drvdata(afe->trig, indio_dev); + + afe->trig->ops = &afe4404_trigger_ops; + afe->trig->dev.parent = afe->dev; + + ret = iio_trigger_register(afe->trig); + if (ret) { + dev_err(afe->dev, "Unable to register IIO trigger\n"); + goto disable_reg; + } + + ret = devm_request_threaded_irq(afe->dev, afe->irq, + iio_trigger_generic_data_rdy_poll, + NULL, IRQF_ONESHOT, + AFE4404_DRIVER_NAME, + afe->trig); + if (ret) { + dev_err(afe->dev, "Unable to request IRQ\n"); + goto disable_reg; + } + } + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + afe4404_trigger_handler, NULL); + if (ret) { + dev_err(afe->dev, "Unable to setup buffer\n"); + goto unregister_trigger; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(afe->dev, "Unable to register IIO device\n"); + goto unregister_triggered_buffer; + } + + return 0; + +unregister_triggered_buffer: + iio_triggered_buffer_cleanup(indio_dev); +unregister_trigger: + if (afe->irq > 0) + iio_trigger_unregister(afe->trig); +disable_reg: + regulator_disable(afe->regulator); + + return ret; +} + +static int afe4404_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct afe4404_data *afe = iio_priv(indio_dev); + int ret; + + iio_device_unregister(indio_dev); + + iio_triggered_buffer_cleanup(indio_dev); + + if (afe->irq > 0) + iio_trigger_unregister(afe->trig); + + ret = regulator_disable(afe->regulator); + if (ret) { + dev_err(afe->dev, "Unable to disable regulator\n"); + return ret; + } + + return 0; +} + +static const struct i2c_device_id afe4404_ids[] = { + { "afe4404", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, afe4404_ids); + +static struct i2c_driver afe4404_i2c_driver = { + .driver = { + .name = AFE4404_DRIVER_NAME, + .of_match_table = of_match_ptr(afe4404_of_match), + .pm = &afe4404_pm_ops, + }, + .probe = afe4404_probe, + .remove = afe4404_remove, + .id_table = afe4404_ids, +}; +module_i2c_driver(afe4404_i2c_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TI AFE4404 Heart Rate and Pulse Oximeter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h new file mode 100644 index 000000000000..c671ab78a23a --- /dev/null +++ b/drivers/iio/health/afe440x.h @@ -0,0 +1,191 @@ +/* + * AFE440X Heart Rate Monitors and Low-Cost Pulse Oximeters + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * + * 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. + * + * 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 _AFE440X_H +#define _AFE440X_H + +/* AFE440X registers */ +#define AFE440X_CONTROL0 0x00 +#define AFE440X_LED2STC 0x01 +#define AFE440X_LED2ENDC 0x02 +#define AFE440X_LED1LEDSTC 0x03 +#define AFE440X_LED1LEDENDC 0x04 +#define AFE440X_ALED2STC 0x05 +#define AFE440X_ALED2ENDC 0x06 +#define AFE440X_LED1STC 0x07 +#define AFE440X_LED1ENDC 0x08 +#define AFE440X_LED2LEDSTC 0x09 +#define AFE440X_LED2LEDENDC 0x0a +#define AFE440X_ALED1STC 0x0b +#define AFE440X_ALED1ENDC 0x0c +#define AFE440X_LED2CONVST 0x0d +#define AFE440X_LED2CONVEND 0x0e +#define AFE440X_ALED2CONVST 0x0f +#define AFE440X_ALED2CONVEND 0x10 +#define AFE440X_LED1CONVST 0x11 +#define AFE440X_LED1CONVEND 0x12 +#define AFE440X_ALED1CONVST 0x13 +#define AFE440X_ALED1CONVEND 0x14 +#define AFE440X_ADCRSTSTCT0 0x15 +#define AFE440X_ADCRSTENDCT0 0x16 +#define AFE440X_ADCRSTSTCT1 0x17 +#define AFE440X_ADCRSTENDCT1 0x18 +#define AFE440X_ADCRSTSTCT2 0x19 +#define AFE440X_ADCRSTENDCT2 0x1a +#define AFE440X_ADCRSTSTCT3 0x1b +#define AFE440X_ADCRSTENDCT3 0x1c +#define AFE440X_PRPCOUNT 0x1d +#define AFE440X_CONTROL1 0x1e +#define AFE440X_LEDCNTRL 0x22 +#define AFE440X_CONTROL2 0x23 +#define AFE440X_ALARM 0x29 +#define AFE440X_LED2VAL 0x2a +#define AFE440X_ALED2VAL 0x2b +#define AFE440X_LED1VAL 0x2c +#define AFE440X_ALED1VAL 0x2d +#define AFE440X_LED2_ALED2VAL 0x2e +#define AFE440X_LED1_ALED1VAL 0x2f +#define AFE440X_CONTROL3 0x31 +#define AFE440X_PDNCYCLESTC 0x32 +#define AFE440X_PDNCYCLEENDC 0x33 + +/* CONTROL0 register fields */ +#define AFE440X_CONTROL0_REG_READ BIT(0) +#define AFE440X_CONTROL0_TM_COUNT_RST BIT(1) +#define AFE440X_CONTROL0_SW_RESET BIT(3) + +/* CONTROL1 register fields */ +#define AFE440X_CONTROL1_TIMEREN BIT(8) + +/* TIAGAIN register fields */ +#define AFE440X_TIAGAIN_ENSEPGAIN_MASK BIT(15) +#define AFE440X_TIAGAIN_ENSEPGAIN_SHIFT 15 + +/* CONTROL2 register fields */ +#define AFE440X_CONTROL2_PDN_AFE BIT(0) +#define AFE440X_CONTROL2_PDN_RX BIT(1) +#define AFE440X_CONTROL2_DYNAMIC4 BIT(3) +#define AFE440X_CONTROL2_DYNAMIC3 BIT(4) +#define AFE440X_CONTROL2_DYNAMIC2 BIT(14) +#define AFE440X_CONTROL2_DYNAMIC1 BIT(20) + +/* CONTROL3 register fields */ +#define AFE440X_CONTROL3_CLKDIV GENMASK(2, 0) + +/* CONTROL0 values */ +#define AFE440X_CONTROL0_WRITE 0x0 +#define AFE440X_CONTROL0_READ 0x1 + +struct afe440x_reg_info { + unsigned int reg; + unsigned int offreg; + unsigned int shift; + unsigned int mask; +}; + +#define AFE440X_REG_INFO(_reg, _offreg, _sm) \ + { \ + .reg = _reg, \ + .offreg = _offreg, \ + .shift = _sm ## _SHIFT, \ + .mask = _sm ## _MASK, \ + } + +#define AFE440X_INTENSITY_CHAN(_index, _name, _mask) \ + { \ + .type = IIO_INTENSITY, \ + .channel = _index, \ + .address = _index, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ + .extend_name = _name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + _mask, \ + } + +#define AFE440X_CURRENT_CHAN(_index, _name) \ + { \ + .type = IIO_CURRENT, \ + .channel = _index, \ + .address = _index, \ + .scan_index = _index, \ + .extend_name = _name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .output = true, \ + } + +enum afe440x_reg_type { + SIMPLE, + RESISTANCE, + CAPACITANCE, +}; + +struct afe440x_val_table { + int integer; + int fract; +}; + +#define AFE440X_TABLE_ATTR(_name, _table) \ +static ssize_t _name ## _show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(_table); i++) \ + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ", \ + _table[i].integer, \ + _table[i].fract); \ + \ + buf[len - 1] = '\n'; \ + \ + return len; \ +} \ +static DEVICE_ATTR_RO(_name) + +struct afe440x_attr { + struct device_attribute dev_attr; + unsigned int reg; + unsigned int shift; + unsigned int mask; + enum afe440x_reg_type type; + const struct afe440x_val_table *val_table; + unsigned int table_size; +}; + +#define to_afe440x_attr(_dev_attr) \ + container_of(_dev_attr, struct afe440x_attr, dev_attr) + +#define AFE440X_ATTR(_name, _reg, _field, _type, _table, _size) \ + struct afe440x_attr afe440x_attr_##_name = { \ + .dev_attr = __ATTR(_name, (S_IRUGO | S_IWUSR), \ + afe440x_show_register, \ + afe440x_store_register), \ + .reg = _reg, \ + .shift = _field ## _SHIFT, \ + .mask = _field ## _MASK, \ + .type = _type, \ + .val_table = _table, \ + .table_size = _size, \ + } + +#endif /* _AFE440X_H */ diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 9d1c81f91dd7..09db89359544 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -13,7 +13,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * TODO: allow LED current and pulse length controls via device tree properties + * TODO: enable pulse length controls via device tree properties */ #include @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ #define MAX30100_REG_SPO2_CONFIG_1600US 0x3 #define MAX30100_REG_LED_CONFIG 0x09 +#define MAX30100_REG_LED_CONFIG_LED_MASK 0x0f #define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4 #define MAX30100_REG_LED_CONFIG_24MA 0x07 @@ -111,6 +113,12 @@ static const struct regmap_config max30100_regmap_config = { .volatile_reg = max30100_is_volatile_reg, }; +static const unsigned int max30100_led_current_mapping[] = { + 4400, 7600, 11000, 14200, 17400, + 20800, 24000, 27100, 30600, 33800, + 37000, 40200, 43600, 46800, 50000 +}; + static const unsigned long max30100_scan_masks[] = {0x3, 0}; static const struct iio_chan_spec max30100_channels[] = { @@ -243,15 +251,76 @@ static irqreturn_t max30100_interrupt_handler(int irq, void *private) return IRQ_HANDLED; } +static int max30100_get_current_idx(unsigned int val, int *reg) +{ + int idx; + + /* LED turned off */ + if (val == 0) { + *reg = 0; + return 0; + } + + for (idx = 0; idx < ARRAY_SIZE(max30100_led_current_mapping); idx++) { + if (max30100_led_current_mapping[idx] == val) { + *reg = idx + 1; + return 0; + } + } + + return -EINVAL; +} + +static int max30100_led_init(struct max30100_data *data) +{ + struct device *dev = &data->client->dev; + struct device_node *np = dev->of_node; + unsigned int val[2]; + int reg, ret; + + ret = of_property_read_u32_array(np, "maxim,led-current-microamp", + (unsigned int *) &val, 2); + if (ret) { + /* Default to 24 mA RED LED, 50 mA IR LED */ + reg = (MAX30100_REG_LED_CONFIG_24MA << + MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) | + MAX30100_REG_LED_CONFIG_50MA; + dev_warn(dev, "no led-current-microamp set"); + + return regmap_write(data->regmap, MAX30100_REG_LED_CONFIG, reg); + } + + /* RED LED current */ + ret = max30100_get_current_idx(val[0], ®); + if (ret) { + dev_err(dev, "invalid RED current setting %d", val[0]); + return ret; + } + + ret = regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG, + MAX30100_REG_LED_CONFIG_LED_MASK << + MAX30100_REG_LED_CONFIG_RED_LED_SHIFT, + reg << MAX30100_REG_LED_CONFIG_RED_LED_SHIFT); + if (ret) + return ret; + + /* IR LED current */ + ret = max30100_get_current_idx(val[1], ®); + if (ret) { + dev_err(dev, "invalid IR current setting %d", val[1]); + return ret; + } + + return regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG, + MAX30100_REG_LED_CONFIG_LED_MASK, reg); +} + static int max30100_chip_init(struct max30100_data *data) { int ret; - /* RED IR LED = 24mA, IR LED = 50mA */ - ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG, - (MAX30100_REG_LED_CONFIG_24MA << - MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) | - MAX30100_REG_LED_CONFIG_50MA); + /* setup LED current settings */ + ret = max30100_led_init(data); if (ret) return ret; diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 6a23698d347c..866dda133336 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -43,14 +43,16 @@ config SI7005 humidity and temperature sensor. To compile this driver as a module, choose M here: the module - will be called si7005. + will be called si7005. This driver also + supports Hoperf TH02 Humidity and Temperature Sensor. config SI7020 tristate "Si7013/20/21 Relative Humidity and Temperature Sensors" depends on I2C help Say yes here to build support for the Silicon Labs Si7013/20/21 - Relative Humidity and Temperature Sensors. + Relative Humidity and Temperature Sensors. This driver also + supports Hoperf TH06 Humidity and Temperature Sensor. To compile this driver as a module, choose M here: the module will be called si7020. diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c index cfc5a051ab9f..20b500da94db 100644 --- a/drivers/iio/humidity/dht11.c +++ b/drivers/iio/humidity/dht11.c @@ -50,12 +50,32 @@ #define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \ DHT11_EDGES_PREAMBLE + 1) -/* Data transmission timing (nano seconds) */ +/* + * Data transmission timing: + * Data bits are encoded as pulse length (high time) on the data line. + * 0-bit: 22-30uS -- typically 26uS (AM2302) + * 1-bit: 68-75uS -- typically 70uS (AM2302) + * The acutal timings also depend on the properties of the cable, with + * longer cables typically making pulses shorter. + * + * Our decoding depends on the time resolution of the system: + * timeres > 34uS ... don't know what a 1-tick pulse is + * 34uS > timeres > 30uS ... no problem (30kHz and 32kHz clocks) + * 30uS > timeres > 23uS ... don't know what a 2-tick pulse is + * timeres < 23uS ... no problem + * + * Luckily clocks in the 33-44kHz range are quite uncommon, so we can + * support most systems if the threshold for decoding a pulse as 1-bit + * is chosen carefully. If somebody really wants to support clocks around + * 40kHz, where this driver is most unreliable, there are two options. + * a) select an implementation using busy loop polling on those systems + * b) use the checksum to do some probabilistic decoding + */ #define DHT11_START_TRANSMISSION 18 /* ms */ -#define DHT11_SENSOR_RESPONSE 80000 -#define DHT11_START_BIT 50000 -#define DHT11_DATA_BIT_LOW 27000 -#define DHT11_DATA_BIT_HIGH 70000 +#define DHT11_MIN_TIMERES 34000 /* ns */ +#define DHT11_THRESHOLD 49000 /* ns */ +#define DHT11_AMBIG_LOW 23000 /* ns */ +#define DHT11_AMBIG_HIGH 30000 /* ns */ struct dht11 { struct device *dev; @@ -76,43 +96,39 @@ struct dht11 { struct {s64 ts; int value; } edges[DHT11_EDGES_PER_READ]; }; -static unsigned char dht11_decode_byte(int *timing, int threshold) +static unsigned char dht11_decode_byte(char *bits) { unsigned char ret = 0; int i; for (i = 0; i < 8; ++i) { ret <<= 1; - if (timing[i] >= threshold) + if (bits[i]) ++ret; } return ret; } -static int dht11_decode(struct dht11 *dht11, int offset, int timeres) +static int dht11_decode(struct dht11 *dht11, int offset) { - int i, t, timing[DHT11_BITS_PER_READ], threshold; + int i, t; + char bits[DHT11_BITS_PER_READ]; unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum; - threshold = DHT11_DATA_BIT_HIGH / timeres; - if (DHT11_DATA_BIT_LOW / timeres + 1 >= threshold) - pr_err("dht11: WARNING: decoding ambiguous\n"); - - /* scale down with timeres and check validity */ for (i = 0; i < DHT11_BITS_PER_READ; ++i) { t = dht11->edges[offset + 2 * i + 2].ts - dht11->edges[offset + 2 * i + 1].ts; if (!dht11->edges[offset + 2 * i + 1].value) return -EIO; /* lost synchronisation */ - timing[i] = t / timeres; + bits[i] = t > DHT11_THRESHOLD; } - hum_int = dht11_decode_byte(timing, threshold); - hum_dec = dht11_decode_byte(&timing[8], threshold); - temp_int = dht11_decode_byte(&timing[16], threshold); - temp_dec = dht11_decode_byte(&timing[24], threshold); - checksum = dht11_decode_byte(&timing[32], threshold); + hum_int = dht11_decode_byte(bits); + hum_dec = dht11_decode_byte(&bits[8]); + temp_int = dht11_decode_byte(&bits[16]); + temp_dec = dht11_decode_byte(&bits[24]); + checksum = dht11_decode_byte(&bits[32]); if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum) return -EIO; @@ -161,12 +177,12 @@ static int dht11_read_raw(struct iio_dev *iio_dev, int *val, int *val2, long m) { struct dht11 *dht11 = iio_priv(iio_dev); - int ret, timeres; + int ret, timeres, offset; mutex_lock(&dht11->lock); if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_boot_ns()) { timeres = ktime_get_resolution_ns(); - if (DHT11_DATA_BIT_HIGH < 2 * timeres) { + if (timeres > DHT11_MIN_TIMERES) { dev_err(dht11->dev, "timeresolution %dns too low\n", timeres); /* In theory a better clock could become available @@ -176,6 +192,10 @@ static int dht11_read_raw(struct iio_dev *iio_dev, ret = -EAGAIN; goto err; } + if (timeres > DHT11_AMBIG_LOW && timeres < DHT11_AMBIG_HIGH) + dev_warn(dht11->dev, + "timeresolution: %dns - decoding ambiguous\n", + timeres); reinit_completion(&dht11->completion); @@ -208,11 +228,14 @@ static int dht11_read_raw(struct iio_dev *iio_dev, if (ret < 0) goto err; - ret = dht11_decode(dht11, - dht11->num_edges == DHT11_EDGES_PER_READ ? - DHT11_EDGES_PREAMBLE : - DHT11_EDGES_PREAMBLE - 2, - timeres); + offset = DHT11_EDGES_PREAMBLE + + dht11->num_edges - DHT11_EDGES_PER_READ; + for (; offset >= 0; --offset) { + ret = dht11_decode(dht11, offset); + if (!ret) + break; + } + if (ret) goto err; } diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index a7f61e881a49..fa4767613173 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -274,7 +274,7 @@ static int hdc100x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) - return -ENODEV; + return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c index d1636a74980e..11cbc38b450f 100644 --- a/drivers/iio/humidity/htu21.c +++ b/drivers/iio/humidity/htu21.c @@ -192,7 +192,7 @@ static int htu21_probe(struct i2c_client *client, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { dev_err(&client->dev, "Adapter does not support some i2c transaction\n"); - return -ENODEV; + return -EOPNOTSUPP; } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data)); diff --git a/drivers/iio/humidity/si7005.c b/drivers/iio/humidity/si7005.c index 91972ccd8aaf..6297766e93d0 100644 --- a/drivers/iio/humidity/si7005.c +++ b/drivers/iio/humidity/si7005.c @@ -135,7 +135,7 @@ static int si7005_probe(struct i2c_client *client, int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; + return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -170,6 +170,7 @@ static int si7005_probe(struct i2c_client *client, static const struct i2c_device_id si7005_id[] = { { "si7005", 0 }, + { "th02", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, si7005_id); diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c index 71991b5c0658..ffc2ccf6374e 100644 --- a/drivers/iio/humidity/si7020.c +++ b/drivers/iio/humidity/si7020.c @@ -121,7 +121,7 @@ static int si7020_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_READ_WORD_DATA)) - return -ENODEV; + return -EOPNOTSUPP; /* Reset device, loads default settings. */ ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); @@ -149,6 +149,7 @@ static int si7020_probe(struct i2c_client *client, static const struct i2c_device_id si7020_id[] = { { "si7020", 0 }, + { "th06", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, si7020_id); diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 8f8d1370ed8b..a7f557af4389 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -3,15 +3,31 @@ # config INV_MPU6050_IIO - tristate "Invensense MPU6050 devices" - depends on I2C && SYSFS - depends on I2C_MUX + tristate select IIO_BUFFER select IIO_TRIGGERED_BUFFER + +config INV_MPU6050_I2C + tristate "Invensense MPU6050 devices (I2C)" + depends on I2C + select INV_MPU6050_IIO + select I2C_MUX + select REGMAP_I2C help This driver supports the Invensense MPU6050 devices. This driver can also support MPU6500 in MPU6050 compatibility mode and also in MPU6500 mode with some limitations. It is a gyroscope/accelerometer combo device. This driver can be built as a module. The module will be called - inv-mpu6050. + inv-mpu6050-i2c. + +config INV_MPU6050_SPI + tristate "Invensense MPU6050 devices (SPI)" + depends on SPI_MASTER + select INV_MPU6050_IIO + select REGMAP_SPI + help + This driver supports the Invensense MPU6050 devices. + It is a gyroscope/accelerometer combo device. + This driver can be built as a module. The module will be called + inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile index f566f6a7b3a9..734af5e6cef9 100644 --- a/drivers/iio/imu/inv_mpu6050/Makefile +++ b/drivers/iio/imu/inv_mpu6050/Makefile @@ -3,4 +3,10 @@ # obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o -inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o inv_mpu_acpi.o +inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o + +obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o +inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o + +obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o +inv-mpu6050-spi-objs := inv_mpu_spi.o diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index 1c982a56acd5..2771106fd650 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -66,11 +66,11 @@ static int asus_acpi_get_sensor_info(struct acpi_device *adev, union acpi_object *elem; int j; - elem = &(cpm->package.elements[i]); + elem = &cpm->package.elements[i]; for (j = 0; j < elem->package.count; ++j) { union acpi_object *sub_elem; - sub_elem = &(elem->package.elements[j]); + sub_elem = &elem->package.elements[j]; if (sub_elem->type == ACPI_TYPE_STRING) strlcpy(info->type, sub_elem->string.pointer, sizeof(info->type)); @@ -139,22 +139,23 @@ static int inv_mpu_process_acpi_config(struct i2c_client *client, return 0; } -int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) +int inv_mpu_acpi_create_mux_client(struct i2c_client *client) { + struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); st->mux_client = NULL; - if (ACPI_HANDLE(&st->client->dev)) { + if (ACPI_HANDLE(&client->dev)) { struct i2c_board_info info; struct acpi_device *adev; int ret = -1; - adev = ACPI_COMPANION(&st->client->dev); + adev = ACPI_COMPANION(&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, + ret = asus_acpi_get_sensor_info(adev, client, &info); break; /* Add more matched product processing here */ @@ -166,7 +167,7 @@ int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) /* No matching DMI, so create device on INV6XX type */ unsigned short primary, secondary; - ret = inv_mpu_process_acpi_config(st->client, &primary, + ret = inv_mpu_process_acpi_config(client, &primary, &secondary); if (!ret && secondary) { char *name; @@ -185,14 +186,15 @@ int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) 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) +void inv_mpu_acpi_delete_mux_client(struct i2c_client *client) { + struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); + if (st->mux_client) i2c_unregister_device(st->mux_client); } @@ -200,12 +202,12 @@ void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) #include "inv_mpu_iio.h" -int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) +int inv_mpu_acpi_create_mux_client(struct i2c_client *client) { return 0; } -void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) +void inv_mpu_acpi_delete_mux_client(struct i2c_client *client) { } #endif diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index f0e06093b5e8..d192953e9a38 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -39,6 +39,26 @@ static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724}; */ static const int accel_scale[] = {598, 1196, 2392, 4785}; +static const struct inv_mpu6050_reg_map reg_set_6500 = { + .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV, + .lpf = INV_MPU6050_REG_CONFIG, + .user_ctrl = INV_MPU6050_REG_USER_CTRL, + .fifo_en = INV_MPU6050_REG_FIFO_EN, + .gyro_config = INV_MPU6050_REG_GYRO_CONFIG, + .accl_config = INV_MPU6050_REG_ACCEL_CONFIG, + .fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H, + .fifo_r_w = INV_MPU6050_REG_FIFO_R_W, + .raw_gyro = INV_MPU6050_REG_RAW_GYRO, + .raw_accl = INV_MPU6050_REG_RAW_ACCEL, + .temperature = INV_MPU6050_REG_TEMPERATURE, + .int_enable = INV_MPU6050_REG_INT_ENABLE, + .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, + .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, + .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, + .accl_offset = INV_MPU6500_REG_ACCEL_OFFSET, + .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, +}; + static const struct inv_mpu6050_reg_map reg_set_6050 = { .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV, .lpf = INV_MPU6050_REG_CONFIG, @@ -55,6 +75,8 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = { .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, + .accl_offset = INV_MPU6050_REG_ACCEL_OFFSET, + .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, }; static const struct inv_mpu6050_chip_config chip_config_6050 = { @@ -66,7 +88,13 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = { .accl_fs = INV_MPU6050_FS_02G, }; -static const struct inv_mpu6050_hw hw_info[INV_NUM_PARTS] = { +static const struct inv_mpu6050_hw hw_info[] = { + { + .num_reg = 117, + .name = "MPU6500", + .reg = ®_set_6500, + .config = &chip_config_6050, + }, { .num_reg = 117, .name = "MPU6050", @@ -75,134 +103,53 @@ static const struct inv_mpu6050_hw hw_info[INV_NUM_PARTS] = { }, }; -int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d) -{ - return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d); -} - -/* - * The i2c read/write needs to happen in unlocked mode. As the parent - * adapter is common. If we use locked versions, it will fail as - * the mux adapter will lock the parent i2c adapter, while calling - * select/deselect functions. - */ -static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st, - u8 reg, u8 d) -{ - int ret; - u8 buf[2]; - struct i2c_msg msg[1] = { - { - .addr = st->client->addr, - .flags = 0, - .len = sizeof(buf), - .buf = buf, - } - }; - - buf[0] = reg; - buf[1] = d; - ret = __i2c_transfer(st->client->adapter, msg, 1); - if (ret != 1) - return ret; - - return 0; -} - -static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv, - u32 chan_id) -{ - struct iio_dev *indio_dev = mux_priv; - struct inv_mpu6050_state *st = iio_priv(indio_dev); - int ret = 0; - - /* Use the same mutex which was used everywhere to protect power-op */ - mutex_lock(&indio_dev->mlock); - if (!st->powerup_count) { - ret = inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1, - 0); - if (ret) - goto write_error; - - msleep(INV_MPU6050_REG_UP_TIME); - } - if (!ret) { - st->powerup_count++; - ret = inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg, - st->client->irq | - INV_MPU6050_BIT_BYPASS_EN); - } -write_error: - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap, - void *mux_priv, u32 chan_id) -{ - struct iio_dev *indio_dev = mux_priv; - struct inv_mpu6050_state *st = iio_priv(indio_dev); - - mutex_lock(&indio_dev->mlock); - /* It doesn't really mattter, if any of the calls fails */ - inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg, - st->client->irq); - st->powerup_count--; - if (!st->powerup_count) - inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1, - INV_MPU6050_BIT_SLEEP); - mutex_unlock(&indio_dev->mlock); - - return 0; -} - int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) { - u8 d, mgmt_1; + unsigned int d, mgmt_1; int result; - - /* switch clock needs to be careful. Only when gyro is on, can - clock source be switched to gyro. Otherwise, it must be set to - internal clock */ - if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) { - result = i2c_smbus_read_i2c_block_data(st->client, - st->reg->pwr_mgmt_1, 1, &mgmt_1); - if (result != 1) + /* + * switch clock needs to be careful. Only when gyro is on, can + * clock source be switched to gyro. Otherwise, it must be set to + * internal clock + */ + if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) { + result = regmap_read(st->map, st->reg->pwr_mgmt_1, &mgmt_1); + if (result) return result; mgmt_1 &= ~INV_MPU6050_BIT_CLK_MASK; } - if ((INV_MPU6050_BIT_PWR_GYRO_STBY == mask) && (!en)) { - /* turning off gyro requires switch to internal clock first. - Then turn off gyro engine */ + if ((mask == INV_MPU6050_BIT_PWR_GYRO_STBY) && (!en)) { + /* + * turning off gyro requires switch to internal clock first. + * Then turn off gyro engine + */ mgmt_1 |= INV_CLK_INTERNAL; - result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, mgmt_1); + result = regmap_write(st->map, st->reg->pwr_mgmt_1, mgmt_1); if (result) return result; } - result = i2c_smbus_read_i2c_block_data(st->client, - st->reg->pwr_mgmt_2, 1, &d); - if (result != 1) + result = regmap_read(st->map, st->reg->pwr_mgmt_2, &d); + if (result) return result; if (en) d &= ~mask; else d |= mask; - result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_2, d); + result = regmap_write(st->map, st->reg->pwr_mgmt_2, d); if (result) return result; if (en) { /* Wait for output stabilize */ msleep(INV_MPU6050_TEMP_UP_TIME); - if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) { + if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) { /* switch internal clock to PLL */ mgmt_1 |= INV_CLK_PLL; - result = inv_mpu6050_write_reg(st, - st->reg->pwr_mgmt_1, mgmt_1); + result = regmap_write(st->map, + st->reg->pwr_mgmt_1, mgmt_1); if (result) return result; } @@ -218,25 +165,26 @@ int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) if (power_on) { /* Already under indio-dev->mlock mutex */ if (!st->powerup_count) - result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, - 0); + result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); if (!result) st->powerup_count++; } else { st->powerup_count--; if (!st->powerup_count) - result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, - INV_MPU6050_BIT_SLEEP); + result = regmap_write(st->map, st->reg->pwr_mgmt_1, + INV_MPU6050_BIT_SLEEP); } if (result) return result; if (power_on) - msleep(INV_MPU6050_REG_UP_TIME); + usleep_range(INV_MPU6050_REG_UP_TIME_MIN, + INV_MPU6050_REG_UP_TIME_MAX); return 0; } +EXPORT_SYMBOL_GPL(inv_mpu6050_set_power_itg); /** * inv_mpu6050_init_config() - Initialize hardware, disable FIFO. @@ -257,59 +205,73 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) if (result) return result; d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d); + result = regmap_write(st->map, st->reg->gyro_config, d); if (result) return result; d = INV_MPU6050_FILTER_20HZ; - result = inv_mpu6050_write_reg(st, st->reg->lpf, d); + result = regmap_write(st->map, st->reg->lpf, d); if (result) return result; d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1; - result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d); + result = regmap_write(st->map, st->reg->sample_rate_div, d); if (result) return result; d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, st->reg->accl_config, d); + result = regmap_write(st->map, st->reg->accl_config, d); if (result) return result; memcpy(&st->chip_config, hw_info[st->chip_type].config, - sizeof(struct inv_mpu6050_chip_config)); + sizeof(struct inv_mpu6050_chip_config)); result = inv_mpu6050_set_power_itg(st, false); return result; } +static int inv_mpu6050_sensor_set(struct inv_mpu6050_state *st, int reg, + int axis, int val) +{ + int ind, result; + __be16 d = cpu_to_be16(val); + + ind = (axis - IIO_MOD_X) * 2; + result = regmap_bulk_write(st->map, reg + ind, (u8 *)&d, 2); + if (result) + return -EINVAL; + + return 0; +} + static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg, - int axis, int *val) + int axis, int *val) { int ind, result; __be16 d; ind = (axis - IIO_MOD_X) * 2; - result = i2c_smbus_read_i2c_block_data(st->client, reg + ind, 2, - (u8 *)&d); - if (result != 2) + result = regmap_bulk_read(st->map, reg + ind, (u8 *)&d, 2); + if (result) return -EINVAL; *val = (short)be16_to_cpup(&d); return IIO_VAL_INT; } -static int inv_mpu6050_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long mask) { +static int +inv_mpu6050_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ struct inv_mpu6050_state *st = iio_priv(indio_dev); + int ret = 0; switch (mask) { case IIO_CHAN_INFO_RAW: { - int ret, result; + int result; ret = IIO_VAL_INT; result = 0; @@ -323,16 +285,16 @@ static int inv_mpu6050_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_ANGL_VEL: if (!st->chip_config.gyro_fifo_enable || - !st->chip_config.enable) { + !st->chip_config.enable) { result = inv_mpu6050_switch_engine(st, true, INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) goto error_read_raw; } - ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, - chan->channel2, val); + ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, + chan->channel2, val); if (!st->chip_config.gyro_fifo_enable || - !st->chip_config.enable) { + !st->chip_config.enable) { result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) @@ -341,16 +303,16 @@ static int inv_mpu6050_read_raw(struct iio_dev *indio_dev, break; case IIO_ACCEL: if (!st->chip_config.accl_fifo_enable || - !st->chip_config.enable) { + !st->chip_config.enable) { result = inv_mpu6050_switch_engine(st, true, INV_MPU6050_BIT_PWR_ACCL_STBY); if (result) goto error_read_raw; } ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, - chan->channel2, val); + chan->channel2, val); if (!st->chip_config.accl_fifo_enable || - !st->chip_config.enable) { + !st->chip_config.enable) { result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_BIT_PWR_ACCL_STBY); if (result) @@ -360,8 +322,8 @@ static int inv_mpu6050_read_raw(struct iio_dev *indio_dev, case IIO_TEMP: /* wait for stablization */ msleep(INV_MPU6050_SENSOR_UP_TIME); - inv_mpu6050_sensor_show(st, st->reg->temperature, - IIO_MOD_X, val); + ret = inv_mpu6050_sensor_show(st, st->reg->temperature, + IIO_MOD_X, val); break; default: ret = -EINVAL; @@ -405,6 +367,20 @@ error_read_raw: default: return -EINVAL; } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ANGL_VEL: + ret = inv_mpu6050_sensor_show(st, st->reg->gyro_offset, + chan->channel2, val); + return IIO_VAL_INT; + case IIO_ACCEL: + ret = inv_mpu6050_sensor_show(st, st->reg->accl_offset, + chan->channel2, val); + return IIO_VAL_INT; + + default: + return -EINVAL; + } default: return -EINVAL; } @@ -418,8 +394,7 @@ static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val) for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) { if (gyro_scale_6050[i] == val) { d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, - st->reg->gyro_config, d); + result = regmap_write(st->map, st->reg->gyro_config, d); if (result) return result; @@ -448,6 +423,7 @@ static int inv_write_raw_get_fmt(struct iio_dev *indio_dev, return -EINVAL; } + static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val) { int result, i; @@ -456,8 +432,7 @@ static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val) for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) { if (accel_scale[i] == val) { d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, - st->reg->accl_config, d); + result = regmap_write(st->map, st->reg->accl_config, d); if (result) return result; @@ -470,16 +445,17 @@ static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val) } static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) { + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ struct inv_mpu6050_state *st = iio_priv(indio_dev); int result; mutex_lock(&indio_dev->mlock); - /* we should only update scale when the chip is disabled, i.e., - not running */ + /* + * we should only update scale when the chip is disabled, i.e. + * not running + */ if (st->chip_config.enable) { result = -EBUSY; goto error_write_raw; @@ -502,6 +478,21 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, break; } break; + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ANGL_VEL: + result = inv_mpu6050_sensor_set(st, + st->reg->gyro_offset, + chan->channel2, val); + break; + case IIO_ACCEL: + result = inv_mpu6050_sensor_set(st, + st->reg->accl_offset, + chan->channel2, val); + break; + default: + result = -EINVAL; + } default: result = -EINVAL; break; @@ -537,7 +528,7 @@ static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate) while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1)) i++; data = d[i]; - result = inv_mpu6050_write_reg(st, st->reg->lpf, data); + result = regmap_write(st->map, st->reg->lpf, data); if (result) return result; st->chip_config.lpf = data; @@ -548,8 +539,9 @@ static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate) /** * inv_mpu6050_fifo_rate_store() - Set fifo rate. */ -static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t +inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { s32 fifo_rate; u8 d; @@ -560,7 +552,7 @@ static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev, if (kstrtoint(buf, 10, &fifo_rate)) return -EINVAL; if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE || - fifo_rate > INV_MPU6050_MAX_FIFO_RATE) + fifo_rate > INV_MPU6050_MAX_FIFO_RATE) return -EINVAL; if (fifo_rate == st->chip_config.fifo_rate) return count; @@ -575,7 +567,7 @@ static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev, goto fifo_rate_fail; d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1; - result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d); + result = regmap_write(st->map, st->reg->sample_rate_div, d); if (result) goto fifo_rate_fail; st->chip_config.fifo_rate = fifo_rate; @@ -596,8 +588,9 @@ fifo_rate_fail: /** * inv_fifo_rate_show() - Get the current sampling rate. */ -static ssize_t inv_fifo_rate_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t +inv_fifo_rate_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev)); @@ -608,16 +601,18 @@ static ssize_t inv_fifo_rate_show(struct device *dev, * inv_attr_show() - calling this function will show current * parameters. */ -static ssize_t inv_attr_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t inv_attr_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev)); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); s8 *m; switch (this_attr->address) { - /* In MPU6050, the two matrix are the same because gyro and accel - are integrated in one chip */ + /* + * In MPU6050, the two matrix are the same because gyro and accel + * are integrated in one chip + */ case ATTR_GYRO_MATRIX: case ATTR_ACCL_MATRIX: m = st->plat_data.orientation; @@ -654,14 +649,15 @@ static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev, .type = _type, \ .modified = 1, \ .channel2 = _channel2, \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ .scan_index = _index, \ .scan_type = { \ .sign = 's', \ .realbits = 16, \ .storagebits = 16, \ - .shift = 0 , \ + .shift = 0, \ .endianness = IIO_BE, \ }, \ } @@ -674,7 +670,7 @@ static const struct iio_chan_spec inv_mpu_channels[] = { */ { .type = IIO_TEMP, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = -1, @@ -727,25 +723,25 @@ static const struct iio_info mpu_info = { /** * inv_check_and_setup_chip() - check and setup chip. */ -static int inv_check_and_setup_chip(struct inv_mpu6050_state *st, - const struct i2c_device_id *id) +static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) { int result; - st->chip_type = INV_MPU6050; st->hw = &hw_info[st->chip_type]; st->reg = hw_info[st->chip_type].reg; /* reset to make sure previous state are not there */ - result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, - INV_MPU6050_BIT_H_RESET); + result = regmap_write(st->map, st->reg->pwr_mgmt_1, + INV_MPU6050_BIT_H_RESET); if (result) return result; msleep(INV_MPU6050_POWER_UP_TIME); - /* toggle power state. After reset, the sleep bit could be on - or off depending on the OTP settings. Toggling power would - make it in a definite state as well as making the hardware - state align with the software state */ + /* + * toggle power state. After reset, the sleep bit could be on + * or off depending on the OTP settings. Toggling power would + * make it in a definite state as well as making the hardware + * state align with the software state + */ result = inv_mpu6050_set_power_itg(st, false); if (result) return result; @@ -754,65 +750,59 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st, return result; result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_ACCL_STBY); + INV_MPU6050_BIT_PWR_ACCL_STBY); if (result) return result; result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_GYRO_STBY); + INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) return result; return 0; } -/** - * inv_mpu_probe() - probe function. - * @client: i2c client. - * @id: i2c device id. - * - * Returns 0 on success, a negative error code otherwise. - */ -static int inv_mpu_probe(struct i2c_client *client, - const struct i2c_device_id *id) +int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, + int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type) { struct inv_mpu6050_state *st; struct iio_dev *indio_dev; struct inv_mpu6050_platform_data *pdata; + struct device *dev = regmap_get_device(regmap); int result; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_I2C_BLOCK)) - return -ENOSYS; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - st->client = client; + st->chip_type = chip_type; st->powerup_count = 0; - pdata = dev_get_platdata(&client->dev); + st->irq = irq; + st->map = regmap; + pdata = dev_get_platdata(dev); if (pdata) st->plat_data = *pdata; /* power is turned on inside check chip type*/ - result = inv_check_and_setup_chip(st, id); + result = inv_check_and_setup_chip(st); if (result) return result; + if (inv_mpu_bus_setup) + inv_mpu_bus_setup(indio_dev); + result = inv_mpu6050_init_config(indio_dev); if (result) { - dev_err(&client->dev, - "Could not initialize device.\n"); + dev_err(dev, "Could not initialize device.\n"); return result; } - i2c_set_clientdata(client, indio_dev); - indio_dev->dev.parent = &client->dev; - /* id will be NULL when enumerated via ACPI */ - if (id) - indio_dev->name = (char *)id->name; + dev_set_drvdata(dev, indio_dev); + indio_dev->dev.parent = dev; + /* name will be NULL when enumerated via ACPI */ + if (name) + indio_dev->name = name; else - indio_dev->name = (char *)dev_name(&client->dev); + indio_dev->name = dev_name(dev); indio_dev->channels = inv_mpu_channels; indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); @@ -824,13 +814,12 @@ static int inv_mpu_probe(struct i2c_client *client, inv_mpu6050_read_fifo, NULL); if (result) { - dev_err(&st->client->dev, "configure buffer fail %d\n", - result); + dev_err(dev, "configure buffer fail %d\n", result); return result; } result = inv_mpu6050_probe_trigger(indio_dev); if (result) { - dev_err(&st->client->dev, "trigger probe fail %d\n", result); + dev_err(dev, "trigger probe fail %d\n", result); goto out_unreg_ring; } @@ -838,102 +827,47 @@ static int inv_mpu_probe(struct i2c_client *client, spin_lock_init(&st->time_stamp_lock); result = iio_device_register(indio_dev); if (result) { - dev_err(&st->client->dev, "IIO register fail %d\n", result); + dev_err(dev, "IIO register fail %d\n", result); goto out_remove_trigger; } - st->mux_adapter = i2c_add_mux_adapter(client->adapter, - &client->dev, - indio_dev, - 0, 0, 0, - inv_mpu6050_select_bypass, - inv_mpu6050_deselect_bypass); - if (!st->mux_adapter) { - result = -ENODEV; - 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: inv_mpu6050_remove_trigger(st); out_unreg_ring: iio_triggered_buffer_cleanup(indio_dev); return result; } +EXPORT_SYMBOL_GPL(inv_mpu_core_probe); -static int inv_mpu_remove(struct i2c_client *client) +int inv_mpu_core_remove(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct iio_dev *indio_dev = dev_get_drvdata(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); + inv_mpu6050_remove_trigger(iio_priv(indio_dev)); iio_triggered_buffer_cleanup(indio_dev); return 0; } +EXPORT_SYMBOL_GPL(inv_mpu_core_remove); + #ifdef CONFIG_PM_SLEEP static int inv_mpu_resume(struct device *dev) { - return inv_mpu6050_set_power_itg( - iio_priv(i2c_get_clientdata(to_i2c_client(dev))), true); + return inv_mpu6050_set_power_itg(iio_priv(dev_get_drvdata(dev)), true); } static int inv_mpu_suspend(struct device *dev) { - return inv_mpu6050_set_power_itg( - iio_priv(i2c_get_clientdata(to_i2c_client(dev))), false); + return inv_mpu6050_set_power_itg(iio_priv(dev_get_drvdata(dev)), false); } -static SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume); - -#define INV_MPU6050_PMOPS (&inv_mpu_pmops) -#else -#define INV_MPU6050_PMOPS NULL #endif /* CONFIG_PM_SLEEP */ -/* - * device id table is used to identify what device can be - * supported by this driver - */ -static const struct i2c_device_id inv_mpu_id[] = { - {"mpu6050", INV_MPU6050}, - {"mpu6500", INV_MPU6500}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, inv_mpu_id); - -static const struct acpi_device_id inv_acpi_match[] = { - {"INVN6500", 0}, - { }, -}; - -MODULE_DEVICE_TABLE(acpi, inv_acpi_match); - -static struct i2c_driver inv_mpu_driver = { - .probe = inv_mpu_probe, - .remove = inv_mpu_remove, - .id_table = inv_mpu_id, - .driver = { - .name = "inv-mpu6050", - .pm = INV_MPU6050_PMOPS, - .acpi_match_table = ACPI_PTR(inv_acpi_match), - }, -}; - -module_i2c_driver(inv_mpu_driver); +SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume); +EXPORT_SYMBOL_GPL(inv_mpu_pmops); MODULE_AUTHOR("Invensense Corporation"); MODULE_DESCRIPTION("Invensense device MPU6050 driver"); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c new file mode 100644 index 000000000000..f581256d9d4c --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -0,0 +1,208 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "inv_mpu_iio.h" + +static const struct regmap_config inv_mpu_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +/* + * The i2c read/write needs to happen in unlocked mode. As the parent + * adapter is common. If we use locked versions, it will fail as + * the mux adapter will lock the parent i2c adapter, while calling + * select/deselect functions. + */ +static int inv_mpu6050_write_reg_unlocked(struct i2c_client *client, + u8 reg, u8 d) +{ + int ret; + u8 buf[2] = {reg, d}; + struct i2c_msg msg[1] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + ret = __i2c_transfer(client->adapter, msg, 1); + if (ret != 1) + return ret; + + return 0; +} + +static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv, + u32 chan_id) +{ + struct i2c_client *client = mux_priv; + struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); + struct inv_mpu6050_state *st = iio_priv(indio_dev); + int ret = 0; + + /* Use the same mutex which was used everywhere to protect power-op */ + mutex_lock(&indio_dev->mlock); + if (!st->powerup_count) { + ret = inv_mpu6050_write_reg_unlocked(client, + st->reg->pwr_mgmt_1, 0); + if (ret) + goto write_error; + + usleep_range(INV_MPU6050_REG_UP_TIME_MIN, + INV_MPU6050_REG_UP_TIME_MAX); + } + if (!ret) { + st->powerup_count++; + ret = inv_mpu6050_write_reg_unlocked(client, + st->reg->int_pin_cfg, + INV_MPU6050_INT_PIN_CFG | + INV_MPU6050_BIT_BYPASS_EN); + } +write_error: + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap, + void *mux_priv, u32 chan_id) +{ + struct i2c_client *client = mux_priv; + struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); + struct inv_mpu6050_state *st = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + /* It doesn't really mattter, if any of the calls fails */ + inv_mpu6050_write_reg_unlocked(client, st->reg->int_pin_cfg, + INV_MPU6050_INT_PIN_CFG); + st->powerup_count--; + if (!st->powerup_count) + inv_mpu6050_write_reg_unlocked(client, st->reg->pwr_mgmt_1, + INV_MPU6050_BIT_SLEEP); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +/** + * inv_mpu_probe() - probe function. + * @client: i2c client. + * @id: i2c device id. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int inv_mpu_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct inv_mpu6050_state *st; + int result; + const char *name = id ? id->name : NULL; + struct regmap *regmap; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EOPNOTSUPP; + + regmap = devm_regmap_init_i2c(client, &inv_mpu_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + result = inv_mpu_core_probe(regmap, client->irq, name, + NULL, id->driver_data); + if (result < 0) + return result; + + st = iio_priv(dev_get_drvdata(&client->dev)); + st->mux_adapter = i2c_add_mux_adapter(client->adapter, + &client->dev, + client, + 0, 0, 0, + inv_mpu6050_select_bypass, + inv_mpu6050_deselect_bypass); + if (!st->mux_adapter) { + result = -ENODEV; + goto out_unreg_device; + } + + result = inv_mpu_acpi_create_mux_client(client); + if (result) + goto out_del_mux; + + return 0; + +out_del_mux: + i2c_del_mux_adapter(st->mux_adapter); +out_unreg_device: + inv_mpu_core_remove(&client->dev); + return result; +} + +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(client); + i2c_del_mux_adapter(st->mux_adapter); + + return inv_mpu_core_remove(&client->dev); +} + +/* + * device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_mpu_id[] = { + {"mpu6050", INV_MPU6050}, + {"mpu6500", INV_MPU6500}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, inv_mpu_id); + +static const struct acpi_device_id inv_acpi_match[] = { + {"INVN6500", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(acpi, inv_acpi_match); + +static struct i2c_driver inv_mpu_driver = { + .probe = inv_mpu_probe, + .remove = inv_mpu_remove, + .id_table = inv_mpu_id, + .driver = { + .acpi_match_table = ACPI_PTR(inv_acpi_match), + .name = "inv-mpu6050-i2c", + .pm = &inv_mpu_pmops, + }, +}; + +module_i2c_driver(inv_mpu_driver); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device MPU6050 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index db0a4a2758ab..e302a49703bf 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,9 @@ * @int_enable: Interrupt enable register. * @pwr_mgmt_1: Controls chip's power state and clock source. * @pwr_mgmt_2: Controls power state of individual sensors. + * @int_pin_cfg; Controls interrupt pin configuration. + * @accl_offset: Controls the accelerometer calibration offset. + * @gyro_offset: Controls the gyroscope calibration offset. */ struct inv_mpu6050_reg_map { u8 sample_rate_div; @@ -55,12 +59,15 @@ struct inv_mpu6050_reg_map { u8 pwr_mgmt_1; u8 pwr_mgmt_2; u8 int_pin_cfg; + u8 accl_offset; + u8 gyro_offset; }; /*device enum */ enum inv_devices { INV_MPU6050, INV_MPU6500, + INV_MPU6000, INV_NUM_PARTS }; @@ -107,9 +114,10 @@ struct inv_mpu6050_hw { * @hw: Other hardware-specific information. * @chip_type: chip type. * @time_stamp_lock: spin lock to time stamp. - * @client: i2c client handle. * @plat_data: platform data. * @timestamps: kfifo queue to store time stamp. + * @map regmap pointer. + * @irq interrupt number. */ struct inv_mpu6050_state { #define TIMESTAMP_FIFO_SIZE 16 @@ -119,15 +127,19 @@ struct inv_mpu6050_state { const struct inv_mpu6050_hw *hw; enum inv_devices chip_type; 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); + struct regmap *map; + int irq; }; /*register and associated bit definition*/ +#define INV_MPU6050_REG_ACCEL_OFFSET 0x06 +#define INV_MPU6050_REG_GYRO_OFFSET 0x13 + #define INV_MPU6050_REG_SAMPLE_RATE_DIV 0x19 #define INV_MPU6050_REG_CONFIG 0x1A #define INV_MPU6050_REG_GYRO_CONFIG 0x1B @@ -151,6 +163,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_BIT_I2C_MST_EN 0x20 #define INV_MPU6050_BIT_FIFO_EN 0x40 #define INV_MPU6050_BIT_DMP_EN 0x80 +#define INV_MPU6050_BIT_I2C_IF_DIS 0x10 #define INV_MPU6050_REG_PWR_MGMT_1 0x6B #define INV_MPU6050_BIT_H_RESET 0x80 @@ -167,10 +180,18 @@ struct inv_mpu6050_state { #define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6 #define INV_MPU6050_FIFO_COUNT_BYTE 2 #define INV_MPU6050_FIFO_THRESHOLD 500 + +/* mpu6500 registers */ +#define INV_MPU6500_REG_ACCEL_OFFSET 0x77 + +/* delay time in milliseconds */ #define INV_MPU6050_POWER_UP_TIME 100 #define INV_MPU6050_TEMP_UP_TIME 100 #define INV_MPU6050_SENSOR_UP_TIME 30 -#define INV_MPU6050_REG_UP_TIME 5 + +/* delay time in microseconds */ +#define INV_MPU6050_REG_UP_TIME_MIN 5000 +#define INV_MPU6050_REG_UP_TIME_MAX 10000 #define INV_MPU6050_TEMP_OFFSET 12421 #define INV_MPU6050_TEMP_SCALE 2941 @@ -185,6 +206,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_INT_PIN_CFG 0x37 #define INV_MPU6050_BIT_BYPASS_EN 0x2 +#define INV_MPU6050_INT_PIN_CFG 0 /* init parameters */ #define INV_MPU6050_INIT_FIFO_RATE 50 @@ -252,5 +274,10 @@ 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); +int inv_mpu_acpi_create_mux_client(struct i2c_client *client); +void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); +int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, + int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type); +int inv_mpu_core_remove(struct device *dev); +int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on); +extern const struct dev_pm_ops inv_mpu_pmops; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index ba27e277511f..d0700628ee6d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -41,23 +40,24 @@ int inv_reset_fifo(struct iio_dev *indio_dev) struct inv_mpu6050_state *st = iio_priv(indio_dev); /* disable interrupt */ - result = inv_mpu6050_write_reg(st, st->reg->int_enable, 0); + result = regmap_write(st->map, st->reg->int_enable, 0); if (result) { - dev_err(&st->client->dev, "int_enable failed %d\n", result); + dev_err(regmap_get_device(st->map), "int_enable failed %d\n", + result); return result; } /* disable the sensor output to FIFO */ - result = inv_mpu6050_write_reg(st, st->reg->fifo_en, 0); + result = regmap_write(st->map, st->reg->fifo_en, 0); if (result) goto reset_fifo_fail; /* disable fifo reading */ - result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, 0); + result = regmap_write(st->map, st->reg->user_ctrl, 0); if (result) goto reset_fifo_fail; /* reset FIFO*/ - result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, - INV_MPU6050_BIT_FIFO_RST); + result = regmap_write(st->map, st->reg->user_ctrl, + INV_MPU6050_BIT_FIFO_RST); if (result) goto reset_fifo_fail; @@ -67,14 +67,14 @@ int inv_reset_fifo(struct iio_dev *indio_dev) /* enable interrupt */ if (st->chip_config.accl_fifo_enable || st->chip_config.gyro_fifo_enable) { - result = inv_mpu6050_write_reg(st, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); + result = regmap_write(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN); if (result) return result; } /* enable FIFO reading and I2C master interface*/ - result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, - INV_MPU6050_BIT_FIFO_EN); + result = regmap_write(st->map, st->reg->user_ctrl, + INV_MPU6050_BIT_FIFO_EN); if (result) goto reset_fifo_fail; /* enable sensor output to FIFO */ @@ -83,16 +83,16 @@ int inv_reset_fifo(struct iio_dev *indio_dev) d |= INV_MPU6050_BITS_GYRO_OUT; if (st->chip_config.accl_fifo_enable) d |= INV_MPU6050_BIT_ACCEL_OUT; - result = inv_mpu6050_write_reg(st, st->reg->fifo_en, d); + result = regmap_write(st->map, st->reg->fifo_en, d); if (result) goto reset_fifo_fail; return 0; reset_fifo_fail: - dev_err(&st->client->dev, "reset fifo failed %d\n", result); - result = inv_mpu6050_write_reg(st, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); + dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result); + result = regmap_write(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN); return result; } @@ -109,7 +109,7 @@ irqreturn_t inv_mpu6050_irq_handler(int irq, void *p) timestamp = iio_get_time_ns(); kfifo_in_spinlocked(&st->timestamps, ×tamp, 1, - &st->time_stamp_lock); + &st->time_stamp_lock); return IRQ_WAKE_THREAD; } @@ -143,10 +143,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) * read fifo_count register to know how many bytes inside FIFO * right now */ - result = i2c_smbus_read_i2c_block_data(st->client, - st->reg->fifo_count_h, - INV_MPU6050_FIFO_COUNT_BYTE, data); - if (result != INV_MPU6050_FIFO_COUNT_BYTE) + result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data, + INV_MPU6050_FIFO_COUNT_BYTE); + if (result) goto end_session; fifo_count = be16_to_cpup((__be16 *)(&data[0])); if (fifo_count < bytes_per_datum) @@ -158,22 +157,21 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) goto flush_fifo; /* Timestamp mismatch. */ if (kfifo_len(&st->timestamps) > - fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR) - goto flush_fifo; + fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR) + goto flush_fifo; while (fifo_count >= bytes_per_datum) { - result = i2c_smbus_read_i2c_block_data(st->client, - st->reg->fifo_r_w, - bytes_per_datum, data); - if (result != bytes_per_datum) + result = regmap_bulk_read(st->map, st->reg->fifo_r_w, + data, bytes_per_datum); + if (result) goto flush_fifo; result = kfifo_out(&st->timestamps, ×tamp, 1); /* when there is no timestamp, put timestamp as 0 */ - if (0 == result) + if (result == 0) timestamp = 0; result = iio_push_to_buffers_with_timestamp(indio_dev, data, - timestamp); + timestamp); if (result) goto flush_fifo; fifo_count -= bytes_per_datum; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c new file mode 100644 index 000000000000..dea6c4361de0 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -0,0 +1,98 @@ +/* +* Copyright (C) 2015 Intel Corporation Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ +#include +#include +#include +#include +#include +#include "inv_mpu_iio.h" + +static const struct regmap_config inv_mpu_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + int ret = 0; + + ret = inv_mpu6050_set_power_itg(st, true); + if (ret) + return ret; + + ret = regmap_write(st->map, INV_MPU6050_REG_USER_CTRL, + INV_MPU6050_BIT_I2C_IF_DIS); + if (ret) { + inv_mpu6050_set_power_itg(st, false); + return ret; + } + + return inv_mpu6050_set_power_itg(st, false); +} + +static int inv_mpu_probe(struct spi_device *spi) +{ + struct regmap *regmap; + const struct spi_device_id *id = spi_get_device_id(spi); + const char *name = id ? id->name : NULL; + + regmap = devm_regmap_init_spi(spi, &inv_mpu_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to register spi regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return inv_mpu_core_probe(regmap, spi->irq, name, + inv_mpu_i2c_disable, id->driver_data); +} + +static int inv_mpu_remove(struct spi_device *spi) +{ + return inv_mpu_core_remove(&spi->dev); +} + +/* + * device id table is used to identify what device can be + * supported by this driver + */ +static const struct spi_device_id inv_mpu_id[] = { + {"mpu6000", INV_MPU6000}, + {} +}; + +MODULE_DEVICE_TABLE(spi, inv_mpu_id); + +static const struct acpi_device_id inv_acpi_match[] = { + {"INVN6000", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, inv_acpi_match); + +static struct spi_driver inv_mpu_driver = { + .probe = inv_mpu_probe, + .remove = inv_mpu_remove, + .id_table = inv_mpu_id, + .driver = { + .acpi_match_table = ACPI_PTR(inv_acpi_match), + .name = "inv-mpu6000-spi", + .pm = &inv_mpu_pmops, + }, +}; + +module_spi_driver(inv_mpu_driver); + +MODULE_AUTHOR("Adriana Reus "); +MODULE_DESCRIPTION("Invensense device MPU6000 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index 844610c3a3a9..e8818d4dd4b8 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -19,19 +19,19 @@ static void inv_scan_query(struct iio_dev *indio_dev) st->chip_config.gyro_fifo_enable = test_bit(INV_MPU6050_SCAN_GYRO_X, - indio_dev->active_scan_mask) || - test_bit(INV_MPU6050_SCAN_GYRO_Y, - indio_dev->active_scan_mask) || - test_bit(INV_MPU6050_SCAN_GYRO_Z, - indio_dev->active_scan_mask); + indio_dev->active_scan_mask) || + test_bit(INV_MPU6050_SCAN_GYRO_Y, + indio_dev->active_scan_mask) || + test_bit(INV_MPU6050_SCAN_GYRO_Z, + indio_dev->active_scan_mask); st->chip_config.accl_fifo_enable = test_bit(INV_MPU6050_SCAN_ACCL_X, - indio_dev->active_scan_mask) || - test_bit(INV_MPU6050_SCAN_ACCL_Y, - indio_dev->active_scan_mask) || - test_bit(INV_MPU6050_SCAN_ACCL_Z, - indio_dev->active_scan_mask); + indio_dev->active_scan_mask) || + test_bit(INV_MPU6050_SCAN_ACCL_Y, + indio_dev->active_scan_mask) || + test_bit(INV_MPU6050_SCAN_ACCL_Z, + indio_dev->active_scan_mask); } /** @@ -65,15 +65,15 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (result) return result; } else { - result = inv_mpu6050_write_reg(st, st->reg->fifo_en, 0); + result = regmap_write(st->map, st->reg->fifo_en, 0); if (result) return result; - result = inv_mpu6050_write_reg(st, st->reg->int_enable, 0); + result = regmap_write(st->map, st->reg->int_enable, 0); if (result) return result; - result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, 0); + result = regmap_write(st->map, st->reg->user_ctrl, 0); if (result) return result; @@ -101,7 +101,7 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) * @state: Desired trigger state */ static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) + bool state) { return inv_mpu6050_set_enable(iio_trigger_get_drvdata(trig), state); } @@ -123,7 +123,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) if (!st->trig) return -ENOMEM; - ret = devm_request_irq(&indio_dev->dev, st->client->irq, + ret = devm_request_irq(&indio_dev->dev, st->irq, &iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING, "inv_mpu", @@ -131,7 +131,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) if (ret) return ret; - st->trig->dev.parent = &st->client->dev; + st->trig->dev.parent = regmap_get_device(st->map); st->trig->ops = &inv_mpu_trigger_ops; iio_trigger_set_drvdata(st->trig, indio_dev); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 139ae916225f..b976332d45d3 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -512,33 +512,41 @@ static ssize_t iio_buffer_show_enable(struct device *dev, return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer)); } +static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev, + unsigned int scan_index) +{ + const struct iio_chan_spec *ch; + unsigned int bytes; + + ch = iio_find_channel_from_si(indio_dev, scan_index); + bytes = ch->scan_type.storagebits / 8; + if (ch->scan_type.repeat > 1) + bytes *= ch->scan_type.repeat; + return bytes; +} + +static unsigned int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) +{ + return iio_storage_bytes_for_si(indio_dev, + indio_dev->scan_index_timestamp); +} + static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const unsigned long *mask, bool timestamp) { - const struct iio_chan_spec *ch; unsigned bytes = 0; int length, i; /* How much space will the demuxed element take? */ for_each_set_bit(i, mask, indio_dev->masklength) { - ch = iio_find_channel_from_si(indio_dev, i); - if (ch->scan_type.repeat > 1) - length = ch->scan_type.storagebits / 8 * - ch->scan_type.repeat; - else - length = ch->scan_type.storagebits / 8; + length = iio_storage_bytes_for_si(indio_dev, i); bytes = ALIGN(bytes, length); bytes += length; } + if (timestamp) { - ch = iio_find_channel_from_si(indio_dev, - indio_dev->scan_index_timestamp); - if (ch->scan_type.repeat > 1) - length = ch->scan_type.storagebits / 8 * - ch->scan_type.repeat; - else - length = ch->scan_type.storagebits / 8; + length = iio_storage_bytes_for_timestamp(indio_dev); bytes = ALIGN(bytes, length); bytes += length; } @@ -1288,7 +1296,6 @@ static int iio_buffer_add_demux(struct iio_buffer *buffer, static int iio_buffer_update_demux(struct iio_dev *indio_dev, struct iio_buffer *buffer) { - const struct iio_chan_spec *ch; int ret, in_ind = -1, out_ind, length; unsigned in_loc = 0, out_loc = 0; struct iio_demux_table *p = NULL; @@ -1315,21 +1322,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, in_ind = find_next_bit(indio_dev->active_scan_mask, indio_dev->masklength, in_ind + 1); - ch = iio_find_channel_from_si(indio_dev, in_ind); - if (ch->scan_type.repeat > 1) - length = ch->scan_type.storagebits / 8 * - ch->scan_type.repeat; - else - length = ch->scan_type.storagebits / 8; + length = iio_storage_bytes_for_si(indio_dev, in_ind); /* Make sure we are aligned */ in_loc = roundup(in_loc, length) + length; } - ch = iio_find_channel_from_si(indio_dev, in_ind); - if (ch->scan_type.repeat > 1) - length = ch->scan_type.storagebits / 8 * - ch->scan_type.repeat; - else - length = ch->scan_type.storagebits / 8; + length = iio_storage_bytes_for_si(indio_dev, in_ind); out_loc = roundup(out_loc, length); in_loc = roundup(in_loc, length); ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length); @@ -1340,13 +1337,7 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, } /* Relies on scan_timestamp being last */ if (buffer->scan_timestamp) { - ch = iio_find_channel_from_si(indio_dev, - indio_dev->scan_index_timestamp); - if (ch->scan_type.repeat > 1) - length = ch->scan_type.storagebits / 8 * - ch->scan_type.repeat; - else - length = ch->scan_type.storagebits / 8; + length = iio_storage_bytes_for_timestamp(indio_dev); out_loc = roundup(out_loc, length); in_loc = roundup(in_loc, length); ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index af7cc1e65656..70cb7eb0a75c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -77,6 +77,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_VELOCITY] = "velocity", [IIO_CONCENTRATION] = "concentration", [IIO_RESISTANCE] = "resistance", + [IIO_PH] = "ph", }; static const char * const iio_modifier_names[] = { diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c index 8b4164343f20..b05946604f80 100644 --- a/drivers/iio/light/bh1750.c +++ b/drivers/iio/light/bh1750.c @@ -241,7 +241,7 @@ static int bh1750_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) - return -ENODEV; + return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index c4e8c6b6c3c3..99a62816c3b4 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -326,7 +326,7 @@ static int jsa1212_probe(struct i2c_client *client, int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; + return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 01e111e72d4b..b776c8ed4387 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -65,19 +65,25 @@ #define OPT3001_REG_EXPONENT(n) ((n) >> 12) #define OPT3001_REG_MANTISSA(n) ((n) & 0xfff) +#define OPT3001_INT_TIME_LONG 800000 +#define OPT3001_INT_TIME_SHORT 100000 + /* * Time to wait for conversion result to be ready. The device datasheet - * worst-case max value is 880ms. Add some slack to be on the safe side. + * sect. 6.5 states results are ready after total integration time plus 3ms. + * This results in worst-case max values of 113ms or 883ms, respectively. + * Add some slack to be on the safe side. */ -#define OPT3001_RESULT_READY_TIMEOUT msecs_to_jiffies(1000) +#define OPT3001_RESULT_READY_SHORT 150 +#define OPT3001_RESULT_READY_LONG 1000 struct opt3001 { struct i2c_client *client; struct device *dev; struct mutex lock; - u16 ok_to_ignore_lock:1; - u16 result_ready:1; + bool ok_to_ignore_lock; + bool result_ready; wait_queue_head_t result_ready_queue; u16 result; @@ -89,6 +95,8 @@ struct opt3001 { u8 high_thresh_exp; u8 low_thresh_exp; + + bool use_irq; }; struct opt3001_scale { @@ -227,26 +235,30 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2) u16 reg; u8 exponent; u16 value; + long timeout; - /* - * Enable the end-of-conversion interrupt mechanism. Note that doing - * so will overwrite the low-level limit value however we will restore - * this value later on. - */ - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT, - OPT3001_LOW_LIMIT_EOC_ENABLE); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; + if (opt->use_irq) { + /* + * Enable the end-of-conversion interrupt mechanism. Note that + * doing so will overwrite the low-level limit value however we + * will restore this value later on. + */ + ret = i2c_smbus_write_word_swapped(opt->client, + OPT3001_LOW_LIMIT, + OPT3001_LOW_LIMIT_EOC_ENABLE); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + return ret; + } + + /* Allow IRQ to access the device despite lock being set */ + opt->ok_to_ignore_lock = true; } - /* Reset data-ready indicator flag (will be set in the IRQ routine) */ + /* Reset data-ready indicator flag */ opt->result_ready = false; - /* Allow IRQ to access the device despite lock being set */ - opt->ok_to_ignore_lock = true; - /* Configure for single-conversion mode and start a new conversion */ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); if (ret < 0) { @@ -266,32 +278,69 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2) goto err; } - /* Wait for the IRQ to indicate the conversion is complete */ - ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready, - OPT3001_RESULT_READY_TIMEOUT); + if (opt->use_irq) { + /* Wait for the IRQ to indicate the conversion is complete */ + ret = wait_event_timeout(opt->result_ready_queue, + opt->result_ready, + msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); + } else { + /* Sleep for result ready time */ + timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ? + OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG; + msleep(timeout); + + /* Check result ready flag */ + ret = i2c_smbus_read_word_swapped(opt->client, + OPT3001_CONFIGURATION); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_CONFIGURATION); + goto err; + } + + if (!(ret & OPT3001_CONFIGURATION_CRF)) { + ret = -ETIMEDOUT; + goto err; + } + + /* Obtain value */ + ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); + if (ret < 0) { + dev_err(opt->dev, "failed to read register %02x\n", + OPT3001_RESULT); + goto err; + } + opt->result = ret; + opt->result_ready = true; + } err: - /* Disallow IRQ to access the device while lock is active */ - opt->ok_to_ignore_lock = false; + if (opt->use_irq) + /* Disallow IRQ to access the device while lock is active */ + opt->ok_to_ignore_lock = false; if (ret == 0) return -ETIMEDOUT; else if (ret < 0) return ret; - /* - * Disable the end-of-conversion interrupt mechanism by restoring the - * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note - * that selectively clearing those enable bits would affect the actual - * limit value due to bit-overlap and therefore can't be done. - */ - value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; - ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT, - value); - if (ret < 0) { - dev_err(opt->dev, "failed to write register %02x\n", - OPT3001_LOW_LIMIT); - return ret; + if (opt->use_irq) { + /* + * Disable the end-of-conversion interrupt mechanism by + * restoring the low-level limit value (clearing + * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing + * those enable bits would affect the actual limit value due to + * bit-overlap and therefore can't be done. + */ + value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; + ret = i2c_smbus_write_word_swapped(opt->client, + OPT3001_LOW_LIMIT, + value); + if (ret < 0) { + dev_err(opt->dev, "failed to write register %02x\n", + OPT3001_LOW_LIMIT); + return ret; + } } exponent = OPT3001_REG_EXPONENT(opt->result); @@ -325,13 +374,13 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time) reg = ret; switch (time) { - case 100000: + case OPT3001_INT_TIME_SHORT: reg &= ~OPT3001_CONFIGURATION_CT; - opt->int_time = 100000; + opt->int_time = OPT3001_INT_TIME_SHORT; break; - case 800000: + case OPT3001_INT_TIME_LONG: reg |= OPT3001_CONFIGURATION_CT; - opt->int_time = 800000; + opt->int_time = OPT3001_INT_TIME_LONG; break; default: return -EINVAL; @@ -597,9 +646,9 @@ static int opt3001_configure(struct opt3001 *opt) /* Reflect status of the device's integration time setting */ if (reg & OPT3001_CONFIGURATION_CT) - opt->int_time = 800000; + opt->int_time = OPT3001_INT_TIME_LONG; else - opt->int_time = 100000; + opt->int_time = OPT3001_INT_TIME_SHORT; /* Ensure device is in shutdown initially */ opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); @@ -733,12 +782,18 @@ static int opt3001_probe(struct i2c_client *client, return ret; } - ret = request_threaded_irq(irq, NULL, opt3001_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "opt3001", iio); - if (ret) { - dev_err(dev, "failed to request IRQ #%d\n", irq); - return ret; + /* Make use of INT pin only if valid IRQ no. is given */ + if (irq > 0) { + ret = request_threaded_irq(irq, NULL, opt3001_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "opt3001", iio); + if (ret) { + dev_err(dev, "failed to request IRQ #%d\n", irq); + return ret; + } + opt->use_irq = true; + } else { + dev_dbg(opt->dev, "enabling interrupt-less operation\n"); } return 0; @@ -751,7 +806,8 @@ static int opt3001_remove(struct i2c_client *client) int ret; u16 reg; - free_irq(client->irq, iio); + if (opt->use_irq) + free_irq(client->irq, iio); ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); if (ret < 0) { diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 868abada3409..021dc5361f53 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -105,4 +105,37 @@ config IIO_ST_MAGN_SPI_3AXIS depends on IIO_ST_MAGN_3AXIS depends on IIO_ST_SENSORS_SPI +config SENSORS_HMC5843 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config SENSORS_HMC5843_I2C + tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer (I2C)" + depends on I2C + select SENSORS_HMC5843 + select REGMAP_I2C + help + Say Y here to add support for the Honeywell HMC5843, HMC5883 and + HMC5883L 3-Axis Magnetometer (digital compass). + + This driver can also be compiled as a set of modules. + If so, these modules will be created: + - hmc5843_core (core functions) + - hmc5843_i2c (support for HMC5843, HMC5883, HMC5883L and HMC5983) + +config SENSORS_HMC5843_SPI + tristate "Honeywell HMC5983 3-Axis Magnetometer (SPI)" + depends on SPI_MASTER + select SENSORS_HMC5843 + select REGMAP_SPI + help + Say Y here to add support for the Honeywell HMC5983 3-Axis Magnetometer + (digital compass). + + This driver can also be compiled as a set of modules. + If so, these modules will be created: + - hmc5843_core (core functions) + - hmc5843_spi (support for HMC5983) + endmenu diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 2c72df458ec2..dd03fe524481 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -15,3 +15,7 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o + +obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o +obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o +obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index b13936dacc78..9c5c9ef3f1da 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -252,7 +252,7 @@ struct ak_def { u8 data_regs[3]; }; -static struct ak_def ak_def_array[AK_MAX_TYPE] = { +static const struct ak_def ak_def_array[AK_MAX_TYPE] = { { .type = AK8975, .raw_to_gauss = ak8975_raw_to_gauss, @@ -360,7 +360,7 @@ static struct ak_def ak_def_array[AK_MAX_TYPE] = { */ struct ak8975_data { struct i2c_client *client; - struct ak_def *def; + const struct ak_def *def; struct attribute_group attrs; struct mutex lock; u8 asa[3]; diff --git a/drivers/staging/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h similarity index 99% rename from drivers/staging/iio/magnetometer/hmc5843.h rename to drivers/iio/magnetometer/hmc5843.h index 06f35d3828e4..76a5d7484d8d 100644 --- a/drivers/staging/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -7,8 +7,7 @@ * 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 HMC5843_CORE_H #define HMC5843_CORE_H @@ -38,7 +37,7 @@ enum hmc5843_ids { * @regmap: hardware access register maps * @variant: describe chip variants * @buffer: 3x 16-bit channels + padding + 64-bit timestamp - **/ + */ struct hmc5843_data { struct device *dev; struct mutex lock; diff --git a/drivers/staging/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c similarity index 86% rename from drivers/staging/iio/magnetometer/hmc5843_core.c rename to drivers/iio/magnetometer/hmc5843_core.c index 394bc141a1b0..77882b466e0f 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -18,7 +18,6 @@ * 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. - * */ #include @@ -66,6 +65,33 @@ #define HMC5843_MEAS_CONF_NEGATIVE_BIAS 0x02 #define HMC5843_MEAS_CONF_MASK 0x03 +/* + * API for setting the measurement configuration to + * Normal, Positive bias and Negative bias + * + * From the datasheet: + * 0 - Normal measurement configuration (default): In normal measurement + * configuration the device follows normal measurement flow. Pins BP + * and BN are left floating and high impedance. + * + * 1 - Positive bias configuration: In positive bias configuration, a + * positive current is forced across the resistive load on pins BP + * and BN. + * + * 2 - Negative bias configuration. In negative bias configuration, a + * negative current is forced across the resistive load on pins BP + * and BN. + * + * 3 - Only available on HMC5983. Magnetic sensor is disabled. + * Temperature sensor is enabled. + */ + +static const char *const hmc5843_meas_conf_modes[] = {"normal", "positivebias", + "negativebias"}; + +static const char *const hmc5983_meas_conf_modes[] = {"normal", "positivebias", + "negativebias", + "disabled"}; /* Scaling factors: 10000000/Gain */ static const int hmc5843_regval_to_nanoscale[] = { 6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714 @@ -174,24 +200,6 @@ static int hmc5843_read_measurement(struct hmc5843_data *data, return IIO_VAL_INT; } -/* - * API for setting the measurement configuration to - * Normal, Positive bias and Negative bias - * - * From the datasheet: - * 0 - Normal measurement configuration (default): In normal measurement - * configuration the device follows normal measurement flow. Pins BP - * and BN are left floating and high impedance. - * - * 1 - Positive bias configuration: In positive bias configuration, a - * positive current is forced across the resistive load on pins BP - * and BN. - * - * 2 - Negative bias configuration. In negative bias configuration, a - * negative current is forced across the resistive load on pins BP - * and BN. - * - */ static int hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf) { int ret; @@ -205,48 +213,55 @@ static int hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf) } static -ssize_t hmc5843_show_measurement_configuration(struct device *dev, - struct device_attribute *attr, - char *buf) +int hmc5843_show_measurement_configuration(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) { - struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev)); + struct hmc5843_data *data = iio_priv(indio_dev); unsigned int val; int ret; ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &val); if (ret) return ret; - val &= HMC5843_MEAS_CONF_MASK; - return sprintf(buf, "%d\n", val); + return val & HMC5843_MEAS_CONF_MASK; } static -ssize_t hmc5843_set_measurement_configuration(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) +int hmc5843_set_measurement_configuration(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int meas_conf) { - struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev)); - unsigned long meas_conf = 0; - int ret; + struct hmc5843_data *data = iio_priv(indio_dev); - ret = kstrtoul(buf, 10, &meas_conf); - if (ret) - return ret; - if (meas_conf >= HMC5843_MEAS_CONF_MASK) - return -EINVAL; - - ret = hmc5843_set_meas_conf(data, meas_conf); - - return (ret < 0) ? ret : count; + return hmc5843_set_meas_conf(data, meas_conf); } -static IIO_DEVICE_ATTR(meas_conf, - S_IWUSR | S_IRUGO, - hmc5843_show_measurement_configuration, - hmc5843_set_measurement_configuration, - 0); +static const struct iio_enum hmc5843_meas_conf_enum = { + .items = hmc5843_meas_conf_modes, + .num_items = ARRAY_SIZE(hmc5843_meas_conf_modes), + .get = hmc5843_show_measurement_configuration, + .set = hmc5843_set_measurement_configuration, +}; + +static const struct iio_chan_spec_ext_info hmc5843_ext_info[] = { + IIO_ENUM("meas_conf", true, &hmc5843_meas_conf_enum), + IIO_ENUM_AVAILABLE("meas_conf", &hmc5843_meas_conf_enum), + { }, +}; + +static const struct iio_enum hmc5983_meas_conf_enum = { + .items = hmc5983_meas_conf_modes, + .num_items = ARRAY_SIZE(hmc5983_meas_conf_modes), + .get = hmc5843_show_measurement_configuration, + .set = hmc5843_set_measurement_configuration, +}; + +static const struct iio_chan_spec_ext_info hmc5983_ext_info[] = { + IIO_ENUM("meas_conf", true, &hmc5983_meas_conf_enum), + IIO_ENUM_AVAILABLE("meas_conf", &hmc5983_meas_conf_enum), + { }, +}; static ssize_t hmc5843_show_samp_freq_avail(struct device *dev, @@ -459,6 +474,25 @@ done: .storagebits = 16, \ .endianness = IIO_BE, \ }, \ + .ext_info = hmc5843_ext_info, \ + } + +#define HMC5983_CHANNEL(axis, idx) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = hmc5983_ext_info, \ } static const struct iio_chan_spec hmc5843_channels[] = { @@ -476,8 +510,14 @@ static const struct iio_chan_spec hmc5883_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(3), }; +static const struct iio_chan_spec hmc5983_channels[] = { + HMC5983_CHANNEL(X, 0), + HMC5983_CHANNEL(Z, 1), + HMC5983_CHANNEL(Y, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + static struct attribute *hmc5843_attributes[] = { - &iio_dev_attr_meas_conf.dev_attr.attr, &iio_dev_attr_scale_available.dev_attr.attr, &iio_dev_attr_sampling_frequency_available.dev_attr.attr, NULL @@ -516,7 +556,7 @@ static const struct hmc5843_chip_info hmc5843_chip_info_tbl[] = { ARRAY_SIZE(hmc5883l_regval_to_nanoscale), }, [HMC5983_ID] = { - .channels = hmc5883_channels, + .channels = hmc5983_channels, .regval_to_samp_freq = hmc5983_regval_to_samp_freq, .n_regval_to_samp_freq = ARRAY_SIZE(hmc5983_regval_to_samp_freq), @@ -565,14 +605,14 @@ static const unsigned long hmc5843_scan_masks[] = {0x7, 0}; int hmc5843_common_suspend(struct device *dev) { return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)), - HMC5843_MODE_CONVERSION_CONTINUOUS); + HMC5843_MODE_SLEEP); } EXPORT_SYMBOL(hmc5843_common_suspend); int hmc5843_common_resume(struct device *dev) { return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)), - HMC5843_MODE_SLEEP); + HMC5843_MODE_CONVERSION_CONTINUOUS); } EXPORT_SYMBOL(hmc5843_common_resume); diff --git a/drivers/staging/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c similarity index 99% rename from drivers/staging/iio/magnetometer/hmc5843_i2c.c rename to drivers/iio/magnetometer/hmc5843_i2c.c index 3e06ceb32059..3de7f4426ac4 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/iio/magnetometer/hmc5843_i2c.c @@ -7,8 +7,7 @@ * 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 #include diff --git a/drivers/staging/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c similarity index 99% rename from drivers/staging/iio/magnetometer/hmc5843_spi.c rename to drivers/iio/magnetometer/hmc5843_spi.c index 8be198058ea2..535f03a70d63 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -6,8 +6,7 @@ * 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 #include diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index b27f0146647b..501f858df413 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -175,6 +175,8 @@ #define ST_MAGN_3_BDU_MASK 0x10 #define ST_MAGN_3_DRDY_IRQ_ADDR 0x62 #define ST_MAGN_3_DRDY_INT_MASK 0x01 +#define ST_MAGN_3_IHL_IRQ_ADDR 0x63 +#define ST_MAGN_3_IHL_IRQ_MASK 0x04 #define ST_MAGN_3_FS_AVL_15000_GAIN 1500 #define ST_MAGN_3_MULTIREAD_BIT false #define ST_MAGN_3_OUT_X_L_ADDR 0x68 @@ -480,6 +482,8 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .drdy_irq = { .addr = ST_MAGN_3_DRDY_IRQ_ADDR, .mask_int1 = ST_MAGN_3_DRDY_INT_MASK, + .addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR, + .mask_ihl = ST_MAGN_3_IHL_IRQ_MASK, }, .multi_read_bit = ST_MAGN_3_MULTIREAD_BIT, .bootime = 2, diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig index fd75db73e582..ffc735c168fb 100644 --- a/drivers/iio/potentiometer/Kconfig +++ b/drivers/iio/potentiometer/Kconfig @@ -17,4 +17,16 @@ config MCP4531 To compile this driver as a module, choose M here: the module will be called mcp4531. +config TPL0102 + tristate "Texas Instruments digital potentiometer driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for the Texas Instruments + TPL0102, TPL0402 + digital potentiometer chips. + + To compile this driver as a module, choose M here: the + module will be called tpl0102. + endmenu diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile index 8afe49227012..b563b492b486 100644 --- a/drivers/iio/potentiometer/Makefile +++ b/drivers/iio/potentiometer/Makefile @@ -4,3 +4,4 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_MCP4531) += mcp4531.o +obj-$(CONFIG_TPL0102) += tpl0102.o diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index a3f66874ee2e..0db67fe14766 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -159,7 +159,7 @@ static int mcp4531_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_err(dev, "SMBUS Word Data not supported\n"); - return -EIO; + return -EOPNOTSUPP; } indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c new file mode 100644 index 000000000000..313124b6fd59 --- /dev/null +++ b/drivers/iio/potentiometer/tpl0102.c @@ -0,0 +1,166 @@ +/* + * tpl0102.c - Support for Texas Instruments digital potentiometers + * + * Copyright (C) 2016 Matt Ranostay + * + * 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. + * + * TODO: enable/disable hi-z output control + */ + +#include +#include +#include +#include + +struct tpl0102_cfg { + int wipers; + int max_pos; + int kohms; +}; + +enum tpl0102_type { + CAT5140_503, + CAT5140_104, + TPL0102_104, + TPL0401_103, +}; + +static const struct tpl0102_cfg tpl0102_cfg[] = { + /* on-semiconductor parts */ + [CAT5140_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, + [CAT5140_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, + /* ti parts */ + [TPL0102_104] = { .wipers = 2, .max_pos = 256, .kohms = 100 }, + [TPL0401_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, +}; + +struct tpl0102_data { + struct regmap *regmap; + unsigned long devid; +}; + +static const struct regmap_config tpl0102_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +#define TPL0102_CHANNEL(ch) { \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec tpl0102_channels[] = { + TPL0102_CHANNEL(0), + TPL0102_CHANNEL(1), +}; + +static int tpl0102_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tpl0102_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + int ret = regmap_read(data->regmap, chan->channel, val); + + return ret ? ret : IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = 1000 * tpl0102_cfg[data->devid].kohms; + *val2 = tpl0102_cfg[data->devid].max_pos; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int tpl0102_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct tpl0102_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + if (val >= tpl0102_cfg[data->devid].max_pos || val < 0) + return -EINVAL; + + return regmap_write(data->regmap, chan->channel, val); +} + +static const struct iio_info tpl0102_info = { + .read_raw = tpl0102_read_raw, + .write_raw = tpl0102_write_raw, + .driver_module = THIS_MODULE, +}; + +static int tpl0102_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct tpl0102_data *data; + struct iio_dev *indio_dev; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENOTSUPP; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + data->devid = id->driver_data; + data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "regmap initialization failed\n"); + return PTR_ERR(data->regmap); + } + + indio_dev->dev.parent = dev; + indio_dev->info = &tpl0102_info; + indio_dev->channels = tpl0102_channels; + indio_dev->num_channels = tpl0102_cfg[data->devid].wipers; + indio_dev->name = client->name; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id tpl0102_id[] = { + { "cat5140-503", CAT5140_503 }, + { "cat5140-104", CAT5140_104 }, + { "tpl0102-104", TPL0102_104 }, + { "tpl0401-103", TPL0401_103 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tpl0102_id); + +static struct i2c_driver tpl0102_driver = { + .driver = { + .name = "tpl0102", + }, + .probe = tpl0102_probe, + .id_table = tpl0102_id, +}; + +module_i2c_driver(tpl0102_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("TPL0102 digital potentiometer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 6f2e7c9ac23e..31c0e1fd2202 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -10,11 +10,11 @@ config BMP280 depends on I2C select REGMAP_I2C help - Say yes here to build support for Bosch Sensortec BMP280 - pressure and temperature sensor. + Say yes here to build support for Bosch Sensortec BMP280 + pressure and temperature sensor. - To compile this driver as a module, choose M here: the module - will be called bmp280. + To compile this driver as a module, choose M here: the module + will be called bmp280. config HID_SENSOR_PRESS depends on HID_SENSOR_HUB @@ -27,18 +27,33 @@ config HID_SENSOR_PRESS Say yes here to build support for the HID SENSOR Pressure driver - To compile this driver as a module, choose M here: the module - will be called hid-sensor-press. + To compile this driver as a module, choose M here: the module + will be called hid-sensor-press. config MPL115 + tristate + +config MPL115_I2C tristate "Freescale MPL115A2 pressure sensor driver" depends on I2C + select MPL115 help Say yes here to build support for the Freescale MPL115A2 pressure sensor connected via I2C. - To compile this driver as a module, choose M here: the module - will be called mpl115. + To compile this driver as a module, choose M here: the module + will be called mpl115_i2c. + +config MPL115_SPI + tristate "Freescale MPL115A1 pressure sensor driver" + depends on SPI_MASTER + select MPL115 + help + Say yes here to build support for the Freescale MPL115A1 + pressure sensor connected via SPI. + + To compile this driver as a module, choose M here: the module + will be called mpl115_spi. config MPL3115 tristate "Freescale MPL3115A2 pressure sensor driver" @@ -49,11 +64,13 @@ config MPL3115 Say yes here to build support for the Freescale MPL3115A2 pressure sensor / altimeter. - To compile this driver as a module, choose M here: the module - will be called mpl3115. + To compile this driver as a module, choose M here: the module + will be called mpl3115. config MS5611 tristate "Measurement Specialties MS5611 pressure sensor driver" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say Y here to build support for the Measurement Specialties MS5611, MS5607 pressure and temperature sensors. @@ -82,7 +99,7 @@ config MS5611_SPI config MS5637 tristate "Measurement Specialties MS5637 pressure & temperature sensor" depends on I2C - select IIO_MS_SENSORS_I2C + select IIO_MS_SENSORS_I2C help If you say yes here you get support for the Measurement Specialties MS5637 pressure and temperature sensor. @@ -128,7 +145,7 @@ config T5403 Say yes here to build support for the EPCOS T5403 pressure sensor connected via I2C. - To compile this driver as a module, choose M here: the module - will be called t5403. + To compile this driver as a module, choose M here: the module + will be called t5403. endmenu diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 46571c96823f..d336af14f3fe 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -6,6 +6,8 @@ obj-$(CONFIG_BMP280) += bmp280.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_MPL115) += mpl115.o +obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o +obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o obj-$(CONFIG_MPL3115) += mpl3115.o obj-$(CONFIG_MS5611) += ms5611_core.o obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index a0d7deeac62f..73f2f0c46e62 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -1,5 +1,5 @@ /* - * mpl115.c - Support for Freescale MPL115A2 pressure/temperature sensor + * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor * * Copyright (c) 2014 Peter Meerwald * @@ -7,17 +7,16 @@ * the GNU General Public License. See the file COPYING in the main * directory of this archive for more details. * - * (7-bit I2C slave address 0x60) - * * TODO: shutdown pin * */ #include -#include #include #include +#include "mpl115.h" + #define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */ #define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */ #define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */ @@ -27,16 +26,18 @@ #define MPL115_CONVERT 0x12 /* convert temperature and pressure */ struct mpl115_data { - struct i2c_client *client; + struct device *dev; struct mutex lock; s16 a0; s16 b1, b2; s16 c12; + const struct mpl115_ops *ops; }; static int mpl115_request(struct mpl115_data *data) { - int ret = i2c_smbus_write_byte_data(data->client, MPL115_CONVERT, 0); + int ret = data->ops->write(data->dev, MPL115_CONVERT, 0); + if (ret < 0) return ret; @@ -57,12 +58,12 @@ static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2) if (ret < 0) goto done; - ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC); + ret = data->ops->read(data->dev, MPL115_PADC); if (ret < 0) goto done; padc = ret >> 6; - ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); + ret = data->ops->read(data->dev, MPL115_TADC); if (ret < 0) goto done; tadc = ret >> 6; @@ -90,7 +91,7 @@ static int mpl115_read_temp(struct mpl115_data *data) ret = mpl115_request(data); if (ret < 0) goto done; - ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); + ret = data->ops->read(data->dev, MPL115_TADC); done: mutex_unlock(&data->lock); return ret; @@ -145,66 +146,53 @@ static const struct iio_info mpl115_info = { .driver_module = THIS_MODULE, }; -static int mpl115_probe(struct i2c_client *client, - const struct i2c_device_id *id) +int mpl115_probe(struct device *dev, const char *name, + const struct mpl115_ops *ops) { struct mpl115_data *data; struct iio_dev *indio_dev; int ret; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); - data->client = client; + data->dev = dev; + data->ops = ops; mutex_init(&data->lock); - i2c_set_clientdata(client, indio_dev); indio_dev->info = &mpl115_info; - indio_dev->name = id->name; - indio_dev->dev.parent = &client->dev; + indio_dev->name = name; + indio_dev->dev.parent = dev; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = mpl115_channels; indio_dev->num_channels = ARRAY_SIZE(mpl115_channels); - ret = i2c_smbus_read_word_swapped(data->client, MPL115_A0); + ret = data->ops->init(data->dev); + if (ret) + return ret; + + ret = data->ops->read(data->dev, MPL115_A0); if (ret < 0) return ret; data->a0 = ret; - ret = i2c_smbus_read_word_swapped(data->client, MPL115_B1); + ret = data->ops->read(data->dev, MPL115_B1); if (ret < 0) return ret; data->b1 = ret; - ret = i2c_smbus_read_word_swapped(data->client, MPL115_B2); + ret = data->ops->read(data->dev, MPL115_B2); if (ret < 0) return ret; data->b2 = ret; - ret = i2c_smbus_read_word_swapped(data->client, MPL115_C12); + ret = data->ops->read(data->dev, MPL115_C12); if (ret < 0) return ret; data->c12 = ret; - return devm_iio_device_register(&client->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } - -static const struct i2c_device_id mpl115_id[] = { - { "mpl115", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mpl115_id); - -static struct i2c_driver mpl115_driver = { - .driver = { - .name = "mpl115", - }, - .probe = mpl115_probe, - .id_table = mpl115_id, -}; -module_i2c_driver(mpl115_driver); +EXPORT_SYMBOL_GPL(mpl115_probe); MODULE_AUTHOR("Peter Meerwald "); MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); diff --git a/drivers/iio/pressure/mpl115.h b/drivers/iio/pressure/mpl115.h new file mode 100644 index 000000000000..01b652774dc3 --- /dev/null +++ b/drivers/iio/pressure/mpl115.h @@ -0,0 +1,24 @@ +/* + * Freescale MPL115A pressure/temperature sensor + * + * Copyright (c) 2014 Peter Meerwald + * Copyright (c) 2016 Akinobu Mita + * + * 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. + */ + +#ifndef _MPL115_H_ +#define _MPL115_H_ + +struct mpl115_ops { + int (*init)(struct device *); + int (*read)(struct device *, u8); + int (*write)(struct device *, u8, u8); +}; + +int mpl115_probe(struct device *dev, const char *name, + const struct mpl115_ops *ops); + +#endif diff --git a/drivers/iio/pressure/mpl115_i2c.c b/drivers/iio/pressure/mpl115_i2c.c new file mode 100644 index 000000000000..1a29be462f6e --- /dev/null +++ b/drivers/iio/pressure/mpl115_i2c.c @@ -0,0 +1,67 @@ +/* + * Freescale MPL115A2 pressure/temperature sensor + * + * Copyright (c) 2014 Peter Meerwald + * + * 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. + * + * (7-bit I2C slave address 0x60) + * + * Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A2.pdf + */ + +#include +#include + +#include "mpl115.h" + +static int mpl115_i2c_init(struct device *dev) +{ + return 0; +} + +static int mpl115_i2c_read(struct device *dev, u8 address) +{ + return i2c_smbus_read_word_swapped(to_i2c_client(dev), address); +} + +static int mpl115_i2c_write(struct device *dev, u8 address, u8 value) +{ + return i2c_smbus_write_byte_data(to_i2c_client(dev), address, value); +} + +static const struct mpl115_ops mpl115_i2c_ops = { + .init = mpl115_i2c_init, + .read = mpl115_i2c_read, + .write = mpl115_i2c_write, +}; + +static int mpl115_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + return mpl115_probe(&client->dev, id->name, &mpl115_i2c_ops); +} + +static const struct i2c_device_id mpl115_i2c_id[] = { + { "mpl115", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpl115_i2c_id); + +static struct i2c_driver mpl115_i2c_driver = { + .driver = { + .name = "mpl115", + }, + .probe = mpl115_i2c_probe, + .id_table = mpl115_i2c_id, +}; +module_i2c_driver(mpl115_i2c_driver); + +MODULE_AUTHOR("Peter Meerwald "); +MODULE_DESCRIPTION("Freescale MPL115A2 pressure/temperature driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c new file mode 100644 index 000000000000..9ebf55f5b3aa --- /dev/null +++ b/drivers/iio/pressure/mpl115_spi.c @@ -0,0 +1,106 @@ +/* + * Freescale MPL115A1 pressure/temperature sensor + * + * Copyright (c) 2016 Akinobu Mita + * + * 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. + * + * Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A1.pdf + */ + +#include +#include + +#include "mpl115.h" + +#define MPL115_SPI_WRITE(address) ((address) << 1) +#define MPL115_SPI_READ(address) (0x80 | (address) << 1) + +struct mpl115_spi_buf { + u8 tx[4]; + u8 rx[4]; +}; + +static int mpl115_spi_init(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct mpl115_spi_buf *buf; + + buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spi_set_drvdata(spi, buf); + + return 0; +} + +static int mpl115_spi_read(struct device *dev, u8 address) +{ + struct spi_device *spi = to_spi_device(dev); + struct mpl115_spi_buf *buf = spi_get_drvdata(spi); + struct spi_transfer xfer = { + .tx_buf = buf->tx, + .rx_buf = buf->rx, + .len = 4, + }; + int ret; + + buf->tx[0] = MPL115_SPI_READ(address); + buf->tx[2] = MPL115_SPI_READ(address + 1); + + ret = spi_sync_transfer(spi, &xfer, 1); + if (ret) + return ret; + + return (buf->rx[1] << 8) | buf->rx[3]; +} + +static int mpl115_spi_write(struct device *dev, u8 address, u8 value) +{ + struct spi_device *spi = to_spi_device(dev); + struct mpl115_spi_buf *buf = spi_get_drvdata(spi); + struct spi_transfer xfer = { + .tx_buf = buf->tx, + .len = 2, + }; + + buf->tx[0] = MPL115_SPI_WRITE(address); + buf->tx[1] = value; + + return spi_sync_transfer(spi, &xfer, 1); +} + +static const struct mpl115_ops mpl115_spi_ops = { + .init = mpl115_spi_init, + .read = mpl115_spi_read, + .write = mpl115_spi_write, +}; + +static int mpl115_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + return mpl115_probe(&spi->dev, id->name, &mpl115_spi_ops); +} + +static const struct spi_device_id mpl115_spi_ids[] = { + { "mpl115", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, mpl115_spi_ids); + +static struct spi_driver mpl115_spi_driver = { + .driver = { + .name = "mpl115", + }, + .probe = mpl115_spi_probe, + .id_table = mpl115_spi_ids, +}; +module_spi_driver(mpl115_spi_driver); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_DESCRIPTION("Freescale MPL115A1 pressure/temperature driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index 23b93c797dba..8b08e4b7e3a9 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -51,6 +51,8 @@ struct ms5611_state { struct ms5611_chip_info *chip_info; }; -int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type); +int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, + const char* name, int type); +int ms5611_remove(struct iio_dev *indio_dev); #endif /* _MS5611_H */ diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 2f3d9b4aca4e..992ad8d3b67a 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -16,7 +16,11 @@ #include #include #include +#include +#include +#include +#include #include "ms5611.h" static bool ms5611_prom_is_valid(u16 *prom, size_t len) @@ -133,17 +137,17 @@ static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_inf t = 2000 + ((chip_info->prom[6] * dt) >> 23); if (t < 2000) { - s64 off2, sens2, t2; + s64 off2, sens2, t2, tmp; t2 = (dt * dt) >> 31; - off2 = (61 * (t - 2000) * (t - 2000)) >> 4; - sens2 = off2 << 1; + tmp = (t - 2000) * (t - 2000); + off2 = (61 * tmp) >> 4; + sens2 = tmp << 1; if (t < -1500) { - s64 tmp = (t + 1500) * (t + 1500); - + tmp = (t + 1500) * (t + 1500); off2 += 15 * tmp; - sens2 += (8 * tmp); + sens2 += 8 * tmp; } t -= t2; @@ -173,6 +177,28 @@ static int ms5611_reset(struct iio_dev *indio_dev) return 0; } +static irqreturn_t ms5611_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ms5611_state *st = iio_priv(indio_dev); + s32 buf[4]; /* s32 (pressure) + s32 (temp) + 2 * s32 (timestamp) */ + int ret; + + mutex_lock(&st->lock); + ret = ms5611_read_temp_and_pressure(indio_dev, &buf[1], &buf[0]); + mutex_unlock(&st->lock); + if (ret < 0) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int ms5611_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -201,11 +227,25 @@ static int ms5611_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = 10; + return IIO_VAL_INT; + case IIO_PRESSURE: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } } return -EINVAL; } +static const unsigned long ms5611_scan_masks[] = {0x3, 0}; + static struct ms5611_chip_info chip_info_tbl[] = { [MS5611] = { .temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate, @@ -218,12 +258,29 @@ static struct ms5611_chip_info chip_info_tbl[] = { static const struct iio_chan_spec ms5611_channels[] = { { .type = IIO_PRESSURE, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_TEMP, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - } + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), }; static const struct iio_info ms5611_info = { @@ -234,6 +291,18 @@ static const struct iio_info ms5611_info = { static int ms5611_init(struct iio_dev *indio_dev) { int ret; + struct regulator *vdd = devm_regulator_get(indio_dev->dev.parent, + "vdd"); + + /* Enable attached regulator if any. */ + if (!IS_ERR(vdd)) { + ret = regulator_enable(vdd); + if (ret) { + dev_err(indio_dev->dev.parent, + "failed to enable Vdd supply: %d\n", ret); + return ret; + } + } ret = ms5611_reset(indio_dev); if (ret < 0) @@ -242,7 +311,8 @@ static int ms5611_init(struct iio_dev *indio_dev) return ms5611_read_prom(indio_dev); } -int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type) +int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, + const char *name, int type) { int ret; struct ms5611_state *st = iio_priv(indio_dev); @@ -250,20 +320,48 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type) mutex_init(&st->lock); st->chip_info = &chip_info_tbl[type]; indio_dev->dev.parent = dev; - indio_dev->name = dev->driver->name; + indio_dev->name = 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; + indio_dev->available_scan_masks = ms5611_scan_masks; ret = ms5611_init(indio_dev); if (ret < 0) return ret; - return devm_iio_device_register(dev, indio_dev); + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ms5611_trigger_handler, NULL); + if (ret < 0) { + dev_err(dev, "iio triggered buffer setup failed\n"); + return ret; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(dev, "unable to register iio device\n"); + goto err_buffer_cleanup; + } + + return 0; + +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; } EXPORT_SYMBOL(ms5611_probe); +int ms5611_remove(struct iio_dev *indio_dev) +{ + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + return 0; +} +EXPORT_SYMBOL(ms5611_remove); + MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("MS5611 core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 245797d1ecf0..7f6fc8eee922 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -92,19 +92,25 @@ static int ms5611_i2c_probe(struct i2c_client *client, I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) - return -ENODEV; + return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); + i2c_set_clientdata(client, 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, id->driver_data); + return ms5611_probe(indio_dev, &client->dev, id->name, id->driver_data); +} + +static int ms5611_i2c_remove(struct i2c_client *client) +{ + return ms5611_remove(i2c_get_clientdata(client)); } static const struct i2c_device_id ms5611_id[] = { @@ -120,6 +126,7 @@ static struct i2c_driver ms5611_driver = { }, .id_table = ms5611_id, .probe = ms5611_i2c_probe, + .remove = ms5611_i2c_remove, }; module_i2c_driver(ms5611_driver); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index aaa0c4ba91a7..5cc009e85f0e 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -90,6 +90,8 @@ static int ms5611_spi_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; + spi_set_drvdata(spi, indio_dev); + spi->mode = SPI_MODE_0; spi->max_speed_hz = 20000000; spi->bits_per_word = 8; @@ -103,8 +105,13 @@ static int ms5611_spi_probe(struct spi_device *spi) st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure; st->client = spi; - return ms5611_probe(indio_dev, &spi->dev, - spi_get_device_id(spi)->driver_data); + return ms5611_probe(indio_dev, &spi->dev, spi_get_device_id(spi)->name, + spi_get_device_id(spi)->driver_data); +} + +static int ms5611_spi_remove(struct spi_device *spi) +{ + return ms5611_remove(spi_get_drvdata(spi)); } static const struct spi_device_id ms5611_id[] = { @@ -120,6 +127,7 @@ static struct spi_driver ms5611_driver = { }, .id_table = ms5611_id, .probe = ms5611_spi_probe, + .remove = ms5611_spi_remove, }; module_spi_driver(ms5611_driver); diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index e8d0e0da938d..e68052c118e6 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -136,7 +136,7 @@ static int ms5637_probe(struct i2c_client *client, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { dev_err(&client->dev, "Adapter does not support some i2c transaction\n"); - return -ENODEV; + return -EOPNOTSUPP; } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data)); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index b39a2fb0671c..172393ad34af 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -62,6 +62,8 @@ #define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22 #define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04 #define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20 +#define ST_PRESS_LPS331AP_IHL_IRQ_ADDR 0x22 +#define ST_PRESS_LPS331AP_IHL_IRQ_MASK 0x80 #define ST_PRESS_LPS331AP_MULTIREAD_BIT true #define ST_PRESS_LPS331AP_TEMP_OFFSET 42500 @@ -100,6 +102,8 @@ #define ST_PRESS_LPS25H_DRDY_IRQ_ADDR 0x23 #define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01 #define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10 +#define ST_PRESS_LPS25H_IHL_IRQ_ADDR 0x22 +#define ST_PRESS_LPS25H_IHL_IRQ_MASK 0x80 #define ST_PRESS_LPS25H_MULTIREAD_BIT true #define ST_PRESS_LPS25H_TEMP_OFFSET 42500 #define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 @@ -220,6 +224,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR, .mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK, .mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_PRESS_LPS331AP_IHL_IRQ_ADDR, + .mask_ihl = ST_PRESS_LPS331AP_IHL_IRQ_MASK, }, .multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT, .bootime = 2, @@ -304,6 +310,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR, .mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK, .mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_PRESS_LPS25H_IHL_IRQ_ADDR, + .mask_ihl = ST_PRESS_LPS25H_IHL_IRQ_MASK, }, .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, .bootime = 2, diff --git a/drivers/iio/pressure/t5403.c b/drivers/iio/pressure/t5403.c index e11cd3938d67..2667e71721f5 100644 --- a/drivers/iio/pressure/t5403.c +++ b/drivers/iio/pressure/t5403.c @@ -221,7 +221,7 @@ static int t5403_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) - return -ENODEV; + return -EOPNOTSUPP; ret = i2c_smbus_read_byte_data(client, T5403_SLAVE_ADDR); if (ret < 0) diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index db35e04a0637..4f502386aa86 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -278,7 +278,7 @@ static int lidar_probe(struct i2c_client *client, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) data->xfer = lidar_smbus_xfer; else - return -ENOTSUPP; + return -EOPNOTSUPP; indio_dev->info = &lidar_info; indio_dev->name = LIDAR_DRV_NAME; diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index a570c2e2aac3..4b645fc672aa 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -516,7 +516,7 @@ static int mlx90614_probe(struct i2c_client *client, int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; + return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index e78c1069a6a9..18c9b43c02cb 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -205,7 +205,7 @@ static int tmp006_probe(struct i2c_client *client, int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; + return -EOPNOTSUPP; if (!tmp006_check_identification(client)) { dev_err(&client->dev, "no TMP006 sensor\n"); diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index 05c12060ce8d..3e60c6189d98 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -190,7 +190,7 @@ static int tsys01_i2c_probe(struct i2c_client *client, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { dev_err(&client->dev, "Adapter does not support some i2c transaction\n"); - return -ENODEV; + return -EOPNOTSUPP; } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data)); diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c index 4c1fbd52ea08..ab6fe8f6f2d1 100644 --- a/drivers/iio/temperature/tsys02d.c +++ b/drivers/iio/temperature/tsys02d.c @@ -137,7 +137,7 @@ static int tsys02d_probe(struct i2c_client *client, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { dev_err(&client->dev, "Adapter does not support some i2c transaction\n"); - return -ENODEV; + return -EOPNOTSUPP; } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data)); diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 91c81965e7ca..c32e45826c2c 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -8,9 +8,6 @@ obj-$(CONFIG_MISDN) += mISDN/ obj-$(CONFIG_ISDN) += hardware/ obj-$(CONFIG_ISDN_DIVERSION) += divert/ obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/ -obj-$(CONFIG_ISDN_DRV_ICN) += icn/ -obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit/ obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop/ -obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000/ obj-$(CONFIG_HYSDN) += hysdn/ obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset/ diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig index f5b714cd7618..68e54d9f2f53 100644 --- a/drivers/isdn/i4l/Kconfig +++ b/drivers/isdn/i4l/Kconfig @@ -123,16 +123,6 @@ comment "ISDN4Linux hardware drivers" source "drivers/isdn/hisax/Kconfig" - -menu "Active cards" - -source "drivers/isdn/icn/Kconfig" - -source "drivers/isdn/pcbit/Kconfig" - -source "drivers/isdn/act2000/Kconfig" - -endmenu # end ISDN_I4L endif diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 15579514d120..a216b4667742 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -525,6 +525,284 @@ config VEXPRESS_SYSCFG ARM Ltd. Versatile Express uses specialised platform configuration bus. System Configuration interface is one of the possible means of generating transactions on this bus. +config PANEL + tristate "Parallel port LCD/Keypad Panel support" + depends on PARPORT + ---help--- + Say Y here if you have an HD44780 or KS-0074 LCD connected to your + parallel port. This driver also features 4 and 6-key keypads. The LCD + is accessible through the /dev/lcd char device (10, 156), and the + keypad through /dev/keypad (10, 185). Both require misc device to be + enabled. This code can either be compiled as a module, or linked into + the kernel and started at boot. If you don't understand what all this + is about, say N. + +config PANEL_PARPORT + int "Default parallel port number (0=LPT1)" + depends on PANEL + range 0 255 + default "0" + ---help--- + This is the index of the parallel port the panel is connected to. One + driver instance only supports one parallel port, so if your keypad + and LCD are connected to two separate ports, you have to start two + modules with different arguments. Numbering starts with '0' for LPT1, + and so on. + +config PANEL_PROFILE + int "Default panel profile (0-5, 0=custom)" + depends on PANEL + range 0 5 + default "5" + ---help--- + To ease configuration, the driver supports different configuration + profiles for past and recent wirings. These profiles can also be + used to define an approximative configuration, completed by a few + other options. Here are the profiles : + + 0 = custom (see further) + 1 = 2x16 parallel LCD, old keypad + 2 = 2x16 serial LCD (KS-0074), new keypad + 3 = 2x16 parallel LCD (Hantronix), no keypad + 4 = 2x16 parallel LCD (Nexcom NSA1045) with Nexcom's keypad + 5 = 2x40 parallel LCD (old one), with old keypad + + Custom configurations allow you to define how your display is + wired to the parallel port, and how it works. This is only intended + for experts. + +config PANEL_KEYPAD + depends on PANEL && PANEL_PROFILE="0" + int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)" + range 0 3 + default 0 + ---help--- + This enables and configures a keypad connected to the parallel port. + The keys will be read from character device 10,185. Valid values are : + + 0 : do not enable this driver + 1 : old 6 keys keypad + 2 : new 6 keys keypad, as used on the server at www.ant-computing.com + 3 : Nexcom NSA1045's 4 keys keypad + + New profiles can be described in the driver source. The driver also + supports simultaneous keys pressed when the keypad supports them. + +config PANEL_LCD + depends on PANEL && PANEL_PROFILE="0" + int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)" + range 0 5 + default 0 + ---help--- + This enables and configures an LCD connected to the parallel port. + The driver includes an interpreter for escape codes starting with + '\e[L' which are specific to the LCD, and a few ANSI codes. The + driver will be registered as character device 10,156, usually + under the name '/dev/lcd'. There are a total of 6 supported types : + + 0 : do not enable the driver + 1 : custom configuration and wiring (see further) + 2 : 2x16 & 2x40 parallel LCD (old wiring) + 3 : 2x16 serial LCD (KS-0074 based) + 4 : 2x16 parallel LCD (Hantronix wiring) + 5 : 2x16 parallel LCD (Nexcom wiring) + + When type '1' is specified, other options will appear to configure + more precise aspects (wiring, dimensions, protocol, ...). Please note + that those values changed from the 2.4 driver for better consistency. + +config PANEL_LCD_HEIGHT + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + int "Number of lines on the LCD (1-2)" + range 1 2 + default 2 + ---help--- + This is the number of visible character lines on the LCD in custom profile. + It can either be 1 or 2. + +config PANEL_LCD_WIDTH + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + int "Number of characters per line on the LCD (1-40)" + range 1 40 + default 40 + ---help--- + This is the number of characters per line on the LCD in custom profile. + Common values are 16,20,24,40. + +config PANEL_LCD_BWIDTH + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + int "Internal LCD line width (1-40, 40 by default)" + range 1 40 + default 40 + ---help--- + Most LCDs use a standard controller which supports hardware lines of 40 + characters, although sometimes only 16, 20 or 24 of them are really wired + to the terminal. This results in some non-visible but addressable characters, + and is the case for most parallel LCDs. Other LCDs, and some serial ones, + however, use the same line width internally as what is visible. The KS0074 + for example, uses 16 characters per line for 16 visible characters per line. + + This option lets you configure the value used by your LCD in 'custom' profile. + If you don't know, put '40' here. + +config PANEL_LCD_HWIDTH + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + int "Hardware LCD line width (1-64, 64 by default)" + range 1 64 + default 64 + ---help--- + Most LCDs use a single address bit to differentiate line 0 and line 1. Since + some of them need to be able to address 40 chars with the lower bits, they + often use the immediately superior power of 2, which is 64, to address the + next line. + + If you don't know what your LCD uses, in doubt let 16 here for a 2x16, and + 64 here for a 2x40. + +config PANEL_LCD_CHARSET + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + int "LCD character set (0=normal, 1=KS0074)" + range 0 1 + default 0 + ---help--- + Some controllers such as the KS0074 use a somewhat strange character set + where many symbols are at unusual places. The driver knows how to map + 'standard' ASCII characters to the character sets used by these controllers. + Valid values are : + + 0 : normal (untranslated) character set + 1 : KS0074 character set + + If you don't know, use the normal one (0). + +config PANEL_LCD_PROTO + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + int "LCD communication mode (0=parallel 8 bits, 1=serial)" + range 0 1 + default 0 + ---help--- + This driver now supports any serial or parallel LCD wired to a parallel + port. But before assigning signals, the driver needs to know if it will + be driving a serial LCD or a parallel one. Serial LCDs only use 2 wires + (SDA/SCL), while parallel ones use 2 or 3 wires for the control signals + (E, RS, sometimes RW), and 4 or 8 for the data. Use 0 here for a 8 bits + parallel LCD, and 1 for a serial LCD. + +config PANEL_LCD_PIN_E + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) " + range -17 17 + default 14 + ---help--- + This describes the number of the parallel port pin to which the LCD 'E' + signal has been connected. It can be : + + 0 : no connection (eg: connected to ground) + 1..17 : directly connected to any of these pins on the DB25 plug + -1..-17 : connected to the same pin through an inverter (eg: transistor). + + Default for the 'E' pin in custom profile is '14' (AUTOFEED). + +config PANEL_LCD_PIN_RS + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) " + range -17 17 + default 17 + ---help--- + This describes the number of the parallel port pin to which the LCD 'RS' + signal has been connected. It can be : + + 0 : no connection (eg: connected to ground) + 1..17 : directly connected to any of these pins on the DB25 plug + -1..-17 : connected to the same pin through an inverter (eg: transistor). + + Default for the 'RS' pin in custom profile is '17' (SELECT IN). + +config PANEL_LCD_PIN_RW + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) " + range -17 17 + default 16 + ---help--- + This describes the number of the parallel port pin to which the LCD 'RW' + signal has been connected. It can be : + + 0 : no connection (eg: connected to ground) + 1..17 : directly connected to any of these pins on the DB25 plug + -1..-17 : connected to the same pin through an inverter (eg: transistor). + + Default for the 'RW' pin in custom profile is '16' (INIT). + +config PANEL_LCD_PIN_SCL + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" + int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) " + range -17 17 + default 1 + ---help--- + This describes the number of the parallel port pin to which the serial + LCD 'SCL' signal has been connected. It can be : + + 0 : no connection (eg: connected to ground) + 1..17 : directly connected to any of these pins on the DB25 plug + -1..-17 : connected to the same pin through an inverter (eg: transistor). + + Default for the 'SCL' pin in custom profile is '1' (STROBE). + +config PANEL_LCD_PIN_SDA + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" + int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) " + range -17 17 + default 2 + ---help--- + This describes the number of the parallel port pin to which the serial + LCD 'SDA' signal has been connected. It can be : + + 0 : no connection (eg: connected to ground) + 1..17 : directly connected to any of these pins on the DB25 plug + -1..-17 : connected to the same pin through an inverter (eg: transistor). + + Default for the 'SDA' pin in custom profile is '2' (D0). + +config PANEL_LCD_PIN_BL + depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) " + range -17 17 + default 0 + ---help--- + This describes the number of the parallel port pin to which the LCD 'BL' signal + has been connected. It can be : + + 0 : no connection (eg: connected to ground) + 1..17 : directly connected to any of these pins on the DB25 plug + -1..-17 : connected to the same pin through an inverter (eg: transistor). + + Default for the 'BL' pin in custom profile is '0' (uncontrolled). + +config PANEL_CHANGE_MESSAGE + depends on PANEL + bool "Change LCD initialization message ?" + default "n" + ---help--- + This allows you to replace the boot message indicating the kernel version + and the driver version with a custom message. This is useful on appliances + where a simple 'Starting system' message can be enough to stop a customer + from worrying. + + If you say 'Y' here, you'll be able to choose a message yourself. Otherwise, + say 'N' and keep the default message with the version. + +config PANEL_BOOT_MESSAGE + depends on PANEL && PANEL_CHANGE_MESSAGE="y" + string "New initialization message" + default "" + ---help--- + This allows you to replace the boot message indicating the kernel version + and the driver version with a custom message. This is useful on appliances + where a simple 'Starting system' message can be enough to stop a customer + from worrying. + + An empty message will only clear the display at driver init time. Any other + printf()-formatted message is valid with newline and escape codes. source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 537d7f3b78da..b2fb6dbffcef 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -56,3 +56,4 @@ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ +obj-$(CONFIG_PANEL) += panel.o diff --git a/drivers/staging/panel/panel.c b/drivers/misc/panel.c similarity index 96% rename from drivers/staging/panel/panel.c rename to drivers/misc/panel.c index 70b8f4fabfad..6030ac5b8c63 100644 --- a/drivers/staging/panel/panel.c +++ b/drivers/misc/panel.c @@ -172,8 +172,6 @@ static __u8 scan_mask_o; /* logical or of the input bits involved in the scan matrix */ static __u8 scan_mask_i; -typedef __u64 pmask_t; - enum input_type { INPUT_TYPE_STD, INPUT_TYPE_KBD, @@ -188,8 +186,8 @@ enum input_state { struct logical_input { struct list_head list; - pmask_t mask; - pmask_t value; + __u64 mask; + __u64 value; enum input_type type; enum input_state state; __u8 rise_time, fall_time; @@ -219,19 +217,19 @@ static LIST_HEAD(logical_inputs); /* list of all defined logical inputs */ * corresponds to the ground. * Within each group, bits are stored in the same order as read on the port : * BAPSE (busy=4, ack=3, paper empty=2, select=1, error=0). - * So, each __u64 (or pmask_t) is represented like this : + * So, each __u64 is represented like this : * 0000000000000000000BAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSE * <-----unused------> */ /* what has just been read from the I/O ports */ -static pmask_t phys_read; +static __u64 phys_read; /* previous phys_read */ -static pmask_t phys_read_prev; +static __u64 phys_read_prev; /* stabilized phys_read (phys_read|phys_read_prev) */ -static pmask_t phys_curr; +static __u64 phys_curr; /* previous phys_curr */ -static pmask_t phys_prev; +static __u64 phys_prev; /* 0 means that at least one logical signal needs be computed */ static char inputs_stable; @@ -650,34 +648,28 @@ static const char nexcom_keypad_profile[][4][9] = { static const char (*keypad_profile)[4][9] = old_keypad_profile; -/* FIXME: this should be converted to a bit array containing signals states */ -static struct { - unsigned char e; /* parallel LCD E (data latch on falling edge) */ - unsigned char rs; /* parallel LCD RS (0 = cmd, 1 = data) */ - unsigned char rw; /* parallel LCD R/W (0 = W, 1 = R) */ - unsigned char bl; /* parallel LCD backlight (0 = off, 1 = on) */ - unsigned char cl; /* serial LCD clock (latch on rising edge) */ - unsigned char da; /* serial LCD data */ -} bits; +static DECLARE_BITMAP(bits, LCD_BITS); + +static void lcd_get_bits(unsigned int port, int *val) +{ + unsigned int bit, state; + + for (bit = 0; bit < LCD_BITS; bit++) { + state = test_bit(bit, bits) ? BIT_SET : BIT_CLR; + *val &= lcd_bits[port][bit][BIT_MSK]; + *val |= lcd_bits[port][bit][state]; + } +} static void init_scan_timer(void); /* sets data port bits according to current signals values */ static int set_data_bits(void) { - int val, bit; + int val; val = r_dtr(pprt); - for (bit = 0; bit < LCD_BITS; bit++) - val &= lcd_bits[LCD_PORT_D][bit][BIT_MSK]; - - val |= lcd_bits[LCD_PORT_D][LCD_BIT_E][bits.e] - | lcd_bits[LCD_PORT_D][LCD_BIT_RS][bits.rs] - | lcd_bits[LCD_PORT_D][LCD_BIT_RW][bits.rw] - | lcd_bits[LCD_PORT_D][LCD_BIT_BL][bits.bl] - | lcd_bits[LCD_PORT_D][LCD_BIT_CL][bits.cl] - | lcd_bits[LCD_PORT_D][LCD_BIT_DA][bits.da]; - + lcd_get_bits(LCD_PORT_D, &val); w_dtr(pprt, val); return val; } @@ -685,19 +677,10 @@ static int set_data_bits(void) /* sets ctrl port bits according to current signals values */ static int set_ctrl_bits(void) { - int val, bit; + int val; val = r_ctr(pprt); - for (bit = 0; bit < LCD_BITS; bit++) - val &= lcd_bits[LCD_PORT_C][bit][BIT_MSK]; - - val |= lcd_bits[LCD_PORT_C][LCD_BIT_E][bits.e] - | lcd_bits[LCD_PORT_C][LCD_BIT_RS][bits.rs] - | lcd_bits[LCD_PORT_C][LCD_BIT_RW][bits.rw] - | lcd_bits[LCD_PORT_C][LCD_BIT_BL][bits.bl] - | lcd_bits[LCD_PORT_C][LCD_BIT_CL][bits.cl] - | lcd_bits[LCD_PORT_C][LCD_BIT_DA][bits.da]; - + lcd_get_bits(LCD_PORT_C, &val); w_ctr(pprt, val); return val; } @@ -793,12 +776,17 @@ static void lcd_send_serial(int byte) * LCD reads D0 on STROBE's rising edge. */ for (bit = 0; bit < 8; bit++) { - bits.cl = BIT_CLR; /* CLK low */ + clear_bit(LCD_BIT_CL, bits); /* CLK low */ panel_set_bits(); - bits.da = byte & 1; + if (byte & 1) { + set_bit(LCD_BIT_DA, bits); + } else { + clear_bit(LCD_BIT_DA, bits); + } + panel_set_bits(); udelay(2); /* maintain the data during 2 us before CLK up */ - bits.cl = BIT_SET; /* CLK high */ + set_bit(LCD_BIT_CL, bits); /* CLK high */ panel_set_bits(); udelay(1); /* maintain the strobe during 1 us */ byte >>= 1; @@ -813,7 +801,10 @@ static void lcd_backlight(int on) /* The backlight is activated by setting the AUTOFEED line to +5V */ spin_lock_irq(&pprt_lock); - bits.bl = on; + if (on) + set_bit(LCD_BIT_BL, bits); + else + clear_bit(LCD_BIT_BL, bits); panel_set_bits(); spin_unlock_irq(&pprt_lock); } @@ -848,14 +839,14 @@ static void lcd_write_cmd_p8(int cmd) w_dtr(pprt, cmd); udelay(20); /* maintain the data during 20 us before the strobe */ - bits.e = BIT_SET; - bits.rs = BIT_CLR; - bits.rw = BIT_CLR; + set_bit(LCD_BIT_E, bits); + clear_bit(LCD_BIT_RS, bits); + clear_bit(LCD_BIT_RW, bits); set_ctrl_bits(); udelay(40); /* maintain the strobe during 40 us */ - bits.e = BIT_CLR; + clear_bit(LCD_BIT_E, bits); set_ctrl_bits(); udelay(120); /* the shortest command takes at least 120 us */ @@ -870,14 +861,14 @@ static void lcd_write_data_p8(int data) w_dtr(pprt, data); udelay(20); /* maintain the data during 20 us before the strobe */ - bits.e = BIT_SET; - bits.rs = BIT_SET; - bits.rw = BIT_CLR; + set_bit(LCD_BIT_E, bits); + set_bit(LCD_BIT_RS, bits); + clear_bit(LCD_BIT_RW, bits); set_ctrl_bits(); udelay(40); /* maintain the strobe during 40 us */ - bits.e = BIT_CLR; + clear_bit(LCD_BIT_E, bits); set_ctrl_bits(); udelay(45); /* the shortest data takes at least 45 us */ @@ -943,7 +934,8 @@ static void lcd_clear_fast_s(void) lcd_send_serial(0x5F); /* R/W=W, RS=1 */ lcd_send_serial(' ' & 0x0F); lcd_send_serial((' ' >> 4) & 0x0F); - udelay(40); /* the shortest data takes at least 40 us */ + /* the shortest data takes at least 40 us */ + udelay(40); } spin_unlock_irq(&pprt_lock); @@ -969,15 +961,15 @@ static void lcd_clear_fast_p8(void) /* maintain the data during 20 us before the strobe */ udelay(20); - bits.e = BIT_SET; - bits.rs = BIT_SET; - bits.rw = BIT_CLR; + set_bit(LCD_BIT_E, bits); + set_bit(LCD_BIT_RS, bits); + clear_bit(LCD_BIT_RW, bits); set_ctrl_bits(); /* maintain the strobe during 40 us */ udelay(40); - bits.e = BIT_CLR; + clear_bit(LCD_BIT_E, bits); set_ctrl_bits(); /* the shortest data takes at least 45 us */ @@ -1784,7 +1776,7 @@ static void phys_scan_contacts(void) gndmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i; /* grounded inputs are signals 40-44 */ - phys_read |= (pmask_t) gndmask << 40; + phys_read |= (__u64)gndmask << 40; if (bitmask != gndmask) { /* @@ -1800,7 +1792,7 @@ static void phys_scan_contacts(void) w_dtr(pprt, oldval & ~bitval); /* enable this output */ bitmask = PNL_PINPUT(r_str(pprt)) & ~gndmask; - phys_read |= (pmask_t) bitmask << (5 * bit); + phys_read |= (__u64)bitmask << (5 * bit); } w_dtr(pprt, oldval); /* disable all outputs */ } @@ -2037,32 +2029,32 @@ static void init_scan_timer(void) * corresponding to out and in bits respectively. * returns 1 if ok, 0 if error (in which case, nothing is written). */ -static int input_name2mask(const char *name, pmask_t *mask, pmask_t *value, - char *imask, char *omask) +static u8 input_name2mask(const char *name, __u64 *mask, __u64 *value, + u8 *imask, u8 *omask) { - static char sigtab[10] = "EeSsPpAaBb"; - char im, om; - pmask_t m, v; + const char sigtab[] = "EeSsPpAaBb"; + u8 im, om; + __u64 m, v; - om = 0ULL; - im = 0ULL; + om = 0; + im = 0; m = 0ULL; v = 0ULL; while (*name) { int in, out, bit, neg; + const char *idx; - for (in = 0; (in < sizeof(sigtab)) && (sigtab[in] != *name); - in++) - ; - - if (in >= sizeof(sigtab)) + idx = strchr(sigtab, *name); + if (!idx) return 0; /* input name not found */ + + in = idx - sigtab; neg = (in & 1); /* odd (lower) names are negated */ in >>= 1; im |= BIT(in); name++; - if (isdigit(*name)) { + if (*name >= '0' && *name <= '7') { out = *name - '0'; om |= BIT(out); } else if (*name == '-') { diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 839df4aace76..9973cebb4d6f 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -216,17 +217,16 @@ static int valid_batchbuffer_addr(struct goldfish_pipe_dev *dev, static int setup_access_params_addr(struct platform_device *pdev, struct goldfish_pipe_dev *dev) { - u64 paddr; + dma_addr_t dma_handle; struct access_params *aps; - aps = devm_kzalloc(&pdev->dev, sizeof(struct access_params), GFP_KERNEL); + aps = dmam_alloc_coherent(&pdev->dev, sizeof(struct access_params), + &dma_handle, GFP_KERNEL); if (!aps) - return -1; + return -ENOMEM; - /* FIXME */ - paddr = __pa(aps); - writel((u32)(paddr >> 32), dev->base + PIPE_REG_PARAMS_ADDR_HIGH); - writel((u32)paddr, dev->base + PIPE_REG_PARAMS_ADDR_LOW); + writel(upper_32_bits(dma_handle), dev->base + PIPE_REG_PARAMS_ADDR_HIGH); + writel(lower_32_bits(dma_handle), dev->base + PIPE_REG_PARAMS_ADDR_LOW); if (valid_batchbuffer_addr(dev, aps)) { dev->aps = aps; diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5d3b86a33857..f0ca4a18b799 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -30,10 +30,6 @@ source "drivers/staging/wlan-ng/Kconfig" source "drivers/staging/comedi/Kconfig" -source "drivers/staging/olpc_dcon/Kconfig" - -source "drivers/staging/panel/Kconfig" - source "drivers/staging/rtl8192u/Kconfig" source "drivers/staging/rtl8192e/Kconfig" @@ -76,8 +72,6 @@ source "drivers/staging/android/Kconfig" source "drivers/staging/board/Kconfig" -source "drivers/staging/gdm72xx/Kconfig" - source "drivers/staging/gdm724x/Kconfig" source "drivers/staging/fwserial/Kconfig" @@ -92,8 +86,6 @@ source "drivers/staging/lustre/Kconfig" source "drivers/staging/dgnc/Kconfig" -source "drivers/staging/dgap/Kconfig" - source "drivers/staging/gs_fpgaboot/Kconfig" source "drivers/staging/skein/Kconfig" @@ -110,4 +102,6 @@ source "drivers/staging/wilc1000/Kconfig" source "drivers/staging/most/Kconfig" +source "drivers/staging/i4l/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 30918edef5e3..22464a09cb27 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -1,14 +1,9 @@ # Makefile for staging directory -# fix for build system bug... -obj-$(CONFIG_STAGING) += staging.o - obj-y += media/ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_COMEDI) += comedi/ -obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ -obj-$(CONFIG_PANEL) += panel/ obj-$(CONFIG_RTL8192U) += rtl8192u/ obj-$(CONFIG_RTL8192E) += rtl8192e/ obj-$(CONFIG_R8712U) += rtl8712/ @@ -31,13 +26,11 @@ obj-$(CONFIG_MFD_NVEC) += nvec/ obj-$(CONFIG_STAGING_RDMA) += rdma/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_STAGING_BOARD) += board/ -obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/ obj-$(CONFIG_LTE_GDM724X) += gdm724x/ obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ obj-$(CONFIG_GOLDFISH) += goldfish/ -obj-$(CONFIG_LUSTRE_FS) += lustre/ +obj-$(CONFIG_LNET) += lustre/ obj-$(CONFIG_DGNC) += dgnc/ -obj-$(CONFIG_DGAP) += dgap/ obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ obj-$(CONFIG_CRYPTO_SKEIN) += skein/ @@ -47,3 +40,4 @@ obj-$(CONFIG_FB_TFT) += fbtft/ obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ obj-$(CONFIG_WILC1000) += wilc1000/ obj-$(CONFIG_MOST) += most/ +obj-$(CONFIG_ISDN_I4L) += i4l/ diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 42b15126aa06..bd90d2002afb 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -57,15 +57,6 @@ config SW_SYNC synchronization. Useful when there is no hardware primitive backing the synchronization. -config SW_SYNC_USER - bool "Userspace API for SW_SYNC" - default n - depends on SW_SYNC - ---help--- - Provides a user space API to the sw sync object. - *WARNING* improper use of this can result in deadlocking kernel - drivers from userspace. - source "drivers/staging/android/ion/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 5bb1283d19cd..8a8078f954d5 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -106,21 +106,34 @@ static struct kmem_cache *ashmem_range_cachep __read_mostly; #define range_on_lru(range) \ ((range)->purged == ASHMEM_NOT_PURGED) -#define page_range_subsumes_range(range, start, end) \ - (((range)->pgstart >= (start)) && ((range)->pgend <= (end))) +static inline int page_range_subsumes_range(struct ashmem_range *range, + size_t start, size_t end) +{ + return (((range)->pgstart >= (start)) && ((range)->pgend <= (end))); +} -#define page_range_subsumed_by_range(range, start, end) \ - (((range)->pgstart <= (start)) && ((range)->pgend >= (end))) +static inline int page_range_subsumed_by_range(struct ashmem_range *range, + size_t start, size_t end) +{ + return (((range)->pgstart <= (start)) && ((range)->pgend >= (end))); +} -#define page_in_range(range, page) \ - (((range)->pgstart <= (page)) && ((range)->pgend >= (page))) +static inline int page_in_range(struct ashmem_range *range, size_t page) +{ + return (((range)->pgstart <= (page)) && ((range)->pgend >= (page))); +} -#define page_range_in_range(range, start, end) \ - (page_in_range(range, start) || page_in_range(range, end) || \ - page_range_subsumes_range(range, start, end)) +static inline int page_range_in_range(struct ashmem_range *range, + size_t start, size_t end) +{ + return (page_in_range(range, start) || page_in_range(range, end) || + page_range_subsumes_range(range, start, end)); +} -#define range_before_page(range, page) \ - ((range)->pgend < (page)) +static inline int range_before_page(struct ashmem_range *range, size_t page) +{ + return ((range)->pgend < (page)); +} #define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE) @@ -441,7 +454,9 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) if (!(sc->gfp_mask & __GFP_FS)) return SHRINK_STOP; - mutex_lock(&ashmem_mutex); + if (!mutex_trylock(&ashmem_mutex)) + return -1; + list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) { loff_t start = range->pgstart * PAGE_SIZE; loff_t end = (range->pgend + 1) * PAGE_SIZE; @@ -661,8 +676,8 @@ restart: if (page_range_subsumed_by_range(range, pgstart, pgend)) return 0; if (page_range_in_range(range, pgstart, pgend)) { - pgstart = min_t(size_t, range->pgstart, pgstart); - pgend = max_t(size_t, range->pgend, pgend); + pgstart = min(range->pgstart, pgstart); + pgend = max(range->pgend, pgend); purged |= range->purged; range_del(range); goto restart; diff --git a/drivers/staging/android/ion/hisilicon/hi6220_ion.c b/drivers/staging/android/ion/hisilicon/hi6220_ion.c index e3c07b2ba00e..fe9f0fd210cd 100644 --- a/drivers/staging/android/ion/hisilicon/hi6220_ion.c +++ b/drivers/staging/android/ion/hisilicon/hi6220_ion.c @@ -214,10 +214,7 @@ static struct platform_driver hi6220_ion_driver = { static int __init hi6220_ion_init(void) { - int ret; - - ret = platform_driver_register(&hi6220_ion_driver); - return ret; + return platform_driver_register(&hi6220_ion_driver); } subsys_initcall(hi6220_ion_init); diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index e237e9f3312d..1c872bdfddf6 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -251,8 +251,10 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, * memory coming from the heaps is ready for dma, ie if it has a * cached mapping that mapping has been invalidated */ - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) + for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { sg_dma_address(sg) = sg_phys(sg); + sg_dma_len(sg) = sg->length; + } mutex_lock(&dev->buffer_lock); ion_buffer_add(dev, buffer); mutex_unlock(&dev->buffer_lock); @@ -385,13 +387,22 @@ static void ion_handle_get(struct ion_handle *handle) kref_get(&handle->ref); } -static int ion_handle_put(struct ion_handle *handle) +static int ion_handle_put_nolock(struct ion_handle *handle) +{ + int ret; + + ret = kref_put(&handle->ref, ion_handle_destroy); + + return ret; +} + +int ion_handle_put(struct ion_handle *handle) { struct ion_client *client = handle->client; int ret; mutex_lock(&client->lock); - ret = kref_put(&handle->ref, ion_handle_destroy); + ret = ion_handle_put_nolock(handle); mutex_unlock(&client->lock); return ret; @@ -415,18 +426,28 @@ static struct ion_handle *ion_handle_lookup(struct ion_client *client, return ERR_PTR(-EINVAL); } -static struct ion_handle *ion_handle_get_by_id(struct ion_client *client, +static struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client, + int id) +{ + struct ion_handle *handle; + + handle = idr_find(&client->idr, id); + if (handle) + ion_handle_get(handle); + + return handle ? handle : ERR_PTR(-EINVAL); +} + +struct ion_handle *ion_handle_get_by_id(struct ion_client *client, int id) { struct ion_handle *handle; mutex_lock(&client->lock); - handle = idr_find(&client->idr, id); - if (handle) - ion_handle_get(handle); + handle = ion_handle_get_by_id_nolock(client, id); mutex_unlock(&client->lock); - return handle ? handle : ERR_PTR(-EINVAL); + return handle; } static bool ion_handle_validate(struct ion_client *client, @@ -530,22 +551,28 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, } EXPORT_SYMBOL(ion_alloc); -void ion_free(struct ion_client *client, struct ion_handle *handle) +static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle) { bool valid_handle; BUG_ON(client != handle->client); - mutex_lock(&client->lock); valid_handle = ion_handle_validate(client, handle); if (!valid_handle) { WARN(1, "%s: invalid handle passed to free.\n", __func__); - mutex_unlock(&client->lock); return; } + ion_handle_put_nolock(handle); +} + +void ion_free(struct ion_client *client, struct ion_handle *handle) +{ + BUG_ON(client != handle->client); + + mutex_lock(&client->lock); + ion_free_nolock(client, handle); mutex_unlock(&client->lock); - ion_handle_put(handle); } EXPORT_SYMBOL(ion_free); @@ -675,6 +702,34 @@ void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) } EXPORT_SYMBOL(ion_unmap_kernel); +static struct mutex debugfs_mutex; +static struct rb_root *ion_root_client; +static int is_client_alive(struct ion_client *client) +{ + struct rb_node *node; + struct ion_client *tmp; + struct ion_device *dev; + + node = ion_root_client->rb_node; + dev = container_of(ion_root_client, struct ion_device, clients); + + down_read(&dev->lock); + while (node) { + tmp = rb_entry(node, struct ion_client, node); + if (client < tmp) { + node = node->rb_left; + } else if (client > tmp) { + node = node->rb_right; + } else { + up_read(&dev->lock); + return 1; + } + } + + up_read(&dev->lock); + return 0; +} + static int ion_debug_client_show(struct seq_file *s, void *unused) { struct ion_client *client = s->private; @@ -683,6 +738,14 @@ static int ion_debug_client_show(struct seq_file *s, void *unused) const char *names[ION_NUM_HEAP_IDS] = {NULL}; int i; + mutex_lock(&debugfs_mutex); + if (!is_client_alive(client)) { + seq_printf(s, "ion_client 0x%p dead, can't dump its buffers\n", + client); + mutex_unlock(&debugfs_mutex); + return 0; + } + mutex_lock(&client->lock); for (n = rb_first(&client->handles); n; n = rb_next(n)) { struct ion_handle *handle = rb_entry(n, struct ion_handle, @@ -694,6 +757,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused) sizes[id] += handle->buffer->size; } mutex_unlock(&client->lock); + mutex_unlock(&debugfs_mutex); seq_printf(s, "%16.16s: %16.16s\n", "heap_name", "size_in_bytes"); for (i = 0; i < ION_NUM_HEAP_IDS; i++) { @@ -830,6 +894,7 @@ void ion_client_destroy(struct ion_client *client) struct rb_node *n; pr_debug("%s: %d\n", __func__, __LINE__); + mutex_lock(&debugfs_mutex); while ((n = rb_first(&client->handles))) { struct ion_handle *handle = rb_entry(n, struct ion_handle, node); @@ -848,6 +913,7 @@ void ion_client_destroy(struct ion_client *client) kfree(client->display_name); kfree(client->name); kfree(client); + mutex_unlock(&debugfs_mutex); } EXPORT_SYMBOL(ion_client_destroy); @@ -1151,22 +1217,18 @@ int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle) } EXPORT_SYMBOL(ion_share_dma_buf_fd); -struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) +struct ion_handle *ion_import_dma_buf(struct ion_client *client, + struct dma_buf *dmabuf) { - struct dma_buf *dmabuf; struct ion_buffer *buffer; struct ion_handle *handle; int ret; - dmabuf = dma_buf_get(fd); - if (IS_ERR(dmabuf)) - return ERR_CAST(dmabuf); /* if this memory came from ion */ if (dmabuf->ops != &dma_buf_ops) { pr_err("%s: can not import dmabuf from another exporter\n", __func__); - dma_buf_put(dmabuf); return ERR_PTR(-EINVAL); } buffer = dmabuf->priv; @@ -1194,11 +1256,25 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) } end: - dma_buf_put(dmabuf); return handle; } EXPORT_SYMBOL(ion_import_dma_buf); +struct ion_handle *ion_import_dma_buf_fd(struct ion_client *client, int fd) +{ + struct dma_buf *dmabuf; + struct ion_handle *handle; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) + return ERR_CAST(dmabuf); + + handle = ion_import_dma_buf(client, dmabuf); + dma_buf_put(dmabuf); + return handle; +} +EXPORT_SYMBOL(ion_import_dma_buf_fd); + static int ion_sync_for_device(struct ion_client *client, int fd) { struct dma_buf *dmabuf; @@ -1281,11 +1357,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct ion_handle *handle; - handle = ion_handle_get_by_id(client, data.handle.handle); - if (IS_ERR(handle)) + mutex_lock(&client->lock); + handle = ion_handle_get_by_id_nolock(client, data.handle.handle); + if (IS_ERR(handle)) { + mutex_unlock(&client->lock); return PTR_ERR(handle); - ion_free(client, handle); - ion_handle_put(handle); + } + ion_free_nolock(client, handle); + ion_handle_put_nolock(handle); + mutex_unlock(&client->lock); break; } case ION_IOC_SHARE: @@ -1306,7 +1386,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct ion_handle *handle; - handle = ion_import_dma_buf(client, data.fd.fd); + handle = ion_import_dma_buf_fd(client, data.fd.fd); if (IS_ERR(handle)) ret = PTR_ERR(handle); else @@ -1403,6 +1483,7 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) seq_printf(s, "%16s %16s %16s\n", "client", "pid", "size"); seq_puts(s, "----------------------------------------------------\n"); + mutex_lock(&debugfs_mutex); for (n = rb_first(&dev->clients); n; n = rb_next(n)) { struct ion_client *client = rb_entry(n, struct ion_client, node); @@ -1421,6 +1502,8 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) client->pid, size); } } + mutex_unlock(&debugfs_mutex); + seq_puts(s, "----------------------------------------------------\n"); seq_puts(s, "orphaned allocations (info is from last known client):\n"); mutex_lock(&dev->buffer_lock); @@ -1472,7 +1555,7 @@ static int debug_shrink_set(void *data, u64 val) struct shrink_control sc; int objs; - sc.gfp_mask = -1; + sc.gfp_mask = GFP_HIGHUSER; sc.nr_to_scan = val; if (!val) { @@ -1490,7 +1573,7 @@ static int debug_shrink_get(void *data, u64 *val) struct shrink_control sc; int objs; - sc.gfp_mask = -1; + sc.gfp_mask = GFP_HIGHUSER; sc.nr_to_scan = 0; objs = heap->shrinker.count_objects(&heap->shrinker, &sc); @@ -1605,6 +1688,8 @@ debugfs_done: init_rwsem(&idev->lock); plist_head_init(&idev->heaps); idev->clients = RB_ROOT; + ion_root_client = &idev->clients; + mutex_init(&debugfs_mutex); return idev; } EXPORT_SYMBOL(ion_device_create); diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h index b860c5f579f5..a1331fc169a1 100644 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -192,14 +192,26 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client, int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle); /** - * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle + * ion_import_dma_buf() - get ion_handle from dma-buf + * @client: the client + * @dmabuf: the dma-buf + * + * Get the ion_buffer associated with the dma-buf and return the ion_handle. + * If no ion_handle exists for this buffer, return newly created ion_handle. + * If dma-buf from another exporter is passed, return ERR_PTR(-EINVAL) + */ +struct ion_handle *ion_import_dma_buf(struct ion_client *client, + struct dma_buf *dmabuf); + +/** + * ion_import_dma_buf_fd() - given a dma-buf fd from the ion exporter get handle * @client: the client * @fd: the dma-buf fd * - * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf, - * import that fd and return a handle representing it. If a dma-buf from + * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf_fd, + * import that fd and return a handle representing it. If a dma-buf from * another exporter is passed in this function will return ERR_PTR(-EINVAL) */ -struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); +struct ion_handle *ion_import_dma_buf_fd(struct ion_client *client, int fd); #endif /* _LINUX_ION_H */ diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c index 9156d8238c97..1fb0d81556da 100644 --- a/drivers/staging/android/ion/ion_carveout_heap.c +++ b/drivers/staging/android/ion/ion_carveout_heap.c @@ -81,7 +81,7 @@ static int ion_carveout_heap_allocate(struct ion_heap *heap, if (align > PAGE_SIZE) return -EINVAL; - table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + table = kmalloc(sizeof(*table), GFP_KERNEL); if (!table) return -ENOMEM; ret = sg_alloc_table(table, 1, GFP_KERNEL); @@ -117,7 +117,7 @@ static void ion_carveout_heap_free(struct ion_buffer *buffer) if (ion_buffer_cached(buffer)) dma_sync_sg_for_device(NULL, table->sgl, table->nents, - DMA_BIDIRECTIONAL); + DMA_BIDIRECTIONAL); ion_carveout_free(heap, paddr, buffer->size); sg_free_table(table); @@ -163,11 +163,11 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) if (ret) return ERR_PTR(ret); - carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL); + carveout_heap = kzalloc(sizeof(*carveout_heap), GFP_KERNEL); if (!carveout_heap) return ERR_PTR(-ENOMEM); - carveout_heap->pool = gen_pool_create(12, -1); + carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1); if (!carveout_heap->pool) { kfree(carveout_heap); return ERR_PTR(-ENOMEM); diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index fd7e23e0c06e..1fe80165a462 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -149,8 +149,8 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order) { - struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool), - GFP_KERNEL); + struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) return NULL; pool->high_count = 0; diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index d4c3e5512dd5..b69dfc706440 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -27,7 +27,7 @@ #include "ion_priv.h" static gfp_t high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN | - __GFP_NORETRY) & ~__GFP_DIRECT_RECLAIM; + __GFP_NORETRY) & ~__GFP_RECLAIM; static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN); static const unsigned int orders[] = {8, 4, 0}; static const int num_orders = ARRAY_SIZE(orders); diff --git a/drivers/staging/android/ion/tegra/tegra_ion.c b/drivers/staging/android/ion/tegra/tegra_ion.c index 4d3c516cc15e..49e55e5acead 100644 --- a/drivers/staging/android/ion/tegra/tegra_ion.c +++ b/drivers/staging/android/ion/tegra/tegra_ion.c @@ -33,12 +33,11 @@ static int tegra_ion_probe(struct platform_device *pdev) num_heaps = pdata->nr; - heaps = devm_kzalloc(&pdev->dev, - sizeof(struct ion_heap *) * pdata->nr, - GFP_KERNEL); + heaps = devm_kcalloc(&pdev->dev, pdata->nr, + sizeof(struct ion_heap *), GFP_KERNEL); idev = ion_device_create(NULL); - if (IS_ERR_OR_NULL(idev)) + if (IS_ERR(idev)) return PTR_ERR(idev); /* create the heaps as specified in the board file */ diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 8b5a4a82d8b8..2509e5df7244 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -50,6 +50,7 @@ static short lowmem_adj[6] = { 6, 12, }; + static int lowmem_adj_size = 4; static int lowmem_minfree[6] = { 3 * 512, /* 6MB */ @@ -57,6 +58,7 @@ static int lowmem_minfree[6] = { 4 * 1024, /* 16MB */ 16 * 1024, /* 64MB */ }; + static int lowmem_minfree_size = 4; static unsigned long lowmem_deathpending_timeout; @@ -84,6 +86,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) int tasksize; int i; short min_score_adj = OOM_SCORE_ADJ_MAX + 1; + int minfree = 0; int selected_tasksize = 0; short selected_oom_score_adj; int array_size = ARRAY_SIZE(lowmem_adj); @@ -97,8 +100,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (lowmem_minfree_size < array_size) array_size = lowmem_minfree_size; for (i = 0; i < array_size; i++) { - if (other_free < lowmem_minfree[i] && - other_file < lowmem_minfree[i]) { + minfree = lowmem_minfree[i]; + if (other_free < minfree && other_file < minfree) { min_score_adj = lowmem_adj[i]; break; } @@ -153,8 +156,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) selected = p; selected_tasksize = tasksize; selected_oom_score_adj = oom_score_adj; - lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n", - p->pid, p->comm, oom_score_adj, tasksize); + lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n", + p->comm, p->pid, oom_score_adj, tasksize); } if (selected) { task_lock(selected); @@ -167,9 +170,18 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (selected->mm) mark_oom_victim(selected); task_unlock(selected); - lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n", - selected->pid, selected->comm, - selected_oom_score_adj, selected_tasksize); + lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" + " to free %ldkB on behalf of '%s' (%d) because\n" + " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" + " Free memory is %ldkB above reserved\n", + selected->comm, selected->pid, + selected_oom_score_adj, + selected_tasksize * (long)(PAGE_SIZE / 1024), + current->comm, current->pid, + other_file * (long)(PAGE_SIZE / 1024), + minfree * (long)(PAGE_SIZE / 1024), + min_score_adj, + other_free * (long)(PAGE_SIZE / 1024)); lowmem_deathpending_timeout = jiffies + HZ; rem += selected_tasksize; } diff --git a/drivers/staging/android/sw_sync.c b/drivers/staging/android/sw_sync.c index c4ff1679ebbc..af39ff58fa33 100644 --- a/drivers/staging/android/sw_sync.c +++ b/drivers/staging/android/sw_sync.c @@ -25,15 +25,7 @@ #include "sw_sync.h" -static int sw_sync_cmp(u32 a, u32 b) -{ - if (a == b) - return 0; - - return ((s32)a - (s32)b) < 0 ? -1 : 1; -} - -struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) +struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) { struct sw_sync_pt *pt; @@ -42,47 +34,17 @@ struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) pt->value = value; - return (struct sync_pt *)pt; + return (struct fence *)pt; } EXPORT_SYMBOL(sw_sync_pt_create); -static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) +static int sw_sync_fence_has_signaled(struct fence *fence) { - struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + struct sw_sync_pt *pt = (struct sw_sync_pt *)fence; struct sw_sync_timeline *obj = - (struct sw_sync_timeline *)sync_pt_parent(sync_pt); + (struct sw_sync_timeline *)fence_parent(fence); - return (struct sync_pt *)sw_sync_pt_create(obj, pt->value); -} - -static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) -{ - struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; - struct sw_sync_timeline *obj = - (struct sw_sync_timeline *)sync_pt_parent(sync_pt); - - return sw_sync_cmp(obj->value, pt->value) >= 0; -} - -static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) -{ - struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a; - struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b; - - return sw_sync_cmp(pt_a->value, pt_b->value); -} - -static int sw_sync_fill_driver_data(struct sync_pt *sync_pt, - void *data, int size) -{ - struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; - - if (size < sizeof(pt->value)) - return -ENOMEM; - - memcpy(data, &pt->value, sizeof(pt->value)); - - return sizeof(pt->value); + return (pt->value > obj->value) ? 0 : 1; } static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline, @@ -93,22 +55,18 @@ static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline, snprintf(str, size, "%d", timeline->value); } -static void sw_sync_pt_value_str(struct sync_pt *sync_pt, - char *str, int size) +static void sw_sync_fence_value_str(struct fence *fence, char *str, int size) { - struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + struct sw_sync_pt *pt = (struct sw_sync_pt *)fence; snprintf(str, size, "%d", pt->value); } static struct sync_timeline_ops sw_sync_timeline_ops = { .driver_name = "sw_sync", - .dup = sw_sync_pt_dup, - .has_signaled = sw_sync_pt_has_signaled, - .compare = sw_sync_pt_compare, - .fill_driver_data = sw_sync_fill_driver_data, + .has_signaled = sw_sync_fence_has_signaled, .timeline_value_str = sw_sync_timeline_value_str, - .pt_value_str = sw_sync_pt_value_str, + .fence_value_str = sw_sync_fence_value_str, }; struct sw_sync_timeline *sw_sync_timeline_create(const char *name) @@ -129,132 +87,3 @@ void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) sync_timeline_signal(&obj->obj); } EXPORT_SYMBOL(sw_sync_timeline_inc); - -#ifdef CONFIG_SW_SYNC_USER -/* *WARNING* - * - * improper use of this can result in deadlocking kernel drivers from userspace. - */ - -/* opening sw_sync create a new sync obj */ -static int sw_sync_open(struct inode *inode, struct file *file) -{ - struct sw_sync_timeline *obj; - char task_comm[TASK_COMM_LEN]; - - get_task_comm(task_comm, current); - - obj = sw_sync_timeline_create(task_comm); - if (!obj) - return -ENOMEM; - - file->private_data = obj; - - return 0; -} - -static int sw_sync_release(struct inode *inode, struct file *file) -{ - struct sw_sync_timeline *obj = file->private_data; - - sync_timeline_destroy(&obj->obj); - return 0; -} - -static long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, - unsigned long arg) -{ - int fd = get_unused_fd_flags(O_CLOEXEC); - int err; - struct sync_pt *pt; - struct sync_fence *fence; - struct sw_sync_create_fence_data data; - - if (fd < 0) - return fd; - - if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { - err = -EFAULT; - goto err; - } - - pt = sw_sync_pt_create(obj, data.value); - if (!pt) { - err = -ENOMEM; - goto err; - } - - data.name[sizeof(data.name) - 1] = '\0'; - fence = sync_fence_create(data.name, pt); - if (!fence) { - sync_pt_free(pt); - err = -ENOMEM; - goto err; - } - - data.fence = fd; - if (copy_to_user((void __user *)arg, &data, sizeof(data))) { - sync_fence_put(fence); - err = -EFAULT; - goto err; - } - - sync_fence_install(fence, fd); - - return 0; - -err: - put_unused_fd(fd); - return err; -} - -static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) -{ - u32 value; - - if (copy_from_user(&value, (void __user *)arg, sizeof(value))) - return -EFAULT; - - sw_sync_timeline_inc(obj, value); - - return 0; -} - -static long sw_sync_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct sw_sync_timeline *obj = file->private_data; - - switch (cmd) { - case SW_SYNC_IOC_CREATE_FENCE: - return sw_sync_ioctl_create_fence(obj, arg); - - case SW_SYNC_IOC_INC: - return sw_sync_ioctl_inc(obj, arg); - - default: - return -ENOTTY; - } -} - -static const struct file_operations sw_sync_fops = { - .owner = THIS_MODULE, - .open = sw_sync_open, - .release = sw_sync_release, - .unlocked_ioctl = sw_sync_ioctl, - .compat_ioctl = sw_sync_ioctl, -}; - -static struct miscdevice sw_sync_dev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "sw_sync", - .fops = &sw_sync_fops, -}; - -static int __init sw_sync_device_init(void) -{ - return misc_register(&sw_sync_dev); -} -device_initcall(sw_sync_device_init); - -#endif /* CONFIG_SW_SYNC_USER */ diff --git a/drivers/staging/android/sw_sync.h b/drivers/staging/android/sw_sync.h index c87ae9ebf267..e18667bfb0ca 100644 --- a/drivers/staging/android/sw_sync.h +++ b/drivers/staging/android/sw_sync.h @@ -29,7 +29,7 @@ struct sw_sync_timeline { }; struct sw_sync_pt { - struct sync_pt pt; + struct fence pt; u32 value; }; @@ -38,7 +38,7 @@ struct sw_sync_pt { struct sw_sync_timeline *sw_sync_timeline_create(const char *name); void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc); -struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); +struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); #else static inline struct sw_sync_timeline *sw_sync_timeline_create(const char *name) { @@ -49,8 +49,8 @@ static inline void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) { } -static inline struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, - u32 value) +static inline struct fence *sw_sync_pt_create(struct sw_sync_timeline *obj, + u32 value) { return NULL; } diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c index ed43796b5b58..3a8f21031440 100644 --- a/drivers/staging/android/sync.c +++ b/drivers/staging/android/sync.c @@ -32,7 +32,7 @@ #include "trace/sync.h" static const struct fence_ops android_fence_ops; -static const struct file_operations sync_fence_fops; +static const struct file_operations sync_file_fops; struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, int size, const char *name) @@ -68,9 +68,6 @@ static void sync_timeline_free(struct kref *kref) sync_timeline_debug_remove(obj); - if (obj->ops->release_obj) - obj->ops->release_obj(obj); - kfree(obj); } @@ -93,10 +90,6 @@ void sync_timeline_destroy(struct sync_timeline *obj) */ smp_wmb(); - /* - * signal any children that their parent is going away. - */ - sync_timeline_signal(obj); sync_timeline_put(obj); } EXPORT_SYMBOL(sync_timeline_destroy); @@ -104,126 +97,115 @@ EXPORT_SYMBOL(sync_timeline_destroy); void sync_timeline_signal(struct sync_timeline *obj) { unsigned long flags; - LIST_HEAD(signaled_pts); - struct sync_pt *pt, *next; + struct fence *fence, *next; trace_sync_timeline(obj); spin_lock_irqsave(&obj->child_list_lock, flags); - list_for_each_entry_safe(pt, next, &obj->active_list_head, + list_for_each_entry_safe(fence, next, &obj->active_list_head, active_list) { - if (fence_is_signaled_locked(&pt->base)) - list_del_init(&pt->active_list); + if (fence_is_signaled_locked(fence)) + list_del_init(&fence->active_list); } spin_unlock_irqrestore(&obj->child_list_lock, flags); } EXPORT_SYMBOL(sync_timeline_signal); -struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size) +struct fence *sync_pt_create(struct sync_timeline *obj, int size) { unsigned long flags; - struct sync_pt *pt; + struct fence *fence; - if (size < sizeof(struct sync_pt)) + if (size < sizeof(*fence)) return NULL; - pt = kzalloc(size, GFP_KERNEL); - if (!pt) - return NULL; - - spin_lock_irqsave(&obj->child_list_lock, flags); - sync_timeline_get(obj); - fence_init(&pt->base, &android_fence_ops, &obj->child_list_lock, - obj->context, ++obj->value); - list_add_tail(&pt->child_list, &obj->child_list_head); - INIT_LIST_HEAD(&pt->active_list); - spin_unlock_irqrestore(&obj->child_list_lock, flags); - return pt; -} -EXPORT_SYMBOL(sync_pt_create); - -void sync_pt_free(struct sync_pt *pt) -{ - fence_put(&pt->base); -} -EXPORT_SYMBOL(sync_pt_free); - -static struct sync_fence *sync_fence_alloc(int size, const char *name) -{ - struct sync_fence *fence; - fence = kzalloc(size, GFP_KERNEL); if (!fence) return NULL; - fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops, - fence, 0); - if (IS_ERR(fence->file)) + spin_lock_irqsave(&obj->child_list_lock, flags); + sync_timeline_get(obj); + fence_init(fence, &android_fence_ops, &obj->child_list_lock, + obj->context, ++obj->value); + list_add_tail(&fence->child_list, &obj->child_list_head); + INIT_LIST_HEAD(&fence->active_list); + spin_unlock_irqrestore(&obj->child_list_lock, flags); + return fence; +} +EXPORT_SYMBOL(sync_pt_create); + +static struct sync_file *sync_file_alloc(int size, const char *name) +{ + struct sync_file *sync_file; + + sync_file = kzalloc(size, GFP_KERNEL); + if (!sync_file) + return NULL; + + sync_file->file = anon_inode_getfile("sync_file", &sync_file_fops, + sync_file, 0); + if (IS_ERR(sync_file->file)) goto err; - kref_init(&fence->kref); - strlcpy(fence->name, name, sizeof(fence->name)); + kref_init(&sync_file->kref); + strlcpy(sync_file->name, name, sizeof(sync_file->name)); - init_waitqueue_head(&fence->wq); + init_waitqueue_head(&sync_file->wq); - return fence; + return sync_file; err: - kfree(fence); + kfree(sync_file); return NULL; } static void fence_check_cb_func(struct fence *f, struct fence_cb *cb) { - struct sync_fence_cb *check; - struct sync_fence *fence; + struct sync_file_cb *check; + struct sync_file *sync_file; - check = container_of(cb, struct sync_fence_cb, cb); - fence = check->fence; + check = container_of(cb, struct sync_file_cb, cb); + sync_file = check->sync_file; - if (atomic_dec_and_test(&fence->status)) - wake_up_all(&fence->wq); + if (atomic_dec_and_test(&sync_file->status)) + wake_up_all(&sync_file->wq); } -/* TODO: implement a create which takes more that one sync_pt */ -struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt) +/* TODO: implement a create which takes more that one fence */ +struct sync_file *sync_file_create(const char *name, struct fence *fence) { - struct sync_fence *fence; + struct sync_file *sync_file; - fence = sync_fence_alloc(offsetof(struct sync_fence, cbs[1]), name); - if (!fence) + sync_file = sync_file_alloc(offsetof(struct sync_file, cbs[1]), + name); + if (!sync_file) return NULL; - fence->num_fences = 1; - atomic_set(&fence->status, 1); + sync_file->num_fences = 1; + atomic_set(&sync_file->status, 1); - fence->cbs[0].sync_pt = pt; - fence->cbs[0].fence = fence; - if (fence_add_callback(pt, &fence->cbs[0].cb, fence_check_cb_func)) - atomic_dec(&fence->status); + sync_file->cbs[0].fence = fence; + sync_file->cbs[0].sync_file = sync_file; + if (fence_add_callback(fence, &sync_file->cbs[0].cb, + fence_check_cb_func)) + atomic_dec(&sync_file->status); - sync_fence_debug_add(fence); + sync_file_debug_add(sync_file); - return fence; + return sync_file; } -EXPORT_SYMBOL(sync_fence_create_dma); +EXPORT_SYMBOL(sync_file_create); -struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) -{ - return sync_fence_create_dma(name, &pt->base); -} -EXPORT_SYMBOL(sync_fence_create); - -struct sync_fence *sync_fence_fdget(int fd) +struct sync_file *sync_file_fdget(int fd) { struct file *file = fget(fd); if (!file) return NULL; - if (file->f_op != &sync_fence_fops) + if (file->f_op != &sync_file_fops) goto err; return file->private_data; @@ -232,70 +214,71 @@ err: fput(file); return NULL; } -EXPORT_SYMBOL(sync_fence_fdget); +EXPORT_SYMBOL(sync_file_fdget); -void sync_fence_put(struct sync_fence *fence) +void sync_file_put(struct sync_file *sync_file) { - fput(fence->file); + fput(sync_file->file); } -EXPORT_SYMBOL(sync_fence_put); +EXPORT_SYMBOL(sync_file_put); -void sync_fence_install(struct sync_fence *fence, int fd) +void sync_file_install(struct sync_file *sync_file, int fd) { - fd_install(fd, fence->file); + fd_install(fd, sync_file->file); } -EXPORT_SYMBOL(sync_fence_install); +EXPORT_SYMBOL(sync_file_install); -static void sync_fence_add_pt(struct sync_fence *fence, - int *i, struct fence *pt) +static void sync_file_add_pt(struct sync_file *sync_file, int *i, + struct fence *fence) { - fence->cbs[*i].sync_pt = pt; - fence->cbs[*i].fence = fence; + sync_file->cbs[*i].fence = fence; + sync_file->cbs[*i].sync_file = sync_file; - if (!fence_add_callback(pt, &fence->cbs[*i].cb, fence_check_cb_func)) { - fence_get(pt); + if (!fence_add_callback(fence, &sync_file->cbs[*i].cb, + fence_check_cb_func)) { + fence_get(fence); (*i)++; } } -struct sync_fence *sync_fence_merge(const char *name, - struct sync_fence *a, struct sync_fence *b) +struct sync_file *sync_file_merge(const char *name, + struct sync_file *a, struct sync_file *b) { int num_fences = a->num_fences + b->num_fences; - struct sync_fence *fence; + struct sync_file *sync_file; int i, i_a, i_b; - unsigned long size = offsetof(struct sync_fence, cbs[num_fences]); + unsigned long size = offsetof(struct sync_file, cbs[num_fences]); - fence = sync_fence_alloc(size, name); - if (!fence) + sync_file = sync_file_alloc(size, name); + if (!sync_file) return NULL; - atomic_set(&fence->status, num_fences); + atomic_set(&sync_file->status, num_fences); /* - * Assume sync_fence a and b are both ordered and have no + * Assume sync_file a and b are both ordered and have no * duplicates with the same context. * - * If a sync_fence can only be created with sync_fence_merge - * and sync_fence_create, this is a reasonable assumption. + * If a sync_file can only be created with sync_file_merge + * and sync_file_create, this is a reasonable assumption. */ for (i = i_a = i_b = 0; i_a < a->num_fences && i_b < b->num_fences; ) { - struct fence *pt_a = a->cbs[i_a].sync_pt; - struct fence *pt_b = b->cbs[i_b].sync_pt; + struct fence *pt_a = a->cbs[i_a].fence; + struct fence *pt_b = b->cbs[i_b].fence; if (pt_a->context < pt_b->context) { - sync_fence_add_pt(fence, &i, pt_a); + sync_file_add_pt(sync_file, &i, pt_a); i_a++; } else if (pt_a->context > pt_b->context) { - sync_fence_add_pt(fence, &i, pt_b); + sync_file_add_pt(sync_file, &i, pt_b); i_b++; } else { if (pt_a->seqno - pt_b->seqno <= INT_MAX) - sync_fence_add_pt(fence, &i, pt_a); + sync_file_add_pt(sync_file, &i, pt_a); else - sync_fence_add_pt(fence, &i, pt_b); + sync_file_add_pt(sync_file, &i, pt_b); i_a++; i_b++; @@ -303,156 +286,55 @@ struct sync_fence *sync_fence_merge(const char *name, } for (; i_a < a->num_fences; i_a++) - sync_fence_add_pt(fence, &i, a->cbs[i_a].sync_pt); + sync_file_add_pt(sync_file, &i, a->cbs[i_a].fence); for (; i_b < b->num_fences; i_b++) - sync_fence_add_pt(fence, &i, b->cbs[i_b].sync_pt); + sync_file_add_pt(sync_file, &i, b->cbs[i_b].fence); if (num_fences > i) - atomic_sub(num_fences - i, &fence->status); - fence->num_fences = i; + atomic_sub(num_fences - i, &sync_file->status); + sync_file->num_fences = i; - sync_fence_debug_add(fence); - return fence; + sync_file_debug_add(sync_file); + return sync_file; } -EXPORT_SYMBOL(sync_fence_merge); - -int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode, - int wake_flags, void *key) -{ - struct sync_fence_waiter *wait; - - wait = container_of(curr, struct sync_fence_waiter, work); - list_del_init(&wait->work.task_list); - - wait->callback(wait->work.private, wait); - return 1; -} - -int sync_fence_wait_async(struct sync_fence *fence, - struct sync_fence_waiter *waiter) -{ - int err = atomic_read(&fence->status); - unsigned long flags; - - if (err < 0) - return err; - - if (!err) - return 1; - - init_waitqueue_func_entry(&waiter->work, sync_fence_wake_up_wq); - waiter->work.private = fence; - - spin_lock_irqsave(&fence->wq.lock, flags); - err = atomic_read(&fence->status); - if (err > 0) - __add_wait_queue_tail(&fence->wq, &waiter->work); - spin_unlock_irqrestore(&fence->wq.lock, flags); - - if (err < 0) - return err; - - return !err; -} -EXPORT_SYMBOL(sync_fence_wait_async); - -int sync_fence_cancel_async(struct sync_fence *fence, - struct sync_fence_waiter *waiter) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&fence->wq.lock, flags); - if (!list_empty(&waiter->work.task_list)) - list_del_init(&waiter->work.task_list); - else - ret = -ENOENT; - spin_unlock_irqrestore(&fence->wq.lock, flags); - return ret; -} -EXPORT_SYMBOL(sync_fence_cancel_async); - -int sync_fence_wait(struct sync_fence *fence, long timeout) -{ - long ret; - int i; - - if (timeout < 0) - timeout = MAX_SCHEDULE_TIMEOUT; - else - timeout = msecs_to_jiffies(timeout); - - trace_sync_wait(fence, 1); - for (i = 0; i < fence->num_fences; ++i) - trace_sync_pt(fence->cbs[i].sync_pt); - ret = wait_event_interruptible_timeout(fence->wq, - atomic_read(&fence->status) <= 0, - timeout); - trace_sync_wait(fence, 0); - - if (ret < 0) { - return ret; - } else if (ret == 0) { - if (timeout) { - pr_info("fence timeout on [%p] after %dms\n", fence, - jiffies_to_msecs(timeout)); - sync_dump(); - } - return -ETIME; - } - - ret = atomic_read(&fence->status); - if (ret) { - pr_info("fence error %ld on [%p]\n", ret, fence); - sync_dump(); - } - return ret; -} -EXPORT_SYMBOL(sync_fence_wait); +EXPORT_SYMBOL(sync_file_merge); static const char *android_fence_get_driver_name(struct fence *fence) { - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); + struct sync_timeline *parent = fence_parent(fence); return parent->ops->driver_name; } static const char *android_fence_get_timeline_name(struct fence *fence) { - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); + struct sync_timeline *parent = fence_parent(fence); return parent->name; } static void android_fence_release(struct fence *fence) { - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); + struct sync_timeline *parent = fence_parent(fence); unsigned long flags; spin_lock_irqsave(fence->lock, flags); - list_del(&pt->child_list); - if (WARN_ON_ONCE(!list_empty(&pt->active_list))) - list_del(&pt->active_list); + list_del(&fence->child_list); + if (WARN_ON_ONCE(!list_empty(&fence->active_list))) + list_del(&fence->active_list); spin_unlock_irqrestore(fence->lock, flags); - if (parent->ops->free_pt) - parent->ops->free_pt(pt); - sync_timeline_put(parent); - fence_free(&pt->base); + fence_free(fence); } static bool android_fence_signaled(struct fence *fence) { - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); + struct sync_timeline *parent = fence_parent(fence); int ret; - ret = parent->ops->has_signaled(pt); + ret = parent->ops->has_signaled(fence); if (ret < 0) fence->status = ret; return ret; @@ -460,46 +342,32 @@ static bool android_fence_signaled(struct fence *fence) static bool android_fence_enable_signaling(struct fence *fence) { - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); + struct sync_timeline *parent = fence_parent(fence); if (android_fence_signaled(fence)) return false; - list_add_tail(&pt->active_list, &parent->active_list_head); + list_add_tail(&fence->active_list, &parent->active_list_head); return true; } -static int android_fence_fill_driver_data(struct fence *fence, - void *data, int size) -{ - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); - - if (!parent->ops->fill_driver_data) - return 0; - return parent->ops->fill_driver_data(pt, data, size); -} - static void android_fence_value_str(struct fence *fence, char *str, int size) { - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); + struct sync_timeline *parent = fence_parent(fence); - if (!parent->ops->pt_value_str) { + if (!parent->ops->fence_value_str) { if (size) *str = 0; return; } - parent->ops->pt_value_str(pt, str, size); + parent->ops->fence_value_str(fence, str, size); } static void android_fence_timeline_value_str(struct fence *fence, char *str, int size) { - struct sync_pt *pt = container_of(fence, struct sync_pt, base); - struct sync_timeline *parent = sync_pt_parent(pt); + struct sync_timeline *parent = fence_parent(fence); if (!parent->ops->timeline_value_str) { if (size) @@ -516,65 +384,57 @@ static const struct fence_ops android_fence_ops = { .signaled = android_fence_signaled, .wait = fence_default_wait, .release = android_fence_release, - .fill_driver_data = android_fence_fill_driver_data, .fence_value_str = android_fence_value_str, .timeline_value_str = android_fence_timeline_value_str, }; -static void sync_fence_free(struct kref *kref) +static void sync_file_free(struct kref *kref) { - struct sync_fence *fence = container_of(kref, struct sync_fence, kref); + struct sync_file *sync_file = container_of(kref, struct sync_file, + kref); int i; - for (i = 0; i < fence->num_fences; ++i) { - fence_remove_callback(fence->cbs[i].sync_pt, &fence->cbs[i].cb); - fence_put(fence->cbs[i].sync_pt); + for (i = 0; i < sync_file->num_fences; ++i) { + fence_remove_callback(sync_file->cbs[i].fence, + &sync_file->cbs[i].cb); + fence_put(sync_file->cbs[i].fence); } - kfree(fence); + kfree(sync_file); } -static int sync_fence_release(struct inode *inode, struct file *file) +static int sync_file_release(struct inode *inode, struct file *file) { - struct sync_fence *fence = file->private_data; + struct sync_file *sync_file = file->private_data; - sync_fence_debug_remove(fence); + sync_file_debug_remove(sync_file); - kref_put(&fence->kref, sync_fence_free); + kref_put(&sync_file->kref, sync_file_free); return 0; } -static unsigned int sync_fence_poll(struct file *file, poll_table *wait) +static unsigned int sync_file_poll(struct file *file, poll_table *wait) { - struct sync_fence *fence = file->private_data; + struct sync_file *sync_file = file->private_data; int status; - poll_wait(file, &fence->wq, wait); + poll_wait(file, &sync_file->wq, wait); - status = atomic_read(&fence->status); + status = atomic_read(&sync_file->status); if (!status) return POLLIN; - else if (status < 0) + if (status < 0) return POLLERR; return 0; } -static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) -{ - __s32 value; - - if (copy_from_user(&value, (void __user *)arg, sizeof(value))) - return -EFAULT; - - return sync_fence_wait(fence, value); -} - -static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) +static long sync_file_ioctl_merge(struct sync_file *sync_file, + unsigned long arg) { int fd = get_unused_fd_flags(O_CLOEXEC); int err; - struct sync_fence *fence2, *fence3; + struct sync_file *fence2, *fence3; struct sync_merge_data data; if (fd < 0) @@ -585,14 +445,14 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) goto err_put_fd; } - fence2 = sync_fence_fdget(data.fd2); + fence2 = sync_file_fdget(data.fd2); if (!fence2) { err = -ENOENT; goto err_put_fd; } data.name[sizeof(data.name) - 1] = '\0'; - fence3 = sync_fence_merge(data.name, fence, fence2); + fence3 = sync_file_merge(data.name, sync_file, fence2); if (!fence3) { err = -ENOMEM; goto err_put_fence2; @@ -604,40 +464,28 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) goto err_put_fence3; } - sync_fence_install(fence3, fd); - sync_fence_put(fence2); + sync_file_install(fence3, fd); + sync_file_put(fence2); return 0; err_put_fence3: - sync_fence_put(fence3); + sync_file_put(fence3); err_put_fence2: - sync_fence_put(fence2); + sync_file_put(fence2); err_put_fd: put_unused_fd(fd); return err; } -static int sync_fill_pt_info(struct fence *fence, void *data, int size) +static int sync_fill_fence_info(struct fence *fence, void *data, int size) { - struct sync_pt_info *info = data; - int ret; + struct sync_fence_info *info = data; - if (size < sizeof(struct sync_pt_info)) + if (size < sizeof(*info)) return -ENOMEM; - info->len = sizeof(struct sync_pt_info); - - if (fence->ops->fill_driver_data) { - ret = fence->ops->fill_driver_data(fence, info->driver_data, - size - sizeof(*info)); - if (ret < 0) - return ret; - - info->len += ret; - } - strlcpy(info->obj_name, fence->ops->get_timeline_name(fence), sizeof(info->obj_name)); strlcpy(info->driver_name, fence->ops->get_driver_name(fence), @@ -648,13 +496,13 @@ static int sync_fill_pt_info(struct fence *fence, void *data, int size) info->status = 0; info->timestamp_ns = ktime_to_ns(fence->timestamp); - return info->len; + return sizeof(*info); } -static long sync_fence_ioctl_fence_info(struct sync_fence *fence, +static long sync_file_ioctl_fence_info(struct sync_file *sync_file, unsigned long arg) { - struct sync_fence_info_data *data; + struct sync_file_info *info; __u32 size; __u32 len = 0; int ret, i; @@ -662,27 +510,27 @@ static long sync_fence_ioctl_fence_info(struct sync_fence *fence, if (copy_from_user(&size, (void __user *)arg, sizeof(size))) return -EFAULT; - if (size < sizeof(struct sync_fence_info_data)) + if (size < sizeof(struct sync_file_info)) return -EINVAL; if (size > 4096) size = 4096; - data = kzalloc(size, GFP_KERNEL); - if (!data) + info = kzalloc(size, GFP_KERNEL); + if (!info) return -ENOMEM; - strlcpy(data->name, fence->name, sizeof(data->name)); - data->status = atomic_read(&fence->status); - if (data->status >= 0) - data->status = !data->status; + strlcpy(info->name, sync_file->name, sizeof(info->name)); + info->status = atomic_read(&sync_file->status); + if (info->status >= 0) + info->status = !info->status; - len = sizeof(struct sync_fence_info_data); + len = sizeof(struct sync_file_info); - for (i = 0; i < fence->num_fences; ++i) { - struct fence *pt = fence->cbs[i].sync_pt; + for (i = 0; i < sync_file->num_fences; ++i) { + struct fence *fence = sync_file->cbs[i].fence; - ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len); + ret = sync_fill_fence_info(fence, (u8 *)info + len, size - len); if (ret < 0) goto out; @@ -690,43 +538,40 @@ static long sync_fence_ioctl_fence_info(struct sync_fence *fence, len += ret; } - data->len = len; + info->len = len; - if (copy_to_user((void __user *)arg, data, len)) + if (copy_to_user((void __user *)arg, info, len)) ret = -EFAULT; else ret = 0; out: - kfree(data); + kfree(info); return ret; } -static long sync_fence_ioctl(struct file *file, unsigned int cmd, +static long sync_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct sync_fence *fence = file->private_data; + struct sync_file *sync_file = file->private_data; switch (cmd) { - case SYNC_IOC_WAIT: - return sync_fence_ioctl_wait(fence, arg); - case SYNC_IOC_MERGE: - return sync_fence_ioctl_merge(fence, arg); + return sync_file_ioctl_merge(sync_file, arg); case SYNC_IOC_FENCE_INFO: - return sync_fence_ioctl_fence_info(fence, arg); + return sync_file_ioctl_fence_info(sync_file, arg); default: return -ENOTTY; } } -static const struct file_operations sync_fence_fops = { - .release = sync_fence_release, - .poll = sync_fence_poll, - .unlocked_ioctl = sync_fence_ioctl, - .compat_ioctl = sync_fence_ioctl, +static const struct file_operations sync_file_fops = { + .release = sync_file_release, + .poll = sync_file_poll, + .unlocked_ioctl = sync_file_ioctl, + .compat_ioctl = sync_file_ioctl, }; diff --git a/drivers/staging/android/sync.h b/drivers/staging/android/sync.h index afa0752275a7..d2a173433a7d 100644 --- a/drivers/staging/android/sync.h +++ b/drivers/staging/android/sync.h @@ -18,63 +18,35 @@ #include #include #include -#include #include #include "uapi/sync.h" struct sync_timeline; -struct sync_pt; -struct sync_fence; +struct sync_file; /** * struct sync_timeline_ops - sync object implementation ops * @driver_name: name of the implementation - * @dup: duplicate a sync_pt * @has_signaled: returns: * 1 if pt has signaled * 0 if pt has not signaled * <0 on error - * @compare: returns: - * 1 if b will signal before a - * 0 if a and b will signal at the same time - * -1 if a will signal before b - * @free_pt: called before sync_pt is freed - * @release_obj: called before sync_timeline is freed - * @fill_driver_data: write implementation specific driver data to data. - * should return an error if there is not enough room - * as specified by size. This information is returned - * to userspace by SYNC_IOC_FENCE_INFO. * @timeline_value_str: fill str with the value of the sync_timeline's counter - * @pt_value_str: fill str with the value of the sync_pt + * @fence_value_str: fill str with the value of the fence */ struct sync_timeline_ops { const char *driver_name; /* required */ - struct sync_pt * (*dup)(struct sync_pt *pt); - - /* required */ - int (*has_signaled)(struct sync_pt *pt); - - /* required */ - int (*compare)(struct sync_pt *a, struct sync_pt *b); - - /* optional */ - void (*free_pt)(struct sync_pt *sync_pt); - - /* optional */ - void (*release_obj)(struct sync_timeline *sync_timeline); - - /* optional */ - int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size); + int (*has_signaled)(struct fence *fence); /* optional */ void (*timeline_value_str)(struct sync_timeline *timeline, char *str, int size); /* optional */ - void (*pt_value_str)(struct sync_pt *pt, char *str, int size); + void (*fence_value_str)(struct fence *fence, char *str, int size); }; /** @@ -85,7 +57,7 @@ struct sync_timeline_ops { * @destroyed: set when sync_timeline is destroyed * @child_list_head: list of children sync_pts for this sync_timeline * @child_list_lock: lock protecting @child_list_head, destroyed, and - * sync_pt.status + * fence.status * @active_list_head: list of active (unsignaled/errored) sync_pts * @sync_timeline_list: membership in global sync_timeline_list */ @@ -108,86 +80,44 @@ struct sync_timeline { #endif }; -/** - * struct sync_pt - sync point - * @fence: base fence class - * @child_list: membership in sync_timeline.child_list_head - * @active_list: membership in sync_timeline.active_list_head - * @signaled_list: membership in temporary signaled_list on stack - * @fence: sync_fence to which the sync_pt belongs - * @pt_list: membership in sync_fence.pt_list_head - * @status: 1: signaled, 0:active, <0: error - * @timestamp: time which sync_pt status transitioned from active to - * signaled or error. - */ -struct sync_pt { - struct fence base; - - struct list_head child_list; - struct list_head active_list; -}; - -static inline struct sync_timeline *sync_pt_parent(struct sync_pt *pt) +static inline struct sync_timeline *fence_parent(struct fence *fence) { - return container_of(pt->base.lock, struct sync_timeline, + return container_of(fence->lock, struct sync_timeline, child_list_lock); } -struct sync_fence_cb { +struct sync_file_cb { struct fence_cb cb; - struct fence *sync_pt; - struct sync_fence *fence; + struct fence *fence; + struct sync_file *sync_file; }; /** - * struct sync_fence - sync fence + * struct sync_file - sync file to export to the userspace * @file: file representing this fence * @kref: reference count on fence. - * @name: name of sync_fence. Useful for debugging - * @pt_list_head: list of sync_pts in the fence. immutable once fence - * is created - * @status: 0: signaled, >0:active, <0: error - * + * @name: name of sync_file. Useful for debugging + * @sync_file_list: membership in global file list + * @num_fences number of sync_pts in the fence * @wq: wait queue for fence signaling - * @sync_fence_list: membership in global fence list + * @status: 0: signaled, >0:active, <0: error + * @cbs: sync_pts callback information */ -struct sync_fence { +struct sync_file { struct file *file; struct kref kref; char name[32]; #ifdef CONFIG_DEBUG_FS - struct list_head sync_fence_list; + struct list_head sync_file_list; #endif int num_fences; wait_queue_head_t wq; atomic_t status; - struct sync_fence_cb cbs[]; + struct sync_file_cb cbs[]; }; -struct sync_fence_waiter; -typedef void (*sync_callback_t)(struct sync_fence *fence, - struct sync_fence_waiter *waiter); - -/** - * struct sync_fence_waiter - metadata for asynchronous waiter on a fence - * @waiter_list: membership in sync_fence.waiter_list_head - * @callback: function pointer to call when fence signals - * @callback_data: pointer to pass to @callback - */ -struct sync_fence_waiter { - wait_queue_t work; - sync_callback_t callback; -}; - -static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter, - sync_callback_t callback) -{ - INIT_LIST_HEAD(&waiter->work.task_list); - waiter->callback = callback; -} - /* * API for sync_timeline implementers */ @@ -200,7 +130,8 @@ static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter, * * Creates a new sync_timeline which will use the implementation specified by * @ops. @size bytes will be allocated allowing for implementation specific - * data to be kept after the generic sync_timeline struct. + * data to be kept after the generic sync_timeline struct. Returns the + * sync_timeline object or NULL in case of error. */ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, int size, const char *name); @@ -211,7 +142,7 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, * * A sync implementation should call this when the @obj is going away * (i.e. module unload.) @obj won't actually be freed until all its children - * sync_pts are freed. + * fences are freed. */ void sync_timeline_destroy(struct sync_timeline *obj); @@ -219,148 +150,92 @@ void sync_timeline_destroy(struct sync_timeline *obj); * sync_timeline_signal() - signal a status change on a sync_timeline * @obj: sync_timeline to signal * - * A sync implementation should call this any time one of it's sync_pts + * A sync implementation should call this any time one of it's fences * has signaled or has an error condition. */ void sync_timeline_signal(struct sync_timeline *obj); /** * sync_pt_create() - creates a sync pt - * @parent: sync_pt's parent sync_timeline + * @parent: fence's parent sync_timeline * @size: size to allocate for this pt * - * Creates a new sync_pt as a child of @parent. @size bytes will be + * Creates a new fence as a child of @parent. @size bytes will be * allocated allowing for implementation specific data to be kept after - * the generic sync_timeline struct. + * the generic sync_timeline struct. Returns the fence object or + * NULL in case of error. */ -struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size); - -/** - * sync_pt_free() - frees a sync pt - * @pt: sync_pt to free - * - * This should only be called on sync_pts which have been created but - * not added to a fence. - */ -void sync_pt_free(struct sync_pt *pt); +struct fence *sync_pt_create(struct sync_timeline *parent, int size); /** * sync_fence_create() - creates a sync fence * @name: name of fence to create - * @pt: sync_pt to add to the fence + * @fence: fence to add to the sync_fence * - * Creates a fence containg @pt. Once this is called, the fence takes - * ownership of @pt. + * Creates a sync_file containg @fence. Once this is called, the sync_file + * takes ownership of @fence. */ -struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt); - -/** - * sync_fence_create_dma() - creates a sync fence from dma-fence - * @name: name of fence to create - * @pt: dma-fence to add to the fence - * - * Creates a fence containg @pt. Once this is called, the fence takes - * ownership of @pt. - */ -struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt); +struct sync_file *sync_file_create(const char *name, struct fence *fence); /* - * API for sync_fence consumers + * API for sync_file consumers */ /** - * sync_fence_merge() - merge two fences + * sync_file_merge() - merge two sync_files * @name: name of new fence - * @a: fence a - * @b: fence b + * @a: sync_file a + * @b: sync_file b * - * Creates a new fence which contains copies of all the sync_pts in both - * @a and @b. @a and @b remain valid, independent fences. + * Creates a new sync_file which contains copies of all the fences in both + * @a and @b. @a and @b remain valid, independent sync_file. Returns the + * new merged sync_file or NULL in case of error. */ -struct sync_fence *sync_fence_merge(const char *name, - struct sync_fence *a, struct sync_fence *b); +struct sync_file *sync_file_merge(const char *name, + struct sync_file *a, struct sync_file *b); /** - * sync_fence_fdget() - get a fence from an fd + * sync_file_fdget() - get a sync_file from an fd * @fd: fd referencing a fence * - * Ensures @fd references a valid fence, increments the refcount of the backing - * file, and returns the fence. + * Ensures @fd references a valid sync_file, increments the refcount of the + * backing file. Returns the sync_file or NULL in case of error. */ -struct sync_fence *sync_fence_fdget(int fd); +struct sync_file *sync_file_fdget(int fd); /** - * sync_fence_put() - puts a reference of a sync fence - * @fence: fence to put + * sync_file_put() - puts a reference of a sync_file + * @sync_file: sync_file to put * - * Puts a reference on @fence. If this is the last reference, the fence and - * all it's sync_pts will be freed + * Puts a reference on @sync_fence. If this is the last reference, the + * sync_fil and all it's sync_pts will be freed */ -void sync_fence_put(struct sync_fence *fence); +void sync_file_put(struct sync_file *sync_file); /** - * sync_fence_install() - installs a fence into a file descriptor - * @fence: fence to install + * sync_file_install() - installs a sync_file into a file descriptor + * @sync_file: sync_file to install * @fd: file descriptor in which to install the fence * - * Installs @fence into @fd. @fd's should be acquired through + * Installs @sync_file into @fd. @fd's should be acquired through * get_unused_fd_flags(O_CLOEXEC). */ -void sync_fence_install(struct sync_fence *fence, int fd); - -/** - * sync_fence_wait_async() - registers and async wait on the fence - * @fence: fence to wait on - * @waiter: waiter callback struck - * - * Returns 1 if @fence has already signaled. - * - * Registers a callback to be called when @fence signals or has an error. - * @waiter should be initialized with sync_fence_waiter_init(). - */ -int sync_fence_wait_async(struct sync_fence *fence, - struct sync_fence_waiter *waiter); - -/** - * sync_fence_cancel_async() - cancels an async wait - * @fence: fence to wait on - * @waiter: waiter callback struck - * - * returns 0 if waiter was removed from fence's async waiter list. - * returns -ENOENT if waiter was not found on fence's async waiter list. - * - * Cancels a previously registered async wait. Will fail gracefully if - * @waiter was never registered or if @fence has already signaled @waiter. - */ -int sync_fence_cancel_async(struct sync_fence *fence, - struct sync_fence_waiter *waiter); - -/** - * sync_fence_wait() - wait on fence - * @fence: fence to wait on - * @tiemout: timeout in ms - * - * Wait for @fence to be signaled or have an error. Waits indefinitely - * if @timeout < 0 - */ -int sync_fence_wait(struct sync_fence *fence, long timeout); +void sync_file_install(struct sync_file *sync_file, int fd); #ifdef CONFIG_DEBUG_FS void sync_timeline_debug_add(struct sync_timeline *obj); void sync_timeline_debug_remove(struct sync_timeline *obj); -void sync_fence_debug_add(struct sync_fence *fence); -void sync_fence_debug_remove(struct sync_fence *fence); +void sync_file_debug_add(struct sync_file *fence); +void sync_file_debug_remove(struct sync_file *fence); void sync_dump(void); #else # define sync_timeline_debug_add(obj) # define sync_timeline_debug_remove(obj) -# define sync_fence_debug_add(fence) -# define sync_fence_debug_remove(fence) +# define sync_file_debug_add(fence) +# define sync_file_debug_remove(fence) # define sync_dump() #endif -int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode, - int wake_flags, void *key); #endif /* _LINUX_SYNC_H */ diff --git a/drivers/staging/android/sync_debug.c b/drivers/staging/android/sync_debug.c index f45d13cdd42b..5a7ec58fbc09 100644 --- a/drivers/staging/android/sync_debug.c +++ b/drivers/staging/android/sync_debug.c @@ -26,14 +26,16 @@ #include #include #include -#include "sync.h" +#include "sw_sync.h" #ifdef CONFIG_DEBUG_FS +static struct dentry *dbgfs; + static LIST_HEAD(sync_timeline_list_head); static DEFINE_SPINLOCK(sync_timeline_list_lock); -static LIST_HEAD(sync_fence_list_head); -static DEFINE_SPINLOCK(sync_fence_list_lock); +static LIST_HEAD(sync_file_list_head); +static DEFINE_SPINLOCK(sync_file_list_lock); void sync_timeline_debug_add(struct sync_timeline *obj) { @@ -53,22 +55,22 @@ void sync_timeline_debug_remove(struct sync_timeline *obj) spin_unlock_irqrestore(&sync_timeline_list_lock, flags); } -void sync_fence_debug_add(struct sync_fence *fence) +void sync_file_debug_add(struct sync_file *sync_file) { unsigned long flags; - spin_lock_irqsave(&sync_fence_list_lock, flags); - list_add_tail(&fence->sync_fence_list, &sync_fence_list_head); - spin_unlock_irqrestore(&sync_fence_list_lock, flags); + spin_lock_irqsave(&sync_file_list_lock, flags); + list_add_tail(&sync_file->sync_file_list, &sync_file_list_head); + spin_unlock_irqrestore(&sync_file_list_lock, flags); } -void sync_fence_debug_remove(struct sync_fence *fence) +void sync_file_debug_remove(struct sync_file *sync_file) { unsigned long flags; - spin_lock_irqsave(&sync_fence_list_lock, flags); - list_del(&fence->sync_fence_list); - spin_unlock_irqrestore(&sync_fence_list_lock, flags); + spin_lock_irqsave(&sync_file_list_lock, flags); + list_del(&sync_file->sync_file_list); + spin_unlock_irqrestore(&sync_file_list_lock, flags); } static const char *sync_status_str(int status) @@ -82,39 +84,40 @@ static const char *sync_status_str(int status) return "error"; } -static void sync_print_pt(struct seq_file *s, struct fence *pt, bool fence) +static void sync_print_fence(struct seq_file *s, struct fence *fence, bool show) { int status = 1; + struct sync_timeline *parent = fence_parent(fence); - if (fence_is_signaled_locked(pt)) - status = pt->status; + if (fence_is_signaled_locked(fence)) + status = fence->status; - seq_printf(s, " %s%spt %s", - fence && pt->ops->get_timeline_name ? - pt->ops->get_timeline_name(pt) : "", - fence ? "_" : "", + seq_printf(s, " %s%sfence %s", + show ? parent->name : "", + show ? "_" : "", sync_status_str(status)); if (status <= 0) { struct timespec64 ts64 = - ktime_to_timespec64(pt->timestamp); + ktime_to_timespec64(fence->timestamp); seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec); } - if ((!fence || pt->ops->timeline_value_str) && - pt->ops->fence_value_str) { + if ((!fence || fence->ops->timeline_value_str) && + fence->ops->fence_value_str) { char value[64]; bool success; - pt->ops->fence_value_str(pt, value, sizeof(value)); + fence->ops->fence_value_str(fence, value, sizeof(value)); success = strlen(value); if (success) seq_printf(s, ": %s", value); if (success && fence) { - pt->ops->timeline_value_str(pt, value, sizeof(value)); + fence->ops->timeline_value_str(fence, value, + sizeof(value)); if (strlen(value)) seq_printf(s, " / %s", value); @@ -142,38 +145,23 @@ static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) spin_lock_irqsave(&obj->child_list_lock, flags); list_for_each(pos, &obj->child_list_head) { - struct sync_pt *pt = - container_of(pos, struct sync_pt, child_list); - sync_print_pt(s, &pt->base, false); + struct fence *fence = + container_of(pos, struct fence, child_list); + sync_print_fence(s, fence, false); } spin_unlock_irqrestore(&obj->child_list_lock, flags); } -static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) +static void sync_print_sync_file(struct seq_file *s, + struct sync_file *sync_file) { - wait_queue_t *pos; - unsigned long flags; int i; - seq_printf(s, "[%p] %s: %s\n", fence, fence->name, - sync_status_str(atomic_read(&fence->status))); + seq_printf(s, "[%p] %s: %s\n", sync_file, sync_file->name, + sync_status_str(atomic_read(&sync_file->status))); - for (i = 0; i < fence->num_fences; ++i) { - sync_print_pt(s, fence->cbs[i].sync_pt, true); - } - - spin_lock_irqsave(&fence->wq.lock, flags); - list_for_each_entry(pos, &fence->wq.task_list, task_list) { - struct sync_fence_waiter *waiter; - - if (pos->func != &sync_fence_wake_up_wq) - continue; - - waiter = container_of(pos, struct sync_fence_waiter, work); - - seq_printf(s, "waiter %pF\n", waiter->callback); - } - spin_unlock_irqrestore(&fence->wq.lock, flags); + for (i = 0; i < sync_file->num_fences; ++i) + sync_print_fence(s, sync_file->cbs[i].fence, true); } static int sync_debugfs_show(struct seq_file *s, void *unused) @@ -196,33 +184,152 @@ static int sync_debugfs_show(struct seq_file *s, void *unused) seq_puts(s, "fences:\n--------------\n"); - spin_lock_irqsave(&sync_fence_list_lock, flags); - list_for_each(pos, &sync_fence_list_head) { - struct sync_fence *fence = - container_of(pos, struct sync_fence, sync_fence_list); + spin_lock_irqsave(&sync_file_list_lock, flags); + list_for_each(pos, &sync_file_list_head) { + struct sync_file *sync_file = + container_of(pos, struct sync_file, sync_file_list); - sync_print_fence(s, fence); + sync_print_sync_file(s, sync_file); seq_puts(s, "\n"); } - spin_unlock_irqrestore(&sync_fence_list_lock, flags); + spin_unlock_irqrestore(&sync_file_list_lock, flags); return 0; } -static int sync_debugfs_open(struct inode *inode, struct file *file) +static int sync_info_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, sync_debugfs_show, inode->i_private); } -static const struct file_operations sync_debugfs_fops = { - .open = sync_debugfs_open, +static const struct file_operations sync_info_debugfs_fops = { + .open = sync_info_debugfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; +/* + * *WARNING* + * + * improper use of this can result in deadlocking kernel drivers from userspace. + */ + +/* opening sw_sync create a new sync obj */ +static int sw_sync_debugfs_open(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj; + char task_comm[TASK_COMM_LEN]; + + get_task_comm(task_comm, current); + + obj = sw_sync_timeline_create(task_comm); + if (!obj) + return -ENOMEM; + + file->private_data = obj; + + return 0; +} + +static int sw_sync_debugfs_release(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj = file->private_data; + + sync_timeline_destroy(&obj->obj); + return 0; +} + +static long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, + unsigned long arg) +{ + int fd = get_unused_fd_flags(O_CLOEXEC); + int err; + struct fence *fence; + struct sync_file *sync_file; + struct sw_sync_create_fence_data data; + + if (fd < 0) + return fd; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { + err = -EFAULT; + goto err; + } + + fence = sw_sync_pt_create(obj, data.value); + if (!fence) { + err = -ENOMEM; + goto err; + } + + data.name[sizeof(data.name) - 1] = '\0'; + sync_file = sync_file_create(data.name, fence); + if (!sync_file) { + fence_put(fence); + err = -ENOMEM; + goto err; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + sync_file_put(sync_file); + err = -EFAULT; + goto err; + } + + sync_file_install(sync_file, fd); + + return 0; + +err: + put_unused_fd(fd); + return err; +} + +static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) +{ + u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + sw_sync_timeline_inc(obj, value); + + return 0; +} + +static long sw_sync_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sw_sync_timeline *obj = file->private_data; + + switch (cmd) { + case SW_SYNC_IOC_CREATE_FENCE: + return sw_sync_ioctl_create_fence(obj, arg); + + case SW_SYNC_IOC_INC: + return sw_sync_ioctl_inc(obj, arg); + + default: + return -ENOTTY; + } +} + +static const struct file_operations sw_sync_debugfs_fops = { + .open = sw_sync_debugfs_open, + .release = sw_sync_debugfs_release, + .unlocked_ioctl = sw_sync_ioctl, + .compat_ioctl = sw_sync_ioctl, +}; + static __init int sync_debugfs_init(void) { - debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops); + dbgfs = debugfs_create_dir("sync", NULL); + + debugfs_create_file("info", 0444, dbgfs, NULL, &sync_info_debugfs_fops); + debugfs_create_file("sw_sync", 0644, dbgfs, NULL, + &sw_sync_debugfs_fops); + return 0; } late_initcall(sync_debugfs_init); diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c index bcd9924d4631..914fd1005467 100644 --- a/drivers/staging/android/timed_gpio.c +++ b/drivers/staging/android/timed_gpio.c @@ -92,9 +92,8 @@ static int timed_gpio_probe(struct platform_device *pdev) if (!pdata) return -EBUSY; - gpio_data = devm_kzalloc(&pdev->dev, - sizeof(*gpio_data) * pdata->num_gpios, - GFP_KERNEL); + gpio_data = devm_kcalloc(&pdev->dev, pdata->num_gpios, + sizeof(*gpio_data), GFP_KERNEL); if (!gpio_data) return -ENOMEM; diff --git a/drivers/staging/android/trace/sync.h b/drivers/staging/android/trace/sync.h index 77edb977a7bf..a0f80f41677e 100644 --- a/drivers/staging/android/trace/sync.h +++ b/drivers/staging/android/trace/sync.h @@ -32,50 +32,6 @@ TRACE_EVENT(sync_timeline, TP_printk("name=%s value=%s", __get_str(name), __entry->value) ); -TRACE_EVENT(sync_wait, - TP_PROTO(struct sync_fence *fence, int begin), - - TP_ARGS(fence, begin), - - TP_STRUCT__entry( - __string(name, fence->name) - __field(s32, status) - __field(u32, begin) - ), - - TP_fast_assign( - __assign_str(name, fence->name); - __entry->status = atomic_read(&fence->status); - __entry->begin = begin; - ), - - TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end", - __get_str(name), __entry->status) -); - -TRACE_EVENT(sync_pt, - TP_PROTO(struct fence *pt), - - TP_ARGS(pt), - - TP_STRUCT__entry( - __string(timeline, pt->ops->get_timeline_name(pt)) - __array(char, value, 32) - ), - - TP_fast_assign( - __assign_str(timeline, pt->ops->get_timeline_name(pt)); - if (pt->ops->fence_value_str) { - pt->ops->fence_value_str(pt, __entry->value, - sizeof(__entry->value)); - } else { - __entry->value[0] = '\0'; - } - ), - - TP_printk("name=%s value=%s", __get_str(timeline), __entry->value) -); - #endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */ /* This part must be outside protection */ diff --git a/drivers/staging/android/uapi/ashmem.h b/drivers/staging/android/uapi/ashmem.h index ba4743c71d6b..13df42d200b7 100644 --- a/drivers/staging/android/uapi/ashmem.h +++ b/drivers/staging/android/uapi/ashmem.h @@ -13,6 +13,7 @@ #define _UAPI_LINUX_ASHMEM_H #include +#include #define ASHMEM_NAME_LEN 256 diff --git a/drivers/staging/android/uapi/sync.h b/drivers/staging/android/uapi/sync.h index e964c751f6b8..a0cf357e598d 100644 --- a/drivers/staging/android/uapi/sync.h +++ b/drivers/staging/android/uapi/sync.h @@ -27,50 +27,38 @@ struct sync_merge_data { }; /** - * struct sync_pt_info - detailed sync_pt information - * @len: length of sync_pt_info including any driver_data + * struct sync_fence_info - detailed fence information * @obj_name: name of parent sync_timeline * @driver_name: name of driver implementing the parent - * @status: status of the sync_pt 0:active 1:signaled <0:error + * @status: status of the fence 0:active 1:signaled <0:error * @timestamp_ns: timestamp of status change in nanoseconds - * @driver_data: any driver dependent data */ -struct sync_pt_info { - __u32 len; +struct sync_fence_info { char obj_name[32]; char driver_name[32]; __s32 status; __u64 timestamp_ns; - - __u8 driver_data[0]; }; /** - * struct sync_fence_info_data - data returned from fence info ioctl + * struct sync_file_info - data returned from fence info ioctl * @len: ioctl caller writes the size of the buffer its passing in. - * ioctl returns length of sync_fence_data returned to userspace - * including pt_info. + * ioctl returns length of sync_file_info returned to + * userspace including pt_info. * @name: name of fence * @status: status of fence. 1: signaled 0:active <0:error - * @pt_info: a sync_pt_info struct for every sync_pt in the fence + * @sync_fence_info: array of sync_fence_info for every fence in the sync_file */ -struct sync_fence_info_data { +struct sync_file_info { __u32 len; char name[32]; __s32 status; - __u8 pt_info[0]; + __u8 sync_fence_info[0]; }; #define SYNC_IOC_MAGIC '>' -/** - * DOC: SYNC_IOC_WAIT - wait for a fence to signal - * - * pass timeout in milliseconds. Waits indefinitely timeout < 0. - */ -#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32) - /** * DOC: SYNC_IOC_MERGE - merge two fences * @@ -83,15 +71,14 @@ struct sync_fence_info_data { /** * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence * - * Takes a struct sync_fence_info_data with extra space allocated for pt_info. + * Takes a struct sync_file_info_data with extra space allocated for pt_info. * Caller should write the size of the buffer into len. On return, len is - * updated to reflect the total size of the sync_fence_info_data including + * updated to reflect the total size of the sync_file_info_data including * pt_info. * * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence. * To iterate over the sync_pt_infos, use the sync_pt_info.len field. */ -#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\ - struct sync_fence_info_data) +#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_file_info) #endif /* _UAPI_LINUX_SYNC_H */ diff --git a/drivers/staging/board/armadillo800eva.c b/drivers/staging/board/armadillo800eva.c index 912c96b0536d..bb63ece4d766 100644 --- a/drivers/staging/board/armadillo800eva.c +++ b/drivers/staging/board/armadillo800eva.c @@ -27,7 +27,6 @@ #include "board.h" - static struct fb_videomode lcdc0_mode = { .name = "AMPIER/AM-800480", .xres = 800, diff --git a/drivers/staging/board/board.c b/drivers/staging/board/board.c index 965afc79aadd..45807d8287d1 100644 --- a/drivers/staging/board/board.c +++ b/drivers/staging/board/board.c @@ -155,7 +155,6 @@ static int board_staging_add_dev_domain(struct platform_device *pdev, if (IS_ERR(pd)) { pr_err("Cannot find genpd %s (%ld)\n", domain, PTR_ERR(pd)); return PTR_ERR(pd); - } pr_debug("Found genpd %s for device %s\n", pd->name, pdev->name); diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index b8e2f611fd47..7b8be5293883 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -32,8 +32,8 @@ #define WZRD_CLK_CFG_REG(n) (0x200 + 4 * (n)) -#define WZRD_CLkOUT0_FRAC_EN BIT(18) -#define WZRD_CLkFBOUT_FRAC_EN BIT(26) +#define WZRD_CLKOUT0_FRAC_EN BIT(18) +#define WZRD_CLKFBOUT_FRAC_EN BIT(26) #define WZRD_CLKFBOUT_MULT_SHIFT 8 #define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT) @@ -71,6 +71,7 @@ struct clk_wzrd { int speed_grade; bool suspended; }; + #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) /* maximum frequencies for input/output clocks per speed grade */ @@ -195,9 +196,9 @@ static int clk_wzrd_probe(struct platform_device *pdev) /* we don't support fractional div/mul yet */ reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) & - WZRD_CLkFBOUT_FRAC_EN; + WZRD_CLKFBOUT_FRAC_EN; reg |= readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2)) & - WZRD_CLkOUT0_FRAC_EN; + WZRD_CLKOUT0_FRAC_EN; if (reg) dev_warn(&pdev->dev, "fractional div/mul not supported\n"); diff --git a/drivers/staging/comedi/TODO b/drivers/staging/comedi/TODO index b68fbdb5eebf..f733c017f181 100644 --- a/drivers/staging/comedi/TODO +++ b/drivers/staging/comedi/TODO @@ -3,6 +3,7 @@ TODO: - Lindent - remove all wrappers - audit userspace interface + - Fix coverity 1195261 - cleanup the individual comedi drivers as well Please send patches to Greg Kroah-Hartman and diff --git a/drivers/staging/comedi/comedi.h b/drivers/staging/comedi/comedi.h index 83bd309d011b..ad5297f6d418 100644 --- a/drivers/staging/comedi/comedi.h +++ b/drivers/staging/comedi/comedi.h @@ -1,6 +1,6 @@ /* - * include/comedi.h (installed as /usr/include/comedi.h) - * header file for comedi + * comedi.h + * header file for COMEDI user API * * COMEDI - Linux Control and Measurement Device Interface * Copyright (C) 1998-2001 David A. Schleef @@ -72,12 +72,12 @@ #define CR_AREF(a) (((a) >> 24) & 0x03) #define CR_FLAGS_MASK 0xfc000000 -#define CR_ALT_FILTER (1 << 26) +#define CR_ALT_FILTER 0x04000000 #define CR_DITHER CR_ALT_FILTER #define CR_DEGLITCH CR_ALT_FILTER -#define CR_ALT_SOURCE (1 << 27) -#define CR_EDGE (1 << 30) -#define CR_INVERT (1 << 31) +#define CR_ALT_SOURCE 0x08000000 +#define CR_EDGE 0x40000000 +#define CR_INVERT 0x80000000 #define AREF_GROUND 0x00 /* analog ref = analog ground */ #define AREF_COMMON 0x01 /* analog ref = analog common */ @@ -120,13 +120,6 @@ #define INSN_WAIT (5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) #define INSN_INTTRIG (6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) -/* trigger flags */ -/* These flags are used in comedi_trig structures */ - -#define TRIG_DITHER 0x0002 /* enable dithering */ -#define TRIG_DEGLITCH 0x0004 /* enable deglitching */ -#define TRIG_CONFIG 0x0010 /* perform configuration, not triggering */ - /* command flags */ /* These flags are used in comedi_cmd structures */ @@ -190,11 +183,8 @@ #define SDF_MAXDATA 0x0010 /* maxdata depends on channel */ #define SDF_FLAGS 0x0020 /* flags depend on channel */ #define SDF_RANGETYPE 0x0040 /* range type depends on channel */ -#define SDF_MODE0 0x0080 /* can do mode 0 */ -#define SDF_MODE1 0x0100 /* can do mode 1 */ -#define SDF_MODE2 0x0200 /* can do mode 2 */ -#define SDF_MODE3 0x0400 /* can do mode 3 */ -#define SDF_MODE4 0x0800 /* can do mode 4 */ +#define SDF_PWM_COUNTER 0x0080 /* PWM can automatically switch off */ +#define SDF_PWM_HBRIDGE 0x0100 /* PWM is signed (H-bridge) */ #define SDF_CMD 0x1000 /* can do commands (deprecated) */ #define SDF_SOFT_CALIBRATED 0x2000 /* subdevice uses software calibration */ #define SDF_CMD_WRITE 0x4000 /* can do output commands */ @@ -217,30 +207,94 @@ #define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */ #define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */ #define SDF_PACKED 0x20000000 /* subdevice can do packed DIO */ -/* re recycle these flags for PWM */ -#define SDF_PWM_COUNTER SDF_MODE0 /* PWM can automatically switch off */ -#define SDF_PWM_HBRIDGE SDF_MODE1 /* PWM is signed (H-bridge) */ /* subdevice types */ +/** + * enum comedi_subdevice_type - COMEDI subdevice types + * @COMEDI_SUBD_UNUSED: Unused subdevice. + * @COMEDI_SUBD_AI: Analog input. + * @COMEDI_SUBD_AO: Analog output. + * @COMEDI_SUBD_DI: Digital input. + * @COMEDI_SUBD_DO: Digital output. + * @COMEDI_SUBD_DIO: Digital input/output. + * @COMEDI_SUBD_COUNTER: Counter. + * @COMEDI_SUBD_TIMER: Timer. + * @COMEDI_SUBD_MEMORY: Memory, EEPROM, DPRAM. + * @COMEDI_SUBD_CALIB: Calibration DACs. + * @COMEDI_SUBD_PROC: Processor, DSP. + * @COMEDI_SUBD_SERIAL: Serial I/O. + * @COMEDI_SUBD_PWM: Pulse-Width Modulation output. + */ enum comedi_subdevice_type { - COMEDI_SUBD_UNUSED, /* unused by driver */ - COMEDI_SUBD_AI, /* analog input */ - COMEDI_SUBD_AO, /* analog output */ - COMEDI_SUBD_DI, /* digital input */ - COMEDI_SUBD_DO, /* digital output */ - COMEDI_SUBD_DIO, /* digital input/output */ - COMEDI_SUBD_COUNTER, /* counter */ - COMEDI_SUBD_TIMER, /* timer */ - COMEDI_SUBD_MEMORY, /* memory, EEPROM, DPRAM */ - COMEDI_SUBD_CALIB, /* calibration DACs */ - COMEDI_SUBD_PROC, /* processor, DSP */ - COMEDI_SUBD_SERIAL, /* serial IO */ - COMEDI_SUBD_PWM /* PWM */ + COMEDI_SUBD_UNUSED, + COMEDI_SUBD_AI, + COMEDI_SUBD_AO, + COMEDI_SUBD_DI, + COMEDI_SUBD_DO, + COMEDI_SUBD_DIO, + COMEDI_SUBD_COUNTER, + COMEDI_SUBD_TIMER, + COMEDI_SUBD_MEMORY, + COMEDI_SUBD_CALIB, + COMEDI_SUBD_PROC, + COMEDI_SUBD_SERIAL, + COMEDI_SUBD_PWM }; /* configuration instructions */ +/** + * enum configuration_ids - COMEDI configuration instruction codes + * @INSN_CONFIG_DIO_INPUT: Configure digital I/O as input. + * @INSN_CONFIG_DIO_OUTPUT: Configure digital I/O as output. + * @INSN_CONFIG_DIO_OPENDRAIN: Configure digital I/O as open-drain (or open + * collector) output. + * @INSN_CONFIG_ANALOG_TRIG: Configure analog trigger. + * @INSN_CONFIG_ALT_SOURCE: Configure alternate input source. + * @INSN_CONFIG_DIGITAL_TRIG: Configure digital trigger. + * @INSN_CONFIG_BLOCK_SIZE: Configure block size for DMA transfers. + * @INSN_CONFIG_TIMER_1: Configure divisor for external clock. + * @INSN_CONFIG_FILTER: Configure a filter. + * @INSN_CONFIG_CHANGE_NOTIFY: Configure change notification for digital + * inputs. (New drivers should use + * %INSN_CONFIG_DIGITAL_TRIG instead.) + * @INSN_CONFIG_SERIAL_CLOCK: Configure clock for serial I/O. + * @INSN_CONFIG_BIDIRECTIONAL_DATA: Send and receive byte over serial I/O. + * @INSN_CONFIG_DIO_QUERY: Query direction of digital I/O channel. + * @INSN_CONFIG_PWM_OUTPUT: Configure pulse-width modulator output. + * @INSN_CONFIG_GET_PWM_OUTPUT: Get pulse-width modulator output configuration. + * @INSN_CONFIG_ARM: Arm a subdevice or channel. + * @INSN_CONFIG_DISARM: Disarm a subdevice or channel. + * @INSN_CONFIG_GET_COUNTER_STATUS: Get counter status. + * @INSN_CONFIG_RESET: Reset a subdevice or channel. + * @INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: Configure counter/timer as + * single pulse generator. + * @INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: Configure counter/timer as + * pulse train generator. + * @INSN_CONFIG_GPCT_QUADRATURE_ENCODER: Configure counter as a quadrature + * encoder. + * @INSN_CONFIG_SET_GATE_SRC: Set counter/timer gate source. + * @INSN_CONFIG_GET_GATE_SRC: Get counter/timer gate source. + * @INSN_CONFIG_SET_CLOCK_SRC: Set counter/timer master clock source. + * @INSN_CONFIG_GET_CLOCK_SRC: Get counter/timer master clock source. + * @INSN_CONFIG_SET_OTHER_SRC: Set counter/timer "other" source. + * @INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE: Get size (in bytes) of subdevice's + * on-board FIFOs used during streaming + * input/output. + * @INSN_CONFIG_SET_COUNTER_MODE: Set counter/timer mode. + * @INSN_CONFIG_8254_SET_MODE: (Deprecated) Same as + * %INSN_CONFIG_SET_COUNTER_MODE. + * @INSN_CONFIG_8254_READ_STATUS: Read status of 8254 counter channel. + * @INSN_CONFIG_SET_ROUTING: Set routing for a channel. + * @INSN_CONFIG_GET_ROUTING: Get routing for a channel. + * @INSN_CONFIG_PWM_SET_PERIOD: Set PWM period in nanoseconds. + * @INSN_CONFIG_PWM_GET_PERIOD: Get PWM period in nanoseconds. + * @INSN_CONFIG_GET_PWM_STATUS: Get PWM status. + * @INSN_CONFIG_PWM_SET_H_BRIDGE: Set PWM H bridge duty cycle and polarity for + * a relay simultaneously. + * @INSN_CONFIG_PWM_GET_H_BRIDGE: Get PWM H bridge duty cycle and polarity. + */ enum configuration_ids { INSN_CONFIG_DIO_INPUT = 0, INSN_CONFIG_DIO_OUTPUT = 1, @@ -265,72 +319,76 @@ enum configuration_ids { INSN_CONFIG_DISARM = 32, INSN_CONFIG_GET_COUNTER_STATUS = 33, INSN_CONFIG_RESET = 34, - /* Use CTR as single pulsegenerator */ INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, - /* Use CTR as pulsetraingenerator */ INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, - /* Use the counter as encoder */ INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, - INSN_CONFIG_SET_GATE_SRC = 2001, /* Set gate source */ - INSN_CONFIG_GET_GATE_SRC = 2002, /* Get gate source */ - /* Set master clock source */ + INSN_CONFIG_SET_GATE_SRC = 2001, + INSN_CONFIG_GET_GATE_SRC = 2002, INSN_CONFIG_SET_CLOCK_SRC = 2003, - INSN_CONFIG_GET_CLOCK_SRC = 2004, /* Get master clock source */ - INSN_CONFIG_SET_OTHER_SRC = 2005, /* Set other source */ - /* INSN_CONFIG_GET_OTHER_SRC = 2006,*//* Get other source */ - /* Get size in bytes of subdevice's on-board fifos used during - * streaming input/output - */ + INSN_CONFIG_GET_CLOCK_SRC = 2004, + INSN_CONFIG_SET_OTHER_SRC = 2005, INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006, INSN_CONFIG_SET_COUNTER_MODE = 4097, - /* INSN_CONFIG_8254_SET_MODE is deprecated */ INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE, INSN_CONFIG_8254_READ_STATUS = 4098, INSN_CONFIG_SET_ROUTING = 4099, INSN_CONFIG_GET_ROUTING = 4109, - /* PWM */ - INSN_CONFIG_PWM_SET_PERIOD = 5000, /* sets frequency */ - INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */ - INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */ - /* sets H bridge: duty cycle and sign bit for a relay at the - * same time - */ + INSN_CONFIG_PWM_SET_PERIOD = 5000, + INSN_CONFIG_PWM_GET_PERIOD = 5001, + INSN_CONFIG_GET_PWM_STATUS = 5002, INSN_CONFIG_PWM_SET_H_BRIDGE = 5003, - /* gets H bridge data: duty cycle and the sign bit */ INSN_CONFIG_PWM_GET_H_BRIDGE = 5004 }; -/* - * Settings for INSN_CONFIG_DIGITAL_TRIG: - * data[0] = INSN_CONFIG_DIGITAL_TRIG - * data[1] = trigger ID - * data[2] = configuration operation - * data[3] = configuration parameter 1 - * data[4] = configuration parameter 2 - * data[5] = configuration parameter 3 +/** + * enum comedi_digital_trig_op - operations for configuring a digital trigger + * @COMEDI_DIGITAL_TRIG_DISABLE: Return digital trigger to its default, + * inactive, unconfigured state. + * @COMEDI_DIGITAL_TRIG_ENABLE_EDGES: Set rising and/or falling edge inputs + * that each can fire the trigger. + * @COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: Set a combination of high and/or low + * level inputs that can fire the trigger. * - * operation parameter 1 parameter 2 parameter 3 - * --------------------------------- ----------- ----------- ----------- - * COMEDI_DIGITAL_TRIG_DISABLE - * COMEDI_DIGITAL_TRIG_ENABLE_EDGES left-shift rising-edges falling-edges - * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS left-shift high-levels low-levels + * These are used with the %INSN_CONFIG_DIGITAL_TRIG configuration instruction. + * The data for the configuration instruction is as follows... * - * COMEDI_DIGITAL_TRIG_DISABLE returns the trigger to its default, inactive, - * unconfigured state. + * data[%0] = %INSN_CONFIG_DIGITAL_TRIG * - * COMEDI_DIGITAL_TRIG_ENABLE_EDGES sets the rising and/or falling edge inputs - * that each can fire the trigger. + * data[%1] = trigger ID * - * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS sets a combination of high and/or low - * level inputs that can fire the trigger. + * data[%2] = configuration operation * - * "left-shift" is useful if the trigger has more than 32 inputs to specify the - * first input for this configuration. + * data[%3] = configuration parameter 1 * - * Some sequences of INSN_CONFIG_DIGITAL_TRIG instructions may have a (partly) + * data[%4] = configuration parameter 2 + * + * data[%5] = configuration parameter 3 + * + * The trigger ID (data[%1]) is used to differentiate multiple digital triggers + * belonging to the same subdevice. The configuration operation (data[%2]) is + * one of the enum comedi_digital_trig_op values. The configuration + * parameters (data[%3], data[%4], and data[%5]) depend on the operation; they + * are not used with %COMEDI_DIGITAL_TRIG_DISABLE. + * + * For %COMEDI_DIGITAL_TRIG_ENABLE_EDGES and %COMEDI_DIGITAL_TRIG_ENABLE_LEVELS, + * configuration parameter 1 (data[%3]) contains a "left-shift" value that + * specifies the input corresponding to bit 0 of configuration parameters 2 + * and 3. This is useful if the trigger has more than 32 inputs. + * + * For %COMEDI_DIGITAL_TRIG_ENABLE_EDGES, configuration parameter 2 (data[%4]) + * specifies which of up to 32 inputs have rising-edge sensitivity, and + * configuration parameter 3 (data[%5]) specifies which of up to 32 inputs + * have falling-edge sensitivity that can fire the trigger. + * + * For %COMEDI_DIGITAL_TRIG_ENABLE_LEVELS, configuration parameter 2 (data[%4]) + * specifies which of up to 32 inputs must be at a high level, and + * configuration parameter 3 (data[%5]) specifies which of up to 32 inputs + * must be at a low level for the trigger to fire. + * + * Some sequences of %INSN_CONFIG_DIGITAL_TRIG instructions may have a (partly) * accumulative effect, depending on the low-level driver. This is useful - * when setting up a trigger that has more than 32 inputs or has a combination - * of edge and level triggered inputs. + * when setting up a trigger that has more than 32 inputs, or has a combination + * of edge- and level-triggered inputs. */ enum comedi_digital_trig_op { COMEDI_DIGITAL_TRIG_DISABLE = 0, @@ -338,18 +396,49 @@ enum comedi_digital_trig_op { COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = 2 }; +/** + * enum comedi_io_direction - COMEDI I/O directions + * @COMEDI_INPUT: Input. + * @COMEDI_OUTPUT: Output. + * @COMEDI_OPENDRAIN: Open-drain (or open-collector) output. + * + * These are used by the %INSN_CONFIG_DIO_QUERY configuration instruction to + * report a direction. They may also be used in other places where a direction + * needs to be specified. + */ enum comedi_io_direction { COMEDI_INPUT = 0, COMEDI_OUTPUT = 1, COMEDI_OPENDRAIN = 2 }; +/** + * enum comedi_support_level - support level for a COMEDI feature + * @COMEDI_UNKNOWN_SUPPORT: Unspecified support for feature. + * @COMEDI_SUPPORTED: Feature is supported. + * @COMEDI_UNSUPPORTED: Feature is unsupported. + */ enum comedi_support_level { COMEDI_UNKNOWN_SUPPORT = 0, COMEDI_SUPPORTED, COMEDI_UNSUPPORTED }; +/** + * enum comedi_counter_status_flags - counter status bits + * @COMEDI_COUNTER_ARMED: Counter is armed. + * @COMEDI_COUNTER_COUNTING: Counter is counting. + * @COMEDI_COUNTER_TERMINAL_COUNT: Counter reached terminal count. + * + * These bitwise values are used by the %INSN_CONFIG_GET_COUNTER_STATUS + * configuration instruction to report the status of a counter. + */ +enum comedi_counter_status_flags { + COMEDI_COUNTER_ARMED = 0x1, + COMEDI_COUNTER_COUNTING = 0x2, + COMEDI_COUNTER_TERMINAL_COUNT = 0x4, +}; + /* ioctls */ #define CIO 'd' @@ -357,7 +446,7 @@ enum comedi_support_level { #define COMEDI_DEVINFO _IOR(CIO, 1, struct comedi_devinfo) #define COMEDI_SUBDINFO _IOR(CIO, 2, struct comedi_subdinfo) #define COMEDI_CHANINFO _IOR(CIO, 3, struct comedi_chaninfo) -#define COMEDI_TRIG _IOWR(CIO, 4, comedi_trig) +/* _IOWR(CIO, 4, ...) is reserved */ #define COMEDI_LOCK _IO(CIO, 5) #define COMEDI_UNLOCK _IO(CIO, 6) #define COMEDI_CANCEL _IO(CIO, 7) @@ -374,21 +463,19 @@ enum comedi_support_level { /* structures */ -struct comedi_trig { - unsigned int subdev; /* subdevice */ - unsigned int mode; /* mode */ - unsigned int flags; - unsigned int n_chan; /* number of channels */ - unsigned int *chanlist; /* channel/range list */ - short *data; /* data list, size depends on subd flags */ - unsigned int n; /* number of scans */ - unsigned int trigsrc; - unsigned int trigvar; - unsigned int trigvar1; - unsigned int data_len; - unsigned int unused[3]; -}; - +/** + * struct comedi_insn - COMEDI instruction + * @insn: COMEDI instruction type (%INSN_xxx). + * @n: Length of @data[]. + * @data: Pointer to data array operated on by the instruction. + * @subdev: Subdevice index. + * @chanspec: A packed "chanspec" value consisting of channel number, + * analog range index, analog reference type, and flags. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_INSN ioctl, and indirectly with the + * %COMEDI_INSNLIST ioctl. + */ struct comedi_insn { unsigned int insn; unsigned int n; @@ -398,11 +485,95 @@ struct comedi_insn { unsigned int unused[3]; }; +/** + * struct comedi_insnlist - list of COMEDI instructions + * @n_insns: Number of COMEDI instructions. + * @insns: Pointer to array COMEDI instructions. + * + * This is used with the %COMEDI_INSNLIST ioctl. + */ struct comedi_insnlist { unsigned int n_insns; struct comedi_insn __user *insns; }; +/** + * struct comedi_cmd - COMEDI asynchronous acquisition command details + * @subdev: Subdevice index. + * @flags: Command flags (%CMDF_xxx). + * @start_src: "Start acquisition" trigger source (%TRIG_xxx). + * @start_arg: "Start acquisition" trigger argument. + * @scan_begin_src: "Scan begin" trigger source. + * @scan_begin_arg: "Scan begin" trigger argument. + * @convert_src: "Convert" trigger source. + * @convert_arg: "Convert" trigger argument. + * @scan_end_src: "Scan end" trigger source. + * @scan_end_arg: "Scan end" trigger argument. + * @stop_src: "Stop acquisition" trigger source. + * @stop_arg: "Stop acquisition" trigger argument. + * @chanlist: Pointer to array of "chanspec" values, containing a + * sequence of channel numbers packed with analog range + * index, etc. + * @chanlist_len: Number of channels in sequence. + * @data: Pointer to miscellaneous set-up data (not used). + * @data_len: Length of miscellaneous set-up data. + * + * This is used with the %COMEDI_CMD or %COMEDI_CMDTEST ioctl to set-up + * or validate an asynchronous acquisition command. The ioctl may modify + * the &struct comedi_cmd and copy it back to the caller. + * + * Optional command @flags values that can be ORed together... + * + * %CMDF_BOGUS - makes %COMEDI_CMD ioctl return error %EAGAIN instead of + * starting the command. + * + * %CMDF_PRIORITY - requests "hard real-time" processing (which is not + * supported in this version of COMEDI). + * + * %CMDF_WAKE_EOS - requests the command makes data available for reading + * after every "scan" period. + * + * %CMDF_WRITE - marks the command as being in the "write" (to device) + * direction. This does not need to be specified by the caller unless the + * subdevice supports commands in either direction. + * + * %CMDF_RAWDATA - prevents the command from "munging" the data between the + * COMEDI sample format and the raw hardware sample format. + * + * %CMDF_ROUND_NEAREST - requests timing periods to be rounded to nearest + * supported values. + * + * %CMDF_ROUND_DOWN - requests timing periods to be rounded down to supported + * values (frequencies rounded up). + * + * %CMDF_ROUND_UP - requests timing periods to be rounded up to supported + * values (frequencies rounded down). + * + * Trigger source values for @start_src, @scan_begin_src, @convert_src, + * @scan_end_src, and @stop_src... + * + * %TRIG_ANY - "all ones" value used to test which trigger sources are + * supported. + * + * %TRIG_INVALID - "all zeroes" value used to indicate that all requested + * trigger sources are invalid. + * + * %TRIG_NONE - never trigger (often used as a @stop_src value). + * + * %TRIG_NOW - trigger after '_arg' nanoseconds. + * + * %TRIG_FOLLOW - trigger follows another event. + * + * %TRIG_TIMER - trigger every '_arg' nanoseconds. + * + * %TRIG_COUNT - trigger when count '_arg' is reached. + * + * %TRIG_EXT - trigger on external signal specified by '_arg'. + * + * %TRIG_INT - trigger on internal, software trigger specified by '_arg'. + * + * %TRIG_OTHER - trigger on other, driver-defined signal specified by '_arg'. + */ struct comedi_cmd { unsigned int subdev; unsigned int flags; @@ -422,13 +593,31 @@ struct comedi_cmd { unsigned int stop_src; unsigned int stop_arg; - unsigned int *chanlist; /* channel/range list */ + unsigned int *chanlist; unsigned int chanlist_len; - short __user *data; /* data list, size depends on subd flags */ + short __user *data; unsigned int data_len; }; +/** + * struct comedi_chaninfo - used to retrieve per-channel information + * @subdev: Subdevice index. + * @maxdata_list: Optional pointer to per-channel maximum data values. + * @flaglist: Optional pointer to per-channel flags. + * @rangelist: Optional pointer to per-channel range types. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_CHANINFO ioctl to get per-channel information + * for the subdevice. Use of this requires knowledge of the number of channels + * and subdevice flags obtained using the %COMEDI_SUBDINFO ioctl. + * + * The @maxdata_list member must be %NULL unless the %SDF_MAXDATA subdevice + * flag is set. The @flaglist member must be %NULL unless the %SDF_FLAGS + * subdevice flag is set. The @rangelist member must be %NULL unless the + * %SDF_RANGETYPE subdevice flag is set. Otherwise, the arrays they point to + * must be at least as long as the number of channels. + */ struct comedi_chaninfo { unsigned int subdev; unsigned int __user *maxdata_list; @@ -437,17 +626,149 @@ struct comedi_chaninfo { unsigned int unused[4]; }; +/** + * struct comedi_rangeinfo - used to retrieve the range table for a channel + * @range_type: Encodes subdevice index (bits 27:24), channel index + * (bits 23:16) and range table length (bits 15:0). + * @range_ptr: Pointer to array of @struct comedi_krange to be filled + * in with the range table for the channel or subdevice. + * + * This is used with the %COMEDI_RANGEINFO ioctl to retrieve the range table + * for a specific channel (if the subdevice has the %SDF_RANGETYPE flag set to + * indicate that the range table depends on the channel), or for the subdevice + * as a whole (if the %SDF_RANGETYPE flag is clear, indicating the range table + * is shared by all channels). + * + * The @range_type value is an input to the ioctl and comes from a previous + * use of the %COMEDI_SUBDINFO ioctl (if the %SDF_RANGETYPE flag is clear), + * or the %COMEDI_CHANINFO ioctl (if the %SDF_RANGETYPE flag is set). + */ struct comedi_rangeinfo { unsigned int range_type; void __user *range_ptr; }; +/** + * struct comedi_krange - describes a range in a range table + * @min: Minimum value in millionths (1e-6) of a unit. + * @max: Maximum value in millionths (1e-6) of a unit. + * @flags: Indicates the units (in bits 7:0) OR'ed with optional flags. + * + * A range table is associated with a single channel, or with all channels in a + * subdevice, and a list of one or more ranges. A %struct comedi_krange + * describes the physical range of units for one of those ranges. Sample + * values in COMEDI are unsigned from %0 up to some 'maxdata' value. The + * mapping from sample values to physical units is assumed to be nomimally + * linear (for the purpose of describing the range), with sample value %0 + * mapping to @min, and the 'maxdata' sample value mapping to @max. + * + * The currently defined units are %UNIT_volt (%0), %UNIT_mA (%1), and + * %UNIT_none (%2). The @min and @max values are the physical range multiplied + * by 1e6, so a @max value of %1000000 (with %UNIT_volt) represents a maximal + * value of 1 volt. + * + * The only defined flag value is %RF_EXTERNAL (%0x100), indicating that the + * the range needs to be multiplied by an external reference. + */ struct comedi_krange { - int min; /* fixed point, multiply by 1e-6 */ - int max; /* fixed point, multiply by 1e-6 */ + int min; + int max; unsigned int flags; }; +/** + * struct comedi_subdinfo - used to retrieve information about a subdevice + * @type: Type of subdevice from &enum comedi_subdevice_type. + * @n_chan: Number of channels the subdevice supports. + * @subd_flags: A mixture of static and dynamic flags describing + * aspects of the subdevice and its current state. + * @timer_type: Timer type. Always set to %5 ("nanosecond timer"). + * @len_chanlist: Maximum length of a channel list if the subdevice + * supports asynchronous acquisition commands. + * @maxdata: Maximum sample value for all channels if the + * %SDF_MAXDATA subdevice flag is clear. + * @flags: Channel flags for all channels if the %SDF_FLAGS + * subdevice flag is clear. + * @range_type: The range type for all channels if the %SDF_RANGETYPE + * subdevice flag is clear. Encodes the subdevice index + * (bits 27:24), a dummy channel index %0 (bits 23:16), + * and the range table length (bits 15:0). + * @settling_time_0: Not used. + * @insn_bits_support: Set to %COMEDI_SUPPORTED if the subdevice supports the + * %INSN_BITS instruction, or to %COMEDI_UNSUPPORTED if it + * does not. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_SUBDINFO ioctl which copies an array of + * &struct comedi_subdinfo back to user space, with one element per subdevice. + * Use of this requires knowledge of the number of subdevices obtained from + * the %COMEDI_DEVINFO ioctl. + * + * These are the @subd_flags values that may be ORed together... + * + * %SDF_BUSY - the subdevice is busy processing an asynchronous command or a + * synchronous instruction. + * + * %SDF_BUSY_OWNER - the subdevice is busy processing an asynchronous + * acquisition command started on the current file object (the file object + * issuing the %COMEDI_SUBDINFO ioctl). + * + * %SDF_LOCKED - the subdevice is locked by a %COMEDI_LOCK ioctl. + * + * %SDF_LOCK_OWNER - the subdevice is locked by a %COMEDI_LOCK ioctl from the + * current file object. + * + * %SDF_MAXDATA - maximum sample values are channel-specific. + * + * %SDF_FLAGS - channel flags are channel-specific. + * + * %SDF_RANGETYPE - range types are channel-specific. + * + * %SDF_PWM_COUNTER - PWM can switch off automatically. + * + * %SDF_PWM_HBRIDGE - or PWM is signed (H-bridge). + * + * %SDF_CMD - the subdevice supports asynchronous commands. + * + * %SDF_SOFT_CALIBRATED - the subdevice uses software calibration. + * + * %SDF_CMD_WRITE - the subdevice supports asynchronous commands in the output + * ("write") direction. + * + * %SDF_CMD_READ - the subdevice supports asynchronous commands in the input + * ("read") direction. + * + * %SDF_READABLE - the subdevice is readable (e.g. analog input). + * + * %SDF_WRITABLE (aliased as %SDF_WRITEABLE) - the subdevice is writable (e.g. + * analog output). + * + * %SDF_INTERNAL - the subdevice has no externally visible lines. + * + * %SDF_GROUND - the subdevice can use ground as an analog reference. + * + * %SDF_COMMON - the subdevice can use a common analog reference. + * + * %SDF_DIFF - the subdevice can use differential inputs (or outputs). + * + * %SDF_OTHER - the subdevice can use some other analog reference. + * + * %SDF_DITHER - the subdevice can do dithering. + * + * %SDF_DEGLITCH - the subdevice can do deglitching. + * + * %SDF_MMAP - this is never set. + * + * %SDF_RUNNING - an asynchronous command is still running. + * + * %SDF_LSAMPL - the subdevice uses "long" (32-bit) samples (for asynchronous + * command data). + * + * %SDF_PACKED - the subdevice packs several DIO samples into a single sample + * (for asynchronous command data). + * + * No "channel flags" (@flags) values are currently defined. + */ struct comedi_subdinfo { unsigned int type; unsigned int n_chan; @@ -455,14 +776,26 @@ struct comedi_subdinfo { unsigned int timer_type; unsigned int len_chanlist; unsigned int maxdata; - unsigned int flags; /* channel flags */ - unsigned int range_type; /* lookup in kernel */ + unsigned int flags; + unsigned int range_type; unsigned int settling_time_0; - /* see support_level enum for values */ unsigned insn_bits_support; unsigned int unused[8]; }; +/** + * struct comedi_devinfo - used to retrieve information about a COMEDI device + * @version_code: COMEDI version code. + * @n_subdevs: Number of subdevices the device has. + * @driver_name: Null-terminated COMEDI driver name. + * @board_name: Null-terminated COMEDI board name. + * @read_subdevice: Index of the current "read" subdevice (%-1 if none). + * @write_subdevice: Index of the current "write" subdevice (%-1 if none). + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_DEVINFO ioctl to get basic information about + * the device. + */ struct comedi_devinfo { unsigned int version_code; unsigned int n_subdevs; @@ -473,11 +806,45 @@ struct comedi_devinfo { int unused[30]; }; +/** + * struct comedi_devconfig - used to configure a legacy COMEDI device + * @board_name: Null-terminated string specifying the type of board + * to configure. + * @options: An array of integer configuration options. + * + * This is used with the %COMEDI_DEVCONFIG ioctl to configure a "legacy" COMEDI + * device, such as an ISA card. Not all COMEDI drivers support this. Those + * that do either expect the specified board name to match one of a list of + * names registered with the COMEDI core, or expect the specified board name + * to match the COMEDI driver name itself. The configuration options are + * handled in a driver-specific manner. + */ struct comedi_devconfig { char board_name[COMEDI_NAMELEN]; int options[COMEDI_NDEVCONFOPTS]; }; +/** + * struct comedi_bufconfig - used to set or get buffer size for a subdevice + * @subdevice: Subdevice index. + * @flags: Not used. + * @maximum_size: Maximum allowed buffer size. + * @size: Buffer size. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_BUFCONFIG ioctl to get or configure the + * maximum buffer size and current buffer size for a COMEDI subdevice that + * supports asynchronous commands. If the subdevice does not support + * asynchronous commands, @maximum_size and @size are ignored and set to 0. + * + * On ioctl input, non-zero values of @maximum_size and @size specify a + * new maximum size and new current size (in bytes), respectively. These + * will by rounded up to a multiple of %PAGE_SIZE. Specifying a new maximum + * size requires admin capabilities. + * + * On ioctl output, @maximum_size and @size and set to the current maximum + * buffer size and current buffer size, respectively. + */ struct comedi_bufconfig { unsigned int subdevice; unsigned int flags; @@ -488,6 +855,23 @@ struct comedi_bufconfig { unsigned int unused[4]; }; +/** + * struct comedi_bufinfo - used to manipulate buffer position for a subdevice + * @subdevice: Subdevice index. + * @bytes_read: Specify amount to advance read position for an + * asynchronous command in the input ("read") direction. + * @buf_write_ptr: Current write position (index) within the buffer. + * @buf_read_ptr: Current read position (index) within the buffer. + * @buf_write_count: Total amount written, modulo 2^32. + * @buf_read_count: Total amount read, modulo 2^32. + * @bytes_written: Specify amount to advance write position for an + * asynchronous command in the output ("write") direction. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_BUFINFO ioctl to optionally advance the + * current read or write position in an asynchronous acquisition data buffer, + * and to get the current read and write positions in the buffer. + */ struct comedi_bufinfo { unsigned int subdevice; unsigned int bytes_read; @@ -510,13 +894,13 @@ struct comedi_bufinfo { #define RANGE_LENGTH(b) ((b) & 0xffff) #define RF_UNIT(flags) ((flags) & 0xff) -#define RF_EXTERNAL (1 << 8) +#define RF_EXTERNAL 0x100 #define UNIT_volt 0 #define UNIT_mA 1 #define UNIT_none 2 -#define COMEDI_MIN_SPEED ((unsigned int)0xffffffff) +#define COMEDI_MIN_SPEED 0xffffffffu /**********************************************************/ /* everything after this line is ALPHA */ @@ -849,13 +1233,6 @@ enum ni_660x_pfi_routing { #define NI_EXT_PFI(x) (NI_USUAL_PFI_SELECT(x) - 1) #define NI_EXT_RTSI(x) (NI_USUAL_RTSI_SELECT(x) - 1) -/* status bits for INSN_CONFIG_GET_COUNTER_STATUS */ -enum comedi_counter_status_flags { - COMEDI_COUNTER_ARMED = 0x1, - COMEDI_COUNTER_COUNTING = 0x2, - COMEDI_COUNTER_TERMINAL_COUNT = 0x4, -}; - /* * Clock sources for CDIO subdevice on NI m-series boards. Used as the * scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index d57fadef47fc..7c7b477b0f28 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -686,13 +686,6 @@ static bool __comedi_is_subdevice_running(struct comedi_subdevice *s) 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); -} - bool comedi_can_auto_free_spriv(struct comedi_subdevice *s) { unsigned runflags = __comedi_get_subdevice_runflags(s); @@ -1111,6 +1104,9 @@ static int do_bufinfo_ioctl(struct comedi_device *dev, struct comedi_bufinfo bi; struct comedi_subdevice *s; struct comedi_async *async; + unsigned int runflags; + int retval = 0; + bool become_nonbusy = false; if (copy_from_user(&bi, arg, sizeof(bi))) return -EFAULT; @@ -1122,48 +1118,56 @@ static int do_bufinfo_ioctl(struct comedi_device *dev, async = s->async; - if (!async) { - dev_dbg(dev->class_dev, - "subdevice does not have async capability\n"); - bi.buf_write_ptr = 0; - bi.buf_read_ptr = 0; - bi.buf_write_count = 0; - bi.buf_read_count = 0; - bi.bytes_read = 0; - bi.bytes_written = 0; - goto copyback; - } - if (!s->busy) { - bi.bytes_read = 0; - bi.bytes_written = 0; - goto copyback_position; - } - if (s->busy != file) - return -EACCES; + if (!async || s->busy != file) + return -EINVAL; - if (bi.bytes_read && !(async->cmd.flags & CMDF_WRITE)) { - bi.bytes_read = comedi_buf_read_alloc(s, bi.bytes_read); - comedi_buf_read_free(s, bi.bytes_read); - - if (comedi_is_subdevice_idle(s) && - comedi_buf_read_n_available(s) == 0) { - do_become_nonbusy(dev, s); + runflags = comedi_get_subdevice_runflags(s); + if (!(async->cmd.flags & CMDF_WRITE)) { + /* command was set up in "read" direction */ + if (bi.bytes_read) { + comedi_buf_read_alloc(s, bi.bytes_read); + bi.bytes_read = comedi_buf_read_free(s, bi.bytes_read); } + /* + * If nothing left to read, and command has stopped, and + * {"read" position not updated or command stopped normally}, + * then become non-busy. + */ + if (comedi_buf_read_n_available(s) == 0 && + !comedi_is_runflags_running(runflags) && + (bi.bytes_read == 0 || + !comedi_is_runflags_in_error(runflags))) { + become_nonbusy = true; + if (comedi_is_runflags_in_error(runflags)) + retval = -EPIPE; + } + bi.bytes_written = 0; + } else { + /* command was set up in "write" direction */ + if (!comedi_is_runflags_running(runflags)) { + bi.bytes_written = 0; + become_nonbusy = true; + if (comedi_is_runflags_in_error(runflags)) + retval = -EPIPE; + } else if (bi.bytes_written) { + comedi_buf_write_alloc(s, bi.bytes_written); + bi.bytes_written = + comedi_buf_write_free(s, bi.bytes_written); + } + bi.bytes_read = 0; } - if (bi.bytes_written && (async->cmd.flags & CMDF_WRITE)) { - bi.bytes_written = - comedi_buf_write_alloc(s, bi.bytes_written); - comedi_buf_write_free(s, bi.bytes_written); - } - -copyback_position: bi.buf_write_count = async->buf_write_count; bi.buf_write_ptr = async->buf_write_ptr; bi.buf_read_count = async->buf_read_count; bi.buf_read_ptr = async->buf_read_ptr; -copyback: + if (become_nonbusy) + do_become_nonbusy(dev, s); + + if (retval) + return retval; + if (copy_to_user(arg, &bi, sizeof(bi))) return -EFAULT; @@ -2220,7 +2224,7 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma) retval = -EFAULT; goto done; } - if (size & (~PAGE_MASK)) { + if (offset_in_page(size)) { retval = -EFAULT; goto done; } diff --git a/drivers/staging/comedi/comedi_pcmcia.h b/drivers/staging/comedi/comedi_pcmcia.h index 5d3db2b9b4a1..5a572c200a8b 100644 --- a/drivers/staging/comedi/comedi_pcmcia.h +++ b/drivers/staging/comedi/comedi_pcmcia.h @@ -39,7 +39,8 @@ void comedi_pcmcia_driver_unregister(struct comedi_driver *, struct pcmcia_driver *); /** - * module_comedi_pcmcia_driver() - Helper macro for registering a comedi PCMCIA driver + * module_comedi_pcmcia_driver() - Helper macro for registering a comedi + * PCMCIA driver * @__comedi_driver: comedi_driver struct * @__pcmcia_driver: pcmcia_driver struct * diff --git a/drivers/staging/comedi/drivers/addi_apci_3xxx.c b/drivers/staging/comedi/drivers/addi_apci_3xxx.c index 995096c78844..b6af3eba91fd 100644 --- a/drivers/staging/comedi/drivers/addi_apci_3xxx.c +++ b/drivers/staging/comedi/drivers/addi_apci_3xxx.c @@ -496,7 +496,7 @@ static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev, switch (flags & CMDF_ROUND_MASK) { case CMDF_ROUND_NEAREST: default: - timer = (*ns + base / 2) / base; + timer = DIV_ROUND_CLOSEST(*ns, base); break; case CMDF_ROUND_DOWN: timer = *ns / base; diff --git a/drivers/staging/comedi/drivers/amplc_pci230.c b/drivers/staging/comedi/drivers/amplc_pci230.c index 4b39f6960c0a..907c39cc89d7 100644 --- a/drivers/staging/comedi/drivers/amplc_pci230.c +++ b/drivers/staging/comedi/drivers/amplc_pci230.c @@ -637,12 +637,12 @@ static unsigned int pci230_divide_ns(uint64_t ns, unsigned int timebase, switch (flags & CMDF_ROUND_MASK) { default: case CMDF_ROUND_NEAREST: - div += (rem + (timebase / 2)) / timebase; + div += DIV_ROUND_CLOSEST(rem, timebase); break; case CMDF_ROUND_DOWN: break; case CMDF_ROUND_UP: - div += (rem + timebase - 1) / timebase; + div += DIV_ROUND_UP(rem, timebase); break; } return div > UINT_MAX ? UINT_MAX : (unsigned int)div; diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c index d33b8fe872a7..c773b8ca6599 100644 --- a/drivers/staging/comedi/drivers/cb_pcidas64.c +++ b/drivers/staging/comedi/drivers/cb_pcidas64.c @@ -1376,7 +1376,7 @@ static int set_ai_fifo_segment_length(struct comedi_device *dev, num_entries = fifo->max_segment_length; /* 1 == 256 entries, 2 == 512 entries, etc */ - num_increments = (num_entries + increment_size / 2) / increment_size; + num_increments = DIV_ROUND_CLOSEST(num_entries, increment_size); bits = (~(num_increments - 1)) & fifo->fifo_size_reg_mask; devpriv->fifo_size_bits &= ~fifo->fifo_size_reg_mask; @@ -1480,35 +1480,39 @@ static int alloc_and_init_dma_members(struct comedi_device *dev) /* allocate pci dma buffers */ for (i = 0; i < ai_dma_ring_count(board); i++) { devpriv->ai_buffer[i] = - pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE, - &devpriv->ai_buffer_bus_addr[i]); + dma_alloc_coherent(&pcidev->dev, DMA_BUFFER_SIZE, + &devpriv->ai_buffer_bus_addr[i], + GFP_KERNEL); if (!devpriv->ai_buffer[i]) return -ENOMEM; } for (i = 0; i < AO_DMA_RING_COUNT; i++) { if (ao_cmd_is_supported(board)) { devpriv->ao_buffer[i] = - pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE, - &devpriv-> - ao_buffer_bus_addr[i]); + dma_alloc_coherent(&pcidev->dev, + DMA_BUFFER_SIZE, + &devpriv-> + ao_buffer_bus_addr[i], + GFP_KERNEL); if (!devpriv->ao_buffer[i]) return -ENOMEM; } } /* allocate dma descriptors */ devpriv->ai_dma_desc = - pci_alloc_consistent(pcidev, sizeof(struct plx_dma_desc) * - ai_dma_ring_count(board), - &devpriv->ai_dma_desc_bus_addr); + dma_alloc_coherent(&pcidev->dev, sizeof(struct plx_dma_desc) * + ai_dma_ring_count(board), + &devpriv->ai_dma_desc_bus_addr, GFP_KERNEL); if (!devpriv->ai_dma_desc) return -ENOMEM; if (ao_cmd_is_supported(board)) { devpriv->ao_dma_desc = - pci_alloc_consistent(pcidev, - sizeof(struct plx_dma_desc) * - AO_DMA_RING_COUNT, - &devpriv->ao_dma_desc_bus_addr); + dma_alloc_coherent(&pcidev->dev, + sizeof(struct plx_dma_desc) * + AO_DMA_RING_COUNT, + &devpriv->ao_dma_desc_bus_addr, + GFP_KERNEL); if (!devpriv->ao_dma_desc) return -ENOMEM; } @@ -1564,31 +1568,31 @@ static void cb_pcidas64_free_dma(struct comedi_device *dev) /* free pci dma buffers */ for (i = 0; i < ai_dma_ring_count(board); i++) { if (devpriv->ai_buffer[i]) - pci_free_consistent(pcidev, - DMA_BUFFER_SIZE, - devpriv->ai_buffer[i], - devpriv->ai_buffer_bus_addr[i]); + dma_free_coherent(&pcidev->dev, + DMA_BUFFER_SIZE, + devpriv->ai_buffer[i], + devpriv->ai_buffer_bus_addr[i]); } for (i = 0; i < AO_DMA_RING_COUNT; i++) { if (devpriv->ao_buffer[i]) - pci_free_consistent(pcidev, - DMA_BUFFER_SIZE, - devpriv->ao_buffer[i], - devpriv->ao_buffer_bus_addr[i]); + dma_free_coherent(&pcidev->dev, + DMA_BUFFER_SIZE, + devpriv->ao_buffer[i], + devpriv->ao_buffer_bus_addr[i]); } /* free dma descriptors */ if (devpriv->ai_dma_desc) - pci_free_consistent(pcidev, - sizeof(struct plx_dma_desc) * - ai_dma_ring_count(board), - devpriv->ai_dma_desc, - devpriv->ai_dma_desc_bus_addr); + dma_free_coherent(&pcidev->dev, + sizeof(struct plx_dma_desc) * + ai_dma_ring_count(board), + devpriv->ai_dma_desc, + devpriv->ai_dma_desc_bus_addr); if (devpriv->ao_dma_desc) - pci_free_consistent(pcidev, - sizeof(struct plx_dma_desc) * - AO_DMA_RING_COUNT, - devpriv->ao_dma_desc, - devpriv->ao_dma_desc_bus_addr); + dma_free_coherent(&pcidev->dev, + sizeof(struct plx_dma_desc) * + AO_DMA_RING_COUNT, + devpriv->ao_dma_desc, + devpriv->ao_dma_desc_bus_addr); } static inline void warn_external_queue(struct comedi_device *dev) @@ -2004,7 +2008,7 @@ static unsigned int get_divisor(unsigned int ns, unsigned int flags) break; case CMDF_ROUND_NEAREST: default: - divisor = (ns + TIMER_BASE / 2) / TIMER_BASE; + divisor = DIV_ROUND_CLOSEST(ns, TIMER_BASE); break; } return divisor; diff --git a/drivers/staging/comedi/drivers/cb_pcimdda.c b/drivers/staging/comedi/drivers/cb_pcimdda.c index 19210d89f2b2..84ef45457c60 100644 --- a/drivers/staging/comedi/drivers/cb_pcimdda.c +++ b/drivers/staging/comedi/drivers/cb_pcimdda.c @@ -1,77 +1,78 @@ /* - comedi/drivers/cb_pcimdda.c - Computer Boards PCIM-DDA06-16 Comedi driver - Author: Calin Culianu - - COMEDI - Linux Control and Measurement Device Interface - Copyright (C) 2000 David A. Schleef - - 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. -*/ + * comedi/drivers/cb_pcimdda.c + * Computer Boards PCIM-DDA06-16 Comedi driver + * Author: Calin Culianu + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2000 David A. Schleef + * + * 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. + */ /* -Driver: cb_pcimdda -Description: Measurement Computing PCIM-DDA06-16 -Devices: [Measurement Computing] PCIM-DDA06-16 (cb_pcimdda) -Author: Calin Culianu -Updated: Mon, 14 Apr 2008 15:15:51 +0100 -Status: works - -All features of the PCIM-DDA06-16 board are supported. This board -has 6 16-bit AO channels, and the usual 8255 DIO setup. (24 channels, -configurable in banks of 8 and 4, etc.). This board does not support commands. - -The board has a peculiar way of specifying AO gain/range settings -- You have -1 jumper bank on the card, which either makes all 6 AO channels either -5 Volt unipolar, 5V bipolar, 10 Volt unipolar or 10V bipolar. - -Since there is absolutely _no_ way to tell in software how this jumper is set -(well, at least according to the rather thin spec. from Measurement Computing - that comes with the board), the driver assumes the jumper is at its factory -default setting of +/-5V. - -Also of note is the fact that this board features another jumper, whose -state is also completely invisible to software. It toggles two possible AO -output modes on the board: - - - Update Mode: Writing to an AO channel instantaneously updates the actual - signal output by the DAC on the board (this is the factory default). - - Simultaneous XFER Mode: Writing to an AO channel has no effect until - you read from any one of the AO channels. This is useful for loading - all 6 AO values, and then reading from any one of the AO channels on the - device to instantly update all 6 AO values in unison. Useful for some - control apps, I would assume? If your jumper is in this setting, then you - need to issue your comedi_data_write()s to load all the values you want, - then issue one comedi_data_read() on any channel on the AO subdevice - to initiate the simultaneous XFER. - -Configuration Options: not applicable, uses PCI auto config -*/ + * Driver: cb_pcimdda + * Description: Measurement Computing PCIM-DDA06-16 + * Devices: [Measurement Computing] PCIM-DDA06-16 (cb_pcimdda) + * Author: Calin Culianu + * Updated: Mon, 14 Apr 2008 15:15:51 +0100 + * Status: works + * + * All features of the PCIM-DDA06-16 board are supported. + * This board has 6 16-bit AO channels, and the usual 8255 DIO setup. + * (24 channels, configurable in banks of 8 and 4, etc.). + * This board does not support commands. + * + * The board has a peculiar way of specifying AO gain/range settings -- You have + * 1 jumper bank on the card, which either makes all 6 AO channels either + * 5 Volt unipolar, 5V bipolar, 10 Volt unipolar or 10V bipolar. + * + * Since there is absolutely _no_ way to tell in software how this jumper is set + * (well, at least according to the rather thin spec. from Measurement Computing + * that comes with the board), the driver assumes the jumper is at its factory + * default setting of +/-5V. + * + * Also of note is the fact that this board features another jumper, whose + * state is also completely invisible to software. It toggles two possible AO + * output modes on the board: + * + * - Update Mode: Writing to an AO channel instantaneously updates the actual + * signal output by the DAC on the board (this is the factory default). + * - Simultaneous XFER Mode: Writing to an AO channel has no effect until + * you read from any one of the AO channels. This is useful for loading + * all 6 AO values, and then reading from any one of the AO channels on the + * device to instantly update all 6 AO values in unison. Useful for some + * control apps, I would assume? If your jumper is in this setting, then you + * need to issue your comedi_data_write()s to load all the values you want, + * then issue one comedi_data_read() on any channel on the AO subdevice + * to initiate the simultaneous XFER. + * + * Configuration Options: not applicable, uses PCI auto config + */ /* - This is a driver for the Computer Boards PCIM-DDA06-16 Analog Output - card. This board has a unique register layout and as such probably - deserves its own driver file. - - It is theoretically possible to integrate this board into the cb_pcidda - file, but since that isn't my code, I didn't want to significantly - modify that file to support this board (I thought it impolite to do so). - - At any rate, if you feel ambitious, please feel free to take - the code out of this file and combine it with a more unified driver - file. - - I would like to thank Timothy Curry - for lending me a board so that I could write this driver. - - -Calin Culianu + * This is a driver for the Computer Boards PCIM-DDA06-16 Analog Output + * card. This board has a unique register layout and as such probably + * deserves its own driver file. + * + * It is theoretically possible to integrate this board into the cb_pcidda + * file, but since that isn't my code, I didn't want to significantly + * modify that file to support this board (I thought it impolite to do so). + * + * At any rate, if you feel ambitious, please feel free to take + * the code out of this file and combine it with a more unified driver + * file. + * + * I would like to thank Timothy Curry + * for lending me a board so that I could write this driver. + * + * -Calin Culianu */ #include diff --git a/drivers/staging/comedi/drivers/comedi_isadma.c b/drivers/staging/comedi/drivers/comedi_isadma.c index 6ba71d114a95..68ef9b1750be 100644 --- a/drivers/staging/comedi/drivers/comedi_isadma.c +++ b/drivers/staging/comedi/drivers/comedi_isadma.c @@ -132,8 +132,7 @@ unsigned int comedi_isadma_poll(struct comedi_isadma *dma) result = result1; if (result >= desc->size || result == 0) return 0; - else - return desc->size - result; + return desc->size - result; } EXPORT_SYMBOL_GPL(comedi_isadma_poll); diff --git a/drivers/staging/comedi/drivers/contec_pci_dio.c b/drivers/staging/comedi/drivers/contec_pci_dio.c index 4956a49a6140..5f848396c2f7 100644 --- a/drivers/staging/comedi/drivers/contec_pci_dio.c +++ b/drivers/staging/comedi/drivers/contec_pci_dio.c @@ -1,29 +1,30 @@ /* - comedi/drivers/contec_pci_dio.c + * comedi/drivers/contec_pci_dio.c + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2000 David A. Schleef + * + * 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. + */ - COMEDI - Linux Control and Measurement Device Interface - Copyright (C) 2000 David A. Schleef - - 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. -*/ /* -Driver: contec_pci_dio -Description: Contec PIO1616L digital I/O board -Devices: [Contec] PIO1616L (contec_pci_dio) -Author: Stefano Rivoir -Updated: Wed, 27 Jun 2007 13:00:06 +0100 -Status: works - -Configuration Options: not applicable, uses comedi PCI auto config -*/ + * Driver: contec_pci_dio + * Description: Contec PIO1616L digital I/O board + * Devices: [Contec] PIO1616L (contec_pci_dio) + * Author: Stefano Rivoir + * Updated: Wed, 27 Jun 2007 13:00:06 +0100 + * Status: works + * + * Configuration Options: not applicable, uses comedi PCI auto config + */ #include diff --git a/drivers/staging/comedi/drivers/dt2801.c b/drivers/staging/comedi/drivers/dt2801.c index 80e38dedd359..6c7b4d27c27c 100644 --- a/drivers/staging/comedi/drivers/dt2801.c +++ b/drivers/staging/comedi/drivers/dt2801.c @@ -68,17 +68,17 @@ Configuration options: /* Command modifiers (only used with read/write), EXTTRIG can be used with some other commands. */ -#define DT_MOD_DMA (1<<4) -#define DT_MOD_CONT (1<<5) -#define DT_MOD_EXTCLK (1<<6) -#define DT_MOD_EXTTRIG (1<<7) +#define DT_MOD_DMA BIT(4) +#define DT_MOD_CONT BIT(5) +#define DT_MOD_EXTCLK BIT(6) +#define DT_MOD_EXTTRIG BIT(7) /* Bits in status register */ -#define DT_S_DATA_OUT_READY (1<<0) -#define DT_S_DATA_IN_FULL (1<<1) -#define DT_S_READY (1<<2) -#define DT_S_COMMAND (1<<3) -#define DT_S_COMPOSITE_ERROR (1<<7) +#define DT_S_DATA_OUT_READY BIT(0) +#define DT_S_DATA_IN_FULL BIT(1) +#define DT_S_READY BIT(2) +#define DT_S_COMMAND BIT(3) +#define DT_S_COMPOSITE_ERROR BIT(7) /* registers */ #define DT2801_DATA 0 diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c index 5a536a00066f..40bf00984fa5 100644 --- a/drivers/staging/comedi/drivers/dt282x.c +++ b/drivers/staging/comedi/drivers/dt282x.c @@ -371,13 +371,13 @@ static unsigned int dt282x_ns_to_timer(unsigned int *ns, unsigned int flags) switch (flags & CMDF_ROUND_MASK) { case CMDF_ROUND_NEAREST: default: - divider = (*ns + base / 2) / base; + divider = DIV_ROUND_CLOSEST(*ns, base); break; case CMDF_ROUND_DOWN: divider = (*ns) / base; break; case CMDF_ROUND_UP: - divider = (*ns + base - 1) / base; + divider = DIV_ROUND_UP(*ns, base); break; } if (divider < 256) { diff --git a/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c index ab7a332fbcc4..19e0b7be8495 100644 --- a/drivers/staging/comedi/drivers/dt3000.c +++ b/drivers/staging/comedi/drivers/dt3000.c @@ -361,7 +361,7 @@ static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec, switch (flags & CMDF_ROUND_MASK) { case CMDF_ROUND_NEAREST: default: - divider = (*nanosec + base / 2) / base; + divider = DIV_ROUND_CLOSEST(*nanosec, base); break; case CMDF_ROUND_DOWN: divider = (*nanosec) / base; diff --git a/drivers/staging/comedi/drivers/gsc_hpdi.c b/drivers/staging/comedi/drivers/gsc_hpdi.c index 46ca5d938d5b..63b5cbc44bda 100644 --- a/drivers/staging/comedi/drivers/gsc_hpdi.c +++ b/drivers/staging/comedi/drivers/gsc_hpdi.c @@ -499,18 +499,18 @@ static void gsc_hpdi_free_dma(struct comedi_device *dev) /* free pci dma buffers */ for (i = 0; i < NUM_DMA_BUFFERS; i++) { if (devpriv->dio_buffer[i]) - pci_free_consistent(pcidev, - DMA_BUFFER_SIZE, - devpriv->dio_buffer[i], - devpriv->dio_buffer_phys_addr[i]); + dma_free_coherent(&pcidev->dev, + DMA_BUFFER_SIZE, + devpriv->dio_buffer[i], + devpriv->dio_buffer_phys_addr[i]); } /* free dma descriptors */ if (devpriv->dma_desc) - pci_free_consistent(pcidev, - sizeof(struct plx_dma_desc) * - NUM_DMA_DESCRIPTORS, - devpriv->dma_desc, - devpriv->dma_desc_phys_addr); + dma_free_coherent(&pcidev->dev, + sizeof(struct plx_dma_desc) * + NUM_DMA_DESCRIPTORS, + devpriv->dma_desc, + devpriv->dma_desc_phys_addr); } static int gsc_hpdi_init(struct comedi_device *dev) @@ -630,14 +630,16 @@ static int gsc_hpdi_auto_attach(struct comedi_device *dev, /* allocate pci dma buffers */ for (i = 0; i < NUM_DMA_BUFFERS; i++) { devpriv->dio_buffer[i] = - pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE, - &devpriv->dio_buffer_phys_addr[i]); + dma_alloc_coherent(&pcidev->dev, DMA_BUFFER_SIZE, + &devpriv->dio_buffer_phys_addr[i], + GFP_KERNEL); } /* allocate dma descriptors */ - devpriv->dma_desc = pci_alloc_consistent(pcidev, - sizeof(struct plx_dma_desc) * - NUM_DMA_DESCRIPTORS, - &devpriv->dma_desc_phys_addr); + devpriv->dma_desc = dma_alloc_coherent(&pcidev->dev, + sizeof(struct plx_dma_desc) * + NUM_DMA_DESCRIPTORS, + &devpriv->dma_desc_phys_addr, + GFP_KERNEL); if (devpriv->dma_desc_phys_addr & 0xf) { dev_warn(dev->class_dev, " dma descriptors not quad-word aligned (bug)\n"); diff --git a/drivers/staging/comedi/drivers/mite.c b/drivers/staging/comedi/drivers/mite.c index fa7ae2c04556..8f24702c3380 100644 --- a/drivers/staging/comedi/drivers/mite.c +++ b/drivers/staging/comedi/drivers/mite.c @@ -297,7 +297,6 @@ int mite_buf_change(struct mite_dma_descriptor_ring *ring, { struct comedi_async *async = s->async; unsigned int n_links; - int i; if (ring->descriptors) { dma_free_coherent(ring->hw_dev, @@ -326,17 +325,58 @@ int mite_buf_change(struct mite_dma_descriptor_ring *ring, } ring->n_links = n_links; - for (i = 0; i < n_links; i++) { + return mite_init_ring_descriptors(ring, s, n_links << PAGE_SHIFT); +} +EXPORT_SYMBOL_GPL(mite_buf_change); + +/* + * initializes the ring buffer descriptors to provide correct DMA transfer links + * to the exact amount of memory required. When the ring buffer is allocated in + * mite_buf_change, the default is to initialize the ring to refer to the entire + * DMA data buffer. A command may call this function later to re-initialize and + * shorten the amount of memory that will be transferred. + */ +int mite_init_ring_descriptors(struct mite_dma_descriptor_ring *ring, + struct comedi_subdevice *s, + unsigned int nbytes) +{ + struct comedi_async *async = s->async; + unsigned int n_full_links = nbytes >> PAGE_SHIFT; + unsigned int remainder = nbytes % PAGE_SIZE; + int i; + + dev_dbg(s->device->class_dev, + "mite: init ring buffer to %u bytes\n", nbytes); + + if ((n_full_links + (remainder > 0 ? 1 : 0)) > ring->n_links) { + dev_err(s->device->class_dev, + "mite: ring buffer too small for requested init\n"); + return -ENOMEM; + } + + /* We set the descriptors for all full links. */ + for (i = 0; i < n_full_links; ++i) { ring->descriptors[i].count = cpu_to_le32(PAGE_SIZE); ring->descriptors[i].addr = cpu_to_le32(async->buf_map->page_list[i].dma_addr); ring->descriptors[i].next = - cpu_to_le32(ring->descriptors_dma_addr + (i + - 1) * - sizeof(struct mite_dma_descriptor)); + cpu_to_le32(ring->descriptors_dma_addr + + (i + 1) * sizeof(struct mite_dma_descriptor)); } - ring->descriptors[n_links - 1].next = - cpu_to_le32(ring->descriptors_dma_addr); + + /* the last link is either a remainder or was a full link. */ + if (remainder > 0) { + /* set the lesser count for the remainder link */ + ring->descriptors[i].count = cpu_to_le32(remainder); + ring->descriptors[i].addr = + cpu_to_le32(async->buf_map->page_list[i].dma_addr); + /* increment i so that assignment below refs last link */ + ++i; + } + + /* Assign the last link->next to point back to the head of the list. */ + ring->descriptors[i - 1].next = cpu_to_le32(ring->descriptors_dma_addr); + /* * barrier is meant to insure that all the writes to the dma descriptors * have completed before the dma controller is commanded to read them @@ -344,7 +384,7 @@ int mite_buf_change(struct mite_dma_descriptor_ring *ring, smp_wmb(); return 0; } -EXPORT_SYMBOL_GPL(mite_buf_change); +EXPORT_SYMBOL_GPL(mite_init_ring_descriptors); void mite_prep_dma(struct mite_channel *mite_chan, unsigned int num_device_bits, unsigned int num_memory_bits) @@ -552,6 +592,7 @@ int mite_sync_output_dma(struct mite_channel *mite_chan, unsigned int old_alloc_count = async->buf_read_alloc_count; u32 nbytes_ub, nbytes_lb; int count; + bool finite_regen = (cmd->stop_src == TRIG_NONE && stop_count != 0); /* read alloc as much as we can */ comedi_buf_read_alloc(s, async->prealloc_bufsz); @@ -561,11 +602,24 @@ int mite_sync_output_dma(struct mite_channel *mite_chan, nbytes_ub = mite_bytes_read_from_memory_ub(mite_chan); if (cmd->stop_src == TRIG_COUNT && (int)(nbytes_ub - stop_count) > 0) nbytes_ub = stop_count; - if ((int)(nbytes_ub - old_alloc_count) > 0) { + + if ((!finite_regen || stop_count > old_alloc_count) && + ((int)(nbytes_ub - old_alloc_count) > 0)) { dev_warn(s->device->class_dev, "mite: DMA underrun\n"); async->events |= COMEDI_CB_OVERFLOW; return -1; } + + if (finite_regen) { + /* + * This is a special case where we continuously output a finite + * buffer. In this case, we do not free any of the memory, + * hence we expect that old_alloc_count will reach a maximum of + * stop_count bytes. + */ + return 0; + } + count = nbytes_lb - async->buf_read_count; if (count <= 0) return 0; diff --git a/drivers/staging/comedi/drivers/mite.h b/drivers/staging/comedi/drivers/mite.h index c32d4e4ddccc..87534b07ec81 100644 --- a/drivers/staging/comedi/drivers/mite.h +++ b/drivers/staging/comedi/drivers/mite.h @@ -110,6 +110,9 @@ void mite_prep_dma(struct mite_channel *mite_chan, unsigned int num_device_bits, unsigned int num_memory_bits); int mite_buf_change(struct mite_dma_descriptor_ring *ring, struct comedi_subdevice *s); +int mite_init_ring_descriptors(struct mite_dma_descriptor_ring *ring, + struct comedi_subdevice *s, + unsigned int nbytes); enum mite_registers { /* diff --git a/drivers/staging/comedi/drivers/ni_mio_c_common.c b/drivers/staging/comedi/drivers/ni_mio_c_common.c new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index 5e8130a7d670..d1226c97664b 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -1166,8 +1166,7 @@ static void ni_ai_fifo_read(struct comedi_device *dev, comedi_buf_write_samples(s, &data, 1); } } else { - if (n > sizeof(devpriv->ai_fifo_buffer) / - sizeof(devpriv->ai_fifo_buffer[0])) { + if (n > ARRAY_SIZE(devpriv->ai_fifo_buffer)) { dev_err(dev->class_dev, "bug! ai_fifo_buffer too small\n"); async->events |= COMEDI_CB_ERROR; @@ -1242,9 +1241,7 @@ static void ni_handle_fifo_dregs(struct comedi_device *dev) NISTC_AI_STATUS1_FIFO_E; while (fifo_empty == 0) { for (i = 0; - i < - sizeof(devpriv->ai_fifo_buffer) / - sizeof(devpriv->ai_fifo_buffer[0]); i++) { + i < ARRAY_SIZE(devpriv->ai_fifo_buffer); i++) { fifo_empty = ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & NISTC_AI_STATUS1_FIFO_E; @@ -1500,7 +1497,8 @@ static void handle_b_interrupt(struct comedi_device *dev, s->async->events |= COMEDI_CB_OVERFLOW; } - if (b_status & NISTC_AO_STATUS1_BC_TC) + if (s->async->cmd.stop_src != TRIG_NONE && + b_status & NISTC_AO_STATUS1_BC_TC) s->async->events |= COMEDI_CB_EOA; #ifndef PCIDMA @@ -2054,13 +2052,13 @@ static int ni_ns_to_timer(const struct comedi_device *dev, unsigned nanosec, switch (flags & CMDF_ROUND_MASK) { case CMDF_ROUND_NEAREST: default: - divider = (nanosec + devpriv->clock_ns / 2) / devpriv->clock_ns; + divider = DIV_ROUND_CLOSEST(nanosec, devpriv->clock_ns); break; case CMDF_ROUND_DOWN: divider = (nanosec) / devpriv->clock_ns; break; case CMDF_ROUND_UP: - divider = (nanosec + devpriv->clock_ns - 1) / devpriv->clock_ns; + divider = DIV_ROUND_UP(nanosec, devpriv->clock_ns); break; } return divider - 1; @@ -2073,6 +2071,37 @@ static unsigned ni_timer_to_ns(const struct comedi_device *dev, int timer) return devpriv->clock_ns * (timer + 1); } +static void ni_cmd_set_mite_transfer(struct mite_dma_descriptor_ring *ring, + struct comedi_subdevice *sdev, + const struct comedi_cmd *cmd, + unsigned int max_count) { +#ifdef PCIDMA + unsigned int nbytes = max_count; + + if (cmd->stop_arg > 0 && cmd->stop_arg < max_count) + nbytes = cmd->stop_arg; + nbytes *= comedi_bytes_per_scan(sdev); + + if (nbytes > sdev->async->prealloc_bufsz) { + if (cmd->stop_arg > 0) + dev_err(sdev->device->class_dev, + "ni_cmd_set_mite_transfer: tried exact data transfer limits greater than buffer size\n"); + + /* + * we can only transfer up to the size of the buffer. In this + * case, the user is expected to continue to write into the + * comedi buffer (already implemented as a ring buffer). + */ + nbytes = sdev->async->prealloc_bufsz; + } + + mite_init_ring_descriptors(ring, sdev, nbytes); +#else + dev_err(sdev->device->class_dev, + "ni_cmd_set_mite_transfer: exact data transfer limits not implemented yet without DMA\n"); +#endif +} + static unsigned ni_min_ai_scan_period_ns(struct comedi_device *dev, unsigned num_channels) { @@ -2428,7 +2457,8 @@ static int ni_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG); break; case TRIG_EXT: - mode1 |= NISTC_AI_MODE1_CONVERT_SRC(1 + cmd->convert_arg); + mode1 |= NISTC_AI_MODE1_CONVERT_SRC(1 + + CR_CHAN(cmd->convert_arg)); if ((cmd->convert_arg & CR_INVERT) == 0) mode1 |= NISTC_AI_MODE1_CONVERT_POLARITY; ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG); @@ -2902,8 +2932,6 @@ static int ni_ao_inttrig(struct comedi_device *dev, ni_stc_writew(dev, NISTC_AO_CMD1_UI_ARM | NISTC_AO_CMD1_UC_ARM | NISTC_AO_CMD1_BC_ARM | - NISTC_AO_CMD1_DAC1_UPDATE_MODE | - NISTC_AO_CMD1_DAC0_UPDATE_MODE | devpriv->ao_cmd1, NISTC_AO_CMD1_REG); @@ -2913,42 +2941,68 @@ static int ni_ao_inttrig(struct comedi_device *dev, return 0; } -static int ni_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) +/* + * begin ni_ao_cmd. + * Organized similar to NI-STC and MHDDK examples. + * ni_ao_cmd is broken out into configuration sub-routines for clarity. + */ + +static void ni_ao_cmd_personalize(struct comedi_device *dev, + const struct comedi_cmd *cmd) { const struct ni_board_struct *board = dev->board_ptr; - struct ni_private *devpriv = dev->private; - const struct comedi_cmd *cmd = &s->async->cmd; - int bits; - int i; - unsigned trigvar; - unsigned val; - - if (dev->irq == 0) { - dev_err(dev->class_dev, "cannot run command without an irq\n"); - return -EIO; - } + unsigned bits; ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); - ni_stc_writew(dev, NISTC_AO_CMD1_DISARM, NISTC_AO_CMD1_REG); + bits = + /* fast CPU interface--only eseries */ + /* ((slow CPU interface) ? 0 : AO_Fast_CPU) | */ + NISTC_AO_PERSONAL_BC_SRC_SEL | + 0 /* (use_original_pulse ? 0 : NISTC_AO_PERSONAL_UPDATE_TIMEBASE) */ | + /* + * FIXME: start setting following bit when appropriate. Need to + * determine whether board is E4 or E1. + * FROM MHHDK: + * if board is E4 or E1 + * Set bit "NISTC_AO_PERSONAL_UPDATE_PW" to 0 + * else + * set it to 1 + */ + NISTC_AO_PERSONAL_UPDATE_PW | + /* FIXME: when should we set following bit to zero? */ + NISTC_AO_PERSONAL_TMRDACWR_PW | + (board->ao_fifo_depth ? + NISTC_AO_PERSONAL_FIFO_ENA : NISTC_AO_PERSONAL_DMA_PIO_CTRL) + ; +#if 0 + /* + * FIXME: + * add something like ".has_individual_dacs = 0" to ni_board_struct + * since, as F Hess pointed out, not all in m series have singles. not + * sure if e-series all have duals... + */ - if (devpriv->is_6xxx) { - ni_ao_win_outw(dev, NI611X_AO_MISC_CLEAR_WG, - NI611X_AO_MISC_REG); + /* + * F Hess: windows driver does not set NISTC_AO_PERSONAL_NUM_DAC bit for + * 6281, verified with bus analyzer. + */ + if (devpriv->is_m_series) + bits |= NISTC_AO_PERSONAL_NUM_DAC; +#endif + ni_stc_writew(dev, bits, NISTC_AO_PERSONAL_REG); - bits = 0; - for (i = 0; i < cmd->chanlist_len; i++) { - int chan; + ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); +} - chan = CR_CHAN(cmd->chanlist[i]); - bits |= 1 << chan; - ni_ao_win_outw(dev, chan, NI611X_AO_WAVEFORM_GEN_REG); - } - ni_ao_win_outw(dev, bits, NI611X_AO_TIMED_REG); - } +static void ni_ao_cmd_set_trigger(struct comedi_device *dev, + const struct comedi_cmd *cmd) +{ + struct ni_private *devpriv = dev->private; - ni_ao_config_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len, 1); + ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); + /* sync */ if (cmd->stop_src == TRIG_NONE) { devpriv->ao_mode1 |= NISTC_AO_MODE1_CONTINUOUS; devpriv->ao_mode1 &= ~NISTC_AO_MODE1_TRIGGER_ONCE; @@ -2958,177 +3012,351 @@ static int ni_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) } ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); - val = devpriv->ao_trigger_select; - switch (cmd->start_src) { - case TRIG_INT: - case TRIG_NOW: - val &= ~(NISTC_AO_TRIG_START1_POLARITY | - NISTC_AO_TRIG_START1_SEL_MASK); - val |= NISTC_AO_TRIG_START1_EDGE | - NISTC_AO_TRIG_START1_SYNC; - break; - case TRIG_EXT: - val = NISTC_AO_TRIG_START1_SEL(CR_CHAN(cmd->start_arg) + 1); - if (cmd->start_arg & CR_INVERT) { - /* 0=active high, 1=active low. see daq-stc 3-24 (p186) */ - val |= NISTC_AO_TRIG_START1_POLARITY; - } - if (cmd->start_arg & CR_EDGE) { - /* 0=edge detection disabled, 1=enabled */ - val |= NISTC_AO_TRIG_START1_EDGE; + { + unsigned int trigsel = devpriv->ao_trigger_select; + + switch (cmd->start_src) { + case TRIG_INT: + case TRIG_NOW: + trigsel &= ~(NISTC_AO_TRIG_START1_POLARITY | + NISTC_AO_TRIG_START1_SEL_MASK); + trigsel |= NISTC_AO_TRIG_START1_EDGE | + NISTC_AO_TRIG_START1_SYNC; + break; + case TRIG_EXT: + trigsel = NISTC_AO_TRIG_START1_SEL( + CR_CHAN(cmd->start_arg) + 1); + if (cmd->start_arg & CR_INVERT) + /* + * 0=active high, 1=active low. + * see daq-stc 3-24 (p186) + */ + trigsel |= NISTC_AO_TRIG_START1_POLARITY; + if (cmd->start_arg & CR_EDGE) + /* 0=edge detection disabled, 1=enabled */ + trigsel |= NISTC_AO_TRIG_START1_EDGE; + break; + default: + BUG(); + break; } + + devpriv->ao_trigger_select = trigsel; ni_stc_writew(dev, devpriv->ao_trigger_select, NISTC_AO_TRIG_SEL_REG); - break; - default: - BUG(); - break; } - devpriv->ao_trigger_select = val; - ni_stc_writew(dev, devpriv->ao_trigger_select, NISTC_AO_TRIG_SEL_REG); + /* AO_Delayed_START1 = 0, we do not support delayed start...yet */ + /* sync */ + /* select DA_START1 as PFI6/AO_START1 when configured as an output */ devpriv->ao_mode3 &= ~NISTC_AO_MODE3_TRIG_LEN; ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); + ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); +} + +static void ni_ao_cmd_set_counters(struct comedi_device *dev, + const struct comedi_cmd *cmd) +{ + struct ni_private *devpriv = dev->private; + /* Not supporting 'waveform staging' or 'local buffer with pauses' */ + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); + /* + * This relies on ao_mode1/(Trigger_Once | Continuous) being set in + * set_trigger above. It is unclear whether we really need to re-write + * this register with these values. The mhddk examples for e-series + * show writing this in both places, but the examples for m-series show + * a single write in the set_counters function (here). + */ ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); + + /* sync (upload number of buffer iterations -1) */ + /* indicate that we want to use BC_Load_A_Register as the source */ devpriv->ao_mode2 &= ~NISTC_AO_MODE2_BC_INIT_LOAD_SRC; ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); - if (cmd->stop_src == TRIG_NONE) - ni_stc_writel(dev, 0xffffff, NISTC_AO_BC_LOADA_REG); - else - ni_stc_writel(dev, 0, NISTC_AO_BC_LOADA_REG); + + /* + * if the BC_TC interrupt is still issued in spite of UC, BC, UI + * ignoring BC_TC, then we will need to find a way to ignore that + * interrupt in continuous mode. + */ + ni_stc_writel(dev, 0, NISTC_AO_BC_LOADA_REG); /* iter once */ + + /* sync (issue command to load number of buffer iterations -1) */ ni_stc_writew(dev, NISTC_AO_CMD1_BC_LOAD, NISTC_AO_CMD1_REG); + + /* sync (upload number of updates in buffer) */ + /* indicate that we want to use UC_Load_A_Register as the source */ devpriv->ao_mode2 &= ~NISTC_AO_MODE2_UC_INIT_LOAD_SRC; ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); - switch (cmd->stop_src) { - case TRIG_COUNT: + + /* + * if a user specifies '0', this automatically assumes the entire 24bit + * address space is available for the (multiple iterations of single + * buffer) MISB. Otherwise, stop_arg specifies the MISB length that + * will be used, regardless of whether we are in continuous mode or not. + * In continuous mode, the output will just iterate indefinitely over + * the MISB. + */ + { + unsigned int stop_arg = cmd->stop_arg > 0 ? + (cmd->stop_arg & 0xffffff) : 0xffffff; + if (devpriv->is_m_series) { - /* this is how the NI example code does it for m-series boards, verified correct with 6259 */ - ni_stc_writel(dev, cmd->stop_arg - 1, - NISTC_AO_UC_LOADA_REG); + /* + * this is how the NI example code does it for m-series + * boards, verified correct with 6259 + */ + ni_stc_writel(dev, stop_arg - 1, NISTC_AO_UC_LOADA_REG); + + /* sync (issue cmd to load number of updates in MISB) */ ni_stc_writew(dev, NISTC_AO_CMD1_UC_LOAD, NISTC_AO_CMD1_REG); } else { - ni_stc_writel(dev, cmd->stop_arg, - NISTC_AO_UC_LOADA_REG); + ni_stc_writel(dev, stop_arg, NISTC_AO_UC_LOADA_REG); + + /* sync (issue cmd to load number of updates in MISB) */ ni_stc_writew(dev, NISTC_AO_CMD1_UC_LOAD, NISTC_AO_CMD1_REG); - ni_stc_writel(dev, cmd->stop_arg - 1, - NISTC_AO_UC_LOADA_REG); + + /* + * sync (upload number of updates-1 in MISB) + * --eseries only? + */ + ni_stc_writel(dev, stop_arg - 1, NISTC_AO_UC_LOADA_REG); } - break; - case TRIG_NONE: - ni_stc_writel(dev, 0xffffff, NISTC_AO_UC_LOADA_REG); - ni_stc_writew(dev, NISTC_AO_CMD1_UC_LOAD, NISTC_AO_CMD1_REG); - ni_stc_writel(dev, 0xffffff, NISTC_AO_UC_LOADA_REG); - break; - default: - ni_stc_writel(dev, 0, NISTC_AO_UC_LOADA_REG); - ni_stc_writew(dev, NISTC_AO_CMD1_UC_LOAD, NISTC_AO_CMD1_REG); - ni_stc_writel(dev, cmd->stop_arg, NISTC_AO_UC_LOADA_REG); } - devpriv->ao_mode1 &= ~(NISTC_AO_MODE1_UPDATE_SRC_MASK | - NISTC_AO_MODE1_UI_SRC_MASK | - NISTC_AO_MODE1_UPDATE_SRC_POLARITY | - NISTC_AO_MODE1_UI_SRC_POLARITY); + ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); +} + +static void ni_ao_cmd_set_update(struct comedi_device *dev, + const struct comedi_cmd *cmd) +{ + struct ni_private *devpriv = dev->private; + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); + + /* + * zero out these bit fields to be set below. Does an ao-reset do this + * automatically? + */ + devpriv->ao_mode1 &= ~( + NISTC_AO_MODE1_UI_SRC_MASK | + NISTC_AO_MODE1_UI_SRC_POLARITY | + NISTC_AO_MODE1_UPDATE_SRC_MASK | + NISTC_AO_MODE1_UPDATE_SRC_POLARITY + ); + switch (cmd->scan_begin_src) { case TRIG_TIMER: - devpriv->ao_cmd2 &= ~NISTC_AO_CMD2_BC_GATE_ENA; - trigvar = - ni_ns_to_timer(dev, cmd->scan_begin_arg, - CMDF_ROUND_NEAREST); - ni_stc_writel(dev, 1, NISTC_AO_UI_LOADA_REG); - ni_stc_writew(dev, NISTC_AO_CMD1_UI_LOAD, NISTC_AO_CMD1_REG); - ni_stc_writel(dev, trigvar, NISTC_AO_UI_LOADA_REG); + devpriv->ao_cmd2 &= ~NISTC_AO_CMD2_BC_GATE_ENA; + + /* + * NOTE: there are several other ways of configuring internal + * updates, but we'll only support one for now: using + * AO_IN_TIMEBASE, w/o waveform staging, w/o a delay between + * START1 and first update, and also w/o local buffer mode w/ + * pauses. + */ + + /* + * This is already done above: + * devpriv->ao_mode1 &= ~( + * // set UPDATE_Source to UI_TC: + * NISTC_AO_MODE1_UPDATE_SRC_MASK | + * // set UPDATE_Source_Polarity to rising (required?) + * NISTC_AO_MODE1_UPDATE_SRC_POLARITY | + * // set UI_Source to AO_IN_TIMEBASE1: + * NISTC_AO_MODE1_UI_SRC_MASK | + * // set UI_Source_Polarity to rising (required?) + * NISTC_AO_MODE1_UI_SRC_POLARITY + * ); + */ + + /* + * TODO: use ao_ui_clock_source to allow all possible signals + * to be routed to UI_Source_Select. See tSTC.h for + * eseries/ni67xx and tMSeries.h for mseries. + */ + + { + unsigned trigvar = ni_ns_to_timer(dev, + cmd->scan_begin_arg, + CMDF_ROUND_NEAREST); + + /* + * Wait N TB3 ticks after the start trigger before + * clocking(N must be >=2). + */ + /* following line: 2-1 per STC */ + ni_stc_writel(dev, 1, NISTC_AO_UI_LOADA_REG); + ni_stc_writew(dev, NISTC_AO_CMD1_UI_LOAD, + NISTC_AO_CMD1_REG); + /* following line: N-1 per STC */ + ni_stc_writel(dev, trigvar - 1, NISTC_AO_UI_LOADA_REG); + } break; case TRIG_EXT: - devpriv->ao_mode1 |= - NISTC_AO_MODE1_UPDATE_SRC(cmd->scan_begin_arg); + /* FIXME: assert scan_begin_arg != 0, ret failure otherwise */ + devpriv->ao_cmd2 |= NISTC_AO_CMD2_BC_GATE_ENA; + devpriv->ao_mode1 |= NISTC_AO_MODE1_UPDATE_SRC( + CR_CHAN(cmd->scan_begin_arg)); if (cmd->scan_begin_arg & CR_INVERT) devpriv->ao_mode1 |= NISTC_AO_MODE1_UPDATE_SRC_POLARITY; - devpriv->ao_cmd2 |= NISTC_AO_CMD2_BC_GATE_ENA; break; default: BUG(); break; } + ni_stc_writew(dev, devpriv->ao_cmd2, NISTC_AO_CMD2_REG); ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); devpriv->ao_mode2 &= ~(NISTC_AO_MODE2_UI_RELOAD_MODE(3) | NISTC_AO_MODE2_UI_INIT_LOAD_SRC); ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); + /* Configure DAQ-STC for Timed update mode */ + devpriv->ao_cmd1 |= NISTC_AO_CMD1_DAC1_UPDATE_MODE | + NISTC_AO_CMD1_DAC0_UPDATE_MODE; + /* We are not using UPDATE2-->don't have to set DACx_Source_Select */ + ni_stc_writew(dev, devpriv->ao_cmd1, NISTC_AO_CMD1_REG); + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); +} + +static void ni_ao_cmd_set_channels(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + struct ni_private *devpriv = dev->private; + const struct comedi_cmd *cmd = &s->async->cmd; + unsigned bits = 0; + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); + + if (devpriv->is_6xxx) { + unsigned int i; + + bits = 0; + for (i = 0; i < cmd->chanlist_len; ++i) { + int chan = CR_CHAN(cmd->chanlist[i]); + + bits |= 1 << chan; + ni_ao_win_outw(dev, chan, NI611X_AO_WAVEFORM_GEN_REG); + } + ni_ao_win_outw(dev, bits, NI611X_AO_TIMED_REG); + } + + ni_ao_config_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len, 1); + if (cmd->scan_end_arg > 1) { devpriv->ao_mode1 |= NISTC_AO_MODE1_MULTI_CHAN; - ni_stc_writew(dev, - NISTC_AO_OUT_CTRL_CHANS(cmd->scan_end_arg - 1) | - NISTC_AO_OUT_CTRL_UPDATE_SEL_HIGHZ, - NISTC_AO_OUT_CTRL_REG); - } else { - unsigned bits; + bits = NISTC_AO_OUT_CTRL_CHANS(cmd->scan_end_arg - 1) + | NISTC_AO_OUT_CTRL_UPDATE_SEL_HIGHZ; + } else { devpriv->ao_mode1 &= ~NISTC_AO_MODE1_MULTI_CHAN; bits = NISTC_AO_OUT_CTRL_UPDATE_SEL_HIGHZ; - if (devpriv->is_m_series || devpriv->is_6xxx) { + if (devpriv->is_m_series | devpriv->is_6xxx) bits |= NISTC_AO_OUT_CTRL_CHANS(0); - } else { - bits |= - NISTC_AO_OUT_CTRL_CHANS(CR_CHAN(cmd->chanlist[0])); - } - ni_stc_writew(dev, bits, NISTC_AO_OUT_CTRL_REG); + else + bits |= NISTC_AO_OUT_CTRL_CHANS( + CR_CHAN(cmd->chanlist[0])); } - ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); - ni_stc_writew(dev, NISTC_AO_CMD1_DAC1_UPDATE_MODE | - NISTC_AO_CMD1_DAC0_UPDATE_MODE, - NISTC_AO_CMD1_REG); + ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); + ni_stc_writew(dev, bits, NISTC_AO_OUT_CTRL_REG); + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); +} + +static void ni_ao_cmd_set_stop_conditions(struct comedi_device *dev, + const struct comedi_cmd *cmd) +{ + struct ni_private *devpriv = dev->private; + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); devpriv->ao_mode3 |= NISTC_AO_MODE3_STOP_ON_OVERRUN_ERR; ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); + /* + * Since we are not supporting waveform staging, we ignore these errors: + * NISTC_AO_MODE3_STOP_ON_BC_TC_ERR, + * NISTC_AO_MODE3_STOP_ON_BC_TC_TRIG_ERR + */ + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); +} + +static void ni_ao_cmd_set_fifo_mode(struct comedi_device *dev) +{ + struct ni_private *devpriv = dev->private; + + ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); + devpriv->ao_mode2 &= ~NISTC_AO_MODE2_FIFO_MODE_MASK; #ifdef PCIDMA devpriv->ao_mode2 |= NISTC_AO_MODE2_FIFO_MODE_HF_F; #else devpriv->ao_mode2 |= NISTC_AO_MODE2_FIFO_MODE_HF; #endif + /* NOTE: this is where use_onboard_memory=True would be implemented */ devpriv->ao_mode2 &= ~NISTC_AO_MODE2_FIFO_REXMIT_ENA; ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); - bits = NISTC_AO_PERSONAL_BC_SRC_SEL | - NISTC_AO_PERSONAL_UPDATE_PW | - NISTC_AO_PERSONAL_TMRDACWR_PW; - if (board->ao_fifo_depth) - bits |= NISTC_AO_PERSONAL_FIFO_ENA; - else - bits |= NISTC_AO_PERSONAL_DMA_PIO_CTRL; -#if 0 - /* - * F Hess: windows driver does not set NISTC_AO_PERSONAL_NUM_DAC bit - * for 6281, verified with bus analyzer. - */ - if (devpriv->is_m_series) - bits |= NISTC_AO_PERSONAL_NUM_DAC; -#endif - ni_stc_writew(dev, bits, NISTC_AO_PERSONAL_REG); - /* enable sending of ao dma requests */ + /* enable sending of ao fifo requests (dma request) */ ni_stc_writew(dev, NISTC_AO_START_AOFREQ_ENA, NISTC_AO_START_SEL_REG); ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); - if (cmd->stop_src == TRIG_COUNT) { - ni_stc_writew(dev, NISTC_INTB_ACK_AO_BC_TC, - NISTC_INTB_ACK_REG); + /* we are not supporting boards with virtual fifos */ +} + +static void ni_ao_cmd_set_interrupts(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + if (s->async->cmd.stop_src == TRIG_COUNT) ni_set_bits(dev, NISTC_INTB_ENA_REG, NISTC_INTB_ENA_AO_BC_TC, 1); - } s->async->inttrig = ni_ao_inttrig; +} +static int ni_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) +{ + struct ni_private *devpriv = dev->private; + const struct comedi_cmd *cmd = &s->async->cmd; + + if (dev->irq == 0) { + dev_err(dev->class_dev, "cannot run command without an irq"); + return -EIO; + } + + /* ni_ao_reset should have already been done */ + ni_ao_cmd_personalize(dev, cmd); + /* clearing fifo and preload happens elsewhere */ + + ni_ao_cmd_set_trigger(dev, cmd); + ni_ao_cmd_set_counters(dev, cmd); + ni_ao_cmd_set_update(dev, cmd); + ni_ao_cmd_set_channels(dev, s); + ni_ao_cmd_set_stop_conditions(dev, cmd); + ni_ao_cmd_set_fifo_mode(dev); + ni_cmd_set_mite_transfer(devpriv->ao_mite_ring, s, cmd, 0x00ffffff); + ni_ao_cmd_set_interrupts(dev, s); + + /* + * arm(ing) and star(ting) happen in ni_ao_inttrig, which _must_ be + * called for ao commands since 1) TRIG_NOW is not supported and 2) DMA + * must be setup and initially written to before arm/start happen. + */ return 0; } +/* end ni_ao_cmd */ + static int ni_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { @@ -3187,11 +3415,7 @@ static int ni_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, 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 |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); - else /* TRIG_NONE */ - err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); + err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); if (err) return 3; @@ -3214,48 +3438,70 @@ static int ni_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, static int ni_ao_reset(struct comedi_device *dev, struct comedi_subdevice *s) { + /* See 3.6.1.2 "Resetting", of DAQ-STC Technical Reference Manual */ + + /* + * In the following, the "--sync" comments are meant to denote + * asynchronous boundaries for setting the registers as described in the + * DAQ-STC mostly in the order also described in the DAQ-STC. + */ + struct ni_private *devpriv = dev->private; ni_release_ao_mite_channel(dev); + /* --sync (reset AO) */ + if (devpriv->is_m_series) + /* following example in mhddk for m-series */ + ni_stc_writew(dev, NISTC_RESET_AO, NISTC_RESET_REG); + + /*--sync (start config) */ ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); + + /*--sync (Disarm) */ ni_stc_writew(dev, NISTC_AO_CMD1_DISARM, NISTC_AO_CMD1_REG); - ni_set_bits(dev, NISTC_INTB_ENA_REG, ~0, 0); - ni_stc_writew(dev, NISTC_AO_PERSONAL_BC_SRC_SEL, NISTC_AO_PERSONAL_REG); - ni_stc_writew(dev, NISTC_INTB_ACK_AO_ALL, NISTC_INTB_ACK_REG); - ni_stc_writew(dev, NISTC_AO_PERSONAL_BC_SRC_SEL | - NISTC_AO_PERSONAL_UPDATE_PW | - NISTC_AO_PERSONAL_TMRDACWR_PW, - NISTC_AO_PERSONAL_REG); - ni_stc_writew(dev, 0, NISTC_AO_OUT_CTRL_REG); - ni_stc_writew(dev, 0, NISTC_AO_START_SEL_REG); - devpriv->ao_cmd1 = 0; - ni_stc_writew(dev, devpriv->ao_cmd1, NISTC_AO_CMD1_REG); - devpriv->ao_cmd2 = 0; - ni_stc_writew(dev, devpriv->ao_cmd2, NISTC_AO_CMD2_REG); + + /* + * --sync + * (clear bunch of registers--mseries mhddk examples do not include + * this) + */ + devpriv->ao_cmd1 = 0; + devpriv->ao_cmd2 = 0; devpriv->ao_mode1 = 0; - ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); devpriv->ao_mode2 = 0; - ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); if (devpriv->is_m_series) devpriv->ao_mode3 = NISTC_AO_MODE3_LAST_GATE_DISABLE; else devpriv->ao_mode3 = 0; - ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); devpriv->ao_trigger_select = 0; - ni_stc_writew(dev, devpriv->ao_trigger_select, - NISTC_AO_TRIG_SEL_REG); - if (devpriv->is_6xxx) { - unsigned immediate_bits = 0; - unsigned i; - for (i = 0; i < s->n_chan; ++i) - immediate_bits |= 1 << i; - ni_ao_win_outw(dev, immediate_bits, NI671X_AO_IMMEDIATE_REG); + ni_stc_writew(dev, 0, NISTC_AO_PERSONAL_REG); + ni_stc_writew(dev, 0, NISTC_AO_CMD1_REG); + ni_stc_writew(dev, 0, NISTC_AO_CMD2_REG); + ni_stc_writew(dev, 0, NISTC_AO_MODE1_REG); + ni_stc_writew(dev, 0, NISTC_AO_MODE2_REG); + ni_stc_writew(dev, 0, NISTC_AO_OUT_CTRL_REG); + ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); + ni_stc_writew(dev, 0, NISTC_AO_START_SEL_REG); + ni_stc_writew(dev, 0, NISTC_AO_TRIG_SEL_REG); + + /*--sync (disable interrupts) */ + ni_set_bits(dev, NISTC_INTB_ENA_REG, ~0, 0); + + /*--sync (ack) */ + ni_stc_writew(dev, NISTC_AO_PERSONAL_BC_SRC_SEL, NISTC_AO_PERSONAL_REG); + ni_stc_writew(dev, NISTC_INTB_ACK_AO_ALL, NISTC_INTB_ACK_REG); + + /*--not in DAQ-STC. which doc? */ + if (devpriv->is_6xxx) { + ni_ao_win_outw(dev, (1u << s->n_chan) - 1u, + NI671X_AO_IMMEDIATE_REG); ni_ao_win_outw(dev, NI611X_AO_MISC_CLEAR_WG, NI611X_AO_MISC_REG); } ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); + /*--end */ return 0; } @@ -3381,7 +3627,9 @@ static int ni_cdio_cmdtest(struct comedi_device *dev, 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); + err |= comedi_check_trigger_arg_max(&cmd->stop_arg, + s->async->prealloc_bufsz / + comedi_bytes_per_scan(s)); if (err) return 3; @@ -3458,6 +3706,7 @@ static int ni_cdo_inttrig(struct comedi_device *dev, static int ni_cdio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { + struct ni_private *devpriv = dev->private; const struct comedi_cmd *cmd = &s->async->cmd; unsigned cdo_mode_bits; int retval; @@ -3482,6 +3731,10 @@ static int ni_cdio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) if (retval < 0) return retval; + ni_cmd_set_mite_transfer(devpriv->cdo_mite_ring, s, cmd, + s->async->prealloc_bufsz / + comedi_bytes_per_scan(s)); + s->async->inttrig = ni_cdo_inttrig; return 0; @@ -3980,34 +4233,30 @@ static int ni_m_series_pwm_config(struct comedi_device *dev, case INSN_CONFIG_PWM_OUTPUT: switch (data[1]) { case CMDF_ROUND_NEAREST: - up_count = - (data[2] + - devpriv->clock_ns / 2) / devpriv->clock_ns; + up_count = DIV_ROUND_CLOSEST(data[2], + devpriv->clock_ns); break; case CMDF_ROUND_DOWN: up_count = data[2] / devpriv->clock_ns; break; case CMDF_ROUND_UP: up_count = - (data[2] + devpriv->clock_ns - - 1) / devpriv->clock_ns; + DIV_ROUND_UP(data[2], devpriv->clock_ns); break; default: return -EINVAL; } switch (data[3]) { case CMDF_ROUND_NEAREST: - down_count = - (data[4] + - devpriv->clock_ns / 2) / devpriv->clock_ns; + down_count = DIV_ROUND_CLOSEST(data[4], + devpriv->clock_ns); break; case CMDF_ROUND_DOWN: down_count = data[4] / devpriv->clock_ns; break; case CMDF_ROUND_UP: down_count = - (data[4] + devpriv->clock_ns - - 1) / devpriv->clock_ns; + DIV_ROUND_UP(data[4], devpriv->clock_ns); break; default: return -EINVAL; @@ -4044,34 +4293,30 @@ static int ni_6143_pwm_config(struct comedi_device *dev, case INSN_CONFIG_PWM_OUTPUT: switch (data[1]) { case CMDF_ROUND_NEAREST: - up_count = - (data[2] + - devpriv->clock_ns / 2) / devpriv->clock_ns; + up_count = DIV_ROUND_CLOSEST(data[2], + devpriv->clock_ns); break; case CMDF_ROUND_DOWN: up_count = data[2] / devpriv->clock_ns; break; case CMDF_ROUND_UP: up_count = - (data[2] + devpriv->clock_ns - - 1) / devpriv->clock_ns; + DIV_ROUND_UP(data[2], devpriv->clock_ns); break; default: return -EINVAL; } switch (data[3]) { case CMDF_ROUND_NEAREST: - down_count = - (data[4] + - devpriv->clock_ns / 2) / devpriv->clock_ns; + down_count = DIV_ROUND_CLOSEST(data[4], + devpriv->clock_ns); break; case CMDF_ROUND_DOWN: down_count = data[4] / devpriv->clock_ns; break; case CMDF_ROUND_UP: down_count = - (data[4] + devpriv->clock_ns - - 1) / devpriv->clock_ns; + DIV_ROUND_UP(data[4], devpriv->clock_ns); break; default: return -EINVAL; @@ -4665,9 +4910,9 @@ static int ni_mseries_get_pll_parameters(unsigned reference_period_ns, *freq_divider = best_div; *freq_multiplier = best_mult; - *actual_period_ns = - (best_period_picosec * fudge_factor_80_to_20Mhz + - (pico_per_nano / 2)) / pico_per_nano; + *actual_period_ns = DIV_ROUND_CLOSEST(best_period_picosec * + fudge_factor_80_to_20Mhz, + pico_per_nano); return 0; } @@ -5024,7 +5269,6 @@ static irqreturn_t ni_E_interrupt(int irq, void *d) unsigned long flags; #ifdef PCIDMA struct ni_private *devpriv = dev->private; - struct mite_struct *mite = devpriv->mite; #endif if (!dev->attached) @@ -5036,8 +5280,7 @@ static irqreturn_t ni_E_interrupt(int irq, void *d) a_status = ni_stc_readw(dev, NISTC_AI_STATUS1_REG); b_status = ni_stc_readw(dev, NISTC_AO_STATUS1_REG); #ifdef PCIDMA - if (mite) { - struct ni_private *devpriv = dev->private; + if (devpriv->mite) { unsigned long flags_too; spin_lock_irqsave(&devpriv->mite_channel_lock, flags_too); @@ -5053,7 +5296,7 @@ static irqreturn_t ni_E_interrupt(int irq, void *d) ao_mite_status = mite_get_status(devpriv->ao_mite_chan); if (ao_mite_status & CHSR_LINKC) writel(CHOR_CLRLC, - mite->mite_io_addr + + devpriv->mite->mite_io_addr + MITE_CHOR(devpriv-> ao_mite_chan->channel)); } diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c index ac79099bc23e..7112c3fec8bb 100644 --- a/drivers/staging/comedi/drivers/ni_pcidio.c +++ b/drivers/staging/comedi/drivers/ni_pcidio.c @@ -525,13 +525,13 @@ static int ni_pcidio_ns_to_timer(int *nanosec, unsigned int flags) switch (flags & CMDF_ROUND_MASK) { case CMDF_ROUND_NEAREST: default: - divider = (*nanosec + base / 2) / base; + divider = DIV_ROUND_CLOSEST(*nanosec, base); break; case CMDF_ROUND_DOWN: divider = (*nanosec) / base; break; case CMDF_ROUND_UP: - divider = (*nanosec + base - 1) / base; + divider = DIV_ROUND_UP(*nanosec, base); break; } diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c index 30a5a75d1fe7..231e37d6b7c6 100644 --- a/drivers/staging/comedi/drivers/ni_pcimio.c +++ b/drivers/staging/comedi/drivers/ni_pcimio.c @@ -26,7 +26,8 @@ Devices: [National Instruments] PCI-MIO-16XE-50 (ni_pcimio), PXI-6040E, PCI-6030E, PCI-6031E, PCI-6032E, PCI-6033E, PCI-6071E, PCI-6023E, PCI-6024E, PCI-6025E, PXI-6025E, PCI-6034E, PCI-6035E, PCI-6052E, PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224, - PCI-6225, PXI-6225, PCI-6229, PCI-6250, PCI-6251, PCIe-6251, PXIe-6251, + PCI-6225, PXI-6225, PCI-6229, PCI-6250, + PCI-6251, PXI-6251, PCIe-6251, PXIe-6251, PCI-6254, PCI-6259, PCIe-6259, PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289, PCI-6711, PXI-6711, PCI-6713, PXI-6713, @@ -193,6 +194,7 @@ enum ni_pcimio_boardid { BOARD_PCI6229, BOARD_PCI6250, BOARD_PCI6251, + BOARD_PXI6251, BOARD_PCIE6251, BOARD_PXIE6251, BOARD_PCI6254, @@ -811,6 +813,21 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 350, .caldac = { caldac_none }, }, + [BOARD_PXI6251] = { + .name = "pxi-6251", + .n_adchan = 16, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_628x, + .ai_speed = 800, + .n_aochan = 2, + .ao_maxdata = 0xffff, + .ao_fifo_depth = 8191, + .ao_range_table = &range_ni_M_625x_ao, + .reg_type = ni_reg_625x, + .ao_speed = 350, + .caldac = { caldac_none }, + }, [BOARD_PCIE6251] = { .name = "pcie-6251", .n_adchan = 16, @@ -1290,6 +1307,7 @@ static const struct pci_device_id ni_pcimio_pci_table[] = { { PCI_VDEVICE(NI, 0x71bc), BOARD_PCI6221_37PIN }, { PCI_VDEVICE(NI, 0x717d), BOARD_PCIE6251 }, { PCI_VDEVICE(NI, 0x72e8), BOARD_PXIE6251 }, + { PCI_VDEVICE(NI, 0x70ad), BOARD_PXI6251 }, { 0 } }; MODULE_DEVICE_TABLE(pci, ni_pcimio_pci_table); diff --git a/drivers/staging/comedi/drivers/ni_tiocmd.c b/drivers/staging/comedi/drivers/ni_tiocmd.c index 437f723bb34d..823e47910004 100644 --- a/drivers/staging/comedi/drivers/ni_tiocmd.c +++ b/drivers/staging/comedi/drivers/ni_tiocmd.c @@ -92,7 +92,7 @@ static int ni_tio_input_inttrig(struct comedi_device *dev, unsigned long flags; int ret = 0; - if (trig_num != cmd->start_src) + if (trig_num != cmd->start_arg) return -EINVAL; spin_lock_irqsave(&counter->lock, flags); diff --git a/drivers/staging/comedi/drivers/rtd520.c b/drivers/staging/comedi/drivers/rtd520.c index 68ac02b68cb2..9b6c56773247 100644 --- a/drivers/staging/comedi/drivers/rtd520.c +++ b/drivers/staging/comedi/drivers/rtd520.c @@ -892,9 +892,8 @@ static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) devpriv->xfer_count = cmd->chanlist_len; } else { /* make a multiple of scan length */ devpriv->xfer_count = - (devpriv->xfer_count + - cmd->chanlist_len - 1) - / cmd->chanlist_len; + DIV_ROUND_UP(devpriv->xfer_count, + cmd->chanlist_len); devpriv->xfer_count *= cmd->chanlist_len; } devpriv->flags |= SEND_EOS; diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c index 35f0f676eb28..c5e08635e01e 100644 --- a/drivers/staging/comedi/drivers/s626.c +++ b/drivers/staging/comedi/drivers/s626.c @@ -1167,12 +1167,6 @@ static void s626_set_clk_mult(struct comedi_device *dev, s626_set_mode(dev, chan, mode, false); } -static uint16_t s626_get_clk_mult(struct comedi_device *dev, - unsigned int chan) -{ - return S626_GET_STD_CLKMULT(s626_get_mode(dev, chan)); -} - /* * Return/set the clock polarity. */ @@ -1188,12 +1182,6 @@ static void s626_set_clk_pol(struct comedi_device *dev, s626_set_mode(dev, chan, mode, false); } -static uint16_t s626_get_clk_pol(struct comedi_device *dev, - unsigned int chan) -{ - return S626_GET_STD_CLKPOL(s626_get_mode(dev, chan)); -} - /* * Return/set the encoder mode. */ @@ -1209,27 +1197,6 @@ static void s626_set_enc_mode(struct comedi_device *dev, s626_set_mode(dev, chan, mode, false); } -static uint16_t s626_get_enc_mode(struct comedi_device *dev, - unsigned int chan) -{ - return S626_GET_STD_ENCMODE(s626_get_mode(dev, chan)); -} - -/* - * Return/set the index polarity. - */ -static void s626_set_index_pol(struct comedi_device *dev, - unsigned int chan, uint16_t value) -{ - uint16_t mode; - - mode = s626_get_mode(dev, chan); - mode &= ~S626_STDMSK_INDXPOL; - mode |= S626_SET_STD_INDXPOL(value != 0); - - s626_set_mode(dev, chan, mode, false); -} - static uint16_t s626_get_index_pol(struct comedi_device *dev, unsigned int chan) { diff --git a/drivers/staging/dgap/Kconfig b/drivers/staging/dgap/Kconfig deleted file mode 100644 index 3bbe9e122365..000000000000 --- a/drivers/staging/dgap/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config DGAP - tristate "Digi EPCA PCI products" - default n - depends on TTY && HAS_IOMEM - ---help--- - Driver for the Digi International EPCA PCI based product line diff --git a/drivers/staging/dgap/Makefile b/drivers/staging/dgap/Makefile deleted file mode 100644 index 0063d044ca71..000000000000 --- a/drivers/staging/dgap/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_DGAP) += dgap.o diff --git a/drivers/staging/dgap/dgap.c b/drivers/staging/dgap/dgap.c deleted file mode 100644 index 294c1c83aa4d..000000000000 --- a/drivers/staging/dgap/dgap.c +++ /dev/null @@ -1,7057 +0,0 @@ -/* - * Copyright 2003 Digi International (www.digi.com) - * Scott H Kilau - * - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - */ - -/* - * In the original out of kernel Digi dgap driver, firmware - * loading was done via user land to driver handshaking. - * - * For cards that support a concentrator (port expander), - * I believe the concentrator its self told the card which - * concentrator is actually attached and then that info - * was used to tell user land which concentrator firmware - * image was to be downloaded. I think even the BIOS or - * FEP images required could change with the connection - * of a particular concentrator. - * - * Since I have no access to any of these cards or - * concentrators, I cannot put the correct concentrator - * firmware file names into the firmware_info structure - * as is now done for the BIOS and FEP images. - * - * I think, but am not certain, that the cards supporting - * concentrators will function without them. So support - * of these cards has been left in this driver. - * - * In order to fully support those cards, they would - * either have to be acquired for dissection or maybe - * Digi International could provide some assistance. - */ -#undef DIGI_CONCENTRATORS_SUPPORTED - -#define pr_fmt(fmt) "dgap: " fmt - -#include -#include -#include -#include /* For udelay */ -#include -#include -#include - -#include /* For tasklet and interrupt structs/defines */ -#include -#include -#include -#include -#include /* For read[bwl]/write[bwl] */ - -#include -#include -#include -#include - -#include "dgap.h" - -/* - * File operations permitted on Control/Management major. - */ -static const struct file_operations dgap_board_fops = { - .owner = THIS_MODULE, -}; - -static uint dgap_numboards; -static struct board_t *dgap_board[MAXBOARDS]; -static ulong dgap_poll_counter; -static int dgap_driver_state = DRIVER_INITIALIZED; -static int dgap_poll_tick = 20; /* Poll interval - 20 ms */ - -static struct class *dgap_class; - -static uint dgap_count = 500; - -/* - * Poller stuff - */ -static DEFINE_SPINLOCK(dgap_poll_lock); /* Poll scheduling lock */ -static ulong dgap_poll_time; /* Time of next poll */ -static uint dgap_poll_stop; /* Used to tell poller to stop */ -static struct timer_list dgap_poll_timer; - -/* - SUPPORTED PRODUCTS - - Card Model Number of Ports Interface - ---------------------------------------------------------------- - Acceleport Xem 4 - 64 (EIA232 & EIA422) - Acceleport Xr 4 & 8 (EIA232) - Acceleport Xr 920 4 & 8 (EIA232) - Acceleport C/X 8 - 128 (EIA232) - Acceleport EPC/X 8 - 224 (EIA232) - Acceleport Xr/422 4 & 8 (EIA422) - Acceleport 2r/920 2 (EIA232) - Acceleport 4r/920 4 (EIA232) - Acceleport 8r/920 8 (EIA232) - - IBM 8-Port Asynchronous PCI Adapter (EIA232) - IBM 128-Port Asynchronous PCI Adapter (EIA232 & EIA422) -*/ - -static struct pci_device_id dgap_pci_tbl[] = { - { DIGI_VID, PCI_DEV_XEM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { DIGI_VID, PCI_DEV_CX_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { DIGI_VID, PCI_DEV_CX_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, - { DIGI_VID, PCI_DEV_EPCJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, - { DIGI_VID, PCI_DEV_920_2_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { DIGI_VID, PCI_DEV_920_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, - { DIGI_VID, PCI_DEV_920_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, - { DIGI_VID, PCI_DEV_XR_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, - { DIGI_VID, PCI_DEV_XRJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { DIGI_VID, PCI_DEV_XR_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, - { DIGI_VID, PCI_DEV_XR_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 }, - { DIGI_VID, PCI_DEV_XR_SAIP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 }, - { DIGI_VID, PCI_DEV_XR_BULL_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 }, - { DIGI_VID, PCI_DEV_920_8_HP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 }, - { DIGI_VID, PCI_DEV_XEM_HP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 }, - {0,} /* 0 terminated list. */ -}; -MODULE_DEVICE_TABLE(pci, dgap_pci_tbl); - -/* - * A generic list of Product names, PCI Vendor ID, and PCI Device ID. - */ -struct board_id { - uint config_type; - u8 *name; - uint maxports; - uint dpatype; -}; - -static struct board_id dgap_ids[] = { - {PPCM, PCI_DEV_XEM_NAME, 64, (T_PCXM | T_PCLITE | T_PCIBUS)}, - {PCX, PCI_DEV_CX_NAME, 128, (T_CX | T_PCIBUS) }, - {PCX, PCI_DEV_CX_IBM_NAME, 128, (T_CX | T_PCIBUS) }, - {PEPC, PCI_DEV_EPCJ_NAME, 224, (T_EPC | T_PCIBUS) }, - {APORT2_920P, PCI_DEV_920_2_NAME, 2, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {APORT4_920P, PCI_DEV_920_4_NAME, 4, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {APORT8_920P, PCI_DEV_920_8_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XRJ_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_422_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_IBM_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_SAIP_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_BULL_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {APORT8_920P, PCI_DEV_920_8_HP_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PPCM, PCI_DEV_XEM_HP_NAME, 64, (T_PCXM | T_PCLITE | T_PCIBUS)}, - {0,} /* 0 terminated list. */ -}; - -struct firmware_info { - u8 *conf_name; /* dgap.conf */ - u8 *bios_name; /* BIOS filename */ - u8 *fep_name; /* FEP filename */ - u8 *con_name; /* Concentrator filename FIXME*/ - int num; /* sequence number */ -}; - -/* - * Firmware - BIOS, FEP, and CONC filenames - */ -static struct firmware_info fw_info[] = { - { "dgap/dgap.conf", "dgap/sxbios.bin", "dgap/sxfep.bin", NULL, 0 }, - { "dgap/dgap.conf", "dgap/cxpbios.bin", "dgap/cxpfep.bin", NULL, 1 }, - { "dgap/dgap.conf", "dgap/cxpbios.bin", "dgap/cxpfep.bin", NULL, 2 }, - { "dgap/dgap.conf", "dgap/pcibios.bin", "dgap/pcifep.bin", NULL, 3 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 4 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 5 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 6 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 7 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 8 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 9 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 10 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 11 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 12 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 13 }, - { "dgap/dgap.conf", "dgap/sxbios.bin", "dgap/sxfep.bin", NULL, 14 }, - {NULL,} -}; - -/* - * Default transparent print information. - */ -static struct digi_t dgap_digi_init = { - .digi_flags = DIGI_COOK, /* Flags */ - .digi_maxcps = 100, /* Max CPS */ - .digi_maxchar = 50, /* Max chars in print queue */ - .digi_bufsize = 100, /* Printer buffer size */ - .digi_onlen = 4, /* size of printer on string */ - .digi_offlen = 4, /* size of printer off string */ - .digi_onstr = "\033[5i", /* ANSI printer on string ] */ - .digi_offstr = "\033[4i", /* ANSI printer off string ] */ - .digi_term = "ansi" /* default terminal type */ -}; - -/* - * Define a local default termios struct. All ports will be created - * with this termios initially. - * - * This defines a raw port at 9600 baud, 8 data bits, no parity, - * 1 stop bit. - */ - -static struct ktermios dgap_default_termios = { - .c_iflag = (DEFAULT_IFLAGS), /* iflags */ - .c_oflag = (DEFAULT_OFLAGS), /* oflags */ - .c_cflag = (DEFAULT_CFLAGS), /* cflags */ - .c_lflag = (DEFAULT_LFLAGS), /* lflags */ - .c_cc = INIT_C_CC, - .c_line = 0, -}; - -/* - * Our needed internal static variables from dgap_parse.c - */ -static struct cnode dgap_head; -#define MAXCWORD 200 -static char dgap_cword[MAXCWORD]; - -struct toklist { - int token; - char *string; -}; - -static struct toklist dgap_brdtype[] = { - { PCX, "Digi_AccelePort_C/X_PCI" }, - { PEPC, "Digi_AccelePort_EPC/X_PCI" }, - { PPCM, "Digi_AccelePort_Xem_PCI" }, - { APORT2_920P, "Digi_AccelePort_2r_920_PCI" }, - { APORT4_920P, "Digi_AccelePort_4r_920_PCI" }, - { APORT8_920P, "Digi_AccelePort_8r_920_PCI" }, - { PAPORT4, "Digi_AccelePort_4r_PCI(EIA-232/RS-422)" }, - { PAPORT8, "Digi_AccelePort_8r_PCI(EIA-232/RS-422)" }, - { 0, NULL } -}; - -static struct toklist dgap_tlist[] = { - { BEGIN, "config_begin" }, - { END, "config_end" }, - { BOARD, "board" }, - { PCIINFO, "pciinfo" }, - { LINE, "line" }, - { CONC, "conc" }, - { CONC, "concentrator" }, - { CX, "cx" }, - { CX, "ccon" }, - { EPC, "epccon" }, - { EPC, "epc" }, - { MOD, "module" }, - { ID, "id" }, - { STARTO, "start" }, - { SPEED, "speed" }, - { CABLE, "cable" }, - { CONNECT, "connect" }, - { METHOD, "method" }, - { STATUS, "status" }, - { CUSTOM, "Custom" }, - { BASIC, "Basic" }, - { MEM, "mem" }, - { MEM, "memory" }, - { PORTS, "ports" }, - { MODEM, "modem" }, - { NPORTS, "nports" }, - { TTYN, "ttyname" }, - { CU, "cuname" }, - { PRINT, "prname" }, - { CMAJOR, "major" }, - { ALTPIN, "altpin" }, - { USEINTR, "useintr" }, - { TTSIZ, "ttysize" }, - { CHSIZ, "chsize" }, - { BSSIZ, "boardsize" }, - { UNTSIZ, "schedsize" }, - { F2SIZ, "f2200size" }, - { VPSIZ, "vpixsize" }, - { 0, NULL } -}; - -/* - * get a word from the input stream, also keep track of current line number. - * words are separated by whitespace. - */ -static char *dgap_getword(char **in) -{ - char *ret_ptr = *in; - - char *ptr = strpbrk(*in, " \t\n"); - - /* If no word found, return null */ - if (!ptr) - return NULL; - - /* Mark new location for our buffer */ - *ptr = '\0'; - *in = ptr + 1; - - /* Eat any extra spaces/tabs/newlines that might be present */ - while (*in && **in && ((**in == ' ') || - (**in == '\t') || - (**in == '\n'))) { - **in = '\0'; - *in = *in + 1; - } - - return ret_ptr; -} - - -/* - * Get a token from the input file; return 0 if end of file is reached - */ -static int dgap_gettok(char **in) -{ - char *w; - struct toklist *t; - - if (strstr(dgap_cword, "board")) { - w = dgap_getword(in); - if (!w) - return 0; - snprintf(dgap_cword, MAXCWORD, "%s", w); - for (t = dgap_brdtype; t->token != 0; t++) { - if (!strcmp(w, t->string)) - return t->token; - } - } else { - while ((w = dgap_getword(in))) { - snprintf(dgap_cword, MAXCWORD, "%s", w); - for (t = dgap_tlist; t->token != 0; t++) { - if (!strcmp(w, t->string)) - return t->token; - } - } - } - - return 0; -} - -/* - * dgap_checknode: see if all the necessary info has been supplied for a node - * before creating the next node. - */ -static int dgap_checknode(struct cnode *p) -{ - switch (p->type) { - case LNODE: - if (p->u.line.v_speed == 0) { - pr_err("line speed not specified"); - return 1; - } - return 0; - - case CNODE: - if (p->u.conc.v_speed == 0) { - pr_err("concentrator line speed not specified"); - return 1; - } - if (p->u.conc.v_nport == 0) { - pr_err("number of ports on concentrator not specified"); - return 1; - } - if (p->u.conc.v_id == 0) { - pr_err("concentrator id letter not specified"); - return 1; - } - return 0; - - case MNODE: - if (p->u.module.v_nport == 0) { - pr_err("number of ports on EBI module not specified"); - return 1; - } - if (p->u.module.v_id == 0) { - pr_err("EBI module id letter not specified"); - return 1; - } - return 0; - } - return 0; -} - -/* - * Given a board pointer, returns whether we should use interrupts or not. - */ -static uint dgap_config_get_useintr(struct board_t *bd) -{ - struct cnode *p; - - if (!bd) - return 0; - - for (p = bd->bd_config; p; p = p->next) { - if (p->type == INTRNODE) { - /* - * check for pcxr types. - */ - return p->u.useintr; - } - } - - /* If not found, then don't turn on interrupts. */ - return 0; -} - -/* - * Given a board pointer, returns whether we turn on altpin or not. - */ -static uint dgap_config_get_altpin(struct board_t *bd) -{ - struct cnode *p; - - if (!bd) - return 0; - - for (p = bd->bd_config; p; p = p->next) { - if (p->type == ANODE) { - /* - * check for pcxr types. - */ - return p->u.altpin; - } - } - - /* If not found, then don't turn on interrupts. */ - return 0; -} - -/* - * Given a specific type of board, if found, detached link and - * returns the first occurrence in the list. - */ -static struct cnode *dgap_find_config(int type, int bus, int slot) -{ - struct cnode *p, *prev, *prev2, *found; - - p = &dgap_head; - - while (p->next) { - prev = p; - p = p->next; - - if (p->type != BNODE) - continue; - - if (p->u.board.type != type) - continue; - - if (p->u.board.v_pcibus && - p->u.board.pcibus != bus) - continue; - - if (p->u.board.v_pcislot && - p->u.board.pcislot != slot) - continue; - - found = p; - /* - * Keep walking thru the list till we - * find the next board. - */ - while (p->next) { - prev2 = p; - p = p->next; - - if (p->type != BNODE) - continue; - - /* - * Mark the end of our 1 board - * chain of configs. - */ - prev2->next = NULL; - - /* - * Link the "next" board to the - * previous board, effectively - * "unlinking" our board from - * the main config. - */ - prev->next = p; - - return found; - } - /* - * It must be the last board in the list. - */ - prev->next = NULL; - return found; - } - return NULL; -} - -/* - * Given a board pointer, walks the config link, counting up - * all ports user specified should be on the board. - * (This does NOT mean they are all actually present right now tho) - */ -static uint dgap_config_get_num_prts(struct board_t *bd) -{ - int count = 0; - struct cnode *p; - - if (!bd) - return 0; - - for (p = bd->bd_config; p; p = p->next) { - switch (p->type) { - case BNODE: - /* - * check for pcxr types. - */ - if (p->u.board.type > EPCFE) - count += p->u.board.nport; - break; - case CNODE: - count += p->u.conc.nport; - break; - case MNODE: - count += p->u.module.nport; - break; - } - } - return count; -} - -static char *dgap_create_config_string(struct board_t *bd, char *string) -{ - char *ptr = string; - struct cnode *p; - struct cnode *q; - int speed; - - if (!bd) { - *ptr = 0xff; - return string; - } - - for (p = bd->bd_config; p; p = p->next) { - switch (p->type) { - case LNODE: - *ptr = '\0'; - ptr++; - *ptr = p->u.line.speed; - ptr++; - break; - case CNODE: - /* - * Because the EPC/con concentrators can have EM modules - * hanging off of them, we have to walk ahead in the - * list and keep adding the number of ports on each EM - * to the config. UGH! - */ - speed = p->u.conc.speed; - q = p->next; - if (q && (q->type == MNODE)) { - *ptr = (p->u.conc.nport + 0x80); - ptr++; - p = q; - while (q->next && (q->next->type) == MNODE) { - *ptr = (q->u.module.nport + 0x80); - ptr++; - p = q; - q = q->next; - } - *ptr = q->u.module.nport; - ptr++; - } else { - *ptr = p->u.conc.nport; - ptr++; - } - - *ptr = speed; - ptr++; - break; - } - } - - *ptr = 0xff; - return string; -} - -/* - * Parse a configuration file read into memory as a string. - */ -static int dgap_parsefile(char **in) -{ - struct cnode *p, *brd, *line, *conc; - int rc; - char *s; - int linecnt = 0; - - p = &dgap_head; - brd = line = conc = NULL; - - /* perhaps we are adding to an existing list? */ - while (p->next) - p = p->next; - - /* file must start with a BEGIN */ - while ((rc = dgap_gettok(in)) != BEGIN) { - if (rc == 0) { - pr_err("unexpected EOF"); - return -1; - } - } - - for (; ;) { - int board_type = 0; - int conc_type = 0; - int module_type = 0; - - rc = dgap_gettok(in); - if (rc == 0) { - pr_err("unexpected EOF"); - return -1; - } - - switch (rc) { - case BEGIN: /* should only be 1 begin */ - pr_err("unexpected config_begin\n"); - return -1; - - case END: - return 0; - - case BOARD: /* board info */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - - p->type = BNODE; - p->u.board.status = kstrdup("No", GFP_KERNEL); - line = conc = NULL; - brd = p; - linecnt = -1; - - board_type = dgap_gettok(in); - if (board_type == 0) { - pr_err("board !!type not specified"); - return -1; - } - - p->u.board.type = board_type; - - break; - - case MEM: /* memory address */ - if (p->type != BNODE) { - pr_err("memory address only valid for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.board.addrstr); - p->u.board.addrstr = kstrdup(s, GFP_KERNEL); - if (kstrtoul(s, 0, &p->u.board.addr)) { - pr_err("bad number for memory address"); - return -1; - } - p->u.board.v_addr = 1; - break; - - case PCIINFO: /* pci information */ - if (p->type != BNODE) { - pr_err("memory address only valid for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.board.pcibusstr); - p->u.board.pcibusstr = kstrdup(s, GFP_KERNEL); - if (kstrtoul(s, 0, &p->u.board.pcibus)) { - pr_err("bad number for pci bus"); - return -1; - } - p->u.board.v_pcibus = 1; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.board.pcislotstr); - p->u.board.pcislotstr = kstrdup(s, GFP_KERNEL); - if (kstrtoul(s, 0, &p->u.board.pcislot)) { - pr_err("bad number for pci slot"); - return -1; - } - p->u.board.v_pcislot = 1; - break; - - case METHOD: - if (p->type != BNODE) { - pr_err("install method only valid for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.board.method); - p->u.board.method = kstrdup(s, GFP_KERNEL); - p->u.board.v_method = 1; - break; - - case STATUS: - if (p->type != BNODE) { - pr_err("config status only valid for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.board.status); - p->u.board.status = kstrdup(s, GFP_KERNEL); - break; - - case NPORTS: /* number of ports */ - if (p->type == BNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.board.nport)) { - pr_err("bad number for number of ports"); - return -1; - } - p->u.board.v_nport = 1; - } else if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.conc.nport)) { - pr_err("bad number for number of ports"); - return -1; - } - p->u.conc.v_nport = 1; - } else if (p->type == MNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.module.nport)) { - pr_err("bad number for number of ports"); - return -1; - } - p->u.module.v_nport = 1; - } else { - pr_err("nports only valid for concentrators or modules"); - return -1; - } - break; - - case ID: /* letter ID used in tty name */ - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.board.status); - p->u.board.status = kstrdup(s, GFP_KERNEL); - - if (p->type == CNODE) { - kfree(p->u.conc.id); - p->u.conc.id = kstrdup(s, GFP_KERNEL); - p->u.conc.v_id = 1; - } else if (p->type == MNODE) { - kfree(p->u.module.id); - p->u.module.id = kstrdup(s, GFP_KERNEL); - p->u.module.v_id = 1; - } else { - pr_err("id only valid for concentrators or modules"); - return -1; - } - break; - - case STARTO: /* start offset of ID */ - if (p->type == BNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.board.start)) { - pr_err("bad number for start of tty count"); - return -1; - } - p->u.board.v_start = 1; - } else if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.conc.start)) { - pr_err("bad number for start of tty count"); - return -1; - } - p->u.conc.v_start = 1; - } else if (p->type == MNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.module.start)) { - pr_err("bad number for start of tty count"); - return -1; - } - p->u.module.v_start = 1; - } else { - pr_err("start only valid for concentrators or modules"); - return -1; - } - break; - - case TTYN: /* tty name prefix */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = TNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpeced end of file"); - return -1; - } - p->u.ttyname = kstrdup(s, GFP_KERNEL); - if (!p->u.ttyname) - return -1; - - break; - - case CU: /* cu name prefix */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = CUNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpeced end of file"); - return -1; - } - p->u.cuname = kstrdup(s, GFP_KERNEL); - if (!p->u.cuname) - return -1; - - break; - - case LINE: /* line information */ - if (dgap_checknode(p)) - return -1; - if (!brd) { - pr_err("must specify board before line info"); - return -1; - } - switch (brd->u.board.type) { - case PPCM: - pr_err("line not valid for PC/em"); - return -1; - } - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = LNODE; - conc = NULL; - line = p; - linecnt++; - break; - - case CONC: /* concentrator information */ - if (dgap_checknode(p)) - return -1; - if (!line) { - pr_err("must specify line info before concentrator"); - return -1; - } - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = CNODE; - conc = p; - - if (linecnt) - brd->u.board.conc2++; - else - brd->u.board.conc1++; - - conc_type = dgap_gettok(in); - if (conc_type == 0 || - (conc_type != CX && conc_type != EPC)) { - pr_err("failed to set a type of concentratros"); - return -1; - } - - p->u.conc.type = conc_type; - - break; - - case MOD: /* EBI module */ - if (dgap_checknode(p)) - return -1; - if (!brd) { - pr_err("must specify board info before EBI modules"); - return -1; - } - switch (brd->u.board.type) { - case PPCM: - linecnt = 0; - break; - default: - if (!conc) { - pr_err("must specify concentrator info before EBI module"); - return -1; - } - } - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = MNODE; - - if (linecnt) - brd->u.board.module2++; - else - brd->u.board.module1++; - - module_type = dgap_gettok(in); - if (module_type == 0 || - (module_type != PORTS && module_type != MODEM)) { - pr_err("failed to set a type of module"); - return -1; - } - - p->u.module.type = module_type; - - break; - - case CABLE: - if (p->type == LNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.line.cable); - p->u.line.cable = kstrdup(s, GFP_KERNEL); - p->u.line.v_cable = 1; - } - break; - - case SPEED: /* sync line speed indication */ - if (p->type == LNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.line.speed)) { - pr_err("bad number for line speed"); - return -1; - } - p->u.line.v_speed = 1; - } else if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.conc.speed)) { - pr_err("bad number for line speed"); - return -1; - } - p->u.conc.v_speed = 1; - } else { - pr_err("speed valid only for lines or concentrators."); - return -1; - } - break; - - case CONNECT: - if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.conc.connect); - p->u.conc.connect = kstrdup(s, GFP_KERNEL); - p->u.conc.v_connect = 1; - } - break; - case PRINT: /* transparent print name prefix */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = PNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpeced end of file"); - return -1; - } - p->u.printname = kstrdup(s, GFP_KERNEL); - if (!p->u.printname) - return -1; - - break; - - case CMAJOR: /* major number */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = JNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.majornumber)) { - pr_err("bad number for major number"); - return -1; - } - break; - - case ALTPIN: /* altpin setting */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = ANODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.altpin)) { - pr_err("bad number for altpin"); - return -1; - } - break; - - case USEINTR: /* enable interrupt setting */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = INTRNODE; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.useintr)) { - pr_err("bad number for useintr"); - return -1; - } - break; - - case TTSIZ: /* size of tty structure */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = TSNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.ttysize)) { - pr_err("bad number for ttysize"); - return -1; - } - break; - - case CHSIZ: /* channel structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = CSNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.chsize)) { - pr_err("bad number for chsize"); - return -1; - } - break; - - case BSSIZ: /* board structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = BSNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.bssize)) { - pr_err("bad number for bssize"); - return -1; - } - break; - - case UNTSIZ: /* sched structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = USNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.unsize)) { - pr_err("bad number for schedsize"); - return -1; - } - break; - - case F2SIZ: /* f2200 structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = FSNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.f2size)) { - pr_err("bad number for f2200size"); - return -1; - } - break; - - case VPSIZ: /* vpix structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = VSNODE; - - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.vpixsize)) { - pr_err("bad number for vpixsize"); - return -1; - } - break; - } - } -} - -static void dgap_cleanup_nodes(void) -{ - struct cnode *p; - - p = &dgap_head; - - while (p) { - struct cnode *tmp = p->next; - - if (p->type == NULLNODE) { - p = tmp; - continue; - } - - switch (p->type) { - case BNODE: - kfree(p->u.board.addrstr); - kfree(p->u.board.pcibusstr); - kfree(p->u.board.pcislotstr); - kfree(p->u.board.method); - break; - case CNODE: - kfree(p->u.conc.id); - kfree(p->u.conc.connect); - break; - case MNODE: - kfree(p->u.module.id); - break; - case TNODE: - kfree(p->u.ttyname); - break; - case CUNODE: - kfree(p->u.cuname); - break; - case LNODE: - kfree(p->u.line.cable); - break; - case PNODE: - kfree(p->u.printname); - break; - } - - kfree(p->u.board.status); - kfree(p); - p = tmp; - } -} - -/* - * Retrives the current custom baud rate from FEP memory, - * and returns it back to the user. - * Returns 0 on error. - */ -static uint dgap_get_custom_baud(struct channel_t *ch) -{ - u8 __iomem *vaddr; - ulong offset; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - - if (!ch->ch_bd || ch->ch_bd->magic != DGAP_BOARD_MAGIC) - return 0; - - if (!(ch->ch_bd->bd_flags & BD_FEP5PLUS)) - return 0; - - vaddr = ch->ch_bd->re_map_membase; - - if (!vaddr) - return 0; - - /* - * Go get from fep mem, what the fep - * believes the custom baud rate is. - */ - offset = (ioread16(vaddr + ECS_SEG) << 4) + (ch->ch_portnum * 0x28) - + LINE_SPEED; - - return readw(vaddr + offset); -} - -/* - * Remap PCI memory. - */ -static int dgap_remap(struct board_t *brd) -{ - if (!brd || brd->magic != DGAP_BOARD_MAGIC) - return -EIO; - - if (!request_mem_region(brd->membase, 0x200000, "dgap")) - return -ENOMEM; - - if (!request_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000, "dgap")) - goto err_req_mem; - - brd->re_map_membase = ioremap(brd->membase, 0x200000); - if (!brd->re_map_membase) - goto err_remap_mem; - - brd->re_map_port = ioremap((brd->membase + PCI_IO_OFFSET), 0x200000); - if (!brd->re_map_port) - goto err_remap_port; - - return 0; - -err_remap_port: - iounmap(brd->re_map_membase); -err_remap_mem: - release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); -err_req_mem: - release_mem_region(brd->membase, 0x200000); - - return -ENOMEM; -} - -static void dgap_unmap(struct board_t *brd) -{ - iounmap(brd->re_map_port); - iounmap(brd->re_map_membase); - release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); - release_mem_region(brd->membase, 0x200000); -} - -/* - * dgap_parity_scan() - * - * Convert the FEP5 way of reporting parity errors and breaks into - * the Linux line discipline way. - */ -static void dgap_parity_scan(struct channel_t *ch, unsigned char *cbuf, - unsigned char *fbuf, int *len) -{ - int l = *len; - int count = 0; - unsigned char *in, *cout, *fout; - unsigned char c; - - in = cbuf; - cout = cbuf; - fout = fbuf; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - while (l--) { - c = *in++; - switch (ch->pscan_state) { - default: - /* reset to sanity and fall through */ - ch->pscan_state = 0; - - case 0: - /* No FF seen yet */ - if (c == (unsigned char)'\377') - /* delete this character from stream */ - ch->pscan_state = 1; - else { - *cout++ = c; - *fout++ = TTY_NORMAL; - count += 1; - } - break; - - case 1: - /* first FF seen */ - if (c == (unsigned char)'\377') { - /* doubled ff, transform to single ff */ - *cout++ = c; - *fout++ = TTY_NORMAL; - count += 1; - ch->pscan_state = 0; - } else { - /* save value examination in next state */ - ch->pscan_savechar = c; - ch->pscan_state = 2; - } - break; - - case 2: - /* third character of ff sequence */ - - *cout++ = c; - - if (ch->pscan_savechar == 0x0) { - if (c == 0x0) { - ch->ch_err_break++; - *fout++ = TTY_BREAK; - } else { - ch->ch_err_parity++; - *fout++ = TTY_PARITY; - } - } - - count += 1; - ch->pscan_state = 0; - } - } - *len = count; -} - -/*======================================================================= - * - * dgap_input - Process received data. - * - * ch - Pointer to channel structure. - * - *=======================================================================*/ - -static void dgap_input(struct channel_t *ch) -{ - struct board_t *bd; - struct bs_t __iomem *bs; - struct tty_struct *tp; - struct tty_ldisc *ld; - uint rmask; - uint head; - uint tail; - int data_len; - ulong lock_flags; - ulong lock_flags2; - int flip_len; - int len; - int n; - u8 *buf; - u8 tmpchar; - int s; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - tp = ch->ch_tun.un_tty; - - bs = ch->ch_bs; - if (!bs) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - /* - * Figure the number of characters in the buffer. - * Exit immediately if none. - */ - - rmask = ch->ch_rsize - 1; - - head = readw(&bs->rx_head); - head &= rmask; - tail = readw(&bs->rx_tail); - tail &= rmask; - - data_len = (head - tail) & rmask; - - if (data_len == 0) { - writeb(1, &bs->idata); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - - /* - * If the device is not open, or CREAD is off, flush - * input data and return immediately. - */ - if ((bd->state != BOARD_READY) || !tp || - (tp->magic != TTY_MAGIC) || - !(ch->ch_tun.un_flags & UN_ISOPEN) || - !C_CREAD(tp) || - (ch->ch_tun.un_flags & UN_CLOSING)) { - writew(head, &bs->rx_tail); - writeb(1, &bs->idata); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - - /* - * If we are throttled, simply don't read any data. - */ - if (ch->ch_flags & CH_RXBLOCK) { - writeb(1, &bs->idata); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - - /* - * Ignore oruns. - */ - tmpchar = readb(&bs->orun); - if (tmpchar) { - ch->ch_err_overrun++; - writeb(0, &bs->orun); - } - - /* Decide how much data we can send into the tty layer */ - flip_len = TTY_FLIPBUF_SIZE; - - /* Chop down the length, if needed */ - len = min(data_len, flip_len); - len = min(len, (N_TTY_BUF_SIZE - 1)); - - ld = tty_ldisc_ref(tp); - -#ifdef TTY_DONT_FLIP - /* - * If the DONT_FLIP flag is on, don't flush our buffer, and act - * like the ld doesn't have any space to put the data right now. - */ - if (test_bit(TTY_DONT_FLIP, &tp->flags)) - len = 0; -#endif - - /* - * If we were unable to get a reference to the ld, - * don't flush our buffer, and act like the ld doesn't - * have any space to put the data right now. - */ - if (!ld) { - len = 0; - } else { - /* - * If ld doesn't have a pointer to a receive_buf function, - * flush the data, then act like the ld doesn't have any - * space to put the data right now. - */ - if (!ld->ops->receive_buf) { - writew(head, &bs->rx_tail); - len = 0; - } - } - - if (len <= 0) { - writeb(1, &bs->idata); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (ld) - tty_ldisc_deref(ld); - return; - } - - buf = ch->ch_bd->flipbuf; - n = len; - - /* - * n now contains the most amount of data we can copy, - * bounded either by our buffer size or the amount - * of data the card actually has pending... - */ - while (n) { - s = ((head >= tail) ? head : ch->ch_rsize) - tail; - s = min(s, n); - - if (s <= 0) - break; - - memcpy_fromio(buf, ch->ch_raddr + tail, s); - - tail += s; - buf += s; - - n -= s; - /* Flip queue if needed */ - tail &= rmask; - } - - writew(tail, &bs->rx_tail); - writeb(1, &bs->idata); - ch->ch_rxcount += len; - - /* - * If we are completely raw, we don't need to go through a lot - * of the tty layers that exist. - * In this case, we take the shortest and fastest route we - * can to relay the data to the user. - * - * On the other hand, if we are not raw, we need to go through - * the tty layer, which has its API more well defined. - */ - if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { - dgap_parity_scan(ch, ch->ch_bd->flipbuf, - ch->ch_bd->flipflagbuf, &len); - - len = tty_buffer_request_room(tp->port, len); - tty_insert_flip_string_flags(tp->port, ch->ch_bd->flipbuf, - ch->ch_bd->flipflagbuf, len); - } else { - len = tty_buffer_request_room(tp->port, len); - tty_insert_flip_string(tp->port, ch->ch_bd->flipbuf, len); - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - /* Tell the tty layer its okay to "eat" the data now */ - tty_flip_buffer_push(tp->port); - - if (ld) - tty_ldisc_deref(ld); -} - -static void dgap_write_wakeup(struct board_t *bd, struct channel_t *ch, - struct un_t *un, u32 mask) -{ - if (!(un->un_flags & mask)) - return; - - un->un_flags &= ~mask; - - if (!(un->un_flags & UN_ISOPEN)) - return; - - tty_wakeup(un->un_tty); - wake_up_interruptible(&un->un_flags_wait); -} - -/************************************************************************ - * Determines when CARRIER changes state and takes appropriate - * action. - ************************************************************************/ -static void dgap_carrier(struct channel_t *ch) -{ - struct board_t *bd; - - int virt_carrier = 0; - int phys_carrier = 0; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - /* Make sure altpin is always set correctly */ - if (ch->ch_digi.digi_flags & DIGI_ALTPIN) { - ch->ch_dsr = DM_CD; - ch->ch_cd = DM_DSR; - } else { - ch->ch_dsr = DM_DSR; - ch->ch_cd = DM_CD; - } - - if (ch->ch_mistat & D_CD(ch)) - phys_carrier = 1; - - if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) - virt_carrier = 1; - - if (ch->ch_c_cflag & CLOCAL) - virt_carrier = 1; - - /* - * Test for a VIRTUAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Test for a PHYSICAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Test for a PHYSICAL transition to low, so long as we aren't - * currently ignoring physical transitions (which is what "virtual - * carrier" indicates). - * - * The transition of the virtual carrier to low really doesn't - * matter... it really only means "ignore carrier state", not - * "make pretend that carrier is there". - */ - if ((virt_carrier == 0) && - ((ch->ch_flags & CH_CD) != 0) && - (phys_carrier == 0)) { - /* - * When carrier drops: - * - * Drop carrier on all open units. - * - * Flush queues, waking up any task waiting in the - * line discipline. - * - * Send a hangup to the control terminal. - * - * Enable all select calls. - */ - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - - if (ch->ch_tun.un_open_count > 0) - tty_hangup(ch->ch_tun.un_tty); - - if (ch->ch_pun.un_open_count > 0) - tty_hangup(ch->ch_pun.un_tty); - } - - /* - * Make sure that our cached values reflect the current reality. - */ - if (virt_carrier == 1) - ch->ch_flags |= CH_FCAR; - else - ch->ch_flags &= ~CH_FCAR; - - if (phys_carrier == 1) - ch->ch_flags |= CH_CD; - else - ch->ch_flags &= ~CH_CD; -} - -/*======================================================================= - * - * dgap_event - FEP to host event processing routine. - * - * bd - Board of current event. - * - *=======================================================================*/ -static int dgap_event(struct board_t *bd) -{ - struct channel_t *ch; - ulong lock_flags; - ulong lock_flags2; - struct bs_t __iomem *bs; - u8 __iomem *event; - u8 __iomem *vaddr; - struct ev_t __iomem *eaddr; - uint head; - uint tail; - int port; - int reason; - int modem; - - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -EIO; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - - vaddr = bd->re_map_membase; - - if (!vaddr) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EIO; - } - - eaddr = (struct ev_t __iomem *)(vaddr + EVBUF); - - /* Get our head and tail */ - head = readw(&eaddr->ev_head); - tail = readw(&eaddr->ev_tail); - - /* - * Forget it if pointers out of range. - */ - - if (head >= EVMAX - EVSTART || tail >= EVMAX - EVSTART || - (head | tail) & 03) { - /* Let go of board lock */ - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EIO; - } - - /* - * Loop to process all the events in the buffer. - */ - while (tail != head) { - /* - * Get interrupt information. - */ - - event = bd->re_map_membase + tail + EVSTART; - - port = ioread8(event); - reason = ioread8(event + 1); - modem = ioread8(event + 2); - ioread8(event + 3); - - /* - * Make sure the interrupt is valid. - */ - if (port >= bd->nasync) - goto next; - - if (!(reason & (IFMODEM | IFBREAK | IFTLW | IFTEM | IFDATA))) - goto next; - - ch = bd->channels[port]; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - goto next; - - /* - * If we have made it here, the event was valid. - * Lock down the channel. - */ - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - bs = ch->ch_bs; - - if (!bs) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - goto next; - } - - /* - * Process received data. - */ - if (reason & IFDATA) { - /* - * ALL LOCKS *MUST* BE DROPPED BEFORE CALLING INPUT! - * input could send some data to ld, which in turn - * could do a callback to one of our other functions. - */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - dgap_input(ch); - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - if (ch->ch_flags & CH_RACTIVE) - ch->ch_flags |= CH_RENABLE; - else - writeb(1, &bs->idata); - - if (ch->ch_flags & CH_RWAIT) { - ch->ch_flags &= ~CH_RWAIT; - - wake_up_interruptible - (&ch->ch_tun.un_flags_wait); - } - } - - /* - * Process Modem change signals. - */ - if (reason & IFMODEM) { - ch->ch_mistat = modem; - dgap_carrier(ch); - } - - /* - * Process break. - */ - if (reason & IFBREAK) { - if (ch->ch_tun.un_tty) { - /* A break has been indicated */ - ch->ch_err_break++; - tty_buffer_request_room - (ch->ch_tun.un_tty->port, 1); - tty_insert_flip_char(ch->ch_tun.un_tty->port, - 0, TTY_BREAK); - tty_flip_buffer_push(ch->ch_tun.un_tty->port); - } - } - - /* - * Process Transmit low. - */ - if (reason & IFTLW) { - dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_LOW); - dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_LOW); - if (ch->ch_flags & CH_WLOW) { - ch->ch_flags &= ~CH_WLOW; - wake_up_interruptible(&ch->ch_flags_wait); - } - } - - /* - * Process Transmit empty. - */ - if (reason & IFTEM) { - dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_EMPTY); - dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_EMPTY); - if (ch->ch_flags & CH_WEMPTY) { - ch->ch_flags &= ~CH_WEMPTY; - wake_up_interruptible(&ch->ch_flags_wait); - } - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - -next: - tail = (tail + 4) & (EVMAX - EVSTART - 4); - } - - writew(tail, &eaddr->ev_tail); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; -} - -/* - * Our board poller function. - */ -static void dgap_poll_tasklet(unsigned long data) -{ - struct board_t *bd = (struct board_t *)data; - ulong lock_flags; - char __iomem *vaddr; - u16 head, tail; - - if (!bd || (bd->magic != DGAP_BOARD_MAGIC)) - return; - - if (bd->inhibit_poller) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - - vaddr = bd->re_map_membase; - - /* - * If board is ready, parse deeper to see if there is anything to do. - */ - if (bd->state == BOARD_READY) { - struct ev_t __iomem *eaddr; - - if (!bd->re_map_membase) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - if (!bd->re_map_port) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - - if (!bd->nasync) - goto out; - - eaddr = (struct ev_t __iomem *)(vaddr + EVBUF); - - /* Get our head and tail */ - head = readw(&eaddr->ev_head); - tail = readw(&eaddr->ev_tail); - - /* - * If there is an event pending. Go service it. - */ - if (head != tail) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - dgap_event(bd); - spin_lock_irqsave(&bd->bd_lock, lock_flags); - } - -out: - /* - * If board is doing interrupts, ACK the interrupt. - */ - if (bd->intr_running) - readb(bd->re_map_port + 2); - - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -/* - * dgap_found_board() - * - * A board has been found, init it. - */ -static struct board_t *dgap_found_board(struct pci_dev *pdev, int id, - int boardnum) -{ - struct board_t *brd; - unsigned int pci_irq; - int i; - int ret; - - /* get the board structure and prep it */ - brd = kzalloc(sizeof(struct board_t), GFP_KERNEL); - if (!brd) - return ERR_PTR(-ENOMEM); - - /* store the info for the board we've found */ - brd->magic = DGAP_BOARD_MAGIC; - brd->boardnum = boardnum; - brd->vendor = dgap_pci_tbl[id].vendor; - brd->device = dgap_pci_tbl[id].device; - brd->pdev = pdev; - brd->pci_bus = pdev->bus->number; - brd->pci_slot = PCI_SLOT(pdev->devfn); - brd->name = dgap_ids[id].name; - brd->maxports = dgap_ids[id].maxports; - brd->type = dgap_ids[id].config_type; - brd->dpatype = dgap_ids[id].dpatype; - brd->dpastatus = BD_NOFEP; - init_waitqueue_head(&brd->state_wait); - - spin_lock_init(&brd->bd_lock); - - brd->inhibit_poller = FALSE; - brd->wait_for_bios = 0; - brd->wait_for_fep = 0; - - for (i = 0; i < MAXPORTS; i++) - brd->channels[i] = NULL; - - /* store which card & revision we have */ - pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor); - pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice); - pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev); - - pci_irq = pdev->irq; - brd->irq = pci_irq; - - /* get the PCI Base Address Registers */ - - /* Xr Jupiter and EPC use BAR 2 */ - if (brd->device == PCI_DEV_XRJ_DID || brd->device == PCI_DEV_EPCJ_DID) { - brd->membase = pci_resource_start(pdev, 2); - brd->membase_end = pci_resource_end(pdev, 2); - } - /* Everyone else uses BAR 0 */ - else { - brd->membase = pci_resource_start(pdev, 0); - brd->membase_end = pci_resource_end(pdev, 0); - } - - if (!brd->membase) { - ret = -ENODEV; - goto free_brd; - } - - if (brd->membase & 1) - brd->membase &= ~3; - else - brd->membase &= ~15; - - /* - * On the PCI boards, there is no IO space allocated - * The I/O registers will be in the first 3 bytes of the - * upper 2MB of the 4MB memory space. The board memory - * will be mapped into the low 2MB of the 4MB memory space - */ - brd->port = brd->membase + PCI_IO_OFFSET; - brd->port_end = brd->port + PCI_IO_SIZE_DGAP; - - /* - * Special initialization for non-PLX boards - */ - if (brd->device != PCI_DEV_XRJ_DID && brd->device != PCI_DEV_EPCJ_DID) { - unsigned short cmd; - - pci_write_config_byte(pdev, 0x40, 0); - pci_write_config_byte(pdev, 0x46, 0); - - /* Limit burst length to 2 doubleword transactions */ - pci_write_config_byte(pdev, 0x42, 1); - - /* - * Enable IO and mem if not already done. - * This was needed for support on Itanium. - */ - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - pci_write_config_word(pdev, PCI_COMMAND, cmd); - } - - /* init our poll helper tasklet */ - tasklet_init(&brd->helper_tasklet, dgap_poll_tasklet, - (unsigned long)brd); - - ret = dgap_remap(brd); - if (ret) - goto free_brd; - - pr_info("dgap: board %d: %s (rev %d), irq %ld\n", - boardnum, brd->name, brd->rev, brd->irq); - - return brd; - -free_brd: - kfree(brd); - - return ERR_PTR(ret); -} - -/* - * dgap_intr() - * - * Driver interrupt handler. - */ -static irqreturn_t dgap_intr(int irq, void *voidbrd) -{ - struct board_t *brd = voidbrd; - - if (!brd) - return IRQ_NONE; - - /* - * Check to make sure its for us. - */ - if (brd->magic != DGAP_BOARD_MAGIC) - return IRQ_NONE; - - brd->intr_count++; - - /* - * Schedule tasklet to run at a better time. - */ - tasklet_schedule(&brd->helper_tasklet); - return IRQ_HANDLED; -} - -/***************************************************************************** -* -* Function: -* -* dgap_poll_handler -* -* Author: -* -* Scott H Kilau -* -* Parameters: -* -* dummy -- ignored -* -* Return Values: -* -* none -* -* Description: -* -* As each timer expires, it determines (a) whether the "transmit" -* waiter needs to be woken up, and (b) whether the poller needs to -* be rescheduled. -* -******************************************************************************/ - -static void dgap_poll_handler(ulong dummy) -{ - unsigned int i; - struct board_t *brd; - unsigned long lock_flags; - ulong new_time; - - dgap_poll_counter++; - - /* - * Do not start the board state machine until - * driver tells us its up and running, and has - * everything it needs. - */ - if (dgap_driver_state != DRIVER_READY) - goto schedule_poller; - - /* - * If we have just 1 board, or the system is not SMP, - * then use the typical old style poller. - * Otherwise, use our new tasklet based poller, which should - * speed things up for multiple boards. - */ - if ((dgap_numboards == 1) || (num_online_cpus() <= 1)) { - for (i = 0; i < dgap_numboards; i++) { - brd = dgap_board[i]; - - if (brd->state == BOARD_FAILED) - continue; - if (!brd->intr_running) - /* Call the real board poller directly */ - dgap_poll_tasklet((unsigned long)brd); - } - } else { - /* - * Go thru each board, kicking off a - * tasklet for each if needed - */ - for (i = 0; i < dgap_numboards; i++) { - brd = dgap_board[i]; - - /* - * Attempt to grab the board lock. - * - * If we can't get it, no big deal, the next poll - * will get it. Basically, I just really don't want - * to spin in here, because I want to kick off my - * tasklets as fast as I can, and then get out the - * poller. - */ - if (!spin_trylock(&brd->bd_lock)) - continue; - - /* - * If board is in a failed state, don't bother - * scheduling a tasklet - */ - if (brd->state == BOARD_FAILED) { - spin_unlock(&brd->bd_lock); - continue; - } - - /* Schedule a poll helper task */ - if (!brd->intr_running) - tasklet_schedule(&brd->helper_tasklet); - - /* - * Can't do DGAP_UNLOCK here, as we don't have - * lock_flags because we did a trylock above. - */ - spin_unlock(&brd->bd_lock); - } - } - -schedule_poller: - - /* - * Schedule ourself back at the nominal wakeup interval. - */ - spin_lock_irqsave(&dgap_poll_lock, lock_flags); - dgap_poll_time += dgap_jiffies_from_ms(dgap_poll_tick); - - new_time = dgap_poll_time - jiffies; - - if ((ulong)new_time >= 2 * dgap_poll_tick) { - dgap_poll_time = - jiffies + dgap_jiffies_from_ms(dgap_poll_tick); - } - - dgap_poll_timer.function = dgap_poll_handler; - dgap_poll_timer.data = 0; - dgap_poll_timer.expires = dgap_poll_time; - spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - - if (!dgap_poll_stop) - add_timer(&dgap_poll_timer); -} - -/*======================================================================= - * - * dgap_cmdb - Sends a 2 byte command to the FEP. - * - * ch - Pointer to channel structure. - * cmd - Command to be sent. - * byte1 - Integer containing first byte to be sent. - * byte2 - Integer containing second byte to be sent. - * ncmds - Wait until ncmds or fewer cmds are left - * in the cmd buffer before returning. - * - *=======================================================================*/ -static void dgap_cmdb(struct channel_t *ch, u8 cmd, u8 byte1, - u8 byte2, uint ncmds) -{ - char __iomem *vaddr; - struct __iomem cm_t *cm_addr; - uint count; - uint n; - u16 head; - u16 tail; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * Check if board is still alive. - */ - if (ch->ch_bd->state == BOARD_FAILED) - return; - - /* - * Make sure the pointers are in range before - * writing to the FEP memory. - */ - vaddr = ch->ch_bd->re_map_membase; - - if (!vaddr) - return; - - cm_addr = (struct cm_t __iomem *)(vaddr + CMDBUF); - head = readw(&cm_addr->cm_head); - - /* - * Forget it if pointers out of range. - */ - if (head >= (CMDMAX - CMDSTART) || (head & 03)) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - - /* - * Put the data in the circular command buffer. - */ - writeb(cmd, (vaddr + head + CMDSTART + 0)); - writeb((u8)ch->ch_portnum, (vaddr + head + CMDSTART + 1)); - writeb(byte1, (vaddr + head + CMDSTART + 2)); - writeb(byte2, (vaddr + head + CMDSTART + 3)); - - head = (head + 4) & (CMDMAX - CMDSTART - 4); - - writew(head, &cm_addr->cm_head); - - /* - * Wait if necessary before updating the head - * pointer to limit the number of outstanding - * commands to the FEP. If the time spent waiting - * is outlandish, declare the FEP dead. - */ - for (count = dgap_count ;;) { - head = readw(&cm_addr->cm_head); - tail = readw(&cm_addr->cm_tail); - - n = (head - tail) & (CMDMAX - CMDSTART - 4); - - if (n <= ncmds * sizeof(struct cm_t)) - break; - - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } -} - -/*======================================================================= - * - * dgap_cmdw - Sends a 1 word command to the FEP. - * - * ch - Pointer to channel structure. - * cmd - Command to be sent. - * word - Integer containing word to be sent. - * ncmds - Wait until ncmds or fewer cmds are left - * in the cmd buffer before returning. - * - *=======================================================================*/ -static void dgap_cmdw(struct channel_t *ch, u8 cmd, u16 word, uint ncmds) -{ - char __iomem *vaddr; - struct __iomem cm_t *cm_addr; - uint count; - uint n; - u16 head; - u16 tail; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * Check if board is still alive. - */ - if (ch->ch_bd->state == BOARD_FAILED) - return; - - /* - * Make sure the pointers are in range before - * writing to the FEP memory. - */ - vaddr = ch->ch_bd->re_map_membase; - if (!vaddr) - return; - - cm_addr = (struct cm_t __iomem *)(vaddr + CMDBUF); - head = readw(&cm_addr->cm_head); - - /* - * Forget it if pointers out of range. - */ - if (head >= (CMDMAX - CMDSTART) || (head & 03)) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - - /* - * Put the data in the circular command buffer. - */ - writeb(cmd, (vaddr + head + CMDSTART + 0)); - writeb((u8)ch->ch_portnum, (vaddr + head + CMDSTART + 1)); - writew((u16)word, (vaddr + head + CMDSTART + 2)); - - head = (head + 4) & (CMDMAX - CMDSTART - 4); - - writew(head, &cm_addr->cm_head); - - /* - * Wait if necessary before updating the head - * pointer to limit the number of outstanding - * commands to the FEP. If the time spent waiting - * is outlandish, declare the FEP dead. - */ - for (count = dgap_count ;;) { - head = readw(&cm_addr->cm_head); - tail = readw(&cm_addr->cm_tail); - - n = (head - tail) & (CMDMAX - CMDSTART - 4); - - if (n <= ncmds * sizeof(struct cm_t)) - break; - - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } -} - -/*======================================================================= - * - * dgap_cmdw_ext - Sends a extended word command to the FEP. - * - * ch - Pointer to channel structure. - * cmd - Command to be sent. - * word - Integer containing word to be sent. - * ncmds - Wait until ncmds or fewer cmds are left - * in the cmd buffer before returning. - * - *=======================================================================*/ -static void dgap_cmdw_ext(struct channel_t *ch, u16 cmd, u16 word, uint ncmds) -{ - char __iomem *vaddr; - struct __iomem cm_t *cm_addr; - uint count; - uint n; - u16 head; - u16 tail; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * Check if board is still alive. - */ - if (ch->ch_bd->state == BOARD_FAILED) - return; - - /* - * Make sure the pointers are in range before - * writing to the FEP memory. - */ - vaddr = ch->ch_bd->re_map_membase; - if (!vaddr) - return; - - cm_addr = (struct cm_t __iomem *)(vaddr + CMDBUF); - head = readw(&cm_addr->cm_head); - - /* - * Forget it if pointers out of range. - */ - if (head >= (CMDMAX - CMDSTART) || (head & 03)) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - - /* - * Put the data in the circular command buffer. - */ - - /* Write an FF to tell the FEP that we want an extended command */ - writeb((u8)0xff, (vaddr + head + CMDSTART + 0)); - - writeb((u8)ch->ch_portnum, (vaddr + head + CMDSTART + 1)); - writew((u16)cmd, (vaddr + head + CMDSTART + 2)); - - /* - * If the second part of the command won't fit, - * put it at the beginning of the circular buffer. - */ - if (((head + 4) >= ((CMDMAX - CMDSTART)) || (head & 03))) - writew((u16)word, (vaddr + CMDSTART)); - else - writew((u16)word, (vaddr + head + CMDSTART + 4)); - - head = (head + 8) & (CMDMAX - CMDSTART - 4); - - writew(head, &cm_addr->cm_head); - - /* - * Wait if necessary before updating the head - * pointer to limit the number of outstanding - * commands to the FEP. If the time spent waiting - * is outlandish, declare the FEP dead. - */ - for (count = dgap_count ;;) { - head = readw(&cm_addr->cm_head); - tail = readw(&cm_addr->cm_tail); - - n = (head - tail) & (CMDMAX - CMDSTART - 4); - - if (n <= ncmds * sizeof(struct cm_t)) - break; - - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } -} - -/*======================================================================= - * - * dgap_wmove - Write data to FEP buffer. - * - * ch - Pointer to channel structure. - * buf - Pointer to characters to be moved. - * cnt - Number of characters to move. - * - *=======================================================================*/ -static void dgap_wmove(struct channel_t *ch, char *buf, uint cnt) -{ - int n; - char __iomem *taddr; - struct bs_t __iomem *bs; - u16 head; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * Check parameters. - */ - bs = ch->ch_bs; - head = readw(&bs->tx_head); - - /* - * If pointers are out of range, just return. - */ - if ((cnt > ch->ch_tsize) || - (unsigned)(head - ch->ch_tstart) >= ch->ch_tsize) - return; - - /* - * If the write wraps over the top of the circular buffer, - * move the portion up to the wrap point, and reset the - * pointers to the bottom. - */ - n = ch->ch_tstart + ch->ch_tsize - head; - - if (cnt >= n) { - cnt -= n; - taddr = ch->ch_taddr + head; - memcpy_toio(taddr, buf, n); - head = ch->ch_tstart; - buf += n; - } - - /* - * Move rest of data. - */ - taddr = ch->ch_taddr + head; - n = cnt; - memcpy_toio(taddr, buf, n); - head += cnt; - - writew(head, &bs->tx_head); -} - -/* - * Calls the firmware to reset this channel. - */ -static void dgap_firmware_reset_port(struct channel_t *ch) -{ - dgap_cmdb(ch, CHRESET, 0, 0, 0); - - /* - * Now that the channel is reset, we need to make sure - * all the current settings get reapplied to the port - * in the firmware. - * - * So we will set the driver's cache of firmware - * settings all to 0, and then call param. - */ - ch->ch_fepiflag = 0; - ch->ch_fepcflag = 0; - ch->ch_fepoflag = 0; - ch->ch_fepstartc = 0; - ch->ch_fepstopc = 0; - ch->ch_fepastartc = 0; - ch->ch_fepastopc = 0; - ch->ch_mostat = 0; - ch->ch_hflow = 0; -} - -/*======================================================================= - * - * dgap_param - Set Digi parameters. - * - * struct tty_struct * - TTY for port. - * - *=======================================================================*/ -static int dgap_param(struct channel_t *ch, struct board_t *bd, u32 un_type) -{ - u16 head; - u16 cflag; - u16 iflag; - u8 mval; - u8 hflow; - - /* - * If baud rate is zero, flush queues, and set mval to drop DTR. - */ - if ((ch->ch_c_cflag & (CBAUD)) == 0) { - /* flush rx */ - head = readw(&ch->ch_bs->rx_head); - writew(head, &ch->ch_bs->rx_tail); - - /* flush tx */ - head = readw(&ch->ch_bs->tx_head); - writew(head, &ch->ch_bs->tx_tail); - - ch->ch_flags |= (CH_BAUD0); - - /* Drop RTS and DTR */ - ch->ch_mval &= ~(D_RTS(ch) | D_DTR(ch)); - mval = D_DTR(ch) | D_RTS(ch); - ch->ch_baud_info = 0; - - } else if (ch->ch_custom_speed && (bd->bd_flags & BD_FEP5PLUS)) { - /* - * Tell the fep to do the command - */ - - dgap_cmdw_ext(ch, 0xff01, ch->ch_custom_speed, 0); - - /* - * Now go get from fep mem, what the fep - * believes the custom baud rate is. - */ - ch->ch_custom_speed = dgap_get_custom_baud(ch); - ch->ch_baud_info = ch->ch_custom_speed; - - /* Handle transition from B0 */ - if (ch->ch_flags & CH_BAUD0) { - ch->ch_flags &= ~(CH_BAUD0); - ch->ch_mval |= (D_RTS(ch) | D_DTR(ch)); - } - mval = D_DTR(ch) | D_RTS(ch); - - } else { - /* - * Set baud rate, character size, and parity. - */ - - - int iindex = 0; - int jindex = 0; - int baud = 0; - - ulong bauds[4][16] = { - { /* slowbaud */ - 0, 50, 75, 110, - 134, 150, 200, 300, - 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 }, - { /* slowbaud & CBAUDEX */ - 0, 57600, 115200, 230400, - 460800, 150, 200, 921600, - 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 }, - { /* fastbaud */ - 0, 57600, 76800, 115200, - 14400, 57600, 230400, 76800, - 115200, 230400, 28800, 460800, - 921600, 9600, 19200, 38400 }, - { /* fastbaud & CBAUDEX */ - 0, 57600, 115200, 230400, - 460800, 150, 200, 921600, - 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 } - }; - - /* - * Only use the TXPrint baud rate if the - * terminal unit is NOT open - */ - if (!(ch->ch_tun.un_flags & UN_ISOPEN) && - un_type == DGAP_PRINT) - baud = C_BAUD(ch->ch_pun.un_tty) & 0xff; - else - baud = C_BAUD(ch->ch_tun.un_tty) & 0xff; - - if (ch->ch_c_cflag & CBAUDEX) - iindex = 1; - - if (ch->ch_digi.digi_flags & DIGI_FAST) - iindex += 2; - - jindex = baud; - - if ((iindex >= 0) && (iindex < 4) && - (jindex >= 0) && (jindex < 16)) - baud = bauds[iindex][jindex]; - else - baud = 0; - - if (baud == 0) - baud = 9600; - - ch->ch_baud_info = baud; - - /* - * CBAUD has bit position 0x1000 set these days to - * indicate Linux baud rate remap. - * We use a different bit assignment for high speed. - * Clear this bit out while grabbing the parts of - * "cflag" we want. - */ - cflag = ch->ch_c_cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | - CSTOPB | CSIZE); - - /* - * HUPCL bit is used by FEP to indicate fast baud - * table is to be used. - */ - if ((ch->ch_digi.digi_flags & DIGI_FAST) || - (ch->ch_c_cflag & CBAUDEX)) - cflag |= HUPCL; - - if ((ch->ch_c_cflag & CBAUDEX) && - !(ch->ch_digi.digi_flags & DIGI_FAST)) { - /* - * The below code is trying to guarantee that only - * baud rates 115200, 230400, 460800, 921600 are - * remapped. We use exclusive or because the various - * baud rates share common bit positions and therefore - * can't be tested for easily. - */ - tcflag_t tcflag = (ch->ch_c_cflag & CBAUD) | CBAUDEX; - int baudpart = 0; - - /* - * Map high speed requests to index - * into FEP's baud table - */ - switch (tcflag) { - case B57600: - baudpart = 1; - break; -#ifdef B76800 - case B76800: - baudpart = 2; - break; -#endif - case B115200: - baudpart = 3; - break; - case B230400: - baudpart = 9; - break; - case B460800: - baudpart = 11; - break; -#ifdef B921600 - case B921600: - baudpart = 12; - break; -#endif - default: - baudpart = 0; - } - - if (baudpart) - cflag = (cflag & ~(CBAUD | CBAUDEX)) | baudpart; - } - - cflag &= 0xffff; - - if (cflag != ch->ch_fepcflag) { - ch->ch_fepcflag = (u16)(cflag & 0xffff); - - /* - * Okay to have channel and board - * locks held calling this - */ - dgap_cmdw(ch, SCFLAG, (u16)cflag, 0); - } - - /* Handle transition from B0 */ - if (ch->ch_flags & CH_BAUD0) { - ch->ch_flags &= ~(CH_BAUD0); - ch->ch_mval |= (D_RTS(ch) | D_DTR(ch)); - } - mval = D_DTR(ch) | D_RTS(ch); - } - - /* - * Get input flags. - */ - iflag = ch->ch_c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | - INPCK | ISTRIP | IXON | IXANY | IXOFF); - - if ((ch->ch_startc == _POSIX_VDISABLE) || - (ch->ch_stopc == _POSIX_VDISABLE)) { - iflag &= ~(IXON | IXOFF); - ch->ch_c_iflag &= ~(IXON | IXOFF); - } - - /* - * Only the IBM Xr card can switch between - * 232 and 422 modes on the fly - */ - if (bd->device == PCI_DEV_XR_IBM_DID) { - if (ch->ch_digi.digi_flags & DIGI_422) - dgap_cmdb(ch, SCOMMODE, MODE_422, 0, 0); - else - dgap_cmdb(ch, SCOMMODE, MODE_232, 0, 0); - } - - if (ch->ch_digi.digi_flags & DIGI_ALTPIN) - iflag |= IALTPIN; - - if (iflag != ch->ch_fepiflag) { - ch->ch_fepiflag = iflag; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdw(ch, SIFLAG, (u16)ch->ch_fepiflag, 0); - } - - /* - * Select hardware handshaking. - */ - hflow = 0; - - if (ch->ch_c_cflag & CRTSCTS) - hflow |= (D_RTS(ch) | D_CTS(ch)); - if (ch->ch_digi.digi_flags & RTSPACE) - hflow |= D_RTS(ch); - if (ch->ch_digi.digi_flags & DTRPACE) - hflow |= D_DTR(ch); - if (ch->ch_digi.digi_flags & CTSPACE) - hflow |= D_CTS(ch); - if (ch->ch_digi.digi_flags & DSRPACE) - hflow |= D_DSR(ch); - if (ch->ch_digi.digi_flags & DCDPACE) - hflow |= D_CD(ch); - - if (hflow != ch->ch_hflow) { - ch->ch_hflow = hflow; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SHFLOW, (u8)hflow, 0xff, 0); - } - - /* - * Set RTS and/or DTR Toggle if needed, - * but only if product is FEP5+ based. - */ - if (bd->bd_flags & BD_FEP5PLUS) { - u16 hflow2 = 0; - - if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) - hflow2 |= (D_RTS(ch)); - if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) - hflow2 |= (D_DTR(ch)); - - dgap_cmdw_ext(ch, 0xff03, hflow2, 0); - } - - /* - * Set modem control lines. - */ - - mval ^= ch->ch_mforce & (mval ^ ch->ch_mval); - - if (ch->ch_mostat ^ mval) { - ch->ch_mostat = mval; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SMODEM, (u8)mval, D_RTS(ch) | D_DTR(ch), 0); - } - - /* - * Read modem signals, and then call carrier function. - */ - ch->ch_mistat = readb(&ch->ch_bs->m_stat); - dgap_carrier(ch); - - /* - * Set the start and stop characters. - */ - if (ch->ch_startc != ch->ch_fepstartc || - ch->ch_stopc != ch->ch_fepstopc) { - ch->ch_fepstartc = ch->ch_startc; - ch->ch_fepstopc = ch->ch_stopc; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SFLOWC, ch->ch_fepstartc, ch->ch_fepstopc, 0); - } - - /* - * Set the Auxiliary start and stop characters. - */ - if (ch->ch_astartc != ch->ch_fepastartc || - ch->ch_astopc != ch->ch_fepastopc) { - ch->ch_fepastartc = ch->ch_astartc; - ch->ch_fepastopc = ch->ch_astopc; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SAFLOWC, ch->ch_fepastartc, ch->ch_fepastopc, 0); - } - - return 0; -} - -/* - * dgap_block_til_ready() - * - * Wait for DCD, if needed. - */ -static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, - struct channel_t *ch) -{ - int retval = 0; - struct un_t *un; - ulong lock_flags; - uint old_flags; - int sleep_on_un_flags; - - if (!tty || tty->magic != TTY_MAGIC || !file || !ch || - ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - ch->ch_wopen++; - - /* Loop forever */ - while (1) { - sleep_on_un_flags = 0; - - /* - * If board has failed somehow during our sleep, - * bail with error. - */ - if (ch->ch_bd->state == BOARD_FAILED) { - retval = -EIO; - break; - } - - /* If tty was hung up, break out of loop and set error. */ - if (tty_hung_up_p(file)) { - retval = -EAGAIN; - break; - } - - /* - * If either unit is in the middle of the fragile part of close, - * we just cannot touch the channel safely. - * Go back to sleep, knowing that when the channel can be - * touched safely, the close routine will signal the - * ch_wait_flags to wake us back up. - */ - if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & - UN_CLOSING)) { - /* - * Our conditions to leave cleanly and happily: - * 1) NONBLOCKING on the tty is set. - * 2) CLOCAL is set. - * 3) DCD (fake or real) is active. - */ - - if (file->f_flags & O_NONBLOCK) - break; - - if (tty->flags & (1 << TTY_IO_ERROR)) - break; - - if (ch->ch_flags & CH_CD) - break; - - if (ch->ch_flags & CH_FCAR) - break; - } else { - sleep_on_un_flags = 1; - } - - /* - * If there is a signal pending, the user probably - * interrupted (ctrl-c) us. - * Leave loop with error set. - */ - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - /* - * Store the flags before we let go of channel lock - */ - if (sleep_on_un_flags) - old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags; - else - old_flags = ch->ch_flags; - - /* - * Let go of channel lock before calling schedule. - * Our poller will get any FEP events and wake us up when DCD - * eventually goes active. - */ - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* - * Wait for something in the flags to change - * from the current value. - */ - if (sleep_on_un_flags) { - retval = wait_event_interruptible(un->un_flags_wait, - (old_flags != (ch->ch_tun.un_flags | - ch->ch_pun.un_flags))); - } else { - retval = wait_event_interruptible(ch->ch_flags_wait, - (old_flags != ch->ch_flags)); - } - - /* - * We got woken up for some reason. - * Before looping around, grab our channel lock. - */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - } - - ch->ch_wopen--; - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return retval; -} - -/* - * dgap_tty_flush_buffer() - * - * Flush Tx buffer (make in == out) - */ -static void dgap_tty_flush_buffer(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - u16 head; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - ch->ch_flags &= ~CH_STOP; - head = readw(&ch->ch_bs->tx_head); - dgap_cmdw(ch, FLUSHTX, (u16)head, 0); - dgap_cmdw(ch, RESUMETX, 0, 0); - if (ch->ch_tun.un_flags & (UN_LOW | UN_EMPTY)) { - ch->ch_tun.un_flags &= ~(UN_LOW | UN_EMPTY); - wake_up_interruptible(&ch->ch_tun.un_flags_wait); - } - if (ch->ch_pun.un_flags & (UN_LOW | UN_EMPTY)) { - ch->ch_pun.un_flags &= ~(UN_LOW | UN_EMPTY); - wake_up_interruptible(&ch->ch_pun.un_flags_wait); - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - tty_wakeup(tty); -} - -/* - * dgap_tty_hangup() - * - * Hangup the port. Like a close, but don't wait for output to drain. - */ -static void dgap_tty_hangup(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - /* flush the transmit queues */ - dgap_tty_flush_buffer(tty); -} - -/* - * dgap_tty_chars_in_buffer() - * - * Return number of characters that have not been transmitted yet. - * - * This routine is used by the line discipline to determine if there - * is data waiting to be transmitted/drained/flushed or not. - */ -static int dgap_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - u8 tbusy; - uint chars; - u16 thead, ttail, tmask, chead, ctail; - ulong lock_flags = 0; - ulong lock_flags2 = 0; - - if (!tty) - return 0; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - - bs = ch->ch_bs; - if (!bs) - return 0; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - tmask = (ch->ch_tsize - 1); - - /* Get Transmit queue pointers */ - thead = readw(&bs->tx_head) & tmask; - ttail = readw(&bs->tx_tail) & tmask; - - /* Get tbusy flag */ - tbusy = readb(&bs->tbusy); - - /* Get Command queue pointers */ - chead = readw(&ch->ch_cm->cm_head); - ctail = readw(&ch->ch_cm->cm_tail); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - /* - * The only way we know for sure if there is no pending - * data left to be transferred, is if: - * 1) Transmit head and tail are equal (empty). - * 2) Command queue head and tail are equal (empty). - * 3) The "TBUSY" flag is 0. (Transmitter not busy). - */ - - if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) { - chars = 0; - } else { - if (thead >= ttail) - chars = thead - ttail; - else - chars = thead - ttail + ch->ch_tsize; - /* - * Fudge factor here. - * If chars is zero, we know that the command queue had - * something in it or tbusy was set. Because we cannot - * be sure if there is still some data to be transmitted, - * lets lie, and tell ld we have 1 byte left. - */ - if (chars == 0) { - /* - * If TBUSY is still set, and our tx buffers are empty, - * force the firmware to send me another wakeup after - * TBUSY has been cleared. - */ - if (tbusy != 0) { - spin_lock_irqsave(&ch->ch_lock, lock_flags); - un->un_flags |= UN_EMPTY; - writeb(1, &bs->iempty); - spin_unlock_irqrestore(&ch->ch_lock, - lock_flags); - } - chars = 1; - } - } - - return chars; -} - -static int dgap_wait_for_drain(struct tty_struct *tty) -{ - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - int ret = 0; - uint count = 1; - ulong lock_flags = 0; - - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - bs = ch->ch_bs; - if (!bs) - return -EIO; - - /* Loop until data is drained */ - while (count != 0) { - count = dgap_tty_chars_in_buffer(tty); - - if (count == 0) - break; - - /* Set flag waiting for drain */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - un->un_flags |= UN_EMPTY; - writeb(1, &bs->iempty); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* Go to sleep till we get woken up */ - ret = wait_event_interruptible(un->un_flags_wait, - ((un->un_flags & UN_EMPTY) == 0)); - /* If ret is non-zero, user ctrl-c'ed us */ - if (ret) - break; - } - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - un->un_flags &= ~(UN_EMPTY); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return ret; -} - -/* - * dgap_maxcps_room - * - * Reduces bytes_available to the max number of characters - * that can be sent currently given the maxcps value, and - * returns the new bytes_available. This only affects printer - * output. - */ -static int dgap_maxcps_room(struct channel_t *ch, struct un_t *un, - int bytes_available) -{ - /* - * If its not the Transparent print device, return - * the full data amount. - */ - if (un->un_type != DGAP_PRINT) - return bytes_available; - - if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0) { - int cps_limit = 0; - unsigned long current_time = jiffies; - unsigned long buffer_time = current_time + - (HZ * ch->ch_digi.digi_bufsize) / - ch->ch_digi.digi_maxcps; - - if (ch->ch_cpstime < current_time) { - /* buffer is empty */ - ch->ch_cpstime = current_time; /* reset ch_cpstime */ - cps_limit = ch->ch_digi.digi_bufsize; - } else if (ch->ch_cpstime < buffer_time) { - /* still room in the buffer */ - cps_limit = ((buffer_time - ch->ch_cpstime) * - ch->ch_digi.digi_maxcps) / HZ; - } else { - /* no room in the buffer */ - cps_limit = 0; - } - - bytes_available = min(cps_limit, bytes_available); - } - - return bytes_available; -} - -static inline void dgap_set_firmware_event(struct un_t *un, unsigned int event) -{ - struct channel_t *ch; - struct bs_t __iomem *bs; - - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - bs = ch->ch_bs; - if (!bs) - return; - - if ((event & UN_LOW) != 0) { - if ((un->un_flags & UN_LOW) == 0) { - un->un_flags |= UN_LOW; - writeb(1, &bs->ilow); - } - } - if ((event & UN_LOW) != 0) { - if ((un->un_flags & UN_EMPTY) == 0) { - un->un_flags |= UN_EMPTY; - writeb(1, &bs->iempty); - } - } -} - -/* - * dgap_tty_write_room() - * - * Return space available in Tx buffer - */ -static int dgap_tty_write_room(struct tty_struct *tty) -{ - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - u16 head, tail, tmask; - int ret; - ulong lock_flags = 0; - - if (!tty) - return 0; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - - bs = ch->ch_bs; - if (!bs) - return 0; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - tmask = ch->ch_tsize - 1; - head = readw(&bs->tx_head) & tmask; - tail = readw(&bs->tx_tail) & tmask; - - ret = tail - head - 1; - if (ret < 0) - ret += ch->ch_tsize; - - /* Limit printer to maxcps */ - ret = dgap_maxcps_room(ch, un, ret); - - /* - * If we are printer device, leave space for - * possibly both the on and off strings. - */ - if (un->un_type == DGAP_PRINT) { - if (!(ch->ch_flags & CH_PRON)) - ret -= ch->ch_digi.digi_onlen; - ret -= ch->ch_digi.digi_offlen; - } else { - if (ch->ch_flags & CH_PRON) - ret -= ch->ch_digi.digi_offlen; - } - - if (ret < 0) - ret = 0; - - /* - * Schedule FEP to wake us up if needed. - * - * TODO: This might be overkill... - * Do we really need to schedule callbacks from the FEP - * in every case? Can we get smarter based on ret? - */ - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return ret; -} - -/* - * dgap_tty_write() - * - * Take data from the user or kernel and send it out to the FEP. - * In here exists all the Transparent Print magic as well. - */ -static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, - int count) -{ - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - char __iomem *vaddr; - u16 head, tail, tmask, remain; - int bufcount, n; - ulong lock_flags; - - if (!tty) - return 0; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - - bs = ch->ch_bs; - if (!bs) - return 0; - - if (!count) - return 0; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - /* Get our space available for the channel from the board */ - tmask = ch->ch_tsize - 1; - head = readw(&(bs->tx_head)) & tmask; - tail = readw(&(bs->tx_tail)) & tmask; - - bufcount = tail - head - 1; - if (bufcount < 0) - bufcount += ch->ch_tsize; - - /* - * Limit printer output to maxcps overall, with bursts allowed - * up to bufsize characters. - */ - bufcount = dgap_maxcps_room(ch, un, bufcount); - - /* - * Take minimum of what the user wants to send, and the - * space available in the FEP buffer. - */ - count = min(count, bufcount); - - /* - * Bail if no space left. - */ - if (count <= 0) { - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - return 0; - } - - /* - * Output the printer ON string, if we are in terminal mode, but - * need to be in printer mode. - */ - if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_onstr, - (int)ch->ch_digi.digi_onlen); - head = readw(&bs->tx_head) & tmask; - ch->ch_flags |= CH_PRON; - } - - /* - * On the other hand, output the printer OFF string, if we are - * currently in printer mode, but need to output to the terminal. - */ - if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int)ch->ch_digi.digi_offlen); - head = readw(&bs->tx_head) & tmask; - ch->ch_flags &= ~CH_PRON; - } - - n = count; - - /* - * If the write wraps over the top of the circular buffer, - * move the portion up to the wrap point, and reset the - * pointers to the bottom. - */ - remain = ch->ch_tstart + ch->ch_tsize - head; - - if (n >= remain) { - n -= remain; - vaddr = ch->ch_taddr + head; - - memcpy_toio(vaddr, (u8 *)buf, remain); - - head = ch->ch_tstart; - buf += remain; - } - - if (n > 0) { - /* - * Move rest of data. - */ - vaddr = ch->ch_taddr + head; - remain = n; - - memcpy_toio(vaddr, (u8 *)buf, remain); - head += remain; - } - - if (count) { - ch->ch_txcount += count; - head &= tmask; - writew(head, &bs->tx_head); - } - - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - - /* - * If this is the print device, and the - * printer is still on, we need to turn it - * off before going idle. If the buffer is - * non-empty, wait until it goes empty. - * Otherwise turn it off right now. - */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - tail = readw(&bs->tx_tail) & tmask; - - if (tail != head) { - un->un_flags |= UN_EMPTY; - writeb(1, &bs->iempty); - } else { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int)ch->ch_digi.digi_offlen); - head = readw(&bs->tx_head) & tmask; - ch->ch_flags &= ~CH_PRON; - } - } - - /* Update printer buffer empty time. */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0) - && (ch->ch_digi.digi_bufsize > 0)) { - ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return count; -} - -/* - * dgap_tty_put_char() - * - * Put a character into ch->ch_buf - * - * - used by the line discipline for OPOST processing - */ -static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c) -{ - /* - * Simply call tty_write. - */ - dgap_tty_write(tty, &c, 1); - return 1; -} - -/* - * Return modem signals to ld. - */ -static int dgap_tty_tiocmget(struct tty_struct *tty) -{ - struct channel_t *ch; - struct un_t *un; - int result; - u8 mstat; - ulong lock_flags; - - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - mstat = readb(&ch->ch_bs->m_stat); - /* Append any outbound signals that might be pending... */ - mstat |= ch->ch_mostat; - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - result = 0; - - if (mstat & D_DTR(ch)) - result |= TIOCM_DTR; - if (mstat & D_RTS(ch)) - result |= TIOCM_RTS; - if (mstat & D_CTS(ch)) - result |= TIOCM_CTS; - if (mstat & D_DSR(ch)) - result |= TIOCM_DSR; - if (mstat & D_RI(ch)) - result |= TIOCM_RI; - if (mstat & D_CD(ch)) - result |= TIOCM_CD; - - return result; -} - -/* - * dgap_tty_tiocmset() - * - * Set modem signals, called by ld. - */ -static int dgap_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -EIO; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - if (set & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } - - if (set & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval |= D_DTR(ch); - } - - if (clear & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval &= ~(D_RTS(ch)); - } - - if (clear & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval &= ~(D_DTR(ch)); - } - - dgap_param(ch, bd, un->un_type); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; -} - -/* - * dgap_tty_send_break() - * - * Send a Break, called by ld. - */ -static int dgap_tty_send_break(struct tty_struct *tty, int msec) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -EIO; - - switch (msec) { - case -1: - msec = 0xFFFF; - break; - case 0: - msec = 1; - break; - default: - msec /= 10; - break; - } - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); -#if 0 - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); -#endif - dgap_cmdw(ch, SBREAK, (u16)msec, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; -} - -/* - * dgap_tty_wait_until_sent() - * - * wait until data has been transmitted, called by ld. - */ -static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - dgap_wait_for_drain(tty); -} - -/* - * dgap_send_xchar() - * - * send a high priority character, called by ld. - */ -static void dgap_tty_send_xchar(struct tty_struct *tty, char c) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - /* - * This is technically what we should do. - * However, the NIST tests specifically want - * to see each XON or XOFF character that it - * sends, so lets just send each character - * by hand... - */ -#if 0 - if (c == STOP_CHAR(tty)) - dgap_cmdw(ch, RPAUSE, 0, 0); - else if (c == START_CHAR(tty)) - dgap_cmdw(ch, RRESUME, 0, 0); - else - dgap_wmove(ch, &c, 1); -#else - dgap_wmove(ch, &c, 1); -#endif - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -/* - * Return modem signals to ld. - */ -static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value) -{ - int result; - u8 mstat; - ulong lock_flags; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - mstat = readb(&ch->ch_bs->m_stat); - /* Append any outbound signals that might be pending... */ - mstat |= ch->ch_mostat; - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - result = 0; - - if (mstat & D_DTR(ch)) - result |= TIOCM_DTR; - if (mstat & D_RTS(ch)) - result |= TIOCM_RTS; - if (mstat & D_CTS(ch)) - result |= TIOCM_CTS; - if (mstat & D_DSR(ch)) - result |= TIOCM_DSR; - if (mstat & D_RI(ch)) - result |= TIOCM_RI; - if (mstat & D_CD(ch)) - result |= TIOCM_CD; - - return put_user(result, value); -} - -/* - * dgap_set_modem_info() - * - * Set modem signals, called by ld. - */ -static int dgap_set_modem_info(struct channel_t *ch, struct board_t *bd, - struct un_t *un, unsigned int command, - unsigned int __user *value) -{ - int ret; - unsigned int arg; - ulong lock_flags; - ulong lock_flags2; - - ret = get_user(arg, value); - if (ret) - return ret; - - switch (command) { - case TIOCMBIS: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } - - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval |= D_DTR(ch); - } - - break; - - case TIOCMBIC: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval &= ~(D_RTS(ch)); - } - - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval &= ~(D_DTR(ch)); - } - - break; - - case TIOCMSET: - ch->ch_mforce = D_DTR(ch) | D_RTS(ch); - - if (arg & TIOCM_RTS) - ch->ch_mval |= D_RTS(ch); - else - ch->ch_mval &= ~(D_RTS(ch)); - - if (arg & TIOCM_DTR) - ch->ch_mval |= (D_DTR(ch)); - else - ch->ch_mval &= ~(D_DTR(ch)); - - break; - - default: - return -EINVAL; - } - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - dgap_param(ch, bd, un->un_type); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; -} - -/* - * dgap_tty_digigeta() - * - * Ioctl to get the information for ditty. - * - * - * - */ -static int dgap_tty_digigeta(struct channel_t *ch, - struct digi_t __user *retinfo) -{ - struct digi_t tmp; - ulong lock_flags; - - if (!retinfo) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -/* - * dgap_tty_digiseta() - * - * Ioctl to set the information for ditty. - * - * - * - */ -static int dgap_tty_digiseta(struct channel_t *ch, struct board_t *bd, - struct un_t *un, struct digi_t __user *new_info) -{ - struct digi_t new_digi; - ulong lock_flags = 0; - unsigned long lock_flags2; - - if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) - return -EFAULT; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - memcpy(&ch->ch_digi, &new_digi, sizeof(struct digi_t)); - - if (ch->ch_digi.digi_maxcps < 1) - ch->ch_digi.digi_maxcps = 1; - - if (ch->ch_digi.digi_maxcps > 10000) - ch->ch_digi.digi_maxcps = 10000; - - if (ch->ch_digi.digi_bufsize < 10) - ch->ch_digi.digi_bufsize = 10; - - if (ch->ch_digi.digi_maxchar < 1) - ch->ch_digi.digi_maxchar = 1; - - if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) - ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; - - if (ch->ch_digi.digi_onlen > DIGI_PLEN) - ch->ch_digi.digi_onlen = DIGI_PLEN; - - if (ch->ch_digi.digi_offlen > DIGI_PLEN) - ch->ch_digi.digi_offlen = DIGI_PLEN; - - dgap_param(ch, bd, un->un_type); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; -} - -/* - * dgap_tty_digigetedelay() - * - * Ioctl to get the current edelay setting. - * - * - * - */ -static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo) -{ - struct channel_t *ch; - struct un_t *un; - int tmp; - ulong lock_flags; - - if (!retinfo) - return -EFAULT; - - if (!tty || tty->magic != TTY_MAGIC) - return -EFAULT; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EFAULT; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - tmp = readw(&ch->ch_bs->edelay); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -/* - * dgap_tty_digisetedelay() - * - * Ioctl to set the EDELAY setting - * - */ -static int dgap_tty_digisetedelay(struct channel_t *ch, struct board_t *bd, - struct un_t *un, int __user *new_info) -{ - int new_digi; - ulong lock_flags; - ulong lock_flags2; - - if (copy_from_user(&new_digi, new_info, sizeof(int))) - return -EFAULT; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - writew((u16)new_digi, &ch->ch_bs->edelay); - - dgap_param(ch, bd, un->un_type); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; -} - -/* - * dgap_tty_digigetcustombaud() - * - * Ioctl to get the current custom baud rate setting. - */ -static int dgap_tty_digigetcustombaud(struct channel_t *ch, struct un_t *un, - int __user *retinfo) -{ - int tmp; - ulong lock_flags; - - if (!retinfo) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - tmp = dgap_get_custom_baud(ch); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -/* - * dgap_tty_digisetcustombaud() - * - * Ioctl to set the custom baud rate setting - */ -static int dgap_tty_digisetcustombaud(struct channel_t *ch, struct board_t *bd, - struct un_t *un, int __user *new_info) -{ - uint new_rate; - ulong lock_flags; - ulong lock_flags2; - - if (copy_from_user(&new_rate, new_info, sizeof(unsigned int))) - return -EFAULT; - - if (bd->bd_flags & BD_FEP5PLUS) { - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - ch->ch_custom_speed = new_rate; - - dgap_param(ch, bd, un->un_type); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - } - - return 0; -} - -/* - * dgap_set_termios() - */ -static void dgap_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - unsigned long lock_flags; - unsigned long lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - ch->ch_c_cflag = tty->termios.c_cflag; - ch->ch_c_iflag = tty->termios.c_iflag; - ch->ch_c_oflag = tty->termios.c_oflag; - ch->ch_c_lflag = tty->termios.c_lflag; - ch->ch_startc = tty->termios.c_cc[VSTART]; - ch->ch_stopc = tty->termios.c_cc[VSTOP]; - - dgap_carrier(ch); - dgap_param(ch, bd, un->un_type); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -static void dgap_tty_throttle(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - ch->ch_flags |= (CH_RXBLOCK); -#if 1 - dgap_cmdw(ch, RPAUSE, 0, 0); -#endif - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -static void dgap_tty_unthrottle(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - ch->ch_flags &= ~(CH_RXBLOCK); - -#if 1 - dgap_cmdw(ch, RRESUME, 0, 0); -#endif - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -static struct board_t *find_board_by_major(unsigned int major) -{ - unsigned int i; - - for (i = 0; i < MAXBOARDS; i++) { - struct board_t *brd = dgap_board[i]; - - if (!brd) - return NULL; - if (major == brd->serial_driver->major || - major == brd->print_driver->major) - return brd; - } - - return NULL; -} - -/************************************************************************ - * - * TTY Entry points and helper functions - * - ************************************************************************/ - -/* - * dgap_tty_open() - * - */ -static int dgap_tty_open(struct tty_struct *tty, struct file *file) -{ - struct board_t *brd; - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - uint major; - uint minor; - int rc; - ulong lock_flags; - ulong lock_flags2; - u16 head; - - major = MAJOR(tty_devnum(tty)); - minor = MINOR(tty_devnum(tty)); - - brd = find_board_by_major(major); - if (!brd) - return -EIO; - - /* - * If board is not yet up to a state of READY, go to - * sleep waiting for it to happen or they cancel the open. - */ - rc = wait_event_interruptible(brd->state_wait, - (brd->state & BOARD_READY)); - - if (rc) - return rc; - - spin_lock_irqsave(&brd->bd_lock, lock_flags); - - /* The wait above should guarantee this cannot happen */ - if (brd->state != BOARD_READY) { - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } - - /* If opened device is greater than our number of ports, bail. */ - if (MINOR(tty_devnum(tty)) > brd->nasync) { - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } - - ch = brd->channels[minor]; - if (!ch) { - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } - - /* Grab channel lock */ - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - /* Figure out our type */ - if (major == brd->serial_driver->major) { - un = &brd->channels[minor]->ch_tun; - un->un_type = DGAP_SERIAL; - } else if (major == brd->print_driver->major) { - un = &brd->channels[minor]->ch_pun; - un->un_type = DGAP_PRINT; - } else { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } - - /* Store our unit into driver_data, so we always have it available. */ - tty->driver_data = un; - - /* - * Error if channel info pointer is NULL. - */ - bs = ch->ch_bs; - if (!bs) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } - - /* - * Initialize tty's - */ - if (!(un->un_flags & UN_ISOPEN)) { - /* Store important variables. */ - un->un_tty = tty; - - /* Maybe do something here to the TTY struct as well? */ - } - - /* - * Initialize if neither terminal or printer is open. - */ - if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { - ch->ch_mforce = 0; - ch->ch_mval = 0; - - /* - * Flush input queue. - */ - head = readw(&bs->rx_head); - writew(head, &bs->rx_tail); - - ch->ch_flags = 0; - ch->pscan_state = 0; - ch->pscan_savechar = 0; - - ch->ch_c_cflag = tty->termios.c_cflag; - ch->ch_c_iflag = tty->termios.c_iflag; - ch->ch_c_oflag = tty->termios.c_oflag; - ch->ch_c_lflag = tty->termios.c_lflag; - ch->ch_startc = tty->termios.c_cc[VSTART]; - ch->ch_stopc = tty->termios.c_cc[VSTOP]; - - /* TODO: flush our TTY struct here? */ - } - - dgap_carrier(ch); - /* - * Run param in case we changed anything - */ - dgap_param(ch, brd, un->un_type); - - /* - * follow protocol for opening port - */ - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - - rc = dgap_block_til_ready(tty, file, ch); - - if (!un->un_tty) - return -ENODEV; - - /* No going back now, increment our unit and channel counters */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - ch->ch_open_count++; - un->un_open_count++; - un->un_flags |= (UN_ISOPEN); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return rc; -} - -/* - * dgap_tty_close() - * - */ -static void dgap_tty_close(struct tty_struct *tty, struct file *file) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - /* - * Determine if this is the last close or not - and if we agree about - * which type of close it is with the Line Discipline - */ - if ((tty->count == 1) && (un->un_open_count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. un_open_count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - un->un_open_count = 1; - } - - if (--un->un_open_count < 0) - un->un_open_count = 0; - - ch->ch_open_count--; - - if (ch->ch_open_count && un->un_open_count) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - return; - } - - /* OK, its the last close on the unit */ - - un->un_flags |= UN_CLOSING; - - tty->closing = 1; - - /* - * Only officially close channel if count is 0 and - * DIGI_PRINTER bit is not set. - */ - if ((ch->ch_open_count == 0) && - !(ch->ch_digi.digi_flags & DIGI_PRINTER)) { - ch->ch_flags &= ~(CH_RXBLOCK); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* wait for output to drain */ - /* This will also return if we take an interrupt */ - - dgap_wait_for_drain(tty); - - dgap_tty_flush_buffer(tty); - tty_ldisc_flush(tty); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - tty->closing = 0; - - /* - * If we have HUPCL set, lower DTR and RTS - */ - if (ch->ch_c_cflag & HUPCL) { - ch->ch_mostat &= ~(D_RTS(ch) | D_DTR(ch)); - dgap_cmdb(ch, SMODEM, 0, D_DTR(ch) | D_RTS(ch), 0); - - /* - * Go to sleep to ensure RTS/DTR - * have been dropped for modems to see it. - */ - spin_unlock_irqrestore(&ch->ch_lock, - lock_flags); - - /* .25 second delay for dropping RTS/DTR */ - schedule_timeout_interruptible(msecs_to_jiffies(250)); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - } - - ch->pscan_state = 0; - ch->pscan_savechar = 0; - ch->ch_baud_info = 0; - } - - /* - * turn off print device when closing print device. - */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int)ch->ch_digi.digi_offlen); - ch->ch_flags &= ~CH_PRON; - } - - un->un_tty = NULL; - un->un_flags &= ~(UN_ISOPEN | UN_CLOSING); - tty->driver_data = NULL; - - wake_up_interruptible(&ch->ch_flags_wait); - wake_up_interruptible(&un->un_flags_wait); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); -} - -static void dgap_tty_start(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, RESUMETX, 0, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -static void dgap_tty_stop(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, PAUSETX, 0, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -/* - * dgap_tty_flush_chars() - * - * Flush the cook buffer - * - * Note to self, and any other poor souls who venture here: - * - * flush in this case DOES NOT mean dispose of the data. - * instead, it means "stop buffering and send it if you - * haven't already." Just guess how I figured that out... SRW 2-Jun-98 - * - * It is also always called in interrupt context - JAR 8-Sept-99 - */ -static void dgap_tty_flush_chars(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - /* TODO: Do something here */ - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -/***************************************************************************** - * - * The IOCTL function and all of its helpers - * - *****************************************************************************/ - -/* - * dgap_tty_ioctl() - * - * The usual assortment of ioctl's - */ -static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int rc; - u16 head; - ulong lock_flags = 0; - ulong lock_flags2 = 0; - void __user *uarg = (void __user *)arg; - - if (!tty || tty->magic != TTY_MAGIC) - return -ENODEV; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -ENODEV; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -ENODEV; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -ENODEV; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - if (un->un_open_count <= 0) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EIO; - } - - switch (cmd) { - /* Here are all the standard ioctl's that we MUST implement */ - case TCSBRK: - /* - * TCSBRK is SVID version: non-zero arg --> no break - * this behaviour is exploited by tcdrain(). - * - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds so we'll ask for something - * in the middle: 0.375 seconds. - */ - rc = tty_check_change(tty); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (rc) - return rc; - - rc = dgap_wait_for_drain(tty); - - if (rc) - return -EINTR; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) - dgap_cmdw(ch, SBREAK, (u16)SBREAK_TIME, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; - - case TCSBRKP: - /* support for POSIX tcsendbreak() - - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds so we'll ask for something - * in the middle: 0.375 seconds. - */ - rc = tty_check_change(tty); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (rc) - return rc; - - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, SBREAK, (u16)SBREAK_TIME, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; - - case TIOCSBRK: - /* - * FEP5 doesn't support turning on a break unconditionally. - * The FEP5 device will stop sending a break automatically - * after the specified time value that was sent when turning on - * the break. - */ - rc = tty_check_change(tty); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (rc) - return rc; - - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - dgap_cmdw(ch, SBREAK, (u16)SBREAK_TIME, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; - - case TIOCCBRK: - /* - * FEP5 doesn't support turning off a break unconditionally. - * The FEP5 device will stop sending a break automatically - * after the specified time value that was sent when turning on - * the break. - */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return 0; - - case TIOCGSOFTCAR: - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long __user *)arg); - - case TIOCSSOFTCAR: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - rc = get_user(arg, (unsigned long __user *)arg); - if (rc) - return rc; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - dgap_param(ch, bd, un->un_type); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; - - case TIOCMGET: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_get_modem_info(ch, uarg); - - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_set_modem_info(ch, bd, un, cmd, uarg); - - /* - * Here are any additional ioctl's that we want to implement - */ - - case TCFLSH: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - rc = tty_check_change(tty); - if (rc) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return rc; - } - - if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { - if (!(un->un_type == DGAP_PRINT)) { - head = readw(&ch->ch_bs->rx_head); - writew(head, &ch->ch_bs->rx_tail); - writeb(0, &ch->ch_bs->orun); - } - } - - if ((arg != TCOFLUSH) && (arg != TCIOFLUSH)) { - /* pretend we didn't recognize this IOCTL */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return -ENOIOCTLCMD; - } - - ch->ch_flags &= ~CH_STOP; - head = readw(&ch->ch_bs->tx_head); - dgap_cmdw(ch, FLUSHTX, (u16)head, 0); - dgap_cmdw(ch, RESUMETX, 0, 0); - if (ch->ch_tun.un_flags & (UN_LOW | UN_EMPTY)) { - ch->ch_tun.un_flags &= ~(UN_LOW | UN_EMPTY); - wake_up_interruptible(&ch->ch_tun.un_flags_wait); - } - if (ch->ch_pun.un_flags & (UN_LOW | UN_EMPTY)) { - ch->ch_pun.un_flags &= ~(UN_LOW | UN_EMPTY); - wake_up_interruptible(&ch->ch_pun.un_flags_wait); - } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - tty_wakeup(tty); - - /* pretend we didn't recognize this IOCTL */ - return -ENOIOCTLCMD; - - case TCSETSF: - case TCSETSW: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - if (cmd == TCSETSF) { - /* flush rx */ - ch->ch_flags &= ~CH_STOP; - head = readw(&ch->ch_bs->rx_head); - writew(head, &ch->ch_bs->rx_tail); - } - - /* now wait for all the output to drain */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; - - /* pretend we didn't recognize this */ - return -ENOIOCTLCMD; - - case TCSETAW: - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; - - /* pretend we didn't recognize this */ - return -ENOIOCTLCMD; - - case TCXONC: - /* - * The Linux Line Discipline (LD) would do this for us if we - * let it, but we have the special firmware options to do this - * the "right way" regardless of hardware or software flow - * control so we'll do it outselves instead of letting the LD - * do it. - */ - rc = tty_check_change(tty); - if (rc) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return rc; - } - - switch (arg) { - case TCOON: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - dgap_tty_start(tty); - return 0; - case TCOOFF: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - dgap_tty_stop(tty); - return 0; - case TCION: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - /* Make the ld do it */ - return -ENOIOCTLCMD; - case TCIOFF: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - /* Make the ld do it */ - return -ENOIOCTLCMD; - default: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EINVAL; - } - - case DIGI_GETA: - /* get information for ditty */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digigeta(ch, uarg); - - case DIGI_SETAW: - case DIGI_SETAF: - - /* set information for ditty */ - if (cmd == (DIGI_SETAW)) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - } else - tty_ldisc_flush(tty); - /* fall thru */ - - case DIGI_SETA: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digiseta(ch, bd, un, uarg); - - case DIGI_GEDELAY: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digigetedelay(tty, uarg); - - case DIGI_SEDELAY: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digisetedelay(ch, bd, un, uarg); - - case DIGI_GETCUSTOMBAUD: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digigetcustombaud(ch, un, uarg); - - case DIGI_SETCUSTOMBAUD: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digisetcustombaud(ch, bd, un, uarg); - - case DIGI_RESET_PORT: - dgap_firmware_reset_port(ch); - dgap_param(ch, bd, un->un_type); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return 0; - - default: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return -ENOIOCTLCMD; - } -} - -static const struct tty_operations dgap_tty_ops = { - .open = dgap_tty_open, - .close = dgap_tty_close, - .write = dgap_tty_write, - .write_room = dgap_tty_write_room, - .flush_buffer = dgap_tty_flush_buffer, - .chars_in_buffer = dgap_tty_chars_in_buffer, - .flush_chars = dgap_tty_flush_chars, - .ioctl = dgap_tty_ioctl, - .set_termios = dgap_tty_set_termios, - .stop = dgap_tty_stop, - .start = dgap_tty_start, - .throttle = dgap_tty_throttle, - .unthrottle = dgap_tty_unthrottle, - .hangup = dgap_tty_hangup, - .put_char = dgap_tty_put_char, - .tiocmget = dgap_tty_tiocmget, - .tiocmset = dgap_tty_tiocmset, - .break_ctl = dgap_tty_send_break, - .wait_until_sent = dgap_tty_wait_until_sent, - .send_xchar = dgap_tty_send_xchar -}; - -/************************************************************************ - * - * TTY Initialization/Cleanup Functions - * - ************************************************************************/ - -/* - * dgap_tty_register() - * - * Init the tty subsystem for this board. - */ -static int dgap_tty_register(struct board_t *brd) -{ - int rc; - - brd->serial_driver = tty_alloc_driver(MAXPORTS, - TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - if (IS_ERR(brd->serial_driver)) - return PTR_ERR(brd->serial_driver); - - snprintf(brd->serial_name, MAXTTYNAMELEN, "tty_dgap_%d_", - brd->boardnum); - brd->serial_driver->name = brd->serial_name; - brd->serial_driver->name_base = 0; - brd->serial_driver->major = 0; - brd->serial_driver->minor_start = 0; - brd->serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - brd->serial_driver->subtype = SERIAL_TYPE_NORMAL; - brd->serial_driver->init_termios = dgap_default_termios; - brd->serial_driver->driver_name = DRVSTR; - - /* - * Entry points for driver. Called by the kernel from - * tty_io.c and n_tty.c. - */ - tty_set_operations(brd->serial_driver, &dgap_tty_ops); - - /* - * If we're doing transparent print, we have to do all of the above - * again, separately so we don't get the LD confused about what major - * we are when we get into the dgap_tty_open() routine. - */ - brd->print_driver = tty_alloc_driver(MAXPORTS, - TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - if (IS_ERR(brd->print_driver)) { - rc = PTR_ERR(brd->print_driver); - goto free_serial_drv; - } - - snprintf(brd->print_name, MAXTTYNAMELEN, "pr_dgap_%d_", - brd->boardnum); - brd->print_driver->name = brd->print_name; - brd->print_driver->name_base = 0; - brd->print_driver->major = 0; - brd->print_driver->minor_start = 0; - brd->print_driver->type = TTY_DRIVER_TYPE_SERIAL; - brd->print_driver->subtype = SERIAL_TYPE_NORMAL; - brd->print_driver->init_termios = dgap_default_termios; - brd->print_driver->driver_name = DRVSTR; - - /* - * Entry points for driver. Called by the kernel from - * tty_io.c and n_tty.c. - */ - tty_set_operations(brd->print_driver, &dgap_tty_ops); - - /* Register tty devices */ - rc = tty_register_driver(brd->serial_driver); - if (rc < 0) - goto free_print_drv; - - /* Register Transparent Print devices */ - rc = tty_register_driver(brd->print_driver); - if (rc < 0) - goto unregister_serial_drv; - - return 0; - -unregister_serial_drv: - tty_unregister_driver(brd->serial_driver); -free_print_drv: - put_tty_driver(brd->print_driver); -free_serial_drv: - put_tty_driver(brd->serial_driver); - - return rc; -} - -static void dgap_tty_unregister(struct board_t *brd) -{ - tty_unregister_driver(brd->print_driver); - tty_unregister_driver(brd->serial_driver); - put_tty_driver(brd->print_driver); - put_tty_driver(brd->serial_driver); -} - -static int dgap_alloc_flipbuf(struct board_t *brd) -{ - /* - * allocate flip buffer for board. - */ - brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); - if (!brd->flipbuf) - return -ENOMEM; - - brd->flipflagbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); - if (!brd->flipflagbuf) { - kfree(brd->flipbuf); - return -ENOMEM; - } - - return 0; -} - -static void dgap_free_flipbuf(struct board_t *brd) -{ - kfree(brd->flipbuf); - kfree(brd->flipflagbuf); -} - -static struct board_t *dgap_verify_board(struct device *p) -{ - struct board_t *bd; - - if (!p) - return NULL; - - bd = dev_get_drvdata(p); - if (!bd || bd->magic != DGAP_BOARD_MAGIC || bd->state != BOARD_READY) - return NULL; - - return bd; -} - -static ssize_t dgap_ports_state_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) { - count += snprintf(buf + count, PAGE_SIZE - count, - "%d %s\n", bd->channels[i]->ch_portnum, - bd->channels[i]->ch_open_count ? "Open" : "Closed"); - } - return count; -} -static DEVICE_ATTR(ports_state, S_IRUSR, dgap_ports_state_show, NULL); - -static ssize_t dgap_ports_baud_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) { - count += snprintf(buf + count, PAGE_SIZE - count, "%d %d\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_baud_info); - } - return count; -} -static DEVICE_ATTR(ports_baud, S_IRUSR, dgap_ports_baud_show, NULL); - -static ssize_t dgap_ports_msignals_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) { - if (bd->channels[i]->ch_open_count) - count += snprintf(buf + count, PAGE_SIZE - count, - "%d %s %s %s %s %s %s\n", - bd->channels[i]->ch_portnum, - (bd->channels[i]->ch_mostat & - UART_MCR_RTS) ? "RTS" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_CTS) ? "CTS" : "", - (bd->channels[i]->ch_mostat & - UART_MCR_DTR) ? "DTR" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_DSR) ? "DSR" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_DCD) ? "DCD" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_RI) ? "RI" : ""); - else - count += snprintf(buf + count, PAGE_SIZE - count, - "%d\n", bd->channels[i]->ch_portnum); - } - return count; -} -static DEVICE_ATTR(ports_msignals, S_IRUSR, dgap_ports_msignals_show, NULL); - -static ssize_t dgap_ports_iflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_iflag); - return count; -} -static DEVICE_ATTR(ports_iflag, S_IRUSR, dgap_ports_iflag_show, NULL); - -static ssize_t dgap_ports_cflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_cflag); - return count; -} -static DEVICE_ATTR(ports_cflag, S_IRUSR, dgap_ports_cflag_show, NULL); - -static ssize_t dgap_ports_oflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_oflag); - return count; -} -static DEVICE_ATTR(ports_oflag, S_IRUSR, dgap_ports_oflag_show, NULL); - -static ssize_t dgap_ports_lflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_lflag); - return count; -} -static DEVICE_ATTR(ports_lflag, S_IRUSR, dgap_ports_lflag_show, NULL); - -static ssize_t dgap_ports_digi_flag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_digi.digi_flags); - return count; -} -static DEVICE_ATTR(ports_digi_flag, S_IRUSR, dgap_ports_digi_flag_show, NULL); - -static ssize_t dgap_ports_rxcount_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_rxcount); - return count; -} -static DEVICE_ATTR(ports_rxcount, S_IRUSR, dgap_ports_rxcount_show, NULL); - -static ssize_t dgap_ports_txcount_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_txcount); - return count; -} -static DEVICE_ATTR(ports_txcount, S_IRUSR, dgap_ports_txcount_show, NULL); - -static ssize_t dgap_tty_state_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%s", un->un_open_count ? - "Open" : "Closed"); -} -static DEVICE_ATTR(state, S_IRUSR, dgap_tty_state_show, NULL); - -static ssize_t dgap_tty_baud_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_baud_info); -} -static DEVICE_ATTR(baud, S_IRUSR, dgap_tty_baud_show, NULL); - -static ssize_t dgap_tty_msignals_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - if (ch->ch_open_count) { - return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n", - (ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "", - (ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "", - (ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "", - (ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "", - (ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "", - (ch->ch_mistat & UART_MSR_RI) ? "RI" : ""); - } - return 0; -} -static DEVICE_ATTR(msignals, S_IRUSR, dgap_tty_msignals_show, NULL); - -static ssize_t dgap_tty_iflag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag); -} -static DEVICE_ATTR(iflag, S_IRUSR, dgap_tty_iflag_show, NULL); - -static ssize_t dgap_tty_cflag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag); -} -static DEVICE_ATTR(cflag, S_IRUSR, dgap_tty_cflag_show, NULL); - -static ssize_t dgap_tty_oflag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag); -} -static DEVICE_ATTR(oflag, S_IRUSR, dgap_tty_oflag_show, NULL); - -static ssize_t dgap_tty_lflag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag); -} -static DEVICE_ATTR(lflag, S_IRUSR, dgap_tty_lflag_show, NULL); - -static ssize_t dgap_tty_digi_flag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags); -} -static DEVICE_ATTR(digi_flag, S_IRUSR, dgap_tty_digi_flag_show, NULL); - -static ssize_t dgap_tty_rxcount_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount); -} -static DEVICE_ATTR(rxcount, S_IRUSR, dgap_tty_rxcount_show, NULL); - -static ssize_t dgap_tty_txcount_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount); -} -static DEVICE_ATTR(txcount, S_IRUSR, dgap_tty_txcount_show, NULL); - -static ssize_t dgap_tty_name_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int cn; - int bn; - struct cnode *cptr; - int found = FALSE; - int ncount = 0; - int starto = 0; - int i; - - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; - - bn = bd->boardnum; - cn = ch->ch_portnum; - - for (cptr = bd->bd_config; cptr; cptr = cptr->next) { - if ((cptr->type == BNODE) && - ((cptr->u.board.type == APORT2_920P) || - (cptr->u.board.type == APORT4_920P) || - (cptr->u.board.type == APORT8_920P) || - (cptr->u.board.type == PAPORT4) || - (cptr->u.board.type == PAPORT8))) { - found = TRUE; - if (cptr->u.board.v_start) - starto = cptr->u.board.start; - else - starto = 1; - } - - if (cptr->type == TNODE && found == TRUE) { - char *ptr1; - - if (strstr(cptr->u.ttyname, "tty")) { - ptr1 = cptr->u.ttyname; - ptr1 += 3; - } else - ptr1 = cptr->u.ttyname; - - for (i = 0; i < dgap_config_get_num_prts(bd); i++) { - if (cn != i) - continue; - - return snprintf(buf, PAGE_SIZE, "%s%s%02d\n", - (un->un_type == DGAP_PRINT) ? - "pr" : "tty", - ptr1, i + starto); - } - } - - if (cptr->type == CNODE) { - for (i = 0; i < cptr->u.conc.nport; i++) { - if (cn != (i + ncount)) - continue; - - return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n", - (un->un_type == DGAP_PRINT) ? - "pr" : "tty", - cptr->u.conc.id, - i + (cptr->u.conc.v_start ? - cptr->u.conc.start : 1)); - } - - ncount += cptr->u.conc.nport; - } - - if (cptr->type == MNODE) { - for (i = 0; i < cptr->u.module.nport; i++) { - if (cn != (i + ncount)) - continue; - - return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n", - (un->un_type == DGAP_PRINT) ? - "pr" : "tty", - cptr->u.module.id, - i + (cptr->u.module.v_start ? - cptr->u.module.start : 1)); - } - - ncount += cptr->u.module.nport; - } - } - - return snprintf(buf, PAGE_SIZE, "%s_dgap_%d_%d\n", - (un->un_type == DGAP_PRINT) ? "pr" : "tty", bn, cn); -} -static DEVICE_ATTR(custom_name, S_IRUSR, dgap_tty_name_show, NULL); - -static struct attribute *dgap_sysfs_tty_entries[] = { - &dev_attr_state.attr, - &dev_attr_baud.attr, - &dev_attr_msignals.attr, - &dev_attr_iflag.attr, - &dev_attr_cflag.attr, - &dev_attr_oflag.attr, - &dev_attr_lflag.attr, - &dev_attr_digi_flag.attr, - &dev_attr_rxcount.attr, - &dev_attr_txcount.attr, - &dev_attr_custom_name.attr, - NULL -}; - - -/* this function creates the sys files that will export each signal status - * to sysfs each value will be put in a separate filename - */ -static void dgap_create_ports_sysfiles(struct board_t *bd) -{ - dev_set_drvdata(&bd->pdev->dev, bd); - device_create_file(&bd->pdev->dev, &dev_attr_ports_state); - device_create_file(&bd->pdev->dev, &dev_attr_ports_baud); - device_create_file(&bd->pdev->dev, &dev_attr_ports_msignals); - device_create_file(&bd->pdev->dev, &dev_attr_ports_iflag); - device_create_file(&bd->pdev->dev, &dev_attr_ports_cflag); - device_create_file(&bd->pdev->dev, &dev_attr_ports_oflag); - device_create_file(&bd->pdev->dev, &dev_attr_ports_lflag); - device_create_file(&bd->pdev->dev, &dev_attr_ports_digi_flag); - device_create_file(&bd->pdev->dev, &dev_attr_ports_rxcount); - device_create_file(&bd->pdev->dev, &dev_attr_ports_txcount); -} - -/* removes all the sys files created for that port */ -static void dgap_remove_ports_sysfiles(struct board_t *bd) -{ - device_remove_file(&bd->pdev->dev, &dev_attr_ports_state); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_baud); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_msignals); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_iflag); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_cflag); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_oflag); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_lflag); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_digi_flag); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_rxcount); - device_remove_file(&bd->pdev->dev, &dev_attr_ports_txcount); -} - -/* - * Copies the BIOS code from the user to the board, - * and starts the BIOS running. - */ -static void dgap_do_bios_load(struct board_t *brd, const u8 *ubios, int len) -{ - u8 __iomem *addr; - uint offset; - unsigned int i; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; - - addr = brd->re_map_membase; - - /* - * clear POST area - */ - for (i = 0; i < 16; i++) - writeb(0, addr + POSTAREA + i); - - /* - * Download bios - */ - offset = 0x1000; - memcpy_toio(addr + offset, ubios, len); - - writel(0x0bf00401, addr); - writel(0, (addr + 4)); - - /* Clear the reset, and change states. */ - writeb(FEPCLR, brd->re_map_port); -} - -/* - * Checks to see if the BIOS completed running on the card. - */ -static int dgap_test_bios(struct board_t *brd) -{ - u8 __iomem *addr; - u16 word; - u16 err1; - u16 err2; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return -EINVAL; - - addr = brd->re_map_membase; - word = readw(addr + POSTAREA); - - /* - * It can take 5-6 seconds for a board to - * pass the bios self test and post results. - * Give it 10 seconds. - */ - brd->wait_for_bios = 0; - while (brd->wait_for_bios < 1000) { - /* Check to see if BIOS thinks board is good. (GD). */ - if (word == *(u16 *)"GD") - return 0; - msleep_interruptible(10); - brd->wait_for_bios++; - word = readw(addr + POSTAREA); - } - - /* Gave up on board after too long of time taken */ - err1 = readw(addr + SEQUENCE); - err2 = readw(addr + ERROR); - dev_warn(&brd->pdev->dev, "%s failed diagnostics. Error #(%x,%x).\n", - brd->name, err1, err2); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOBIOS; - - return -EIO; -} - -/* - * Copies the FEP code from the user to the board, - * and starts the FEP running. - */ -static void dgap_do_fep_load(struct board_t *brd, const u8 *ufep, int len) -{ - u8 __iomem *addr; - uint offset; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; - - addr = brd->re_map_membase; - - /* - * Download FEP - */ - offset = 0x1000; - memcpy_toio(addr + offset, ufep, len); - - /* - * If board is a concentrator product, we need to give - * it its config string describing how the concentrators look. - */ - if ((brd->type == PCX) || (brd->type == PEPC)) { - u8 string[100]; - u8 __iomem *config; - u8 *xconfig; - unsigned int i = 0; - - xconfig = dgap_create_config_string(brd, string); - - /* Write string to board memory */ - config = addr + CONFIG; - for (; i < CONFIGSIZE; i++, config++, xconfig++) { - writeb(*xconfig, config); - if ((*xconfig & 0xff) == 0xff) - break; - } - } - - writel(0xbfc01004, (addr + 0xc34)); - writel(0x3, (addr + 0xc30)); -} - -/* - * Waits for the FEP to report thats its ready for us to use. - */ -static int dgap_test_fep(struct board_t *brd) -{ - u8 __iomem *addr; - u16 word; - u16 err1; - u16 err2; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return -EINVAL; - - addr = brd->re_map_membase; - word = readw(addr + FEPSTAT); - - /* - * It can take 2-3 seconds for the FEP to - * be up and running. Give it 5 secs. - */ - brd->wait_for_fep = 0; - while (brd->wait_for_fep < 500) { - /* Check to see if FEP is up and running now. */ - if (word == *(u16 *)"OS") { - /* - * Check to see if the board can support FEP5+ commands. - */ - word = readw(addr + FEP5_PLUS); - if (word == *(u16 *)"5A") - brd->bd_flags |= BD_FEP5PLUS; - - return 0; - } - msleep_interruptible(10); - brd->wait_for_fep++; - word = readw(addr + FEPSTAT); - } - - /* Gave up on board after too long of time taken */ - err1 = readw(addr + SEQUENCE); - err2 = readw(addr + ERROR); - dev_warn(&brd->pdev->dev, - "FEPOS for %s not functioning. Error #(%x,%x).\n", - brd->name, err1, err2); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; - - return -EIO; -} - -/* - * Physically forces the FEP5 card to reset itself. - */ -static void dgap_do_reset_board(struct board_t *brd) -{ - u8 check; - u32 check1; - u32 check2; - unsigned int i; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || - !brd->re_map_membase || !brd->re_map_port) - return; - - /* FEPRST does not vary among supported boards */ - writeb(FEPRST, brd->re_map_port); - - for (i = 0; i <= 1000; i++) { - check = readb(brd->re_map_port) & 0xe; - if (check == FEPRST) - break; - udelay(10); - } - if (i > 1000) { - dev_warn(&brd->pdev->dev, - "dgap: Board not resetting... Failing board.\n"); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; - return; - } - - /* - * Make sure there really is memory out there. - */ - writel(0xa55a3cc3, (brd->re_map_membase + LOWMEM)); - writel(0x5aa5c33c, (brd->re_map_membase + HIGHMEM)); - check1 = readl(brd->re_map_membase + LOWMEM); - check2 = readl(brd->re_map_membase + HIGHMEM); - - if ((check1 != 0xa55a3cc3) || (check2 != 0x5aa5c33c)) { - dev_warn(&brd->pdev->dev, - "No memory at %p for board.\n", - brd->re_map_membase); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; - return; - } -} - -#ifdef DIGI_CONCENTRATORS_SUPPORTED -/* - * Sends a concentrator image into the FEP5 board. - */ -static void dgap_do_conc_load(struct board_t *brd, u8 *uaddr, int len) -{ - char __iomem *vaddr; - u16 offset; - struct downld_t *to_dp; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; - - vaddr = brd->re_map_membase; - - offset = readw((u16 *)(vaddr + DOWNREQ)); - to_dp = (struct downld_t *)(vaddr + (int)offset); - memcpy_toio(to_dp, uaddr, len); - - /* Tell card we have data for it */ - writew(0, vaddr + (DOWNREQ)); - - brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; -} -#endif - -#define EXPANSION_ROM_SIZE (64 * 1024) -#define FEP5_ROM_MAGIC (0xFEFFFFFF) - -static void dgap_get_vpd(struct board_t *brd) -{ - u32 magic; - u32 base_offset; - u16 rom_offset; - u16 vpd_offset; - u16 image_length; - u16 i; - u8 byte1; - u8 byte2; - - /* - * Poke the magic number at the PCI Rom Address location. - * If VPD is supported, the value read from that address - * will be non-zero. - */ - magic = FEP5_ROM_MAGIC; - pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); - pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic); - - /* VPD not supported, bail */ - if (!magic) - return; - - /* - * To get to the OTPROM memory, we have to send the boards base - * address or'ed with 1 into the PCI Rom Address location. - */ - magic = brd->membase | 0x01; - pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); - pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic); - - byte1 = readb(brd->re_map_membase); - byte2 = readb(brd->re_map_membase + 1); - - /* - * If the board correctly swapped to the OTPROM memory, - * the first 2 bytes (header) should be 0x55, 0xAA - */ - if (byte1 == 0x55 && byte2 == 0xAA) { - base_offset = 0; - - /* - * We have to run through all the OTPROM memory looking - * for the VPD offset. - */ - while (base_offset <= EXPANSION_ROM_SIZE) { - /* - * Lots of magic numbers here. - * - * The VPD offset is located inside the ROM Data - * Structure. - * - * We also have to remember the length of each - * ROM Data Structure, so we can "hop" to the next - * entry if the VPD isn't in the current - * ROM Data Structure. - */ - rom_offset = readw(brd->re_map_membase + - base_offset + 0x18); - image_length = readw(brd->re_map_membase + - rom_offset + 0x10) * 512; - vpd_offset = readw(brd->re_map_membase + - rom_offset + 0x08); - - /* Found the VPD entry */ - if (vpd_offset) - break; - - /* We didn't find a VPD entry, go to next ROM entry. */ - base_offset += image_length; - - byte1 = readb(brd->re_map_membase + base_offset); - byte2 = readb(brd->re_map_membase + base_offset + 1); - - /* - * If the new ROM offset doesn't have 0x55, 0xAA - * as its header, we have run out of ROM. - */ - if (byte1 != 0x55 || byte2 != 0xAA) - break; - } - - /* - * If we have a VPD offset, then mark the board - * as having a valid VPD, and copy VPDSIZE (512) bytes of - * that VPD to the buffer we have in our board structure. - */ - if (vpd_offset) { - brd->bd_flags |= BD_HAS_VPD; - for (i = 0; i < VPDSIZE; i++) { - brd->vpd[i] = readb(brd->re_map_membase + - vpd_offset + i); - } - } - } - - /* - * We MUST poke the magic number at the PCI Rom Address location again. - * This makes the card report the regular board memory back to us, - * rather than the OTPROM memory. - */ - magic = FEP5_ROM_MAGIC; - pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); -} - - -static ssize_t dgap_driver_version_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", DG_PART); -} -static DRIVER_ATTR(version, S_IRUSR, dgap_driver_version_show, NULL); - - -static ssize_t dgap_driver_boards_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", dgap_numboards); -} -static DRIVER_ATTR(boards, S_IRUSR, dgap_driver_boards_show, NULL); - - -static ssize_t dgap_driver_maxboards_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", MAXBOARDS); -} -static DRIVER_ATTR(maxboards, S_IRUSR, dgap_driver_maxboards_show, NULL); - - -static ssize_t dgap_driver_pollcounter_show(struct device_driver *ddp, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%ld\n", dgap_poll_counter); -} -static DRIVER_ATTR(pollcounter, S_IRUSR, dgap_driver_pollcounter_show, NULL); - -static ssize_t dgap_driver_pollrate_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%dms\n", dgap_poll_tick); -} - -static ssize_t dgap_driver_pollrate_store(struct device_driver *ddp, - const char *buf, size_t count) -{ - if (sscanf(buf, "%d\n", &dgap_poll_tick) != 1) - return -EINVAL; - return count; -} -static DRIVER_ATTR(pollrate, (S_IRUSR | S_IWUSR), dgap_driver_pollrate_show, - dgap_driver_pollrate_store); - - -static int dgap_create_driver_sysfiles(struct pci_driver *dgap_driver) -{ - int rc = 0; - struct device_driver *driverfs = &dgap_driver->driver; - - rc |= driver_create_file(driverfs, &driver_attr_version); - rc |= driver_create_file(driverfs, &driver_attr_boards); - rc |= driver_create_file(driverfs, &driver_attr_maxboards); - rc |= driver_create_file(driverfs, &driver_attr_pollrate); - rc |= driver_create_file(driverfs, &driver_attr_pollcounter); - - return rc; -} - -static void dgap_remove_driver_sysfiles(struct pci_driver *dgap_driver) -{ - struct device_driver *driverfs = &dgap_driver->driver; - - driver_remove_file(driverfs, &driver_attr_version); - driver_remove_file(driverfs, &driver_attr_boards); - driver_remove_file(driverfs, &driver_attr_maxboards); - driver_remove_file(driverfs, &driver_attr_pollrate); - driver_remove_file(driverfs, &driver_attr_pollcounter); -} - -static struct attribute_group dgap_tty_attribute_group = { - .name = NULL, - .attrs = dgap_sysfs_tty_entries, -}; - -static void dgap_create_tty_sysfs(struct un_t *un, struct device *c) -{ - int ret; - - ret = sysfs_create_group(&c->kobj, &dgap_tty_attribute_group); - if (ret) - return; - - dev_set_drvdata(c, un); -} - -static void dgap_remove_tty_sysfs(struct device *c) -{ - sysfs_remove_group(&c->kobj, &dgap_tty_attribute_group); -} - -/* - * Create pr and tty device entries - */ -static int dgap_tty_register_ports(struct board_t *brd) -{ - struct channel_t *ch; - int i; - int ret; - - brd->serial_ports = kcalloc(brd->nasync, sizeof(*brd->serial_ports), - GFP_KERNEL); - if (!brd->serial_ports) - return -ENOMEM; - - brd->printer_ports = kcalloc(brd->nasync, sizeof(*brd->printer_ports), - GFP_KERNEL); - if (!brd->printer_ports) { - ret = -ENOMEM; - goto free_serial_ports; - } - - for (i = 0; i < brd->nasync; i++) { - tty_port_init(&brd->serial_ports[i]); - tty_port_init(&brd->printer_ports[i]); - } - - ch = brd->channels[0]; - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { - struct device *classp; - - classp = tty_port_register_device(&brd->serial_ports[i], - brd->serial_driver, - i, NULL); - - if (IS_ERR(classp)) { - ret = PTR_ERR(classp); - goto unregister_ttys; - } - - dgap_create_tty_sysfs(&ch->ch_tun, classp); - ch->ch_tun.un_sysfs = classp; - - classp = tty_port_register_device(&brd->printer_ports[i], - brd->print_driver, - i, NULL); - - if (IS_ERR(classp)) { - ret = PTR_ERR(classp); - goto unregister_ttys; - } - - dgap_create_tty_sysfs(&ch->ch_pun, classp); - ch->ch_pun.un_sysfs = classp; - } - dgap_create_ports_sysfiles(brd); - - return 0; - -unregister_ttys: - while (i >= 0) { - ch = brd->channels[i]; - if (ch->ch_tun.un_sysfs) { - dgap_remove_tty_sysfs(ch->ch_tun.un_sysfs); - tty_unregister_device(brd->serial_driver, i); - } - - if (ch->ch_pun.un_sysfs) { - dgap_remove_tty_sysfs(ch->ch_pun.un_sysfs); - tty_unregister_device(brd->print_driver, i); - } - i--; - } - - for (i = 0; i < brd->nasync; i++) { - tty_port_destroy(&brd->serial_ports[i]); - tty_port_destroy(&brd->printer_ports[i]); - } - - kfree(brd->printer_ports); - brd->printer_ports = NULL; - -free_serial_ports: - kfree(brd->serial_ports); - brd->serial_ports = NULL; - - return ret; -} - -/* - * dgap_cleanup_tty() - * - * Uninitialize the TTY portion of this driver. Free all memory and - * resources. - */ -static void dgap_cleanup_tty(struct board_t *brd) -{ - struct device *dev; - unsigned int i; - - for (i = 0; i < brd->nasync; i++) { - tty_port_destroy(&brd->serial_ports[i]); - dev = brd->channels[i]->ch_tun.un_sysfs; - dgap_remove_tty_sysfs(dev); - tty_unregister_device(brd->serial_driver, i); - } - tty_unregister_driver(brd->serial_driver); - put_tty_driver(brd->serial_driver); - kfree(brd->serial_ports); - - for (i = 0; i < brd->nasync; i++) { - tty_port_destroy(&brd->printer_ports[i]); - dev = brd->channels[i]->ch_pun.un_sysfs; - dgap_remove_tty_sysfs(dev); - tty_unregister_device(brd->print_driver, i); - } - tty_unregister_driver(brd->print_driver); - put_tty_driver(brd->print_driver); - kfree(brd->printer_ports); -} - -static int dgap_request_irq(struct board_t *brd) -{ - int rc; - - if (!brd || brd->magic != DGAP_BOARD_MAGIC) - return -ENODEV; - - /* - * Set up our interrupt handler if we are set to do interrupts. - */ - if (dgap_config_get_useintr(brd) && brd->irq) { - rc = request_irq(brd->irq, dgap_intr, IRQF_SHARED, "DGAP", brd); - - if (!rc) - brd->intr_used = 1; - } - return 0; -} - -static void dgap_free_irq(struct board_t *brd) -{ - if (brd->intr_used && brd->irq) - free_irq(brd->irq, brd); -} - -static int dgap_firmware_load(struct pci_dev *pdev, int card_type, - struct board_t *brd) -{ - const struct firmware *fw; - char *tmp_ptr; - int ret; - char *dgap_config_buf; - - dgap_get_vpd(brd); - dgap_do_reset_board(brd); - - if (fw_info[card_type].conf_name) { - ret = request_firmware(&fw, fw_info[card_type].conf_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "config file %s not found\n", - fw_info[card_type].conf_name); - return ret; - } - - dgap_config_buf = kzalloc(fw->size + 1, GFP_KERNEL); - if (!dgap_config_buf) { - release_firmware(fw); - return -ENOMEM; - } - - memcpy(dgap_config_buf, fw->data, fw->size); - release_firmware(fw); - - /* - * preserve dgap_config_buf - * as dgap_parsefile would - * otherwise alter it. - */ - tmp_ptr = dgap_config_buf; - - if (dgap_parsefile(&tmp_ptr) != 0) { - kfree(dgap_config_buf); - return -EINVAL; - } - kfree(dgap_config_buf); - } - - /* - * Match this board to a config the user created for us. - */ - brd->bd_config = - dgap_find_config(brd->type, brd->pci_bus, brd->pci_slot); - - /* - * Because the 4 port Xr products share the same PCI ID - * as the 8 port Xr products, if we receive a NULL config - * back, and this is a PAPORT8 board, retry with a - * PAPORT4 attempt as well. - */ - if (brd->type == PAPORT8 && !brd->bd_config) - brd->bd_config = - dgap_find_config(PAPORT4, brd->pci_bus, brd->pci_slot); - - if (!brd->bd_config) { - dev_err(&pdev->dev, "No valid configuration found\n"); - return -EINVAL; - } - - if (fw_info[card_type].bios_name) { - ret = request_firmware(&fw, fw_info[card_type].bios_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "bios file %s not found\n", - fw_info[card_type].bios_name); - return ret; - } - dgap_do_bios_load(brd, fw->data, fw->size); - release_firmware(fw); - - /* Wait for BIOS to test board... */ - ret = dgap_test_bios(brd); - if (ret) - return ret; - } - - if (fw_info[card_type].fep_name) { - ret = request_firmware(&fw, fw_info[card_type].fep_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "dgap: fep file %s not found\n", - fw_info[card_type].fep_name); - return ret; - } - dgap_do_fep_load(brd, fw->data, fw->size); - release_firmware(fw); - - /* Wait for FEP to load on board... */ - ret = dgap_test_fep(brd); - if (ret) - return ret; - } - -#ifdef DIGI_CONCENTRATORS_SUPPORTED - /* - * If this is a CX or EPCX, we need to see if the firmware - * is requesting a concentrator image from us. - */ - if ((bd->type == PCX) || (bd->type == PEPC)) { - chk_addr = (u16 *)(vaddr + DOWNREQ); - /* Nonzero if FEP is requesting concentrator image. */ - check = readw(chk_addr); - vaddr = brd->re_map_membase; - } - - if (fw_info[card_type].con_name && check && vaddr) { - ret = request_firmware(&fw, fw_info[card_type].con_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "conc file %s not found\n", - fw_info[card_type].con_name); - return ret; - } - /* Put concentrator firmware loading code here */ - offset = readw((u16 *)(vaddr + DOWNREQ)); - memcpy_toio(offset, fw->data, fw->size); - - dgap_do_conc_load(brd, (char *)fw->data, fw->size) - release_firmware(fw); - } -#endif - - return 0; -} - -/* - * dgap_tty_init() - * - * Init the tty subsystem. Called once per board after board has been - * downloaded and init'ed. - */ -static int dgap_tty_init(struct board_t *brd) -{ - int i; - int tlw; - uint true_count; - u8 __iomem *vaddr; - u8 modem; - struct channel_t *ch; - struct bs_t __iomem *bs; - struct cm_t __iomem *cm; - int ret; - - /* - * Initialize board structure elements. - */ - - vaddr = brd->re_map_membase; - true_count = readw((vaddr + NCHAN)); - - brd->nasync = dgap_config_get_num_prts(brd); - - if (!brd->nasync) - brd->nasync = brd->maxports; - - if (brd->nasync > brd->maxports) - brd->nasync = brd->maxports; - - if (true_count != brd->nasync) { - dev_warn(&brd->pdev->dev, - "%s configured for %d ports, has %d ports.\n", - brd->name, brd->nasync, true_count); - - if ((brd->type == PPCM) && - (true_count == 64 || true_count == 0)) { - dev_warn(&brd->pdev->dev, - "Please make SURE the EBI cable running from the card\n"); - dev_warn(&brd->pdev->dev, - "to each EM module is plugged into EBI IN!\n"); - } - - brd->nasync = true_count; - - /* If no ports, don't bother going any further */ - if (!brd->nasync) { - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; - return -EIO; - } - } - - /* - * Allocate channel memory that might not have been allocated - * when the driver was first loaded. - */ - for (i = 0; i < brd->nasync; i++) { - brd->channels[i] = - kzalloc(sizeof(struct channel_t), GFP_KERNEL); - if (!brd->channels[i]) { - ret = -ENOMEM; - goto free_chan; - } - } - - ch = brd->channels[0]; - vaddr = brd->re_map_membase; - - bs = (struct bs_t __iomem *)((ulong)vaddr + CHANBUF); - cm = (struct cm_t __iomem *)((ulong)vaddr + CMDBUF); - - brd->bd_bs = bs; - - /* Set up channel variables */ - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) { - spin_lock_init(&ch->ch_lock); - - /* Store all our magic numbers */ - ch->magic = DGAP_CHANNEL_MAGIC; - ch->ch_tun.magic = DGAP_UNIT_MAGIC; - ch->ch_tun.un_type = DGAP_SERIAL; - ch->ch_tun.un_ch = ch; - ch->ch_tun.un_dev = i; - - ch->ch_pun.magic = DGAP_UNIT_MAGIC; - ch->ch_pun.un_type = DGAP_PRINT; - ch->ch_pun.un_ch = ch; - ch->ch_pun.un_dev = i; - - ch->ch_vaddr = vaddr; - ch->ch_bs = bs; - ch->ch_cm = cm; - ch->ch_bd = brd; - ch->ch_portnum = i; - ch->ch_digi = dgap_digi_init; - - /* - * Set up digi dsr and dcd bits based on altpin flag. - */ - if (dgap_config_get_altpin(brd)) { - ch->ch_dsr = DM_CD; - ch->ch_cd = DM_DSR; - ch->ch_digi.digi_flags |= DIGI_ALTPIN; - } else { - ch->ch_cd = DM_CD; - ch->ch_dsr = DM_DSR; - } - - ch->ch_taddr = vaddr + (ioread16(&ch->ch_bs->tx_seg) << 4); - ch->ch_raddr = vaddr + (ioread16(&ch->ch_bs->rx_seg) << 4); - ch->ch_tx_win = 0; - ch->ch_rx_win = 0; - ch->ch_tsize = readw(&ch->ch_bs->tx_max) + 1; - ch->ch_rsize = readw(&ch->ch_bs->rx_max) + 1; - ch->ch_tstart = 0; - ch->ch_rstart = 0; - - /* - * Set queue water marks, interrupt mask, - * and general tty parameters. - */ - tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : - ch->ch_tsize / 2; - ch->ch_tlw = tlw; - - dgap_cmdw(ch, STLOW, tlw, 0); - - dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0); - - dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0); - - ch->ch_mistat = readb(&ch->ch_bs->m_stat); - - init_waitqueue_head(&ch->ch_flags_wait); - init_waitqueue_head(&ch->ch_tun.un_flags_wait); - init_waitqueue_head(&ch->ch_pun.un_flags_wait); - - /* Turn on all modem interrupts for now */ - modem = (DM_CD | DM_DSR | DM_CTS | DM_RI); - writeb(modem, &ch->ch_bs->m_int); - - /* - * Set edelay to 0 if interrupts are turned on, - * otherwise set edelay to the usual 100. - */ - if (brd->intr_used) - writew(0, &ch->ch_bs->edelay); - else - writew(100, &ch->ch_bs->edelay); - - writeb(1, &ch->ch_bs->idata); - } - - return 0; - -free_chan: - while (--i >= 0) { - kfree(brd->channels[i]); - brd->channels[i] = NULL; - } - return ret; -} - -/* - * dgap_tty_free() - * - * Free the channles which are allocated in dgap_tty_init(). - */ -static void dgap_tty_free(struct board_t *brd) -{ - int i; - - for (i = 0; i < brd->nasync; i++) - kfree(brd->channels[i]); -} - -static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int rc; - struct board_t *brd; - - if (dgap_numboards >= MAXBOARDS) - return -EPERM; - - rc = pci_enable_device(pdev); - if (rc) - return -EIO; - - brd = dgap_found_board(pdev, ent->driver_data, dgap_numboards); - if (IS_ERR(brd)) - return PTR_ERR(brd); - - rc = dgap_firmware_load(pdev, ent->driver_data, brd); - if (rc) - goto cleanup_brd; - - rc = dgap_alloc_flipbuf(brd); - if (rc) - goto cleanup_brd; - - rc = dgap_tty_register(brd); - if (rc) - goto free_flipbuf; - - rc = dgap_request_irq(brd); - if (rc) - goto unregister_tty; - - /* - * Do tty device initialization. - */ - rc = dgap_tty_init(brd); - if (rc < 0) - goto free_irq; - - rc = dgap_tty_register_ports(brd); - if (rc) - goto tty_free; - - brd->state = BOARD_READY; - brd->dpastatus = BD_RUNNING; - - dgap_board[dgap_numboards++] = brd; - - return 0; - -tty_free: - dgap_tty_free(brd); -free_irq: - dgap_free_irq(brd); -unregister_tty: - dgap_tty_unregister(brd); -free_flipbuf: - dgap_free_flipbuf(brd); -cleanup_brd: - dgap_cleanup_nodes(); - dgap_unmap(brd); - kfree(brd); - - return rc; -} - -/* - * dgap_cleanup_board() - * - * Free all the memory associated with a board - */ -static void dgap_cleanup_board(struct board_t *brd) -{ - unsigned int i; - - if (!brd || brd->magic != DGAP_BOARD_MAGIC) - return; - - dgap_free_irq(brd); - - tasklet_kill(&brd->helper_tasklet); - - dgap_unmap(brd); - - /* Free all allocated channels structs */ - for (i = 0; i < MAXPORTS ; i++) - kfree(brd->channels[i]); - - kfree(brd->flipbuf); - kfree(brd->flipflagbuf); - - dgap_board[brd->boardnum] = NULL; - - kfree(brd); -} - -static void dgap_stop(bool removesys, struct pci_driver *drv) -{ - unsigned long lock_flags; - - spin_lock_irqsave(&dgap_poll_lock, lock_flags); - dgap_poll_stop = 1; - spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - - del_timer_sync(&dgap_poll_timer); - if (removesys) - dgap_remove_driver_sysfiles(drv); - - device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0)); - class_destroy(dgap_class); - unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); -} - -static void dgap_remove_one(struct pci_dev *dev) -{ - unsigned int i; - struct pci_driver *drv = to_pci_driver(dev->dev.driver); - - dgap_stop(true, drv); - for (i = 0; i < dgap_numboards; ++i) { - dgap_remove_ports_sysfiles(dgap_board[i]); - dgap_cleanup_tty(dgap_board[i]); - dgap_cleanup_board(dgap_board[i]); - } - - dgap_cleanup_nodes(); -} - -static struct pci_driver dgap_driver = { - .name = "dgap", - .probe = dgap_init_one, - .id_table = dgap_pci_tbl, - .remove = dgap_remove_one, -}; - -/* - * Start of driver. - */ -static int dgap_start(void) -{ - int rc; - unsigned long flags; - struct device *device; - - dgap_numboards = 0; - - pr_info("For the tools package please visit http://www.digi.com\n"); - - /* - * Register our base character device into the kernel. - */ - - /* - * Register management/dpa devices - */ - rc = register_chrdev(DIGI_DGAP_MAJOR, "dgap", &dgap_board_fops); - if (rc < 0) - return rc; - - dgap_class = class_create(THIS_MODULE, "dgap_mgmt"); - if (IS_ERR(dgap_class)) { - rc = PTR_ERR(dgap_class); - goto failed_class; - } - - device = device_create(dgap_class, NULL, - MKDEV(DIGI_DGAP_MAJOR, 0), - NULL, "dgap_mgmt"); - if (IS_ERR(device)) { - rc = PTR_ERR(device); - goto failed_device; - } - - /* Start the poller */ - spin_lock_irqsave(&dgap_poll_lock, flags); - setup_timer(&dgap_poll_timer, dgap_poll_handler, 0); - dgap_poll_timer.data = 0; - dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick); - dgap_poll_timer.expires = dgap_poll_time; - spin_unlock_irqrestore(&dgap_poll_lock, flags); - - add_timer(&dgap_poll_timer); - - return rc; - -failed_device: - class_destroy(dgap_class); -failed_class: - unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); - return rc; -} - -/************************************************************************ - * - * Driver load/unload functions - * - ************************************************************************/ - -/* - * init_module() - * - * Module load. This is where it all starts. - */ -static int dgap_init_module(void) -{ - int rc; - - pr_info("%s, Digi International Part Number %s\n", DG_NAME, DG_PART); - - rc = dgap_start(); - if (rc) - return rc; - - rc = pci_register_driver(&dgap_driver); - if (rc) { - dgap_stop(false, NULL); - return rc; - } - - rc = dgap_create_driver_sysfiles(&dgap_driver); - if (rc) - goto err_unregister; - - dgap_driver_state = DRIVER_READY; - - return 0; - -err_unregister: - pci_unregister_driver(&dgap_driver); - return rc; -} - -/* - * dgap_cleanup_module() - * - * Module unload. This is where it all ends. - */ -static void dgap_cleanup_module(void) -{ - if (dgap_numboards) - pci_unregister_driver(&dgap_driver); -} - -module_init(dgap_init_module); -module_exit(dgap_cleanup_module); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Digi International, http://www.digi.com"); -MODULE_DESCRIPTION("Driver for the Digi International EPCA PCI based product line"); -MODULE_SUPPORTED_DEVICE("dgap"); diff --git a/drivers/staging/dgap/dgap.h b/drivers/staging/dgap/dgap.h deleted file mode 100644 index c84dbf2a0684..000000000000 --- a/drivers/staging/dgap/dgap.h +++ /dev/null @@ -1,1229 +0,0 @@ -/* - * Copyright 2003 Digi International (www.digi.com) - * Scott H Kilau - * - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! - * - ************************************************************************* - * - * Driver includes - * - *************************************************************************/ - -#ifndef __DGAP_DRIVER_H -#define __DGAP_DRIVER_H - -#include /* To pick up the varions Linux types */ -#include /* To pick up the various tty structs/defines */ -#include /* For irqreturn_t type */ - -#ifndef TRUE -# define TRUE 1 -#endif - -#ifndef FALSE -# define FALSE 0 -#endif - -#if !defined(TTY_FLIPBUF_SIZE) -# define TTY_FLIPBUF_SIZE 512 -#endif - -/************************************************************************* - * - * Driver defines - * - *************************************************************************/ - -/* - * Driver identification - */ -#define DG_NAME "dgap-1.3-16" -#define DG_PART "40002347_C" -#define DRVSTR "dgap" - -/* - * defines from dgap_pci.h - */ -#define PCIMAX 32 /* maximum number of PCI boards */ - -#define DIGI_VID 0x114F - -#define PCI_DEV_EPC_DID 0x0002 -#define PCI_DEV_XEM_DID 0x0004 -#define PCI_DEV_XR_DID 0x0005 -#define PCI_DEV_CX_DID 0x0006 -#define PCI_DEV_XRJ_DID 0x0009 /* PLX-based Xr adapter */ -#define PCI_DEV_XR_IBM_DID 0x0011 /* IBM 8-port Async Adapter */ -#define PCI_DEV_XR_BULL_DID 0x0013 /* BULL 8-port Async Adapter */ -#define PCI_DEV_XR_SAIP_DID 0x001c /* SAIP card - Xr adapter */ -#define PCI_DEV_XR_422_DID 0x0012 /* Xr-422 */ -#define PCI_DEV_920_2_DID 0x0034 /* XR-Plus 920 K, 2 port */ -#define PCI_DEV_920_4_DID 0x0026 /* XR-Plus 920 K, 4 port */ -#define PCI_DEV_920_8_DID 0x0027 /* XR-Plus 920 K, 8 port */ -#define PCI_DEV_EPCJ_DID 0x000a /* PLX 9060 chip for PCI */ -#define PCI_DEV_CX_IBM_DID 0x001b /* IBM 128-port Async Adapter */ -#define PCI_DEV_920_8_HP_DID 0x0058 /* HP XR-Plus 920 K, 8 port */ -#define PCI_DEV_XEM_HP_DID 0x0059 /* HP Xem PCI */ - -#define PCI_DEV_XEM_NAME "AccelePort XEM" -#define PCI_DEV_CX_NAME "AccelePort CX" -#define PCI_DEV_XR_NAME "AccelePort Xr" -#define PCI_DEV_XRJ_NAME "AccelePort Xr (PLX)" -#define PCI_DEV_XR_SAIP_NAME "AccelePort Xr (SAIP)" -#define PCI_DEV_920_2_NAME "AccelePort Xr920 2 port" -#define PCI_DEV_920_4_NAME "AccelePort Xr920 4 port" -#define PCI_DEV_920_8_NAME "AccelePort Xr920 8 port" -#define PCI_DEV_XR_422_NAME "AccelePort Xr 422" -#define PCI_DEV_EPCJ_NAME "AccelePort EPC (PLX)" -#define PCI_DEV_XR_BULL_NAME "AccelePort Xr (BULL)" -#define PCI_DEV_XR_IBM_NAME "AccelePort Xr (IBM)" -#define PCI_DEV_CX_IBM_NAME "AccelePort CX (IBM)" -#define PCI_DEV_920_8_HP_NAME "AccelePort Xr920 8 port (HP)" -#define PCI_DEV_XEM_HP_NAME "AccelePort XEM (HP)" - -/* - * On the PCI boards, there is no IO space allocated - * The I/O registers will be in the first 3 bytes of the - * upper 2MB of the 4MB memory space. The board memory - * will be mapped into the low 2MB of the 4MB memory space - */ - -/* Potential location of PCI Bios from E0000 to FFFFF*/ -#define PCI_BIOS_SIZE 0x00020000 - -/* Size of Memory and I/O for PCI (4MB) */ -#define PCI_RAM_SIZE 0x00400000 - -/* Size of Memory (2MB) */ -#define PCI_MEM_SIZE 0x00200000 - -/* Max PCI Window Size (2MB) */ -#define PCI_WIN_SIZE 0x00200000 - -#define PCI_WIN_SHIFT 21 /* 21 bits max */ - -/* Offset of I/0 in Memory (2MB) */ -#define PCI_IO_OFFSET 0x00200000 - -/* Size of IO (2MB) */ -#define PCI_IO_SIZE_DGAP 0x00200000 - -/* Number of boards we support at once. */ -#define MAXBOARDS 32 -#define MAXPORTS 224 -#define MAXTTYNAMELEN 200 - -/* Our 3 magic numbers for our board, channel and unit structs */ -#define DGAP_BOARD_MAGIC 0x5c6df104 -#define DGAP_CHANNEL_MAGIC 0x6c6df104 -#define DGAP_UNIT_MAGIC 0x7c6df104 - -/* Serial port types */ -#define DGAP_SERIAL 0 -#define DGAP_PRINT 1 - -#define SERIAL_TYPE_NORMAL 1 - -/* 4 extra for alignment play space */ -#define WRITEBUFLEN ((4096) + 4) -#define MYFLIPLEN N_TTY_BUF_SIZE - -#define SBREAK_TIME 0x25 -#define U2BSIZE 0x400 - -#define dgap_jiffies_from_ms(a) (((a) * HZ) / 1000) - -/* - * Our major for the mgmt devices. - * - * We can use 22, because Digi was allocated 22 and 23 for the epca driver. - * 22 has now become obsolete now that the "cu" devices have - * been removed from 2.6. - * Also, this *IS* the epca driver, just PCI only now. - */ -#ifndef DIGI_DGAP_MAJOR -# define DIGI_DGAP_MAJOR 22 -#endif - -/* - * The parameters we use to define the periods of the moving averages. - */ -#define MA_PERIOD (HZ / 10) -#define SMA_DUR (1 * HZ) -#define EMA_DUR (1 * HZ) -#define SMA_NPERIODS (SMA_DUR / MA_PERIOD) -#define EMA_NPERIODS (EMA_DUR / MA_PERIOD) - -/* - * Define a local default termios struct. All ports will be created - * with this termios initially. This is the same structure that is defined - * as the default in tty_io.c with the same settings overridden as in serial.c - * - * In short, this should match the internal serial ports' defaults. - */ -#define DEFAULT_IFLAGS (ICRNL | IXON) -#define DEFAULT_OFLAGS (OPOST | ONLCR) -#define DEFAULT_CFLAGS (B9600 | CS8 | CREAD | HUPCL | CLOCAL) -#define DEFAULT_LFLAGS (ISIG | ICANON | ECHO | ECHOE | ECHOK | \ - ECHOCTL | ECHOKE | IEXTEN) - -#ifndef _POSIX_VDISABLE -#define _POSIX_VDISABLE ('\0') -#endif - -#define SNIFF_MAX 65536 /* Sniff buffer size (2^n) */ -#define SNIFF_MASK (SNIFF_MAX - 1) /* Sniff wrap mask */ - -#define VPDSIZE (512) - -/************************************************************************ - * FEP memory offsets - ************************************************************************/ -#define START 0x0004L /* Execution start address */ - -#define CMDBUF 0x0d10L /* Command (cm_t) structure offset */ -#define CMDSTART 0x0400L /* Start of command buffer */ -#define CMDMAX 0x0800L /* End of command buffer */ - -#define EVBUF 0x0d18L /* Event (ev_t) structure */ -#define EVSTART 0x0800L /* Start of event buffer */ -#define EVMAX 0x0c00L /* End of event buffer */ -#define FEP5_PLUS 0x0E40 /* ASCII '5' and ASCII 'A' is here */ -#define ECS_SEG 0x0E44 /* Segment of the extended */ - /* channel structure */ -#define LINE_SPEED 0x10 /* Offset into ECS_SEG for line */ - /* speed if the fep has extended */ - /* capabilities */ - -/* BIOS MAGIC SPOTS */ -#define ERROR 0x0C14L /* BIOS error code */ -#define SEQUENCE 0x0C12L /* BIOS sequence indicator */ -#define POSTAREA 0x0C00L /* POST complete message area */ - -/* FEP MAGIC SPOTS */ -#define FEPSTAT POSTAREA /* OS here when FEP comes up */ -#define NCHAN 0x0C02L /* number of ports FEP sees */ -#define PANIC 0x0C10L /* PANIC area for FEP */ -#define KMEMEM 0x0C30L /* Memory for KME use */ -#define CONFIG 0x0CD0L /* Concentrator configuration info */ -#define CONFIGSIZE 0x0030 /* configuration info size */ -#define DOWNREQ 0x0D00 /* Download request buffer pointer */ - -#define CHANBUF 0x1000L /* Async channel (bs_t) structs */ -#define FEPOSSIZE 0x1FFF /* 8K FEPOS */ - -#define XEMPORTS 0xC02 /* - * Offset in board memory where FEP5 stores - * how many ports it has detected. - * NOTE: FEP5 reports 64 ports when the user - * has the cable in EBI OUT instead of EBI IN. - */ - -#define FEPCLR 0x00 -#define FEPMEM 0x02 -#define FEPRST 0x04 -#define FEPINT 0x08 -#define FEPMASK 0x0e -#define FEPWIN 0x80 - -#define LOWMEM 0x0100 -#define HIGHMEM 0x7f00 - -#define FEPTIMEOUT 200000 - -#define ENABLE_INTR 0x0e04 /* Enable interrupts flag */ -#define FEPPOLL_MIN 1 /* minimum of 1 millisecond */ -#define FEPPOLL_MAX 20 /* maximum of 20 milliseconds */ -#define FEPPOLL 0x0c26 /* Fep event poll interval */ - -#define IALTPIN 0x0080 /* Input flag to swap DSR <-> DCD */ - -/************************************************************************ - * FEP supported functions - ************************************************************************/ -#define SRLOW 0xe0 /* Set receive low water */ -#define SRHIGH 0xe1 /* Set receive high water */ -#define FLUSHTX 0xe2 /* Flush transmit buffer */ -#define PAUSETX 0xe3 /* Pause data transmission */ -#define RESUMETX 0xe4 /* Resume data transmission */ -#define SMINT 0xe5 /* Set Modem Interrupt */ -#define SAFLOWC 0xe6 /* Set Aux. flow control chars */ -#define SBREAK 0xe8 /* Send break */ -#define SMODEM 0xe9 /* Set 8530 modem control lines */ -#define SIFLAG 0xea /* Set UNIX iflags */ -#define SFLOWC 0xeb /* Set flow control characters */ -#define STLOW 0xec /* Set transmit low water mark */ -#define RPAUSE 0xee /* Pause receive */ -#define RRESUME 0xef /* Resume receive */ -#define CHRESET 0xf0 /* Reset Channel */ -#define BUFSETALL 0xf2 /* Set Tx & Rx buffer size avail*/ -#define SOFLAG 0xf3 /* Set UNIX oflags */ -#define SHFLOW 0xf4 /* Set hardware handshake */ -#define SCFLAG 0xf5 /* Set UNIX cflags */ -#define SVNEXT 0xf6 /* Set VNEXT character */ -#define SPINTFC 0xfc /* Reserved */ -#define SCOMMODE 0xfd /* Set RS232/422 mode */ - -/************************************************************************ - * Modes for SCOMMODE - ************************************************************************/ -#define MODE_232 0x00 -#define MODE_422 0x01 - -/************************************************************************ - * Event flags. - ************************************************************************/ -#define IFBREAK 0x01 /* Break received */ -#define IFTLW 0x02 /* Transmit low water */ -#define IFTEM 0x04 /* Transmitter empty */ -#define IFDATA 0x08 /* Receive data present */ -#define IFMODEM 0x20 /* Modem status change */ - -/************************************************************************ - * Modem flags - ************************************************************************/ -# define DM_RTS 0x02 /* Request to send */ -# define DM_CD 0x80 /* Carrier detect */ -# define DM_DSR 0x20 /* Data set ready */ -# define DM_CTS 0x10 /* Clear to send */ -# define DM_RI 0x40 /* Ring indicator */ -# define DM_DTR 0x01 /* Data terminal ready */ - -/* - * defines from dgap_conf.h - */ -#define NULLNODE 0 /* header node, not used */ -#define BNODE 1 /* Board node */ -#define LNODE 2 /* Line node */ -#define CNODE 3 /* Concentrator node */ -#define MNODE 4 /* EBI Module node */ -#define TNODE 5 /* tty name prefix node */ -#define CUNODE 6 /* cu name prefix (non-SCO) */ -#define PNODE 7 /* trans. print prefix node */ -#define JNODE 8 /* maJor number node */ -#define ANODE 9 /* altpin */ -#define TSNODE 10 /* tty structure size */ -#define CSNODE 11 /* channel structure size */ -#define BSNODE 12 /* board structure size */ -#define USNODE 13 /* unit schedule structure size */ -#define FSNODE 14 /* f2200 structure size */ -#define VSNODE 15 /* size of VPIX structures */ -#define INTRNODE 16 /* enable interrupt */ - -/* Enumeration of tokens */ -#define BEGIN 1 -#define END 2 -#define BOARD 10 - -#define EPCFS 11 /* start of EPC family definitions */ -#define ICX 11 -#define MCX 13 -#define PCX 14 -#define IEPC 15 -#define EEPC 16 -#define MEPC 17 -#define IPCM 18 -#define EPCM 19 -#define MPCM 20 -#define PEPC 21 -#define PPCM 22 -#ifdef CP -#define ICP 23 -#define ECP 24 -#define MCP 25 -#endif -#define EPCFE 25 /* end of EPC family definitions */ -#define PC2E 26 -#define PC4E 27 -#define PC4E8K 28 -#define PC8E 29 -#define PC8E8K 30 -#define PC16E 31 -#define MC2E8K 34 -#define MC4E8K 35 -#define MC8E8K 36 - -#define AVANFS 42 /* start of Avanstar family definitions */ -#define A8P 42 -#define A16P 43 -#define AVANFE 43 /* end of Avanstar family definitions */ - -#define DA2000FS 44 /* start of AccelePort 2000 family definitions */ -#define DA22 44 /* AccelePort 2002 */ -#define DA24 45 /* AccelePort 2004 */ -#define DA28 46 /* AccelePort 2008 */ -#define DA216 47 /* AccelePort 2016 */ -#define DAR4 48 /* AccelePort RAS 4 port */ -#define DAR8 49 /* AccelePort RAS 8 port */ -#define DDR24 50 /* DataFire RAS 24 port */ -#define DDR30 51 /* DataFire RAS 30 port */ -#define DDR48 52 /* DataFire RAS 48 port */ -#define DDR60 53 /* DataFire RAS 60 port */ -#define DA2000FE 53 /* end of AccelePort 2000/RAS family definitions */ - -#define PCXRFS 106 /* start of PCXR family definitions */ -#define APORT4 106 -#define APORT8 107 -#define PAPORT4 108 -#define PAPORT8 109 -#define APORT4_920I 110 -#define APORT8_920I 111 -#define APORT4_920P 112 -#define APORT8_920P 113 -#define APORT2_920P 114 -#define PCXRFE 117 /* end of PCXR family definitions */ - -#define LINE 82 -#ifdef T1 -#define T1M 83 -#define E1M 84 -#endif -#define CONC 64 -#define CX 65 -#define EPC 66 -#define MOD 67 -#define PORTS 68 -#define METHOD 69 -#define CUSTOM 70 -#define BASIC 71 -#define STATUS 72 -#define MODEM 73 -/* The following tokens can appear in multiple places */ -#define SPEED 74 -#define NPORTS 75 -#define ID 76 -#define CABLE 77 -#define CONNECT 78 -#define MEM 80 -#define DPSZ 81 - -#define TTYN 90 -#define CU 91 -#define PRINT 92 -#define XPRINT 93 -#define CMAJOR 94 -#define ALTPIN 95 -#define STARTO 96 -#define USEINTR 97 -#define PCIINFO 98 - -#define TTSIZ 100 -#define CHSIZ 101 -#define BSSIZ 102 -#define UNTSIZ 103 -#define F2SIZ 104 -#define VPSIZ 105 - -#define TOTAL_BOARD 2 -#define CURRENT_BRD 4 -#define BOARD_TYPE 6 -#define IO_ADDRESS 8 -#define MEM_ADDRESS 10 - -#define FIELDS_PER_PAGE 18 - -#define TB_FIELD 1 -#define CB_FIELD 3 -#define BT_FIELD 5 -#define IO_FIELD 7 -#define ID_FIELD 8 -#define ME_FIELD 9 -#define TTY_FIELD 11 -#define CU_FIELD 13 -#define PR_FIELD 15 -#define MPR_FIELD 17 - -#define MAX_FIELD 512 - -#define INIT 0 -#define NITEMS 128 -#define MAX_ITEM 512 - -#define DSCRINST 1 -#define DSCRNUM 3 -#define ALTPINQ 5 -#define SSAVE 7 - -#define DSCR "32" -#define ONETONINE "123456789" -#define ALL "1234567890" - -/* - * All the possible states the driver can be while being loaded. - */ -enum { - DRIVER_INITIALIZED = 0, - DRIVER_READY -}; - -/* - * All the possible states the board can be while booting up. - */ -enum { - BOARD_FAILED = 0, - BOARD_READY -}; - -/* - * All the possible states that a requested concentrator image can be in. - */ -enum { - NO_PENDING_CONCENTRATOR_REQUESTS = 0, - NEED_CONCENTRATOR, - REQUESTED_CONCENTRATOR -}; - -/* - * Modem line constants are defined as macros because DSR and - * DCD are swapable using the ditty altpin option. - */ -#define D_CD(ch) ch->ch_cd /* Carrier detect */ -#define D_DSR(ch) ch->ch_dsr /* Data set ready */ -#define D_RTS(ch) DM_RTS /* Request to send */ -#define D_CTS(ch) DM_CTS /* Clear to send */ -#define D_RI(ch) DM_RI /* Ring indicator */ -#define D_DTR(ch) DM_DTR /* Data terminal ready */ - -/************************************************************************* - * - * Structures and closely related defines. - * - *************************************************************************/ - -/* - * A structure to hold a statistics counter. We also - * compute moving averages for this counter. - */ -struct macounter { - u32 cnt; /* Total count */ - ulong accum; /* Acuumulator per period */ - ulong sma; /* Simple moving average */ - ulong ema; /* Exponential moving average */ -}; - -/************************************************************************ - * Device flag definitions for bd_flags. - ************************************************************************/ -#define BD_FEP5PLUS 0x0001 /* Supports FEP5 Plus commands */ -#define BD_HAS_VPD 0x0002 /* Board has VPD info available */ - -/* - * Per-board information - */ -struct board_t { - int magic; /* Board Magic number. */ - int boardnum; /* Board number: 0-3 */ - - int type; /* Type of board */ - char *name; /* Product Name */ - struct pci_dev *pdev; /* Pointer to the pci_dev struct */ - u16 vendor; /* PCI vendor ID */ - u16 device; /* PCI device ID */ - u16 subvendor; /* PCI subsystem vendor ID */ - u16 subdevice; /* PCI subsystem device ID */ - u8 rev; /* PCI revision ID */ - uint pci_bus; /* PCI bus value */ - uint pci_slot; /* PCI slot value */ - u16 maxports; /* MAX ports this board can handle */ - u8 vpd[VPDSIZE]; /* VPD of board, if found */ - u32 bd_flags; /* Board flags */ - - spinlock_t bd_lock; /* Used to protect board */ - - u32 state; /* State of card. */ - wait_queue_head_t state_wait; /* Place to sleep on for state change */ - - struct tasklet_struct helper_tasklet; /* Poll helper tasklet */ - - u32 wait_for_bios; - u32 wait_for_fep; - - struct cnode *bd_config; /* Config of board */ - - u16 nasync; /* Number of ports on card */ - - ulong irq; /* Interrupt request number */ - ulong intr_count; /* Count of interrupts */ - u32 intr_used; /* Non-zero if using interrupts */ - u32 intr_running; /* Non-zero if FEP knows its doing */ - /* interrupts */ - - ulong port; /* Start of base io port of the card */ - ulong port_end; /* End of base io port of the card */ - ulong membase; /* Start of base memory of the card */ - ulong membase_end; /* End of base memory of the card */ - - u8 __iomem *re_map_port; /* Remapped io port of the card */ - u8 __iomem *re_map_membase;/* Remapped memory of the card */ - - u8 inhibit_poller; /* Tells the poller to leave us alone */ - - struct channel_t *channels[MAXPORTS]; /* array of pointers to our */ - /* channels. */ - - struct tty_driver *serial_driver; - struct tty_port *serial_ports; - char serial_name[200]; - struct tty_driver *print_driver; - struct tty_port *printer_ports; - char print_name[200]; - - struct bs_t __iomem *bd_bs; /* Base structure pointer */ - - char *flipbuf; /* Our flip buffer, alloced if */ - /* board is found */ - char *flipflagbuf; /* Our flip flag buffer, alloced */ - /* if board is found */ - - u16 dpatype; /* The board "type", as defined */ - /* by DPA */ - u16 dpastatus; /* The board "status", as defined */ - /* by DPA */ - - u32 conc_dl_status; /* Status of any pending conc */ - /* download */ -}; - -/************************************************************************ - * Unit flag definitions for un_flags. - ************************************************************************/ -#define UN_ISOPEN 0x0001 /* Device is open */ -#define UN_CLOSING 0x0002 /* Line is being closed */ -#define UN_IMM 0x0004 /* Service immediately */ -#define UN_BUSY 0x0008 /* Some work this channel */ -#define UN_BREAKI 0x0010 /* Input break received */ -#define UN_PWAIT 0x0020 /* Printer waiting for terminal */ -#define UN_TIME 0x0040 /* Waiting on time */ -#define UN_EMPTY 0x0080 /* Waiting output queue empty */ -#define UN_LOW 0x0100 /* Waiting output low water mark*/ -#define UN_EXCL_OPEN 0x0200 /* Open for exclusive use */ -#define UN_WOPEN 0x0400 /* Device waiting for open */ -#define UN_WIOCTL 0x0800 /* Device waiting for open */ -#define UN_HANGUP 0x8000 /* Carrier lost */ - -struct device; - -/************************************************************************ - * Structure for terminal or printer unit. - ************************************************************************/ -struct un_t { - int magic; /* Unit Magic Number. */ - struct channel_t *un_ch; - u32 un_time; - u32 un_type; - int un_open_count; /* Counter of opens to port */ - struct tty_struct *un_tty;/* Pointer to unit tty structure */ - u32 un_flags; /* Unit flags */ - wait_queue_head_t un_flags_wait; /* Place to sleep to wait on unit */ - u32 un_dev; /* Minor device number */ - tcflag_t un_oflag; /* oflags being done on board */ - tcflag_t un_lflag; /* lflags being done on board */ - struct device *un_sysfs; -}; - -/************************************************************************ - * Device flag definitions for ch_flags. - ************************************************************************/ -#define CH_PRON 0x0001 /* Printer on string */ -#define CH_OUT 0x0002 /* Dial-out device open */ -#define CH_STOP 0x0004 /* Output is stopped */ -#define CH_STOPI 0x0008 /* Input is stopped */ -#define CH_CD 0x0010 /* Carrier is present */ -#define CH_FCAR 0x0020 /* Carrier forced on */ - -#define CH_RXBLOCK 0x0080 /* Enable rx blocked flag */ -#define CH_WLOW 0x0100 /* Term waiting low event */ -#define CH_WEMPTY 0x0200 /* Term waiting empty event */ -#define CH_RENABLE 0x0400 /* Buffer just emptied */ -#define CH_RACTIVE 0x0800 /* Process active in xxread() */ -#define CH_RWAIT 0x1000 /* Process waiting in xxread() */ -#define CH_BAUD0 0x2000 /* Used for checking B0 transitions */ -#define CH_HANGUP 0x8000 /* Hangup received */ - -/* - * Definitions for ch_sniff_flags - */ -#define SNIFF_OPEN 0x1 -#define SNIFF_WAIT_DATA 0x2 -#define SNIFF_WAIT_SPACE 0x4 - -/************************************************************************ - *** Definitions for Digi ditty(1) command. - ************************************************************************/ - -/************************************************************************ - * This module provides application access to special Digi - * serial line enhancements which are not standard UNIX(tm) features. - ************************************************************************/ - -#if !defined(TIOCMODG) - -#define TIOCMODG (('d'<<8) | 250) /* get modem ctrl state */ -#define TIOCMODS (('d'<<8) | 251) /* set modem ctrl state */ - -#ifndef TIOCM_LE -#define TIOCM_LE 0x01 /* line enable */ -#define TIOCM_DTR 0x02 /* data terminal ready */ -#define TIOCM_RTS 0x04 /* request to send */ -#define TIOCM_ST 0x08 /* secondary transmit */ -#define TIOCM_SR 0x10 /* secondary receive */ -#define TIOCM_CTS 0x20 /* clear to send */ -#define TIOCM_CAR 0x40 /* carrier detect */ -#define TIOCM_RNG 0x80 /* ring indicator */ -#define TIOCM_DSR 0x100 /* data set ready */ -#define TIOCM_RI TIOCM_RNG /* ring (alternate) */ -#define TIOCM_CD TIOCM_CAR /* carrier detect (alt) */ -#endif - -#endif - -#if !defined(TIOCMSET) -#define TIOCMSET (('d'<<8) | 252) /* set modem ctrl state */ -#define TIOCMGET (('d'<<8) | 253) /* set modem ctrl state */ -#endif - -#if !defined(TIOCMBIC) -#define TIOCMBIC (('d'<<8) | 254) /* set modem ctrl state */ -#define TIOCMBIS (('d'<<8) | 255) /* set modem ctrl state */ -#endif - -#if !defined(TIOCSDTR) -#define TIOCSDTR (('e'<<8) | 0) /* set DTR */ -#define TIOCCDTR (('e'<<8) | 1) /* clear DTR */ -#endif - -/************************************************************************ - * Ioctl command arguments for DIGI parameters. - ************************************************************************/ -#define DIGI_GETA (('e'<<8) | 94) /* Read params */ - -#define DIGI_SETA (('e'<<8) | 95) /* Set params */ -#define DIGI_SETAW (('e'<<8) | 96) /* Drain & set params */ -#define DIGI_SETAF (('e'<<8) | 97) /* Drain, flush & set params */ - -#define DIGI_KME (('e'<<8) | 98) /* Read/Write Host */ - /* Adapter Memory */ - -#define DIGI_GETFLOW (('e'<<8) | 99) /* Get startc/stopc flow */ - /* control characters */ -#define DIGI_SETFLOW (('e'<<8) | 100) /* Set startc/stopc flow */ - /* control characters */ -#define DIGI_GETAFLOW (('e'<<8) | 101) /* Get Aux. startc/stopc */ - /* flow control chars */ -#define DIGI_SETAFLOW (('e'<<8) | 102) /* Set Aux. startc/stopc */ - /* flow control chars */ - -#define DIGI_GEDELAY (('d'<<8) | 246) /* Get edelay */ -#define DIGI_SEDELAY (('d'<<8) | 247) /* Set edelay */ - -struct digiflow_t { - unsigned char startc; /* flow cntl start char */ - unsigned char stopc; /* flow cntl stop char */ -}; - -#ifdef FLOW_2200 -#define F2200_GETA (('e'<<8) | 104) /* Get 2x36 flow cntl flags */ -#define F2200_SETAW (('e'<<8) | 105) /* Set 2x36 flow cntl flags */ -#define F2200_MASK 0x03 /* 2200 flow cntl bit mask */ -#define FCNTL_2200 0x01 /* 2x36 terminal flow cntl */ -#define PCNTL_2200 0x02 /* 2x36 printer flow cntl */ -#define F2200_XON 0xf8 -#define P2200_XON 0xf9 -#define F2200_XOFF 0xfa -#define P2200_XOFF 0xfb - -#define FXOFF_MASK 0x03 /* 2200 flow status mask */ -#define RCVD_FXOFF 0x01 /* 2x36 Terminal XOFF rcvd */ -#define RCVD_PXOFF 0x02 /* 2x36 Printer XOFF rcvd */ -#endif - -/************************************************************************ - * Values for digi_flags - ************************************************************************/ -#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */ -#define DIGI_FAST 0x0002 /* Fast baud rates */ -#define RTSPACE 0x0004 /* RTS input flow control */ -#define CTSPACE 0x0008 /* CTS output flow control */ -#define DSRPACE 0x0010 /* DSR output flow control */ -#define DCDPACE 0x0020 /* DCD output flow control */ -#define DTRPACE 0x0040 /* DTR input flow control */ -#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */ -#define DIGI_FORCEDCD 0x0100 /* Force carrier */ -#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */ -#define DIGI_AIXON 0x0400 /* Aux flow control in fep */ -#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl*/ -#define DIGI_PP_INPUT 0x1000 /* Change parallel port to input*/ -#define DIGI_DTR_TOGGLE 0x2000 /* Support DTR Toggle */ -#define DIGI_422 0x4000 /* for 422/232 selectable panel */ -#define DIGI_RTS_TOGGLE 0x8000 /* Support RTS Toggle */ - -/************************************************************************ - * These options are not supported on the comxi. - ************************************************************************/ -#define DIGI_COMXI (DIGI_FAST|DIGI_COOK|DSRPACE|DCDPACE|DTRPACE) - -#define DIGI_PLEN 28 /* String length */ -#define DIGI_TSIZ 10 /* Terminal string len */ - -/************************************************************************ - * Structure used with ioctl commands for DIGI parameters. - ************************************************************************/ -struct digi_t { - unsigned short digi_flags; /* Flags (see above) */ - unsigned short digi_maxcps; /* Max printer CPS */ - unsigned short digi_maxchar; /* Max chars in print queue */ - unsigned short digi_bufsize; /* Buffer size */ - unsigned char digi_onlen; /* Length of ON string */ - unsigned char digi_offlen; /* Length of OFF string */ - char digi_onstr[DIGI_PLEN]; /* Printer on string */ - char digi_offstr[DIGI_PLEN]; /* Printer off string */ - char digi_term[DIGI_TSIZ]; /* terminal string */ -}; - -/************************************************************************ - * KME definitions and structures. - ************************************************************************/ -#define RW_IDLE 0 /* Operation complete */ -#define RW_READ 1 /* Read Concentrator Memory */ -#define RW_WRITE 2 /* Write Concentrator Memory */ - -struct rw_t { - unsigned char rw_req; /* Request type */ - unsigned char rw_board; /* Host Adapter board number */ - unsigned char rw_conc; /* Concentrator number */ - unsigned char rw_reserved; /* Reserved for expansion */ - unsigned long rw_addr; /* Address in concentrator */ - unsigned short rw_size; /* Read/write request length */ - unsigned char rw_data[128]; /* Data to read/write */ -}; - -/************************************************************************ - * Structure to get driver status information - ************************************************************************/ -struct digi_dinfo { - unsigned long dinfo_nboards; /* # boards configured */ - char dinfo_reserved[12]; /* for future expansion */ - char dinfo_version[16]; /* driver version */ -}; - -#define DIGI_GETDD (('d'<<8) | 248) /* get driver info */ - -/************************************************************************ - * Structure used with ioctl commands for per-board information - * - * physsize and memsize differ when board has "windowed" memory - ************************************************************************/ -struct digi_info { - unsigned long info_bdnum; /* Board number (0 based) */ - unsigned long info_ioport; /* io port address */ - unsigned long info_physaddr; /* memory address */ - unsigned long info_physsize; /* Size of host mem window */ - unsigned long info_memsize; /* Amount of dual-port mem */ - /* on board */ - unsigned short info_bdtype; /* Board type */ - unsigned short info_nports; /* number of ports */ - char info_bdstate; /* board state */ - char info_reserved[7]; /* for future expansion */ -}; - -#define DIGI_GETBD (('d'<<8) | 249) /* get board info */ - -struct digi_stat { - unsigned int info_chan; /* Channel number (0 based) */ - unsigned int info_brd; /* Board number (0 based) */ - unsigned long info_cflag; /* cflag for channel */ - unsigned long info_iflag; /* iflag for channel */ - unsigned long info_oflag; /* oflag for channel */ - unsigned long info_mstat; /* mstat for channel */ - unsigned long info_tx_data; /* tx_data for channel */ - unsigned long info_rx_data; /* rx_data for channel */ - unsigned long info_hflow; /* hflow for channel */ - unsigned long info_reserved[8]; /* for future expansion */ -}; - -#define DIGI_GETSTAT (('d'<<8) | 244) /* get board info */ -/************************************************************************ - * - * Structure used with ioctl commands for per-channel information - * - ************************************************************************/ -struct digi_ch { - unsigned long info_bdnum; /* Board number (0 based) */ - unsigned long info_channel; /* Channel index number */ - unsigned long info_ch_cflag; /* Channel cflag */ - unsigned long info_ch_iflag; /* Channel iflag */ - unsigned long info_ch_oflag; /* Channel oflag */ - unsigned long info_chsize; /* Channel structure size */ - unsigned long info_sleep_stat; /* sleep status */ - dev_t info_dev; /* device number */ - unsigned char info_initstate; /* Channel init state */ - unsigned char info_running; /* Channel running state */ - long reserved[8]; /* reserved for future use */ -}; - -/* -* This structure is used with the DIGI_FEPCMD ioctl to -* tell the driver which port to send the command for. -*/ -struct digi_cmd { - int cmd; - int word; - int ncmds; - int chan; /* channel index (zero based) */ - int bdid; /* board index (zero based) */ -}; - -/* -* info_sleep_stat defines -*/ -#define INFO_RUNWAIT 0x0001 -#define INFO_WOPEN 0x0002 -#define INFO_TTIOW 0x0004 -#define INFO_CH_RWAIT 0x0008 -#define INFO_CH_WEMPTY 0x0010 -#define INFO_CH_WLOW 0x0020 -#define INFO_XXBUF_BUSY 0x0040 - -#define DIGI_GETCH (('d'<<8) | 245) /* get board info */ - -/* Board type definitions */ - -#define SUBTYPE 0007 -#define T_PCXI 0000 -#define T_PCXM 0001 -#define T_PCXE 0002 -#define T_PCXR 0003 -#define T_SP 0004 -#define T_SP_PLUS 0005 -# define T_HERC 0000 -# define T_HOU 0001 -# define T_LON 0002 -# define T_CHA 0003 -#define FAMILY 0070 -#define T_COMXI 0000 -#define T_PCXX 0010 -#define T_CX 0020 -#define T_EPC 0030 -#define T_PCLITE 0040 -#define T_SPXX 0050 -#define T_AVXX 0060 -#define T_DXB 0070 -#define T_A2K_4_8 0070 -#define BUSTYPE 0700 -#define T_ISABUS 0000 -#define T_MCBUS 0100 -#define T_EISABUS 0200 -#define T_PCIBUS 0400 - -/* Board State Definitions */ - -#define BD_RUNNING 0x0 -#define BD_REASON 0x7f -#define BD_NOTFOUND 0x1 -#define BD_NOIOPORT 0x2 -#define BD_NOMEM 0x3 -#define BD_NOBIOS 0x4 -#define BD_NOFEP 0x5 -#define BD_FAILED 0x6 -#define BD_ALLOCATED 0x7 -#define BD_TRIBOOT 0x8 -#define BD_BADKME 0x80 - -#define DIGI_LOOPBACK (('d'<<8) | 252) /* Enable/disable UART */ - /* internal loopback */ -#define DIGI_SPOLL (('d'<<8) | 254) /* change poller rate */ - -#define DIGI_SETCUSTOMBAUD _IOW('e', 106, int) /* Set integer baud rate */ -#define DIGI_GETCUSTOMBAUD _IOR('e', 107, int) /* Get integer baud rate */ -#define DIGI_RESET_PORT (('e'<<8) | 93) /* Reset port */ - -/************************************************************************ - * Channel information structure. - ************************************************************************/ -struct channel_t { - int magic; /* Channel Magic Number */ - struct bs_t __iomem *ch_bs; /* Base structure pointer */ - struct cm_t __iomem *ch_cm; /* Command queue pointer */ - struct board_t *ch_bd; /* Board structure pointer */ - u8 __iomem *ch_vaddr; /* FEP memory origin */ - u8 __iomem *ch_taddr; /* Write buffer origin */ - u8 __iomem *ch_raddr; /* Read buffer origin */ - struct digi_t ch_digi; /* Transparent Print structure */ - struct un_t ch_tun; /* Terminal unit info */ - struct un_t ch_pun; /* Printer unit info */ - - spinlock_t ch_lock; /* provide for serialization */ - wait_queue_head_t ch_flags_wait; - - u32 pscan_state; - u8 pscan_savechar; - - u32 ch_portnum; /* Port number, 0 offset. */ - u32 ch_open_count; /* open count */ - u32 ch_flags; /* Channel flags */ - - u32 ch_cpstime; /* Time for CPS calculations */ - - tcflag_t ch_c_iflag; /* channel iflags */ - tcflag_t ch_c_cflag; /* channel cflags */ - tcflag_t ch_c_oflag; /* channel oflags */ - tcflag_t ch_c_lflag; /* channel lflags */ - - u16 ch_fepiflag; /* FEP tty iflags */ - u16 ch_fepcflag; /* FEP tty cflags */ - u16 ch_fepoflag; /* FEP tty oflags */ - u16 ch_wopen; /* Waiting for open process cnt */ - u16 ch_tstart; /* Transmit buffer start */ - u16 ch_tsize; /* Transmit buffer size */ - u16 ch_rstart; /* Receive buffer start */ - u16 ch_rsize; /* Receive buffer size */ - u16 ch_rdelay; /* Receive delay time */ - - u16 ch_tlw; /* Our currently set low water mark */ - - u16 ch_cook; /* Output character mask */ - - u8 ch_card; /* Card channel is on */ - u8 ch_stopc; /* Stop character */ - u8 ch_startc; /* Start character */ - - u8 ch_mostat; /* FEP output modem status */ - u8 ch_mistat; /* FEP input modem status */ - u8 ch_mforce; /* Modem values to be forced */ - u8 ch_mval; /* Force values */ - u8 ch_fepstopc; /* FEP stop character */ - u8 ch_fepstartc; /* FEP start character */ - - u8 ch_astopc; /* Auxiliary Stop character */ - u8 ch_astartc; /* Auxiliary Start character */ - u8 ch_fepastopc; /* Auxiliary FEP stop char */ - u8 ch_fepastartc; /* Auxiliary FEP start char */ - - u8 ch_hflow; /* FEP hardware handshake */ - u8 ch_dsr; /* stores real dsr value */ - u8 ch_cd; /* stores real cd value */ - u8 ch_tx_win; /* channel tx buffer window */ - u8 ch_rx_win; /* channel rx buffer window */ - uint ch_custom_speed; /* Custom baud, if set */ - uint ch_baud_info; /* Current baud info for /proc output */ - ulong ch_rxcount; /* total of data received so far */ - ulong ch_txcount; /* total of data transmitted so far */ - ulong ch_err_parity; /* Count of parity errors on channel */ - ulong ch_err_frame; /* Count of framing errors on channel */ - ulong ch_err_break; /* Count of breaks on channel */ - ulong ch_err_overrun; /* Count of overruns on channel */ -}; - -/************************************************************************ - * Command structure definition. - ************************************************************************/ -struct cm_t { - unsigned short cm_head; /* Command buffer head offset */ - unsigned short cm_tail; /* Command buffer tail offset */ - unsigned short cm_start; /* start offset of buffer */ - unsigned short cm_max; /* last offset of buffer */ -}; - -/************************************************************************ - * Event structure definition. - ************************************************************************/ -struct ev_t { - unsigned short ev_head; /* Command buffer head offset */ - unsigned short ev_tail; /* Command buffer tail offset */ - unsigned short ev_start; /* start offset of buffer */ - unsigned short ev_max; /* last offset of buffer */ -}; - -/************************************************************************ - * Download buffer structure. - ************************************************************************/ -struct downld_t { - u8 dl_type; /* Header */ - u8 dl_seq; /* Download sequence */ - ushort dl_srev; /* Software revision number */ - ushort dl_lrev; /* Low revision number */ - ushort dl_hrev; /* High revision number */ - ushort dl_seg; /* Start segment address */ - ushort dl_size; /* Number of bytes to download */ - u8 dl_data[1024]; /* Download data */ -}; - -/************************************************************************ - * Per channel buffer structure - ************************************************************************ - * Base Structure Entries Usage Meanings to Host * - * * - * W = read write R = read only * - * C = changed by commands only * - * U = unknown (may be changed w/o notice) * - ************************************************************************/ -struct bs_t { - unsigned short tp_jmp; /* Transmit poll jump */ - unsigned short tc_jmp; /* Cooked procedure jump */ - unsigned short ri_jmp; /* Not currently used */ - unsigned short rp_jmp; /* Receive poll jump */ - - unsigned short tx_seg; /* W Tx segment */ - unsigned short tx_head; /* W Tx buffer head offset */ - unsigned short tx_tail; /* R Tx buffer tail offset */ - unsigned short tx_max; /* W Tx buffer size - 1 */ - - unsigned short rx_seg; /* W Rx segment */ - unsigned short rx_head; /* W Rx buffer head offset */ - unsigned short rx_tail; /* R Rx buffer tail offset */ - unsigned short rx_max; /* W Rx buffer size - 1 */ - - unsigned short tx_lw; /* W Tx buffer low water mark */ - unsigned short rx_lw; /* W Rx buffer low water mark */ - unsigned short rx_hw; /* W Rx buffer high water mark*/ - unsigned short incr; /* W Increment to next channel*/ - - unsigned short fepdev; /* U SCC device base address */ - unsigned short edelay; /* W Exception delay */ - unsigned short blen; /* W Break length */ - unsigned short btime; /* U Break complete time */ - - unsigned short iflag; /* C UNIX input flags */ - unsigned short oflag; /* C UNIX output flags */ - unsigned short cflag; /* C UNIX control flags */ - unsigned short wfill[13]; /* U Reserved for expansion */ - - unsigned char num; /* U Channel number */ - unsigned char ract; /* U Receiver active counter */ - unsigned char bstat; /* U Break status bits */ - unsigned char tbusy; /* W Transmit busy */ - unsigned char iempty; /* W Transmit empty event */ - /* enable */ - unsigned char ilow; /* W Transmit low-water event */ - /* enable */ - unsigned char idata; /* W Receive data interrupt */ - /* enable */ - unsigned char eflag; /* U Host event flags */ - - unsigned char tflag; /* U Transmit flags */ - unsigned char rflag; /* U Receive flags */ - unsigned char xmask; /* U Transmit ready flags */ - unsigned char xval; /* U Transmit ready value */ - unsigned char m_stat; /* RC Modem status bits */ - unsigned char m_change; /* U Modem bits which changed */ - unsigned char m_int; /* W Modem interrupt enable */ - /* bits */ - unsigned char m_last; /* U Last modem status */ - - unsigned char mtran; /* C Unreported modem trans */ - unsigned char orun; /* C Buffer overrun occurred */ - unsigned char astartc; /* W Auxiliary Xon char */ - unsigned char astopc; /* W Auxiliary Xoff char */ - unsigned char startc; /* W Xon character */ - unsigned char stopc; /* W Xoff character */ - unsigned char vnextc; /* W Vnext character */ - unsigned char hflow; /* C Software flow control */ - - unsigned char fillc; /* U Delay Fill character */ - unsigned char ochar; /* U Saved output character */ - unsigned char omask; /* U Output character mask */ - - unsigned char bfill[13]; /* U Reserved for expansion */ - - unsigned char scc[16]; /* U SCC registers */ -}; - -struct cnode { - struct cnode *next; - int type; - int numbrd; - - union { - struct { - char type; /* Board Type */ - long addr; /* Memory Address */ - char *addrstr; /* Memory Address in string */ - long pcibus; /* PCI BUS */ - char *pcibusstr; /* PCI BUS in string */ - long pcislot; /* PCI SLOT */ - char *pcislotstr; /* PCI SLOT in string */ - long nport; /* Number of Ports */ - char *id; /* tty id */ - long start; /* start of tty counting */ - char *method; /* Install method */ - char v_addr; - char v_pcibus; - char v_pcislot; - char v_nport; - char v_id; - char v_start; - char v_method; - char line1; - char line2; - char conc1; /* total concs in line1 */ - char conc2; /* total concs in line2 */ - char module1; /* total modules for line1 */ - char module2; /* total modules for line2 */ - char *status; /* config status */ - char *dimstatus; /* Y/N */ - int status_index; /* field pointer */ - } board; - - struct { - char *cable; - char v_cable; - long speed; - char v_speed; - } line; - - struct { - char type; - char *connect; - long speed; - long nport; - char *id; - char *idstr; - long start; - char v_connect; - char v_speed; - char v_nport; - char v_id; - char v_start; - } conc; - - struct { - char type; - long nport; - char *id; - char *idstr; - long start; - char v_nport; - char v_id; - char v_start; - } module; - - char *ttyname; - char *cuname; - char *printname; - long majornumber; - long altpin; - long ttysize; - long chsize; - long bssize; - long unsize; - long f2size; - long vpixsize; - long useintr; - } u; -}; -#endif diff --git a/drivers/staging/dgnc/dgnc_cls.c b/drivers/staging/dgnc/dgnc_cls.c index 72f0aaa6911f..0ff3139e52b6 100644 --- a/drivers/staging/dgnc/dgnc_cls.c +++ b/drivers/staging/dgnc/dgnc_cls.c @@ -823,7 +823,7 @@ static void cls_copy_data_from_uart_to_queue(struct channel_t *ch) tail = ch->ch_r_tail; /* Store how much space we have left in the queue */ - qleft = (tail - head - 1); + qleft = tail - head - 1; if (qleft < 0) qleft += RQUEUEMASK + 1; diff --git a/drivers/staging/dgnc/dgnc_driver.c b/drivers/staging/dgnc/dgnc_driver.c index fc6d2989e28f..4eb410e09609 100644 --- a/drivers/staging/dgnc/dgnc_driver.c +++ b/drivers/staging/dgnc/dgnc_driver.c @@ -125,12 +125,7 @@ static struct pci_driver dgnc_driver = { * ************************************************************************/ -/* - * dgnc_cleanup_module() - * - * Module unload. This is where it all ends. - */ -static void dgnc_cleanup_module(void) +static void cleanup(bool sysfiles) { int i; unsigned long flags; @@ -142,7 +137,8 @@ static void dgnc_cleanup_module(void) /* Turn off poller right away. */ del_timer_sync(&dgnc_poll_timer); - dgnc_remove_driver_sysfiles(&dgnc_driver); + if (sysfiles) + dgnc_remove_driver_sysfiles(&dgnc_driver); device_destroy(dgnc_class, MKDEV(dgnc_Major, 0)); class_destroy(dgnc_class); @@ -155,9 +151,17 @@ static void dgnc_cleanup_module(void) } dgnc_tty_post_uninit(); +} - if (dgnc_NumBoards) - pci_unregister_driver(&dgnc_driver); +/* + * dgnc_cleanup_module() + * + * Module unload. This is where it all ends. + */ +static void dgnc_cleanup_module(void) +{ + cleanup(true); + pci_unregister_driver(&dgnc_driver); } /* @@ -181,23 +185,14 @@ static int __init dgnc_init_module(void) * Find and configure all the cards */ rc = pci_register_driver(&dgnc_driver); - - /* - * If something went wrong in the scan, bail out of driver. - */ - if (rc < 0) { - /* Only unregister if it was actually registered. */ - if (dgnc_NumBoards) - pci_unregister_driver(&dgnc_driver); - else - pr_warn("WARNING: dgnc driver load failed. No Digi Neo or Classic boards found.\n"); - - dgnc_cleanup_module(); - } else { - dgnc_create_driver_sysfiles(&dgnc_driver); + if (rc) { + pr_warn("WARNING: dgnc driver load failed. No Digi Neo or Classic boards found.\n"); + cleanup(false); + return rc; } + dgnc_create_driver_sysfiles(&dgnc_driver); - return rc; + return 0; } module_init(dgnc_init_module); @@ -283,13 +278,13 @@ static int dgnc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* wake up and enable device */ rc = pci_enable_device(pdev); - if (rc < 0) { - rc = -EIO; - } else { - rc = dgnc_found_board(pdev, ent->driver_data); - if (rc == 0) - dgnc_NumBoards++; - } + if (rc) + return -EIO; + + rc = dgnc_found_board(pdev, ent->driver_data); + if (rc == 0) + dgnc_NumBoards++; + return rc; } diff --git a/drivers/staging/dgnc/dgnc_driver.h b/drivers/staging/dgnc/dgnc_driver.h index ce7cd9b96542..e4be81b66041 100644 --- a/drivers/staging/dgnc/dgnc_driver.h +++ b/drivers/staging/dgnc/dgnc_driver.h @@ -88,7 +88,6 @@ #define _POSIX_VDISABLE '\0' #endif - /* * All the possible states the driver can be while being loaded. */ @@ -106,7 +105,6 @@ enum { BOARD_READY }; - /************************************************************************* * * Structures and closely related defines. @@ -145,7 +143,6 @@ struct board_ops { ************************************************************************/ #define BD_IS_PCI_EXPRESS 0x0001 /* Is a PCI Express board */ - /* * Per-board information */ @@ -241,7 +238,6 @@ struct dgnc_board { }; - /************************************************************************ * Unit flag definitions for un_flags. ************************************************************************/ @@ -277,7 +273,6 @@ struct un_t { struct device *un_sysfs; }; - /************************************************************************ * Device flag definitions for ch_flags. ************************************************************************/ @@ -300,7 +295,6 @@ struct un_t { #define CH_FORCED_STOP 0x20000 /* Output is forcibly stopped */ #define CH_FORCED_STOPI 0x40000 /* Input is forcibly stopped */ - /* Our Read/Error/Write queue sizes */ #define RQUEUEMASK 0x1FFF /* 8 K - 1 */ #define EQUEUEMASK 0x1FFF /* 8 K - 1 */ @@ -309,7 +303,6 @@ struct un_t { #define EQUEUESIZE RQUEUESIZE #define WQUEUESIZE (WQUEUEMASK + 1) - /************************************************************************ * Channel information structure. ************************************************************************/ @@ -397,7 +390,6 @@ struct channel_t { ulong ch_intr_tx; /* Count of interrupts */ ulong ch_intr_rx; /* Count of interrupts */ - /* /proc// entries */ struct proc_dir_entry *proc_entry_pointer; struct dgnc_proc_entry *dgnc_channel_table; diff --git a/drivers/staging/dgnc/dgnc_mgmt.c b/drivers/staging/dgnc/dgnc_mgmt.c index 518fbd5e2d0e..ba29a8d913f2 100644 --- a/drivers/staging/dgnc/dgnc_mgmt.c +++ b/drivers/staging/dgnc/dgnc_mgmt.c @@ -192,7 +192,7 @@ long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) spin_lock_irqsave(&ch->ch_lock, flags); - mstat = (ch->ch_mostat | ch->ch_mistat); + mstat = ch->ch_mostat | ch->ch_mistat; if (mstat & UART_MCR_DTR) { ni.mstat |= TIOCM_DTR; diff --git a/drivers/staging/dgnc/dgnc_neo.c b/drivers/staging/dgnc/dgnc_neo.c index 39c76e78e56a..31ac437cb4a4 100644 --- a/drivers/staging/dgnc/dgnc_neo.c +++ b/drivers/staging/dgnc/dgnc_neo.c @@ -1306,10 +1306,10 @@ static int neo_drain(struct tty_struct *tty, uint seconds) /* * Go to sleep waiting for the tty layer to wake me back up when * the empty flag goes away. - * - * NOTE: TODO: Do something with time passed in. */ - rc = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0)); + rc = wait_event_interruptible_timeout(un->un_flags_wait, + ((un->un_flags & UN_EMPTY) == 0), + msecs_to_jiffies(seconds * 1000)); /* If ret is non-zero, user ctrl-c'ed us */ return rc; @@ -1735,7 +1735,7 @@ static unsigned int neo_read_eeprom(unsigned char __iomem *base, unsigned int ad /* enable chip select */ writeb(NEO_EECS, base + NEO_EEREG); /* READ */ - enable = (address | 0x180); + enable = address | 0x180; for (bits = 9; bits--; ) { databit = (enable & (1 << bits)) ? NEO_EEDI : 0; diff --git a/drivers/staging/dgnc/dgnc_neo.h b/drivers/staging/dgnc/dgnc_neo.h index c528df5a0e5a..abddd48353d0 100644 --- a/drivers/staging/dgnc/dgnc_neo.h +++ b/drivers/staging/dgnc/dgnc_neo.h @@ -65,7 +65,6 @@ struct neo_uart_struct { #define NEO_EEDO 0x80 /* Data Out is an Input Pin */ #define NEO_EEREG 0x8E /* offset to EEPROM control reg */ - #define NEO_VPD_IMAGESIZE 0x40 /* size of image to read from EEPROM in words */ #define NEO_VPD_IMAGEBYTES (NEO_VPD_IMAGESIZE * 2) diff --git a/drivers/staging/dgnc/dgnc_pci.h b/drivers/staging/dgnc/dgnc_pci.h index 617d40d1ec19..4e170c47f4a3 100644 --- a/drivers/staging/dgnc/dgnc_pci.h +++ b/drivers/staging/dgnc/dgnc_pci.h @@ -59,7 +59,6 @@ #define PCI_DEVICE_NEO_EXPRESS_8RJ45_PCI_NAME "Neo 8 PCI Express RJ45" #define PCI_DEVICE_NEO_EXPRESS_4_IBM_PCI_NAME "Neo 4 PCI Express IBM" - /* Size of Memory and I/O for PCI (4 K) */ #define PCI_RAM_SIZE 0x1000 diff --git a/drivers/staging/dgnc/dgnc_tty.c b/drivers/staging/dgnc/dgnc_tty.c index 8b1ba65a6984..bcd2bdfb9c8f 100644 --- a/drivers/staging/dgnc/dgnc_tty.c +++ b/drivers/staging/dgnc/dgnc_tty.c @@ -443,15 +443,13 @@ void dgnc_tty_uninit(struct dgnc_board *brd) brd->PrintDriver.termios = NULL; } -/*======================================================================= - * +/* * dgnc_wmove - Write data to transmit queue. * * ch - Pointer to channel structure. * buf - Pointer to characters to be moved. * n - Number of characters to move. - * - *=======================================================================*/ + */ static void dgnc_wmove(struct channel_t *ch, char *buf, uint n) { int remain; @@ -489,13 +487,11 @@ static void dgnc_wmove(struct channel_t *ch, char *buf, uint n) ch->ch_w_head = head; } -/*======================================================================= - * +/* * dgnc_input - Process received data. * * ch - Pointer to channel structure. - * - *=======================================================================*/ + */ void dgnc_input(struct channel_t *ch) { struct dgnc_board *bd; @@ -796,7 +792,7 @@ static void dgnc_set_custom_speed(struct channel_t *ch, uint newrate) * And of course, rates above the dividend won't fly. */ if (newrate && newrate < ((ch->ch_bd->bd_dividend / 0xFFFF) + 1)) - newrate = ((ch->ch_bd->bd_dividend / 0xFFFF) + 1); + newrate = (ch->ch_bd->bd_dividend / 0xFFFF) + 1; if (newrate && newrate > ch->ch_bd->bd_dividend) newrate = ch->ch_bd->bd_dividend; @@ -1786,8 +1782,8 @@ static int dgnc_tty_write(struct tty_struct *tty, } /* Update printer buffer empty time. */ - if ((un->un_type == DGNC_PRINT) && (ch->ch_digi.digi_maxcps > 0) - && (ch->ch_digi.digi_bufsize > 0)) { + if ((un->un_type == DGNC_PRINT) && (ch->ch_digi.digi_maxcps > 0) && + (ch->ch_digi.digi_bufsize > 0)) { ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; } @@ -1834,7 +1830,7 @@ static int dgnc_tty_tiocmget(struct tty_struct *tty) spin_lock_irqsave(&ch->ch_lock, flags); - mstat = (ch->ch_mostat | ch->ch_mistat); + mstat = ch->ch_mostat | ch->ch_mistat; spin_unlock_irqrestore(&ch->ch_lock, flags); @@ -2034,7 +2030,7 @@ static inline int dgnc_get_mstat(struct channel_t *ch) spin_lock_irqsave(&ch->ch_lock, flags); - mstat = (ch->ch_mostat | ch->ch_mistat); + mstat = ch->ch_mostat | ch->ch_mistat; spin_unlock_irqrestore(&ch->ch_lock, flags); @@ -2506,12 +2502,12 @@ static void dgnc_tty_flush_buffer(struct tty_struct *tty) /* Flush UARTs transmit FIFO */ ch->ch_bd->bd_ops->flush_uart_write(ch); - if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); + if (ch->ch_tun.un_flags & (UN_LOW | UN_EMPTY)) { + ch->ch_tun.un_flags &= ~(UN_LOW | UN_EMPTY); wake_up_interruptible(&ch->ch_tun.un_flags_wait); } - if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); + if (ch->ch_pun.un_flags & (UN_LOW | UN_EMPTY)) { + ch->ch_pun.un_flags &= ~(UN_LOW | UN_EMPTY); wake_up_interruptible(&ch->ch_pun.un_flags_wait); } @@ -2705,13 +2701,13 @@ static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd, if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { ch->ch_tun.un_flags &= - ~(UN_LOW|UN_EMPTY); + ~(UN_LOW | UN_EMPTY); wake_up_interruptible(&ch->ch_tun.un_flags_wait); } if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { ch->ch_pun.un_flags &= - ~(UN_LOW|UN_EMPTY); + ~(UN_LOW | UN_EMPTY); wake_up_interruptible(&ch->ch_pun.un_flags_wait); } } diff --git a/drivers/staging/dgnc/digi.h b/drivers/staging/dgnc/digi.h index cf9dcae7cc3f..523a2d34f747 100644 --- a/drivers/staging/dgnc/digi.h +++ b/drivers/staging/dgnc/digi.h @@ -31,21 +31,21 @@ #endif #if !defined(TIOCMSET) -#define TIOCMSET (('d'<<8) | 252) /* set modem ctrl state */ -#define TIOCMGET (('d'<<8) | 253) /* set modem ctrl state */ +#define TIOCMSET (('d' << 8) | 252) /* set modem ctrl state */ +#define TIOCMGET (('d' << 8) | 253) /* set modem ctrl state */ #endif #if !defined(TIOCMBIC) -#define TIOCMBIC (('d'<<8) | 254) /* set modem ctrl state */ -#define TIOCMBIS (('d'<<8) | 255) /* set modem ctrl state */ +#define TIOCMBIC (('d' << 8) | 254) /* set modem ctrl state */ +#define TIOCMBIS (('d' << 8) | 255) /* set modem ctrl state */ #endif -#define DIGI_GETA (('e'<<8) | 94) /* Read params */ -#define DIGI_SETA (('e'<<8) | 95) /* Set params */ -#define DIGI_SETAW (('e'<<8) | 96) /* Drain & set params */ -#define DIGI_SETAF (('e'<<8) | 97) /* Drain, flush & set params */ -#define DIGI_GET_NI_INFO (('d'<<8) | 250) /* Non-intelligent state info */ -#define DIGI_LOOPBACK (('d'<<8) | 252) /* +#define DIGI_GETA (('e' << 8) | 94) /* Read params */ +#define DIGI_SETA (('e' << 8) | 95) /* Set params */ +#define DIGI_SETAW (('e' << 8) | 96) /* Drain & set params */ +#define DIGI_SETAF (('e' << 8) | 97) /* Drain, flush & set params */ +#define DIGI_GET_NI_INFO (('d' << 8) | 250) /* Non-intelligent state info */ +#define DIGI_LOOPBACK (('d' << 8) | 252) /* * Enable/disable UART * internal loopback */ @@ -85,7 +85,7 @@ struct digi_dinfo { char dinfo_version[16]; /* driver version */ }; -#define DIGI_GETDD (('d'<<8) | 248) /* get driver info */ +#define DIGI_GETDD (('d' << 8) | 248) /* get driver info */ /************************************************************************ * Structure used with ioctl commands for per-board information @@ -105,7 +105,7 @@ struct digi_info { char info_reserved[7]; /* for future expansion */ }; -#define DIGI_GETBD (('d'<<8) | 249) /* get board info */ +#define DIGI_GETBD (('d' << 8) | 249) /* get board info */ struct digi_getbuffer /* Struct for holding buffer use counts */ { @@ -133,10 +133,10 @@ struct digi_getcounter { #define DIGI_SETCUSTOMBAUD _IOW('e', 106, int) /* Set integer baud rate */ #define DIGI_GETCUSTOMBAUD _IOR('e', 107, int) /* Get integer baud rate */ -#define DIGI_REALPORT_GETBUFFERS (('e'<<8) | 108) -#define DIGI_REALPORT_SENDIMMEDIATE (('e'<<8) | 109) -#define DIGI_REALPORT_GETCOUNTERS (('e'<<8) | 110) -#define DIGI_REALPORT_GETEVENTS (('e'<<8) | 111) +#define DIGI_REALPORT_GETBUFFERS (('e' << 8) | 108) +#define DIGI_REALPORT_SENDIMMEDIATE (('e' << 8) | 109) +#define DIGI_REALPORT_GETCOUNTERS (('e' << 8) | 110) +#define DIGI_REALPORT_GETEVENTS (('e' << 8) | 111) #define EV_OPU 0x0001 /* ! #include #include -#include #include #include #include @@ -160,7 +159,7 @@ static void _nbu2ss_ep0_complete(struct usb_ep *_ep, struct usb_request *_req) recipient = (u8)(p_ctrl->bRequestType & USB_RECIP_MASK); selector = p_ctrl->wValue; if ((recipient == USB_RECIP_DEVICE) && - (selector == USB_DEVICE_TEST_MODE)) { + (selector == USB_DEVICE_TEST_MODE)) { test_mode = (u32)(p_ctrl->wIndex >> 8); _nbu2ss_set_test_mode(udc, test_mode); } @@ -271,21 +270,21 @@ static int _nbu2ss_ep_init(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep) data = EPn_EN | EPn_BCLR | EPn_DIR0; _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); - data = (EPn_ONAK | EPn_OSTL_EN | EPn_OSTL); + data = EPn_ONAK | EPn_OSTL_EN | EPn_OSTL; _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); - data = (EPn_OUT_EN | EPn_OUT_END_EN); + data = EPn_OUT_EN | EPn_OUT_END_EN; _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data); } else { /*---------------------------------------------------------*/ /* IN */ - data = (EPn_EN | EPn_BCLR | EPn_AUTO); + data = EPn_EN | EPn_BCLR | EPn_AUTO; _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); - data = (EPn_ISTL); + data = EPn_ISTL; _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); - data = (EPn_IN_EN | EPn_IN_END_EN); + data = EPn_IN_EN | EPn_IN_END_EN; _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data); } @@ -460,7 +459,7 @@ static void _nbu2ss_ep_in_end( if (length) _nbu2ss_writel(&preg->EP_REGS[num].EP_WRITE, data32); - data = (((((u32)length) << 5) & EPn_DW) | EPn_DEND); + data = ((((u32)length) << 5) & EPn_DW) | EPn_DEND; _nbu2ss_bitset(&preg->EP_REGS[num].EP_CONTROL, data); _nbu2ss_bitset(&preg->EP_REGS[num].EP_CONTROL, EPn_AUTO); @@ -526,10 +525,10 @@ static void _nbu2ss_dma_unmap_single( if (req->unaligned) { if (direct == USB_DIR_OUT) memcpy(req->req.buf, ep->virt_buf, - req->req.actual & 0xfffffffc); + req->req.actual & 0xfffffffc); } else dma_unmap_single(udc->gadget.dev.parent, - req->req.dma, req->req.length, + req->req.dma, req->req.length, (direct == USB_DIR_IN) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); @@ -538,7 +537,7 @@ static void _nbu2ss_dma_unmap_single( } else { if (!req->unaligned) dma_sync_single_for_cpu(udc->gadget.dev.parent, - req->req.dma, req->req.length, + req->req.dma, req->req.length, (direct == USB_DIR_IN) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); @@ -844,7 +843,7 @@ static int _nbu2ss_out_dma( /* Number of transfer packets */ mpkt = _nbu2ss_readl(&preg->EP_REGS[num].EP_PCKT_ADRS) & EPn_MPKT; - dmacnt = (length / mpkt); + dmacnt = length / mpkt; lmpkt = (length % mpkt) & ~(u32)0x03; if (dmacnt > DMA_MAX_COUNT) { @@ -1490,7 +1489,7 @@ static inline int _nbu2ss_req_feature(struct nbu2ss_udc *udc, bool bset) int result = -EOPNOTSUPP; if ((udc->ctrl.wLength != 0x0000) || - (direction != USB_DIR_OUT)) { + (direction != USB_DIR_OUT)) { return -EINVAL; } @@ -1648,7 +1647,7 @@ static int std_req_set_address(struct nbu2ss_udc *udc) u32 wValue = udc->ctrl.wValue; if ((udc->ctrl.bRequestType != 0x00) || - (udc->ctrl.wIndex != 0x0000) || + (udc->ctrl.wIndex != 0x0000) || (udc->ctrl.wLength != 0x0000)) { return -EINVAL; } @@ -1670,7 +1669,7 @@ static int std_req_set_configuration(struct nbu2ss_udc *udc) u32 ConfigValue = (u32)(udc->ctrl.wValue & 0x00ff); if ((udc->ctrl.wIndex != 0x0000) || - (udc->ctrl.wLength != 0x0000) || + (udc->ctrl.wLength != 0x0000) || (udc->ctrl.bRequestType != 0x00)) { return -EINVAL; } @@ -1949,7 +1948,7 @@ static void _nbu2ss_ep_done( #ifdef USE_DMA if ((ep->direct == USB_DIR_OUT) && (ep->epnum > 0) && - (req->req.dma != 0)) + (req->req.dma != 0)) _nbu2ss_dma_unmap_single(udc, ep, req, USB_DIR_OUT); #endif @@ -2277,7 +2276,7 @@ static int _nbu2ss_enable_controller(struct nbu2ss_udc *udc) _nbu2ss_writel(&udc->p_regs->AHBSCTR, WAIT_MODE); _nbu2ss_writel(&udc->p_regs->AHBMCTR, - HBUSREQ_MODE | HTRANS_MODE | WBURST_TYPE); + HBUSREQ_MODE | HTRANS_MODE | WBURST_TYPE); while (!(_nbu2ss_readl(&udc->p_regs->EPCTR) & PLL_LOCK)) { waitcnt++; @@ -2626,7 +2625,7 @@ static struct usb_request *nbu2ss_ep_alloc_request( req = kzalloc(sizeof(*req), gfp_flags); if (!req) - return 0; + return NULL; #ifdef USE_DMA req->req.dma = DMA_ADDR_INVALID; @@ -2701,7 +2700,7 @@ static int nbu2ss_ep_queue( if (unlikely(!udc->driver)) { dev_err(udc->dev, "%s, bogus device state %p\n", __func__, - udc->driver); + udc->driver); return -ESHUTDOWN; } @@ -2721,12 +2720,12 @@ static int nbu2ss_ep_queue( if (ep->epnum > 0) { if (ep->direct == USB_DIR_IN) memcpy(ep->virt_buf, req->req.buf, - req->req.length); + req->req.length); } } if ((ep->epnum > 0) && (ep->direct == USB_DIR_OUT) && - (req->req.dma != 0)) + (req->req.dma != 0)) _nbu2ss_dma_map_single(udc, ep, req, USB_DIR_OUT); #endif @@ -2741,12 +2740,12 @@ static int nbu2ss_ep_queue( result = _nbu2ss_start_transfer(udc, ep, req, FALSE); if (result < 0) { dev_err(udc->dev, " *** %s, result = %d\n", __func__, - result); + result); list_del(&req->queue); } else if ((ep->epnum > 0) && (ep->direct == USB_DIR_OUT)) { #ifdef USE_DMA if (req->req.length < 4 && - req->req.length == req->req.actual) + req->req.length == req->req.actual) #else if (req->req.length == req->req.actual) #endif @@ -3026,7 +3025,7 @@ static int nbu2ss_gad_wakeup(struct usb_gadget *pgadget) /*-------------------------------------------------------------------------*/ static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget, - int is_selfpowered) + int is_selfpowered) { struct nbu2ss_udc *udc; unsigned long flags; @@ -3180,7 +3179,8 @@ static void __init nbu2ss_drv_ep_init(struct nbu2ss_udc *udc) ep->ep.ops = &nbu2ss_ep_ops; usb_ep_set_maxpacket_limit(&ep->ep, - i == 0 ? EP0_PACKETSIZE : EP_PACKETSIZE); + i == 0 ? EP0_PACKETSIZE + : EP_PACKETSIZE); list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); INIT_LIST_HEAD(&ep->queue); @@ -3273,10 +3273,7 @@ static int nbu2ss_drv_probe(struct platform_device *pdev) /* VBUS Interrupt */ irq_set_irq_type(INT_VBUS, IRQ_TYPE_EDGE_BOTH); status = request_irq(INT_VBUS, - _nbu2ss_vbus_irq, - IRQF_SHARED, - driver_name, - udc); + _nbu2ss_vbus_irq, IRQF_SHARED, driver_name, udc); if (status != 0) { dev_err(udc->dev, "request_irq(INT_VBUS) failed\n"); diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig index 883ff5b8fdab..6f5e82464d78 100644 --- a/drivers/staging/fbtft/Kconfig +++ b/drivers/staging/fbtft/Kconfig @@ -117,12 +117,24 @@ config FB_TFT_SSD1289 help Framebuffer support for SSD1289 +config FB_TFT_SSD1305 + tristate "FB driver for the SSD1305 OLED Controller" + depends on FB_TFT + help + Framebuffer support for SSD1305 + config FB_TFT_SSD1306 tristate "FB driver for the SSD1306 OLED Controller" depends on FB_TFT help Framebuffer support for SSD1306 +config FB_TFT_SSD1325 + tristate "FB driver for the SSD1325 OLED Controller" + depends on FB_TFT + help + Framebuffer support for SSD1305 + config FB_TFT_SSD1331 tristate "FB driver for the SSD1331 LCD Controller" depends on FB_TFT diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile index 4f9071d96d01..2725ea9a4afc 100644 --- a/drivers/staging/fbtft/Makefile +++ b/drivers/staging/fbtft/Makefile @@ -21,7 +21,9 @@ obj-$(CONFIG_FB_TFT_RA8875) += fb_ra8875.o obj-$(CONFIG_FB_TFT_S6D02A1) += fb_s6d02a1.o obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o +obj-$(CONFIG_FB_TFT_SSD1305) += fb_ssd1305.o obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o +obj-$(CONFIG_FB_TFT_SSD1305) += fb_ssd1325.o obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c index 2a50cf957101..ba9fc444b848 100644 --- a/drivers/staging/fbtft/fb_agm1264k-fl.c +++ b/drivers/staging/fbtft/fb_agm1264k-fl.c @@ -272,8 +272,8 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) int ret = 0; /* buffer to convert RGB565 -> grayscale16 -> Dithered image 1bpp */ - signed short *convert_buf = kmalloc(par->info->var.xres * - par->info->var.yres * sizeof(signed short), GFP_NOIO); + signed short *convert_buf = kmalloc_array(par->info->var.xres * + par->info->var.yres, sizeof(signed short), GFP_NOIO); if (!convert_buf) return -ENOMEM; diff --git a/drivers/staging/fbtft/fb_hx8340bn.c b/drivers/staging/fbtft/fb_hx8340bn.c index e1ed177f9184..9970ed74bb38 100644 --- a/drivers/staging/fbtft/fb_hx8340bn.c +++ b/drivers/staging/fbtft/fb_hx8340bn.c @@ -25,6 +25,7 @@ #include #include #include +#include