diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index b8f220f978dd..530809ccfacf 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -170,6 +170,16 @@ Description: Has all of the equivalent parameters as per voltageY. Units after application of scale and offset are m/s^2. +What: /sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw +What: /sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw +What: /sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw +KernelVersion: 4.11 +Contact: linux-iio@vger.kernel.org +Description: + Gravity in direction x, y or z (may be arbitrarily assigned + but should match other such assignments on device). + Units after application of scale and offset are m/s^2. + What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_raw What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_raw @@ -805,7 +815,7 @@ Description: attribute. E.g. if in_voltage0_raw_thresh_rising_value is set to 1200 and in_voltage0_raw_thresh_rising_hysteresis is set to 50. The event will get activated once in_voltage0_raw goes above 1200 and will become - deactived again once the value falls below 1150. + deactivated again once the value falls below 1150. What: /sys/.../events/in_accel_x_raw_roc_rising_value What: /sys/.../events/in_accel_x_raw_roc_falling_value @@ -1245,7 +1255,8 @@ Description: reflectivity of infrared or ultrasound emitted. Often these sensors are unit less and as such conversion to SI units is not possible. Higher proximity measurements - indicate closer objects, and vice versa. + indicate closer objects, and vice versa. Units after + application of scale and offset are meters. What: /sys/.../iio:deviceX/in_illuminance_input What: /sys/.../iio:deviceX/in_illuminance_raw diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-adc-stm32 new file mode 100644 index 000000000000..efe4c85e3c8b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-stm32 @@ -0,0 +1,18 @@ +What: /sys/bus/iio/devices/triggerX/trigger_polarity +KernelVersion: 4.11 +Contact: fabrice.gasnier@st.com +Description: + The STM32 ADC can be configured to use external trigger sources + (e.g. timers, pwm or exti gpio). Then, it can be tuned to start + conversions on external trigger by either: + - "rising-edge" + - "falling-edge" + - "both-edges". + Reading returns current trigger polarity. + Writing value before enabling conversions sets trigger polarity. + +What: /sys/bus/iio/devices/triggerX/trigger_polarity_available +KernelVersion: 4.11 +Contact: fabrice.gasnier@st.com +Description: + List all available trigger_polarity settings. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 new file mode 100644 index 000000000000..0a1ca1487fa9 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 @@ -0,0 +1,22 @@ +What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity +Date: January 2017 +KernelVersion: 4.11 +Contact: linux-iio@vger.kernel.org +Description: + Show or set the gain boost of the amp, from 0-31 range. + default 31 + +What /sys/bus/iio/devices/iio:deviceX/sensor_max_range +Date: January 2017 +KernelVersion: 4.11 +Contact: linux-iio@vger.kernel.org +Description: + Show or set the maximum range between the sensor and the + first object echoed in meters. Default value is 6.020. + This setting limits the time the driver is waiting for a + echo. + Showing the range of available values is represented as the + minimum value, the step and the maximum value, all enclosed + in square brackets. + Example: + [0.043 0.043 11.008] diff --git a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 new file mode 100644 index 000000000000..6534a60037ff --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 @@ -0,0 +1,29 @@ +What: /sys/bus/iio/devices/triggerX/master_mode_available +KernelVersion: 4.11 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the list possible master modes which are: + - "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO). + - "enable" : The Counter Enable signal CNT_EN is used as trigger output. + - "update" : The update event is selected as trigger output. + For instance a master timer can then be used as a prescaler for a slave timer. + - "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set. + - "OC1REF" : OC1REF signal is used as trigger output. + - "OC2REF" : OC2REF signal is used as trigger output. + - "OC3REF" : OC3REF signal is used as trigger output. + - "OC4REF" : OC4REF signal is used as trigger output. + +What: /sys/bus/iio/devices/triggerX/master_mode +KernelVersion: 4.11 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the current master modes. + Writing set the master mode + +What: /sys/bus/iio/devices/triggerX/sampling_frequency +KernelVersion: 4.11 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the current sampling frequency. + Writing an value different of 0 set and start sampling. + Writing 0 stop sampling. diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index cdd7b48826c3..ad10fbe61562 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -36,6 +36,7 @@ dallas,ds1775 Tiny Digital Thermometer and Thermostat dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O dallas,ds75 Digital Thermometer and Thermostat +devantech,srf08 Devantech SRF08 ultrasonic ranger dlg,da9053 DA9053: flexible system level PMIC with multicore support dlg,da9063 DA9063: system PMIC for quad-core application processors domintech,dmard09 DMARD09: 3-axis Accelerometer diff --git a/Documentation/devicetree/bindings/iio/accel/lis302.txt b/Documentation/devicetree/bindings/iio/accel/lis302.txt index 2a19bff9693f..dfdce67826ba 100644 --- a/Documentation/devicetree/bindings/iio/accel/lis302.txt +++ b/Documentation/devicetree/bindings/iio/accel/lis302.txt @@ -5,7 +5,7 @@ that apply in on the generic device (independent from the bus). Required properties for the SPI bindings: - - compatible: should be set to "st,lis3lv02d_spi" + - compatible: should be set to "st,lis3lv02d-spi" - reg: the chipselect index - spi-max-frequency: maximal bus speed, should be set to 1000000 unless constrained by external circuitry diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt new file mode 100644 index 000000000000..f9e3ff2c656e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt @@ -0,0 +1,32 @@ +* Amlogic Meson SAR (Successive Approximation Register) A/D converter + +Required properties: +- compatible: depending on the SoC this should be one of: + - "amlogic,meson-gxbb-saradc" for GXBB + - "amlogic,meson-gxl-saradc" for GXL + - "amlogic,meson-gxm-saradc" for GXM + along with the generic "amlogic,meson-saradc" +- reg: the physical base address and length of the registers +- clocks: phandle and clock identifier (see clock-names) +- clock-names: mandatory clocks: + - "clkin" for the reference clock (typically XTAL) + - "core" for the SAR ADC core clock + optional clocks: + - "sana" for the analog clock + - "adc_clk" for the ADC (sampling) clock + - "adc_sel" for the ADC (sampling) clock mux +- vref-supply: the regulator supply for the ADC reference voltage +- #io-channel-cells: must be 1, see ../iio-bindings.txt + +Example: + saradc: adc@8680 { + compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc"; + #io-channel-cells = <1>; + reg = <0x0 0x8680 0x0 0x34>; + clocks = <&xtal>, + <&clkc CLKID_SAR_ADC>, + <&clkc CLKID_SANA>, + <&clkc CLKID_SAR_ADC_CLK>, + <&clkc CLKID_SAR_ADC_SEL>; + clock-names = "clkin", "core", "sana", "adc_clk", "adc_sel"; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt new file mode 100644 index 000000000000..b3629405f568 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt @@ -0,0 +1,18 @@ +* AVIA HX711 ADC chip for weight cells + Bit-banging driver + +Required properties: + - compatible: Should be "avia,hx711" + - sck-gpios: Definition of the GPIO for the clock + - dout-gpios: Definition of the GPIO for data-out + See Documentation/devicetree/bindings/gpio/gpio.txt + - avdd-supply: Definition of the regulator used as analog supply + +Example: +weight@0 { + compatible = "avia,hx711"; + sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>; + dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; + avdd-suppy = <&avdd>; +}; + diff --git a/Documentation/devicetree/bindings/iio/adc/max11100.txt b/Documentation/devicetree/bindings/iio/adc/max11100.txt new file mode 100644 index 000000000000..b7f7177b8aca --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/max11100.txt @@ -0,0 +1,18 @@ +* Maxim max11100 Analog to Digital Converter (ADC) + +Required properties: + - compatible: Should be "maxim,max11100" + - reg: the adc unit address + - vref-supply: phandle to the regulator that provides reference voltage + +Optional properties: + - spi-max-frequency: SPI maximum frequency + +Example: + +max11100: adc@0 { + compatible = "maxim,max11100"; + reg = <0>; + vref-supply = <&adc0_vref>; + spi-max-frequency = <240000>; +}; diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt new file mode 100644 index 000000000000..53cd146d8096 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt @@ -0,0 +1,149 @@ +Qualcomm's PM8xxx voltage XOADC + +The Qualcomm PM8xxx PMICs contain a HK/XO ADC (Housekeeping/Crystal +oscillator ADC) encompassing PM8018, PM8038, PM8058 and PM8921. + +Required properties: + +- compatible: should be one of: + "qcom,pm8018-adc" + "qcom,pm8038-adc" + "qcom,pm8058-adc" + "qcom,pm8921-adc" + +- reg: should contain the ADC base address in the PMIC, typically + 0x197. + +- xoadc-ref-supply: should reference a regulator that can supply + a reference voltage on demand. The reference voltage may vary + with PMIC variant but is typically something like 2.2 or 1.8V. + +The following required properties are standard for IO channels, see +iio-bindings.txt for more details: + +- #address-cells: should be set to <1> + +- #size-cells: should be set to <0> + +- #io-channel-cells: should be set to <1> + +- interrupts: should refer to the parent PMIC interrupt controller + and reference the proper ADC interrupt. + +Required subnodes: + +The ADC channels are configured as subnodes of the ADC. Since some of +them are used for calibrating the ADC, these nodes are compulsory: + +adc-channel@c { + reg = <0x0c>; +}; + +adc-channel@d { + reg = <0x0d>; +}; + +adc-channel@f { + reg = <0x0f>; +}; + +These three nodes are used for absolute and ratiometric calibration +and only need to have these reg values: they are by hardware definition +1:1 ratio converters that sample 625, 1250 and 0 milliV and create +an interpolation calibration for all other ADCs. + +Optional subnodes: any channels other than channel 0x0c, 0x0d and +0x0f are optional. + +Required channel node properties: + +- reg: should contain the hardware channel number in the range + 0 .. 0x0f (4 bits). The hardware only supports 16 channels. + +Optional channel node properties: + +- qcom,decimation: + Value type: + Definition: This parameter is used to decrease the ADC sampling rate. + Quicker measurements can be made by reducing the decimation ratio. + Valid values are 512, 1024, 2048, 4096. + If the property is not found, a default value of 512 will be used. + +- qcom,ratiometric: + Value type: + Definition: Channel calibration type. If this property is specified + VADC will use a special voltage references for channel + calibration. The available references are specified in the + as a u32 value setting (see below) and it is compulsory + to also specify this reference if ratiometric calibration + is selected. + + If the property is not found, the channel will be + calibrated with the 0.625V and 1.25V reference channels, also + known as an absolute calibration. + The reference voltage pairs when using ratiometric calibration: + 0 = XO_IN/XOADC_GND + 1 = PMIC_IN/XOADC_GND + 2 = PMIC_IN/BMS_CSP + 3 (invalid) + 4 = XOADC_GND/XOADC_GND + 5 = XOADC_VREF/XOADC_GND + +Example: + +xoadc: xoadc@197 { + compatible = "qcom,pm8058-adc"; + reg = <0x197>; + interrupt-parent = <&pm8058>; + interrupts = <76 1>; + #address-cells = <1>; + #size-cells = <0>; + #io-channel-cells = <1>; + + vcoin: adc-channel@0 { + reg = <0x00>; + }; + vbat: adc-channel@1 { + reg = <0x01>; + }; + dcin: adc-channel@2 { + reg = <0x02>; + }; + ichg: adc-channel@3 { + reg = <0x03>; + }; + vph_pwr: adc-channel@4 { + reg = <0x04>; + }; + usb_vbus: adc-channel@a { + reg = <0x0a>; + }; + die_temp: adc-channel@b { + reg = <0x0b>; + }; + ref_625mv: adc-channel@c { + reg = <0x0c>; + }; + ref_1250mv: adc-channel@d { + reg = <0x0d>; + }; + ref_325mv: adc-channel@e { + reg = <0x0e>; + }; + ref_muxoff: adc-channel@f { + reg = <0x0f>; + }; +}; + + +/* IIO client node */ +iio-hwmon { + compatible = "iio-hwmon"; + io-channels = <&xoadc 0x01>, /* Battery */ + <&xoadc 0x02>, /* DC in (charger) */ + <&xoadc 0x04>, /* VPH the main system voltage */ + <&xoadc 0x0b>, /* Die temperature */ + <&xoadc 0x0c>, /* Reference voltage 1.25V */ + <&xoadc 0x0d>, /* Reference voltage 0.625V */ + <&xoadc 0x0e>; /* Reference voltage 0.325V */ +}; diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt new file mode 100644 index 000000000000..f5b0adae6010 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt @@ -0,0 +1,99 @@ +* Renesas RCar GyroADC device driver + +The GyroADC block is a reduced SPI block with up to 8 chipselect lines, +which supports the SPI protocol of a selected few SPI ADCs. The SPI ADCs +are sampled by the GyroADC block in a round-robin fashion and the result +presented in the GyroADC registers. + +Required properties: +- compatible: Should be "", "renesas,rcar-gyroadc". + The should be one of: + renesas,r8a7791-gyroadc - for the GyroADC block present + in r8a7791 SoC + renesas,r8a7792-gyroadc - for the GyroADC with interrupt + block present in r8a7792 SoC +- reg: Address and length of the register set for the device +- clocks: References to all the clocks specified in the clock-names + property as specified in + Documentation/devicetree/bindings/clock/clock-bindings.txt. +- clock-names: Shall contain "fck" and "if". The "fck" is the GyroADC block + clock, the "if" is the interface clock. +- power-domains: Must contain a reference to the PM domain, if available. +- #address-cells: Should be <1> (setting for the subnodes) for all ADCs + except for "fujitsu,mb88101a". Should be <0> (setting for + only subnode) for "fujitsu,mb88101a". +- #size-cells: Should be <0> (setting for the subnodes) + +Sub-nodes: +You must define subnode(s) which select the connected ADC type and reference +voltage for the GyroADC channels. + +Required properties for subnodes: +- compatible: Should be either of: + "fujitsu,mb88101a" + - Fujitsu MB88101A compatible mode, + 12bit sampling, up to 4 channels can be sampled in + round-robin fashion. One Fujitsu chip supplies four + GyroADC channels with data as it contains four ADCs + on the chip and thus for 4-channel operation, single + MB88101A is required. The Cx chipselect lines of the + MB88101A connect directly to two CHS lines of the + GyroADC, no demuxer is required. The data out line + of each MB88101A connects to a shared input pin of + the GyroADC. + "ti,adcs7476" or "ti,adc121" or "adi,ad7476" + - TI ADCS7476 / TI ADC121 / ADI AD7476 compatible mode, + 15bit sampling, up to 8 channels can be sampled in + round-robin fashion. One TI/ADI chip supplies single + ADC channel with data, thus for 8-channel operation, + 8 chips are required. A 3:8 chipselect demuxer is + required to connect the nCS line of the TI/ADI chips + to the GyroADC, while MISO line of each TI/ADI ADC + connects to a shared input pin of the GyroADC. + "maxim,max1162" or "maxim,max11100" + - Maxim MAX1162 / Maxim MAX11100 compatible mode, + 16bit sampling, up to 8 channels can be sampled in + round-robin fashion. One Maxim chip supplies single + ADC channel with data, thus for 8-channel operation, + 8 chips are required. A 3:8 chipselect demuxer is + required to connect the nCS line of the MAX chips + to the GyroADC, while MISO line of each Maxim ADC + connects to a shared input pin of the GyroADC. +- reg: Should be the number of the analog input. Should be present + for all ADCs except "fujitsu,mb88101a". +- vref-supply: Reference to the channel reference voltage regulator. + +Example: + vref_max1162: regulator-vref-max1162 { + compatible = "regulator-fixed"; + + regulator-name = "MAX1162 Vref"; + regulator-min-microvolt = <4096000>; + regulator-max-microvolt = <4096000>; + }; + + adc@e6e54000 { + compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc"; + reg = <0 0xe6e54000 0 64>; + clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>; + clock-names = "fck", "if"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + + pinctrl-0 = <&adc_pins>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + reg = <0>; + compatible = "maxim,max1162"; + vref-supply = <&vref_max1162>; + }; + + adc@1 { + reg = <1>; + compatible = "maxim,max1162"; + vref-supply = <&vref_max1162>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt index 49ed82e89870..5dfc88ec24a4 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt @@ -53,6 +53,11 @@ Required properties: - #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in Documentation/devicetree/bindings/iio/iio-bindings.txt +Optional properties: +- dmas: Phandle to dma channel for this ADC instance. + See ../../dma/dma.txt for details. +- dma-names: Must be "rx" when dmas property is being used. + Example: adc: adc@40012000 { compatible = "st,stm32f4-adc-core"; @@ -77,6 +82,8 @@ Example: interrupt-parent = <&adc>; interrupts = <0>; st,adc-channels = <8>; + dmas = <&dma2 0 0 0x400 0x0>; + dma-names = "rx"; }; ... other adc child nodes follow... diff --git a/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt b/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt new file mode 100644 index 000000000000..e77a6f7e1001 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt @@ -0,0 +1,23 @@ +* Texas Instruments ADS7950 family of A/DC chips + +Required properties: + - compatible: Must be one of "ti,ads7950", "ti,ads7951", "ti,ads7952", + "ti,ads7953", "ti,ads7954", "ti,ads7955", "ti,ads7956", "ti,ads7957", + "ti,ads7958", "ti,ads7959", "ti,ads7960", or "ti,ads7961" + - reg: SPI chip select number for the device + - #io-channel-cells: Must be 1 as per ../iio-bindings.txt + - vref-supply: phandle to a regulator node that supplies the 2.5V or 5V + reference voltage + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: +adc@0 { + compatible = "ti,ads7957"; + reg = <0>; + #io-channel-cells = <1>; + vref-supply = <&refin_supply>; + spi-max-frequency = <10000000>; +}; diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.txt b/Documentation/devicetree/bindings/iio/imu/bmi160.txt new file mode 100644 index 000000000000..ae0112c7debc --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/bmi160.txt @@ -0,0 +1,36 @@ +Bosch BMI160 - Inertial Measurement Unit with Accelerometer, Gyroscope +and externally connectable Magnetometer + +https://www.bosch-sensortec.com/bst/products/all_products/bmi160 + +Required properties: + - compatible : should be "bosch,bmi160" + - reg : the I2C address or SPI chip select number of the sensor + - spi-max-frequency : set maximum clock frequency (only for SPI) + +Optional properties: + - interrupt-parent : should be the phandle of the interrupt controller + - interrupts : interrupt mapping for IRQ, must be IRQ_TYPE_LEVEL_LOW + - interrupt-names : set to "INT1" if INT1 pin should be used as interrupt + input, set to "INT2" if INT2 pin should be used instead + +Examples: + +bmi160@68 { + compatible = "bosch,bmi160"; + reg = <0x68>; + + interrupt-parent = <&gpio4>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "INT1"; +}; + +bmi160@0 { + compatible = "bosch,bmi160"; + reg = <0>; + spi-max-frequency = <10000000>; + + interrupt-parent = <&gpio2>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "INT2"; +}; diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt new file mode 100644 index 000000000000..cf81afdf7803 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -0,0 +1,26 @@ +* ST_LSM6DSx driver for STM 6-axis (acc + gyro) imu Mems sensors + +Required properties: +- compatible: must be one of: + "st,lsm6ds3" + "st,lsm6dsm" +- reg: i2c address of the sensor / spi cs line + +Optional properties: +- st,drdy-int-pin: the pin on the package that will be used to signal + "data ready" (valid values: 1 or 2). +- interrupt-parent: should be the phandle for the interrupt controller +- interrupts: interrupt mapping for IRQ. It should be configured with + flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING. + + Refer to interrupt-controller/interrupts.txt for generic interrupt + client node bindings. + +Example: + +lsm6dsm@6b { + compatible = "st,lsm6dsm"; + reg = <0x6b>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_EDGE_RISING>; +}; diff --git a/Documentation/devicetree/bindings/iio/light/cm3605.txt b/Documentation/devicetree/bindings/iio/light/cm3605.txt new file mode 100644 index 000000000000..56331a79f9ab --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/cm3605.txt @@ -0,0 +1,41 @@ +Capella Microsystems CM3605 +Ambient Light and Short Distance Proximity Sensor + +The CM3605 is an entirely analog part which however require quite a bit of +software logic to interface a host operating system. + +This ALS and proximity sensor was one of the very first deployed in mobile +handsets, notably it is used in the very first Nexus One Android phone from +2010. + +Required properties: +- compatible: must be: "capella,cm3605" +- aset-gpios: GPIO line controlling the ASET line (drive low + to activate the ALS, should be flagged GPIO_ACTIVE_LOW) +- interrupts: the IRQ line (such as a GPIO) that is connected to + the POUT (proximity sensor out) line. The edge detection must + be set to IRQ_TYPE_EDGE_BOTH so as to detect movements toward + and away from the proximity sensor. +- io-channels: the ADC channel used for converting the voltage from + AOUT to a digital representation. +- io-channel-names: must be "aout" + +Optional properties: +- vdd-supply: regulator supplying VDD power to the component. +- capella,aset-resistance-ohms: the sensitivity calibration resistance, + in Ohms. Valid values are: 50000, 100000, 300000 and 600000, + as these are the resistance values that we are supplied with + calibration curves for. If not supplied, 100 kOhm will be assumed + but it is strongly recommended to supply this. + +Example: + +cm3605 { + compatible = "capella,cm3605"; + vdd-supply = <&foo_reg>; + aset-gpios = <&foo_gpio 1 GPIO_ACTIVE_LOW>; + capella,aset-resistance-ohms = <100000>; + interrupts = <1 IRQ_TYPE_EDGE_BOTH>; + io-channels = <&adc 0x01>; + io-channel-names = "aout"; +}; diff --git a/Documentation/devicetree/bindings/iio/potentiometer/max5481.txt b/Documentation/devicetree/bindings/iio/potentiometer/max5481.txt new file mode 100644 index 000000000000..6a91b106e076 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/potentiometer/max5481.txt @@ -0,0 +1,23 @@ +* Maxim Linear-Taper Digital Potentiometer MAX5481-MAX5484 + +The node for this driver must be a child node of a SPI controller, hence +all mandatory properties described in + + Documentation/devicetree/bindings/spi/spi-bus.txt + +must be specified. + +Required properties: + - compatible: Must be one of the following, depending on the + model: + "maxim,max5481" + "maxim,max5482" + "maxim,max5483" + "maxim,max5484" + +Example: +max548x: max548x@0 { + compatible = "maxim,max5482"; + spi-max-frequency = <7000000>; + reg = <0>; /* chip-select */ +}; diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index c040c9ad1889..eaa8fbba34e2 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -27,6 +27,8 @@ standard bindings from pinctrl/pinctrl-bindings.txt. Valid compatible strings: Accelerometers: +- st,lis3lv02d (deprecated, use st,lis3lv02dl-accel) +- st,lis302dl-spi (deprecated, use st,lis3lv02dl-accel) - st,lis3lv02dl-accel - st,lsm303dlh-accel - st,lsm303dlhc-accel diff --git a/Documentation/devicetree/bindings/iio/temperature/tmp007.txt b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt new file mode 100644 index 000000000000..b63aba91ef03 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt @@ -0,0 +1,35 @@ +* TI TMP007 - IR thermopile sensor with integrated math engine + +Link to datasheet: http://www.ti.com/lit/ds/symlink/tmp007.pdf + +Required properties: + + - compatible: should be "ti,tmp007" + - reg: the I2C address of the sensor (changeable via ADR pins) + ------------------------------ + |ADR1 | ADR0 | Device Address| + ------------------------------ + 0 0 0x40 + 0 1 0x41 + 0 SDA 0x42 + 0 SCL 0x43 + 1 0 0x44 + 1 1 0x45 + 1 SDA 0x46 + 1 SCL 0x47 + +Optional properties: + + - interrupt-parent: should be the phandle for the interrupt controller + + - interrupts: interrupt mapping for GPIO IRQ (level active low) + +Example: + +tmp007@40 { + compatible = "ti,tmp007"; + reg = <0x40>; + interrupt-parent = <&gpio0>; + interrupts = <5 0x08>; +}; + diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-timer-trigger.txt b/Documentation/devicetree/bindings/iio/timer/stm32-timer-trigger.txt new file mode 100644 index 000000000000..55a653d15303 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/timer/stm32-timer-trigger.txt @@ -0,0 +1,23 @@ +STMicroelectronics STM32 Timers IIO timer bindings + +Must be a sub-node of an STM32 Timers device tree node. +See ../mfd/stm32-timers.txt for details about the parent node. + +Required parameters: +- compatible: Must be "st,stm32-timer-trigger". +- reg: Identify trigger hardware block. + +Example: + timers@40010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "st,stm32-timers"; + reg = <0x40010000 0x400>; + clocks = <&rcc 0 160>; + clock-names = "clk_int"; + + timer@0 { + compatible = "st,stm32-timer-trigger"; + reg = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/stm32-timers.txt b/Documentation/devicetree/bindings/mfd/stm32-timers.txt new file mode 100644 index 000000000000..bbd083f5600a --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stm32-timers.txt @@ -0,0 +1,46 @@ +STM32 Timers driver bindings + +This IP provides 3 types of timer along with PWM functionality: +- advanced-control timers consist of a 16-bit auto-reload counter driven by a programmable + prescaler, break input feature, PWM outputs and complementary PWM ouputs channels. +- general-purpose timers consist of a 16-bit or 32-bit auto-reload counter driven by a + programmable prescaler and PWM outputs. +- basic timers consist of a 16-bit auto-reload counter driven by a programmable prescaler. + +Required parameters: +- compatible: must be "st,stm32-timers" + +- reg: Physical base address and length of the controller's + registers. +- clock-names: Set to "int". +- clocks: Phandle to the clock used by the timer module. + For Clk properties, please refer to ../clock/clock-bindings.txt + +Optional parameters: +- resets: Phandle to the parent reset controller. + See ../reset/st,stm32-rcc.txt + +Optional subnodes: +- pwm: See ../pwm/pwm-stm32.txt +- timer: See ../iio/timer/stm32-timer-trigger.txt + +Example: + timers@40010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "st,stm32-timers"; + reg = <0x40010000 0x400>; + clocks = <&rcc 0 160>; + clock-names = "clk_int"; + + pwm { + compatible = "st,stm32-pwm"; + pinctrl-0 = <&pwm1_pins>; + pinctrl-names = "default"; + }; + + timer@0 { + compatible = "st,stm32-timer-trigger"; + reg = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt new file mode 100644 index 000000000000..6dd040363e5e --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt @@ -0,0 +1,35 @@ +STMicroelectronics STM32 Timers PWM bindings + +Must be a sub-node of an STM32 Timers device tree node. +See ../mfd/stm32-timers.txt for details about the parent node. + +Required parameters: +- compatible: Must be "st,stm32-pwm". +- pinctrl-names: Set to "default". +- pinctrl-0: List of phandles pointing to pin configuration nodes for PWM module. + For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt + +Optional parameters: +- st,breakinput: One or two to describe break input configurations. + "index" indicates on which break input (0 or 1) the configuration + should be applied. + "level" gives the active level (0=low or 1=high) of the input signal + for this configuration. + "filter" gives the filtering value to be applied. + +Example: + timers@40010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "st,stm32-timers"; + reg = <0x40010000 0x400>; + clocks = <&rcc 0 160>; + clock-names = "clk_int"; + + pwm { + compatible = "st,stm32-pwm"; + pinctrl-0 = <&pwm1_pins>; + pinctrl-names = "default"; + st,breakinput = <0 1 5>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 16d3b5e7f5d1..cf1c36616507 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -40,6 +40,7 @@ atmel Atmel Corporation auo AU Optronics Corporation auvidea Auvidea GmbH avago Avago Technologies +avia avia semiconductor avic Shanghai AVIC Optoelectronics Co., Ltd. axentia Axentia Technologies AB axis Axis Communications AB @@ -75,6 +76,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor) davicom DAVICOM Semiconductor, Inc. delta Delta Electronics, Inc. denx Denx Software Engineering +devantech Devantech, Ltd. digi Digi International Inc. digilent Diglent, Inc. dlg Dialog Semiconductor @@ -118,6 +120,7 @@ gmt Global Mixed-mode Technology, Inc. goodix Shenzhen Huiding Technology Co., Ltd. google Google, Inc. grinn Grinn +grmn Garmin Limited gumstix Gumstix, Inc. gw Gateworks Corporation hannstar HannStar Display Corporation diff --git a/MAINTAINERS b/MAINTAINERS index 427c97e429bf..7021feed5aa6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10251,7 +10251,8 @@ F: include/uapi/linux/qnx4_fs.h F: include/uapi/linux/qnxtypes.h QORIQ DPAA2 FSL-MC BUS DRIVER -M: Stuart Yoder +M: Stuart Yoder +M: Laurentiu Tudor L: linux-kernel@vger.kernel.org S: Maintained F: drivers/staging/fsl-mc/ @@ -10528,6 +10529,12 @@ L: linux-renesas-soc@vger.kernel.org F: drivers/net/ethernet/renesas/ F: include/linux/sh_eth.h +RENESAS R-CAR GYROADC DRIVER +M: Marek Vasut +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/adc/rcar_gyro_adc.c + RENESAS USB2 PHY DRIVER M: Yoshihiro Shimoda L: linux-renesas-soc@vger.kernel.org diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index c68bdb649005..ef8401ac1141 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -120,6 +120,8 @@ config HID_SENSOR_ACCEL_3D config IIO_ST_ACCEL_3AXIS tristate "STMicroelectronics accelerometers 3-Axis Driver" depends on (I2C || SPI_MASTER) && SYSFS + depends on !SENSORS_LIS3_I2C + depends on !SENSORS_LIS3_SPI select IIO_ST_SENSORS_CORE select IIO_ST_ACCEL_I2C_3AXIS if (I2C) select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 59b380dbf27f..6b5d3be283c4 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1638,7 +1638,8 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, if (block_supported) { indio_dev->modes |= INDIO_BUFFER_SOFTWARE; indio_dev->info = &bmc150_accel_info_fifo; - indio_dev->buffer->attrs = bmc150_accel_fifo_attributes; + iio_buffer_set_attrs(indio_dev->buffer, + bmc150_accel_fifo_attributes); } } diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index ab1e238d5c75..ca5759c0c318 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -42,11 +42,13 @@ struct accel_3d_state { struct hid_sensor_hub_callbacks callbacks; struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; - u32 accel_val[ACCEL_3D_CHANNEL_MAX]; + /* Reserve for 3 channels + padding + timestamp */ + u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3]; int scale_pre_decml; int scale_post_decml; int scale_precision; int value_offset; + int64_t timestamp; }; static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = { @@ -87,6 +89,42 @@ static const struct iio_chan_spec accel_3d_channels[] = { BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_HYSTERESIS), .scan_index = CHANNEL_SCAN_INDEX_Z, + }, + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +/* Channel definitions */ +static const struct iio_chan_spec gravity_channels[] = { + { + .type = IIO_GRAVITY, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .scan_index = CHANNEL_SCAN_INDEX_X, + }, { + .type = IIO_GRAVITY, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .scan_index = CHANNEL_SCAN_INDEX_Y, + }, { + .type = IIO_GRAVITY, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .scan_index = CHANNEL_SCAN_INDEX_Z, } }; @@ -111,6 +149,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, int report_id = -1; u32 address; int ret_type; + struct hid_sensor_hub_device *hsdev = + accel_state->common_attributes.hsdev; *val = 0; *val2 = 0; @@ -122,8 +162,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, if (report_id >= 0) *val = sensor_hub_input_attr_get_raw_value( accel_state->common_attributes.hsdev, - HID_USAGE_SENSOR_ACCEL_3D, address, - report_id, + hsdev->usage, address, report_id, SENSOR_HUB_SYNC); else { *val = 0; @@ -192,11 +231,11 @@ static const struct iio_info accel_3d_info = { }; /* Function to push data to buffer */ -static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, - int len) +static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data, + int len, int64_t timestamp) { dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - iio_push_to_buffers(indio_dev, data); + iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp); } /* Callback handler to send event after all samples are received and captured */ @@ -208,10 +247,17 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct accel_3d_state *accel_state = iio_priv(indio_dev); dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n"); - if (atomic_read(&accel_state->common_attributes.data_ready)) + if (atomic_read(&accel_state->common_attributes.data_ready)) { + if (!accel_state->timestamp) + accel_state->timestamp = iio_get_time_ns(indio_dev); + hid_sensor_push_data(indio_dev, - accel_state->accel_val, - sizeof(accel_state->accel_val)); + accel_state->accel_val, + sizeof(accel_state->accel_val), + accel_state->timestamp); + + accel_state->timestamp = 0; + } return 0; } @@ -236,6 +282,12 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, *(u32 *)raw_data; ret = 0; break; + case HID_USAGE_SENSOR_TIME_TIMESTAMP: + accel_state->timestamp = + hid_sensor_convert_timestamp( + &accel_state->common_attributes, + *(int64_t *)raw_data); + break; default: break; } @@ -272,7 +324,7 @@ static int accel_3d_parse_report(struct platform_device *pdev, st->accel[2].index, st->accel[2].report_id); st->scale_precision = hid_sensor_format_scale( - HID_USAGE_SENSOR_ACCEL_3D, + hsdev->usage, &st->accel[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); @@ -295,9 +347,12 @@ static int accel_3d_parse_report(struct platform_device *pdev, static int hid_accel_3d_probe(struct platform_device *pdev) { int ret = 0; - static const char *name = "accel_3d"; + static const char *name; struct iio_dev *indio_dev; struct accel_3d_state *accel_state; + const struct iio_chan_spec *channel_spec; + int channel_size; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; indio_dev = devm_iio_device_alloc(&pdev->dev, @@ -311,24 +366,30 @@ static int hid_accel_3d_probe(struct platform_device *pdev) accel_state->common_attributes.hsdev = hsdev; accel_state->common_attributes.pdev = pdev; - ret = hid_sensor_parse_common_attributes(hsdev, - HID_USAGE_SENSOR_ACCEL_3D, + if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) { + name = "accel_3d"; + channel_spec = accel_3d_channels; + channel_size = sizeof(accel_3d_channels); + } else { + name = "gravity"; + channel_spec = gravity_channels; + channel_size = sizeof(gravity_channels); + } + ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, &accel_state->common_attributes); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; } + indio_dev->channels = kmemdup(channel_spec, channel_size, GFP_KERNEL); - indio_dev->channels = kmemdup(accel_3d_channels, - sizeof(accel_3d_channels), GFP_KERNEL); if (!indio_dev->channels) { dev_err(&pdev->dev, "failed to duplicate channels\n"); return -ENOMEM; } - ret = accel_3d_parse_report(pdev, hsdev, - (struct iio_chan_spec *)indio_dev->channels, - HID_USAGE_SENSOR_ACCEL_3D, accel_state); + (struct iio_chan_spec *)indio_dev->channels, + hsdev->usage, accel_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); goto error_free_dev_mem; @@ -363,7 +424,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) accel_state->callbacks.send_event = accel_3d_proc_event; accel_state->callbacks.capture_sample = accel_3d_capture_sample; accel_state->callbacks.pdev = pdev; - ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D, + ret = sensor_hub_register_callback(hsdev, hsdev->usage, &accel_state->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); @@ -390,7 +451,7 @@ static int hid_accel_3d_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct accel_3d_state *accel_state = iio_priv(indio_dev); - sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D); + sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(&accel_state->common_attributes); iio_triggered_buffer_cleanup(indio_dev); @@ -404,6 +465,9 @@ static const struct platform_device_id hid_accel_3d_ids[] = { /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-200073", }, + { /* gravity sensor */ + .name = "HID-SENSOR-20007b", + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids); diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index f418c588af6a..eb6e3dc789b2 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -248,7 +248,7 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n, return -EINVAL; } -static int mma8452_get_odr_index(struct mma8452_data *data) +static unsigned int mma8452_get_odr_index(struct mma8452_data *data) { return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >> MMA8452_CTRL_DR_SHIFT; @@ -260,7 +260,7 @@ static const int mma8452_samp_freq[8][2] = { }; /* Datasheet table: step time "Relationship with the ODR" (sample frequency) */ -static const int mma8452_transient_time_step_us[4][8] = { +static const unsigned int mma8452_transient_time_step_us[4][8] = { { 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */ { 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */ { 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/ diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c index 31db00970fa0..dd6ece8f9239 100644 --- a/drivers/iio/accel/ssp_accel_sensor.c +++ b/drivers/iio/accel/ssp_accel_sensor.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -135,7 +136,7 @@ static int ssp_accel_probe(struct platform_device *pdev) platform_set_drvdata(pdev, indio_dev); - ret = iio_device_register(indio_dev); + ret = devm_iio_device_register(&pdev->dev, indio_dev); if (ret < 0) return ret; @@ -145,21 +146,11 @@ static int ssp_accel_probe(struct platform_device *pdev) return 0; } -static int ssp_accel_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - - return 0; -} - static struct platform_driver ssp_accel_driver = { .driver = { .name = SSP_ACCEL_NAME, }, .probe = ssp_accel_probe, - .remove = ssp_accel_remove, }; module_platform_driver(ssp_accel_driver); diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 7c231687109a..3ad44ce7ae82 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -14,6 +14,24 @@ #include #include +enum st_accel_type { + LSM303DLH, + LSM303DLHC, + LIS3DH, + LSM330D, + LSM330DL, + LSM330DLC, + LIS331DLH, + LSM303DL, + LSM303DLM, + LSM330, + LSM303AGR, + LIS2DH12, + LIS3L02DQ, + LNG2DM, + ST_ACCEL_MAX, +}; + #define H3LIS331DL_DRIVER_NAME "h3lis331dl_accel" #define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel" #define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index c0f8867aa1ea..543f0ad7fd7e 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,11 @@ #ifdef CONFIG_OF static const struct of_device_id st_accel_of_match[] = { + { + /* An older compatible */ + .compatible = "st,lis3lv02d", + .data = LIS3LV02DL_ACCEL_DEV_NAME, + }, { .compatible = "st,lis3lv02dl-accel", .data = LIS3LV02DL_ACCEL_DEV_NAME, @@ -95,25 +101,67 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match); #define st_accel_of_match NULL #endif +#ifdef CONFIG_ACPI +static const struct acpi_device_id st_accel_acpi_match[] = { + {"SMO8A90", LNG2DM}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); +#else +#define st_accel_acpi_match NULL +#endif + +static const struct i2c_device_id st_accel_id_table[] = { + { LSM303DLH_ACCEL_DEV_NAME, LSM303DLH }, + { LSM303DLHC_ACCEL_DEV_NAME, LSM303DLHC }, + { LIS3DH_ACCEL_DEV_NAME, LIS3DH }, + { LSM330D_ACCEL_DEV_NAME, LSM330D }, + { LSM330DL_ACCEL_DEV_NAME, LSM330DL }, + { LSM330DLC_ACCEL_DEV_NAME, LSM330DLC }, + { LIS331DLH_ACCEL_DEV_NAME, LIS331DLH }, + { LSM303DL_ACCEL_DEV_NAME, LSM303DL }, + { LSM303DLM_ACCEL_DEV_NAME, LSM303DLM }, + { LSM330_ACCEL_DEV_NAME, LSM330 }, + { LSM303AGR_ACCEL_DEV_NAME, LSM303AGR }, + { LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 }, + { LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ }, + { LNG2DM_ACCEL_DEV_NAME, LNG2DM }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_accel_id_table); + static int st_accel_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct st_sensor_data *adata; - int err; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata)); if (!indio_dev) return -ENOMEM; adata = iio_priv(indio_dev); - st_sensors_of_i2c_probe(client, st_accel_of_match); + + if (client->dev.of_node) { + st_sensors_of_i2c_probe(client, st_accel_of_match); + } else if (ACPI_HANDLE(&client->dev)) { + ret = st_sensors_match_acpi_device(&client->dev); + if ((ret < 0) || (ret >= ST_ACCEL_MAX)) + return -ENODEV; + + strncpy(client->name, st_accel_id_table[ret].name, + sizeof(client->name)); + client->name[sizeof(client->name) - 1] = '\0'; + } else if (!id) + return -ENODEV; + st_sensors_i2c_configure(indio_dev, client, adata); - err = st_accel_common_probe(indio_dev); - if (err < 0) - return err; + ret = st_accel_common_probe(indio_dev); + if (ret < 0) + return ret; return 0; } @@ -125,29 +173,11 @@ static int st_accel_i2c_remove(struct i2c_client *client) return 0; } -static const struct i2c_device_id st_accel_id_table[] = { - { LSM303DLH_ACCEL_DEV_NAME }, - { LSM303DLHC_ACCEL_DEV_NAME }, - { LIS3DH_ACCEL_DEV_NAME }, - { LSM330D_ACCEL_DEV_NAME }, - { LSM330DL_ACCEL_DEV_NAME }, - { LSM330DLC_ACCEL_DEV_NAME }, - { LIS331DLH_ACCEL_DEV_NAME }, - { LSM303DL_ACCEL_DEV_NAME }, - { LSM303DLM_ACCEL_DEV_NAME }, - { LSM330_ACCEL_DEV_NAME }, - { LSM303AGR_ACCEL_DEV_NAME }, - { LIS2DH12_ACCEL_DEV_NAME }, - { LIS3L02DQ_ACCEL_DEV_NAME }, - { LNG2DM_ACCEL_DEV_NAME }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, st_accel_id_table); - static struct i2c_driver st_accel_driver = { .driver = { .name = "st-accel-i2c", .of_match_table = of_match_ptr(st_accel_of_match), + .acpi_match_table = ACPI_PTR(st_accel_acpi_match), }, .probe = st_accel_i2c_probe, .remove = st_accel_i2c_remove, diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index c25ac50d4600..29a15f27a51b 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -65,9 +65,18 @@ static const struct spi_device_id st_accel_id_table[] = { }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); +#ifdef CONFIG_OF +static const struct of_device_id lis302dl_spi_dt_ids[] = { + { .compatible = "st,lis302dl-spi" }, + {} +}; +MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids); +#endif + static struct spi_driver st_accel_driver = { .driver = { .name = "st-accel-spi", + .of_match_table = of_match_ptr(lis302dl_spi_dt_ids), }, .probe = st_accel_spi_probe, .remove = st_accel_spi_remove, diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 9c8b558ba19e..dedae7adbce9 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -247,6 +247,25 @@ config HI8435 This driver can also be built as a module. If so, the module will be called hi8435. +config HX711 + tristate "AVIA HX711 ADC for weight cells" + depends on GPIOLIB + help + If you say yes here you get support for AVIA HX711 ADC which is used + for weigh cells + + This driver uses two GPIOs, one acts as the clock and controls the + channel selection and gain, the other one is used for the measurement + data + + Currently the raw value is read from the chip and delivered. + To get an actual weight one needs to subtract the + zero offset and multiply by a scale factor. + This should be done in userspace. + + This driver can also be built as a module. If so, the module will be + called hx711. + config INA2XX_ADC tristate "Texas Instruments INA2xx Power Monitors IIO driver" depends on I2C && !SENSORS_INA2XX @@ -307,6 +326,15 @@ config MAX1027 To compile this driver as a module, choose M here: the module will be called max1027. +config MAX11100 + tristate "Maxim max11100 ADC driver" + depends on SPI_MASTER + help + Say yes here to build support for Maxim max11100 SPI ADC + + To compile this driver as a module, choose M here: the module will be + called max11100. + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C @@ -371,6 +399,18 @@ config MEN_Z188_ADC This driver can also be built as a module. If so, the module will be called men_z188_adc. +config MESON_SARADC + tristate "Amlogic Meson SAR ADC driver" + default ARCH_MESON + depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST) + select REGMAP_MMIO + help + Say yes here to build support for the SAR ADC found in Amlogic Meson + SoCs. + + To compile this driver as a module, choose M here: the + module will be called meson_saradc. + config MXS_LRADC tristate "Freescale i.MX23/i.MX28 LRADC" depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM @@ -430,6 +470,19 @@ config QCOM_SPMI_VADC To compile this driver as a module, choose M here: the module will be called qcom-spmi-vadc. +config RCAR_GYRO_ADC + tristate "Renesas R-Car GyroADC driver" + depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST) + help + Say yes here to build support for the GyroADC found in Renesas + R-Car Gen2 SoCs. This block is a simple SPI offload engine for + reading data out of attached compatible ADCs in a round-robin + fashion. Up to 4 or 8 ADC channels are supported by this block, + depending on which ADCs are attached. + + To compile this driver as a module, choose M here: the + module will be called rcar-gyroadc. + config ROCKCHIP_SARADC tristate "Rockchip SARADC driver" depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST) @@ -444,8 +497,13 @@ config ROCKCHIP_SARADC config STM32_ADC_CORE tristate "STMicroelectronics STM32 adc core" depends on ARCH_STM32 || COMPILE_TEST + depends on HAS_DMA depends on OF depends on REGULATOR + select IIO_BUFFER + select MFD_STM32_TIMERS + select IIO_STM32_TIMER_TRIGGER + select IIO_TRIGGERED_BUFFER help Select this option to enable the core driver for STMicroelectronics STM32 analog-to-digital converter (ADC). @@ -549,6 +607,19 @@ config TI_ADS1015 This driver can also be built as a module. If so, the module will be called ti-ads1015. +config TI_ADS7950 + tristate "Texas Instruments ADS7950 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments ADS7950, ADS7951, + ADS7952, ADS7953, ADS7954, ADS7955, ADS7956, ADS7957, ADS7958, ADS7959. + ADS7960, ADS7961. + + To compile this driver as a module, choose M here: the + module will be called ti-ads7950. + config TI_ADS8688 tristate "Texas Instruments ADS8688" depends on SPI && OF @@ -571,6 +642,18 @@ config TI_AM335X_ADC To compile this driver as a module, choose M here: the module will be called ti_am335x_adc. +config TI_TLC4541 + tristate "Texas Instruments TLC4541 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments TLC4541 / TLC3541 + ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-tlc4541. + config TWL4030_MADC tristate "TWL4030 MADC (Monitoring A/D Converter)" depends on TWL4030_CORE diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d36c4be8d1fc..d0012620cd1c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -25,22 +25,26 @@ obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o obj-$(CONFIG_HI8435) += hi8435.o +obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_MAX1027) += max1027.o +obj-$(CONFIG_MAX11100) += max11100.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o +obj-$(CONFIG_MESON_SARADC) += meson_saradc.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_RCAR_GYRO_ADC) += rcar-gyroadc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_STX104) += stx104.o obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o @@ -51,8 +55,10 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o +obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 7fd24949c0c1..64799ad7ebad 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -28,8 +28,6 @@ #include #define AXP288_ADC_EN_MASK 0xF1 -#define AXP288_ADC_TS_PIN_GPADC 0xF2 -#define AXP288_ADC_TS_PIN_ON 0xF3 enum axp288_adc_id { AXP288_ADC_TS, @@ -123,16 +121,6 @@ static int axp288_adc_read_channel(int *val, unsigned long address, return IIO_VAL_INT; } -static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode, - unsigned long address) -{ - /* channels other than GPADC do not need to switch TS pin */ - if (address != AXP288_GP_ADC_H) - return 0; - - return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode); -} - static int axp288_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -143,16 +131,7 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev, mutex_lock(&indio_dev->mlock); switch (mask) { case IIO_CHAN_INFO_RAW: - if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC, - chan->address)) { - dev_err(&indio_dev->dev, "GPADC mode\n"); - ret = -EINVAL; - break; - } ret = axp288_adc_read_channel(val, chan->address, info->regmap); - if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON, - chan->address)) - dev_err(&indio_dev->dev, "TS pin restore\n"); break; default: ret = -EINVAL; @@ -162,15 +141,6 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev, return ret; } -static int axp288_adc_set_state(struct regmap *regmap) -{ - /* ADC should be always enabled for internal FG to function */ - if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON)) - return -EIO; - - return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); -} - static const struct iio_info axp288_adc_iio_info = { .read_raw = &axp288_adc_read_raw, .driver_module = THIS_MODULE, @@ -199,7 +169,7 @@ static int axp288_adc_probe(struct platform_device *pdev) * Set ADC to enabled state at all time, including system suspend. * otherwise internal fuel gauge functionality may be affected. */ - ret = axp288_adc_set_state(axp20x->regmap); + ret = regmap_write(info->regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); if (ret) { dev_err(&pdev->dev, "unable to enable ADC device\n"); return ret; diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index c15756d7bf7f..ad1775b5f83c 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -632,7 +632,7 @@ static irqreturn_t exynos_ts_isr(int irq, void *dev_id) input_report_key(info->input, BTN_TOUCH, 1); input_sync(info->input); - msleep(1); + usleep_range(1000, 1100); }; writel(0, ADC_V1_CLRINTPNDNUP(info->regs)); diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index 72b32c1ab257..ea264fa9e567 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -401,6 +401,7 @@ static const struct of_device_id mx25_gcq_ids[] = { { .compatible = "fsl,imx25-gcq", }, { /* Sentinel */ } }; +MODULE_DEVICE_TABLE(of, mx25_gcq_ids); static struct platform_driver mx25_gcq_driver = { .driver = { diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c new file mode 100644 index 000000000000..139639f73769 --- /dev/null +++ b/drivers/iio/adc/hx711.c @@ -0,0 +1,532 @@ +/* + * HX711: analog to digital converter for weight sensor module + * + * Copyright (c) 2016 Andreas Klinger + * + * 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 + +/* gain to pulse and scale conversion */ +#define HX711_GAIN_MAX 3 + +struct hx711_gain_to_scale { + int gain; + int gain_pulse; + int scale; + int channel; +}; + +/* + * .scale depends on AVDD which in turn is known as soon as the regulator + * is available + * therefore we set .scale in hx711_probe() + * + * channel A in documentation is channel 0 in source code + * channel B in documentation is channel 1 in source code + */ +static struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = { + { 128, 1, 0, 0 }, + { 32, 2, 0, 1 }, + { 64, 3, 0, 0 } +}; + +static int hx711_get_gain_to_pulse(int gain) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].gain == gain) + return hx711_gain_to_scale[i].gain_pulse; + return 1; +} + +static int hx711_get_gain_to_scale(int gain) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].gain == gain) + return hx711_gain_to_scale[i].scale; + return 0; +} + +static int hx711_get_scale_to_gain(int scale) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].scale == scale) + return hx711_gain_to_scale[i].gain; + return -EINVAL; +} + +struct hx711_data { + struct device *dev; + struct gpio_desc *gpiod_pd_sck; + struct gpio_desc *gpiod_dout; + struct regulator *reg_avdd; + int gain_set; /* gain set on device */ + int gain_chan_a; /* gain for channel A */ + struct mutex lock; +}; + +static int hx711_cycle(struct hx711_data *hx711_data) +{ + int val; + + /* + * if preempted for more then 60us while PD_SCK is high: + * hx711 is going in reset + * ==> measuring is false + */ + preempt_disable(); + gpiod_set_value(hx711_data->gpiod_pd_sck, 1); + val = gpiod_get_value(hx711_data->gpiod_dout); + /* + * here we are not waiting for 0.2 us as suggested by the datasheet, + * because the oscilloscope showed in a test scenario + * at least 1.15 us for PD_SCK high (T3 in datasheet) + * and 0.56 us for PD_SCK low on TI Sitara with 800 MHz + */ + gpiod_set_value(hx711_data->gpiod_pd_sck, 0); + preempt_enable(); + + return val; +} + +static int hx711_read(struct hx711_data *hx711_data) +{ + int i, ret; + int value = 0; + int val = gpiod_get_value(hx711_data->gpiod_dout); + + /* we double check if it's really down */ + if (val) + return -EIO; + + for (i = 0; i < 24; i++) { + value <<= 1; + ret = hx711_cycle(hx711_data); + if (ret) + value++; + } + + value ^= 0x800000; + + for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++) + hx711_cycle(hx711_data); + + return value; +} + +static int hx711_wait_for_ready(struct hx711_data *hx711_data) +{ + int i, val; + + /* + * a maximum reset cycle time of 56 ms was measured. + * we round it up to 100 ms + */ + for (i = 0; i < 100; i++) { + val = gpiod_get_value(hx711_data->gpiod_dout); + if (!val) + break; + /* sleep at least 1 ms */ + msleep(1); + } + if (val) + return -EIO; + + return 0; +} + +static int hx711_reset(struct hx711_data *hx711_data) +{ + int ret; + int val = gpiod_get_value(hx711_data->gpiod_dout); + + if (val) { + /* + * an examination with the oszilloscope indicated + * that the first value read after the reset is not stable + * if we reset too short; + * the shorter the reset cycle + * the less reliable the first value after reset is; + * there were no problems encountered with a value + * of 10 ms or higher + */ + gpiod_set_value(hx711_data->gpiod_pd_sck, 1); + msleep(10); + gpiod_set_value(hx711_data->gpiod_pd_sck, 0); + + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + /* + * after a reset the gain is 128 so we do a dummy read + * to set the gain for the next read + */ + ret = hx711_read(hx711_data); + if (ret < 0) + return ret; + + /* + * after a dummy read we need to wait vor readiness + * for not mixing gain pulses with the clock + */ + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + } + + return val; +} + +static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) +{ + int ret; + + if (chan == 0) { + if (hx711_data->gain_set == 32) { + hx711_data->gain_set = hx711_data->gain_chan_a; + + ret = hx711_read(hx711_data); + if (ret < 0) + return ret; + + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + } + } else { + if (hx711_data->gain_set != 32) { + hx711_data->gain_set = 32; + + ret = hx711_read(hx711_data); + if (ret < 0) + return ret; + + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + } + } + + return 0; +} + +static int hx711_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct hx711_data *hx711_data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&hx711_data->lock); + + /* + * hx711_reset() must be called from here + * because it could be calling hx711_read() by itself + */ + if (hx711_reset(hx711_data)) { + mutex_unlock(&hx711_data->lock); + dev_err(hx711_data->dev, "reset failed!"); + return -EIO; + } + + ret = hx711_set_gain_for_channel(hx711_data, chan->channel); + if (ret < 0) { + mutex_unlock(&hx711_data->lock); + return ret; + } + + *val = hx711_read(hx711_data); + + mutex_unlock(&hx711_data->lock); + + if (*val < 0) + return *val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + mutex_lock(&hx711_data->lock); + + *val2 = hx711_get_gain_to_scale(hx711_data->gain_set); + + mutex_unlock(&hx711_data->lock); + + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int hx711_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct hx711_data *hx711_data = iio_priv(indio_dev); + int ret; + int gain; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + /* + * a scale greater than 1 mV per LSB is not possible + * with the HX711, therefore val must be 0 + */ + if (val != 0) + return -EINVAL; + + mutex_lock(&hx711_data->lock); + + gain = hx711_get_scale_to_gain(val2); + if (gain < 0) { + mutex_unlock(&hx711_data->lock); + return gain; + } + + if (gain != hx711_data->gain_set) { + hx711_data->gain_set = gain; + if (gain != 32) + hx711_data->gain_chan_a = gain; + + ret = hx711_read(hx711_data); + if (ret < 0) { + mutex_unlock(&hx711_data->lock); + return ret; + } + } + + mutex_unlock(&hx711_data->lock); + return 0; + default: + return -EINVAL; + } + + return 0; +} + +static int hx711_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static ssize_t hx711_scale_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); + int channel = iio_attr->address; + int i, len = 0; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].channel == channel) + len += sprintf(buf + len, "0.%09d ", + hx711_gain_to_scale[i].scale); + + len += sprintf(buf + len, "\n"); + + return len; +} + +static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO, + hx711_scale_available_show, NULL, 0); + +static IIO_DEVICE_ATTR(in_voltage1_scale_available, S_IRUGO, + hx711_scale_available_show, NULL, 1); + +static struct attribute *hx711_attributes[] = { + &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, + NULL, +}; + +static struct attribute_group hx711_attribute_group = { + .attrs = hx711_attributes, +}; + +static const struct iio_info hx711_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = hx711_read_raw, + .write_raw = hx711_write_raw, + .write_raw_get_fmt = hx711_write_raw_get_fmt, + .attrs = &hx711_attribute_group, +}; + +static const struct iio_chan_spec hx711_chan_spec[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .channel = 1, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int hx711_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hx711_data *hx711_data; + struct iio_dev *indio_dev; + int ret; + int i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct hx711_data)); + if (!indio_dev) { + dev_err(dev, "failed to allocate IIO device\n"); + return -ENOMEM; + } + + hx711_data = iio_priv(indio_dev); + hx711_data->dev = dev; + + mutex_init(&hx711_data->lock); + + /* + * PD_SCK stands for power down and serial clock input of HX711 + * in the driver it is an output + */ + hx711_data->gpiod_pd_sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW); + if (IS_ERR(hx711_data->gpiod_pd_sck)) { + dev_err(dev, "failed to get sck-gpiod: err=%ld\n", + PTR_ERR(hx711_data->gpiod_pd_sck)); + return PTR_ERR(hx711_data->gpiod_pd_sck); + } + + /* + * DOUT stands for serial data output of HX711 + * for the driver it is an input + */ + hx711_data->gpiod_dout = devm_gpiod_get(dev, "dout", GPIOD_IN); + if (IS_ERR(hx711_data->gpiod_dout)) { + dev_err(dev, "failed to get dout-gpiod: err=%ld\n", + PTR_ERR(hx711_data->gpiod_dout)); + return PTR_ERR(hx711_data->gpiod_dout); + } + + hx711_data->reg_avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(hx711_data->reg_avdd)) + return PTR_ERR(hx711_data->reg_avdd); + + ret = regulator_enable(hx711_data->reg_avdd); + if (ret < 0) + return ret; + + /* + * with + * full scale differential input range: AVDD / GAIN + * full scale output data: 2^24 + * we can say: + * AVDD / GAIN = 2^24 + * therefore: + * 1 LSB = AVDD / GAIN / 2^24 + * AVDD is in uV, but we need 10^-9 mV + * approximately to fit into a 32 bit number: + * 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV] + */ + ret = regulator_get_voltage(hx711_data->reg_avdd); + if (ret < 0) { + regulator_disable(hx711_data->reg_avdd); + return ret; + } + /* we need 10^-9 mV */ + ret *= 100; + + for (i = 0; i < HX711_GAIN_MAX; i++) + hx711_gain_to_scale[i].scale = + ret / hx711_gain_to_scale[i].gain / 1678; + + hx711_data->gain_set = 128; + hx711_data->gain_chan_a = 128; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = "hx711"; + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &hx711_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = hx711_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(dev, "Couldn't register the device\n"); + regulator_disable(hx711_data->reg_avdd); + } + + return ret; +} + +static int hx711_remove(struct platform_device *pdev) +{ + struct hx711_data *hx711_data; + struct iio_dev *indio_dev; + + indio_dev = platform_get_drvdata(pdev); + hx711_data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + regulator_disable(hx711_data->reg_avdd); + + return 0; +} + +static const struct of_device_id of_hx711_match[] = { + { .compatible = "avia,hx711", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_hx711_match); + +static struct platform_driver hx711_driver = { + .probe = hx711_probe, + .remove = hx711_remove, + .driver = { + .name = "hx711-gpio", + .of_match_table = of_hx711_match, + }, +}; + +module_platform_driver(hx711_driver); + +MODULE_AUTHOR("Andreas Klinger "); +MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hx711-gpio"); + diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 59b7d76e1ad2..3263231276ca 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include #include diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c new file mode 100644 index 000000000000..a088cf99bfe1 --- /dev/null +++ b/drivers/iio/adc/max11100.c @@ -0,0 +1,181 @@ +/* + * iio/adc/max11100.c + * Maxim max11100 ADC Driver with IIO interface + * + * Copyright (C) 2016-17 Renesas Electronics Corporation + * Copyright (C) 2016-17 Jacopo Mondi + * + * 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 +#include +#include +#include + +#include +#include + +/* + * LSB is the ADC single digital step + * 1 LSB = (vref_mv / 2 ^ 16) + * + * LSB is used to calculate analog voltage value + * from the number of ADC steps count + * + * Ain = (count * LSB) + */ +#define MAX11100_LSB_DIV (1 << 16) + +struct max11100_state { + struct regulator *vref_reg; + struct spi_device *spi; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 buffer[3] ____cacheline_aligned; +}; + +static struct iio_chan_spec max11100_channels[] = { + { /* [0] */ + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int max11100_read_single(struct iio_dev *indio_dev, int *val) +{ + int ret; + struct max11100_state *state = iio_priv(indio_dev); + + ret = spi_read(state->spi, state->buffer, sizeof(state->buffer)); + if (ret) { + dev_err(&indio_dev->dev, "SPI transfer failed\n"); + return ret; + } + + /* the first 8 bits sent out from ADC must be 0s */ + if (state->buffer[0]) { + dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n"); + return -EINVAL; + } + + *val = (state->buffer[1] << 8) | state->buffer[2]; + + return 0; +} + +static int max11100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + int ret, vref_uv; + struct max11100_state *state = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = max11100_read_single(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(state->vref_reg); + if (vref_uv < 0) + /* dummy regulator "get_voltage" returns -EINVAL */ + return -EINVAL; + + *val = vref_uv / 1000; + *val2 = MAX11100_LSB_DIV; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static const struct iio_info max11100_info = { + .driver_module = THIS_MODULE, + .read_raw = max11100_read_raw, +}; + +static int max11100_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct max11100_state *state; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + spi_set_drvdata(spi, indio_dev); + + state = iio_priv(indio_dev); + state->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->name = "max11100"; + indio_dev->info = &max11100_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max11100_channels, + indio_dev->num_channels = ARRAY_SIZE(max11100_channels), + + state->vref_reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(state->vref_reg)) + return PTR_ERR(state->vref_reg); + + ret = regulator_enable(state->vref_reg); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto disable_regulator; + + return 0; + +disable_regulator: + regulator_disable(state->vref_reg); + + return ret; +} + +static int max11100_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max11100_state *state = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(state->vref_reg); + + return 0; +} + +static const struct of_device_id max11100_ids[] = { + {.compatible = "maxim,max11100"}, + { }, +}; +MODULE_DEVICE_TABLE(of, max11100_ids); + +static struct spi_driver max11100_driver = { + .driver = { + .name = "max11100", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max11100_ids), + }, + .probe = max11100_probe, + .remove = max11100_remove, +}; + +module_spi_driver(max11100_driver); + +MODULE_AUTHOR("Jacopo Mondi "); +MODULE_DESCRIPTION("Maxim max11100 ADC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 841a13c9b6ea..c6c12feb4a08 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1567,6 +1567,7 @@ static const struct of_device_id max1363_of_match[] = { MAX1363_COMPATIBLE("maxim,max11647", max11647), { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, max1363_of_match); #endif static int max1363_probe(struct i2c_client *client, diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c new file mode 100644 index 000000000000..89def6034f40 --- /dev/null +++ b/drivers/iio/adc/meson_saradc.c @@ -0,0 +1,922 @@ +/* + * Amlogic Meson Successive Approximation Register (SAR) A/D Converter + * + * Copyright (C) 2017 Martin Blumenstingl + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MESON_SAR_ADC_REG0 0x00 + #define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31) + #define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28) + #define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30) + #define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29) + #define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28) + #define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27) + #define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26) + #define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21) + #define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19) + #define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16) + #define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15) + #define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14) + #define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9) + #define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4) + #define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3) + #define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2) + #define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1) + #define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0) + +#define MESON_SAR_ADC_CHAN_LIST 0x04 + #define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24) + #define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \ + (GENMASK(2, 0) << ((_chan) * 3)) + +#define MESON_SAR_ADC_AVG_CNTL 0x08 + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \ + (16 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \ + (GENMASK(17, 16) << ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \ + (0 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \ + (GENMASK(1, 0) << ((_chan) * 2)) + +#define MESON_SAR_ADC_REG3 0x0c + #define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31) + #define MESON_SAR_ADC_REG3_CLK_EN BIT(30) + #define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28) + #define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27) + #define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26) + #define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_REG3_DETECT_EN BIT(22) + #define MESON_SAR_ADC_REG3_ADC_EN BIT(21) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16) + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10 + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 5 + #define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_DELAY 0x10 + #define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24) + #define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15) + #define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14) + #define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_LAST_RD 0x14 + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0) + +#define MESON_SAR_ADC_FIFO_RD 0x18 + #define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12) + #define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0) + +#define MESON_SAR_ADC_AUX_SW 0x1c + #define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_MASK(_chan) \ + (GENMASK(10, 8) << (((_chan) - 2) * 2)) + #define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4) + #define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_CHAN_10_SW 0x20 + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24 + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DELTA_10 0x28 + #define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27) + #define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26) + #define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16) + #define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15) + #define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11 + #define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11) + #define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10) + #define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0) + +/* + * NOTE: registers from here are undocumented (the vendor Linux kernel driver + * and u-boot source served as reference). These only seem to be relevant on + * GXBB and newer. + */ +#define MESON_SAR_ADC_REG11 0x2c + #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) + +#define MESON_SAR_ADC_REG13 0x34 + #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) + +#define MESON_SAR_ADC_MAX_FIFO_SIZE 32 + +#define MESON_SAR_ADC_CHAN(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "SAR_ADC_CH"#_chan, \ +} + +/* + * TODO: the hardware supports IIO_TEMP for channel 6 as well which is + * currently not supported by this driver. + */ +static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { + MESON_SAR_ADC_CHAN(0), + MESON_SAR_ADC_CHAN(1), + MESON_SAR_ADC_CHAN(2), + MESON_SAR_ADC_CHAN(3), + MESON_SAR_ADC_CHAN(4), + MESON_SAR_ADC_CHAN(5), + MESON_SAR_ADC_CHAN(6), + MESON_SAR_ADC_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +enum meson_sar_adc_avg_mode { + NO_AVERAGING = 0x0, + MEAN_AVERAGING = 0x1, + MEDIAN_AVERAGING = 0x2, +}; + +enum meson_sar_adc_num_samples { + ONE_SAMPLE = 0x0, + TWO_SAMPLES = 0x1, + FOUR_SAMPLES = 0x2, + EIGHT_SAMPLES = 0x3, +}; + +enum meson_sar_adc_chan7_mux_sel { + CHAN7_MUX_VSS = 0x0, + CHAN7_MUX_VDD_DIV4 = 0x1, + CHAN7_MUX_VDD_DIV2 = 0x2, + CHAN7_MUX_VDD_MUL3_DIV4 = 0x3, + CHAN7_MUX_VDD = 0x4, + CHAN7_MUX_CH7_INPUT = 0x7, +}; + +struct meson_sar_adc_data { + unsigned int resolution; + const char *name; +}; + +struct meson_sar_adc_priv { + struct regmap *regmap; + struct regulator *vref; + const struct meson_sar_adc_data *data; + struct clk *clkin; + struct clk *core_clk; + struct clk *sana_clk; + struct clk *adc_sel_clk; + struct clk *adc_clk; + struct clk_gate clk_gate; + struct clk *adc_div_clk; + struct clk_divider clk_div; +}; + +static const struct regmap_config meson_sar_adc_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_SAR_ADC_REG13, +}; + +static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + + return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); +} + +static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int regval, timeout = 10000; + + /* + * NOTE: we need a small delay before reading the status, otherwise + * the sample engine may not have started internally (which would + * seem to us that sampling is already finished). + */ + do { + udelay(1); + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + } while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && timeout--); + + if (timeout < 0) + return -ETIMEDOUT; + + return 0; +} + +static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret, regval, fifo_chan, fifo_val, sum = 0, count = 0; + + ret = meson_sar_adc_wait_busy_clear(indio_dev); + if (ret) + return ret; + + while (meson_sar_adc_get_fifo_count(indio_dev) > 0 && + count < MESON_SAR_ADC_MAX_FIFO_SIZE) { + regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val); + + fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, + regval); + if (fifo_chan != chan->channel) + continue; + + fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, + regval); + fifo_val &= (BIT(priv->data->resolution) - 1); + + sum += fifo_val; + count++; + } + + if (!count) + return -ENOENT; + + *val = sum / count; + + return 0; +} + +static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum meson_sar_adc_avg_mode mode, + enum meson_sar_adc_num_samples samples) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int val, channel = chan->channel; + + val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, + MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel), + val); + + val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, + MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val); +} + +static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + /* + * the SAR ADC engine allows sampling multiple channels at the same + * time. to keep it simple we're only working with one *internal* + * channel, which starts counting at index 0 (which means: count = 1). + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, + MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval); + + /* map channel index 0 to the channel which we want to read */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), + chan->channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, + MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval); + + regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, + chan->channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, + MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, + regval); + + regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, + chan->channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, + MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, + regval); + + if (chan->channel == 6) + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0); +} + +static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev, + enum meson_sar_adc_chan7_mux_sel sel) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval); + + usleep_range(10, 20); +} + +static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_START, + MESON_SAR_ADC_REG0_SAMPLING_START); +} + +static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_STOP, + MESON_SAR_ADC_REG0_SAMPLING_STOP); + + /* wait until all modules are stopped */ + meson_sar_adc_wait_busy_clear(indio_dev); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, 0); +} + +static int meson_sar_adc_lock(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int val, timeout = 10000; + + mutex_lock(&indio_dev->mlock); + + /* prevent BL30 from using the SAR ADC while we are using it */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY); + + /* wait until BL30 releases it's lock (so we can use the SAR ADC) */ + do { + udelay(1); + regmap_read(priv->regmap, MESON_SAR_ADC_DELAY, &val); + } while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && timeout--); + + if (timeout < 0) + return -ETIMEDOUT; + + return 0; +} + +static void meson_sar_adc_unlock(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + /* allow BL30 to use the SAR ADC again */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0); + + mutex_unlock(&indio_dev->mlock); +} + +static void meson_sar_adc_clear_fifo(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int count; + + for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) { + if (!meson_sar_adc_get_fifo_count(indio_dev)) + break; + + regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, 0); + } +} + +static int meson_sar_adc_get_sample(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum meson_sar_adc_avg_mode avg_mode, + enum meson_sar_adc_num_samples avg_samples, + int *val) +{ + int ret; + + ret = meson_sar_adc_lock(indio_dev); + if (ret) + return ret; + + /* clear the FIFO to make sure we're not reading old values */ + meson_sar_adc_clear_fifo(indio_dev); + + meson_sar_adc_set_averaging(indio_dev, chan, avg_mode, avg_samples); + + meson_sar_adc_enable_channel(indio_dev, chan); + + meson_sar_adc_start_sample_engine(indio_dev); + ret = meson_sar_adc_read_raw_sample(indio_dev, chan, val); + meson_sar_adc_stop_sample_engine(indio_dev); + + meson_sar_adc_unlock(indio_dev); + + if (ret) { + dev_warn(indio_dev->dev.parent, + "failed to read sample for channel %d: %d\n", + chan->channel, ret); + return ret; + } + + return IIO_VAL_INT; +} + +static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return meson_sar_adc_get_sample(indio_dev, chan, NO_AVERAGING, + ONE_SAMPLE, val); + break; + + case IIO_CHAN_INFO_AVERAGE_RAW: + return meson_sar_adc_get_sample(indio_dev, chan, + MEAN_AVERAGING, EIGHT_SAMPLES, + val); + break; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(priv->vref); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "failed to get vref voltage: %d\n", ret); + return ret; + } + + *val = ret / 1000; + *val2 = priv->data->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, + void __iomem *base) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct clk_init_data init; + const char *clk_parents[1]; + + init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div", + of_node_full_name(indio_dev->dev.of_node)); + init.flags = 0; + init.ops = &clk_divider_ops; + clk_parents[0] = __clk_get_name(priv->clkin); + init.parent_names = clk_parents; + init.num_parents = 1; + + priv->clk_div.reg = base + MESON_SAR_ADC_REG3; + priv->clk_div.shift = MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT; + priv->clk_div.width = MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH; + priv->clk_div.hw.init = &init; + priv->clk_div.flags = 0; + + priv->adc_div_clk = devm_clk_register(&indio_dev->dev, + &priv->clk_div.hw); + if (WARN_ON(IS_ERR(priv->adc_div_clk))) + return PTR_ERR(priv->adc_div_clk); + + init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en", + of_node_full_name(indio_dev->dev.of_node)); + init.flags = CLK_SET_RATE_PARENT; + init.ops = &clk_gate_ops; + clk_parents[0] = __clk_get_name(priv->adc_div_clk); + init.parent_names = clk_parents; + init.num_parents = 1; + + priv->clk_gate.reg = base + MESON_SAR_ADC_REG3; + priv->clk_gate.bit_idx = fls(MESON_SAR_ADC_REG3_CLK_EN); + priv->clk_gate.hw.init = &init; + + priv->adc_clk = devm_clk_register(&indio_dev->dev, &priv->clk_gate.hw); + if (WARN_ON(IS_ERR(priv->adc_clk))) + return PTR_ERR(priv->adc_clk); + + return 0; +} + +static int meson_sar_adc_init(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int regval, ret; + + /* + * make sure we start at CH7 input since the other muxes are only used + * for internal calibration. + */ + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); + + /* + * leave sampling delay and the input clocks as configured by BL30 to + * make sure BL30 gets the values it expects when reading the + * temperature sensor. + */ + regmap_read(priv->regmap, MESON_SAR_ADC_REG3, ®val); + if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED) + return 0; + + meson_sar_adc_stop_sample_engine(indio_dev); + + /* update the channel 6 MUX to select the temperature sensor */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, + MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL); + + /* disable all channels by default */ + regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY, + MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + 0)); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + 1)); + + ret = clk_set_parent(priv->adc_sel_clk, priv->clkin); + if (ret) { + dev_err(indio_dev->dev.parent, + "failed to set adc parent to clkin\n"); + return ret; + } + + ret = clk_set_rate(priv->adc_clk, 1200000); + if (ret) { + dev_err(indio_dev->dev.parent, + "failed to set adc clock rate\n"); + return ret; + } + + return 0; +} + +static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + ret = meson_sar_adc_lock(indio_dev); + if (ret) + goto err_lock; + + ret = regulator_enable(priv->vref); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "failed to enable vref regulator\n"); + goto err_vref; + } + + ret = clk_prepare_enable(priv->core_clk); + if (ret) { + dev_err(indio_dev->dev.parent, "failed to enable core clk\n"); + goto err_core_clk; + } + + ret = clk_prepare_enable(priv->sana_clk); + if (ret) { + dev_err(indio_dev->dev.parent, "failed to enable sana clk\n"); + goto err_sana_clk; + } + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_BANDGAP_EN, + MESON_SAR_ADC_REG11_BANDGAP_EN); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN, + MESON_SAR_ADC_REG3_ADC_EN); + + udelay(5); + + ret = clk_prepare_enable(priv->adc_clk); + if (ret) { + dev_err(indio_dev->dev.parent, "failed to enable adc clk\n"); + goto err_adc_clk; + } + + meson_sar_adc_unlock(indio_dev); + + return 0; + +err_adc_clk: + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_BANDGAP_EN, 0); + clk_disable_unprepare(priv->sana_clk); +err_sana_clk: + clk_disable_unprepare(priv->core_clk); +err_core_clk: + regulator_disable(priv->vref); +err_vref: + meson_sar_adc_unlock(indio_dev); +err_lock: + return ret; +} + +static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + ret = meson_sar_adc_lock(indio_dev); + if (ret) + return ret; + + clk_disable_unprepare(priv->adc_clk); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_BANDGAP_EN, 0); + + clk_disable_unprepare(priv->sana_clk); + clk_disable_unprepare(priv->core_clk); + + regulator_disable(priv->vref); + + meson_sar_adc_unlock(indio_dev); + + return 0; +} + +static const struct iio_info meson_sar_adc_iio_info = { + .read_raw = meson_sar_adc_iio_info_read_raw, + .driver_module = THIS_MODULE, +}; + +struct meson_sar_adc_data meson_sar_adc_gxbb_data = { + .resolution = 10, + .name = "meson-gxbb-saradc", +}; + +struct meson_sar_adc_data meson_sar_adc_gxl_data = { + .resolution = 12, + .name = "meson-gxl-saradc", +}; + +struct meson_sar_adc_data meson_sar_adc_gxm_data = { + .resolution = 12, + .name = "meson-gxm-saradc", +}; + +static const struct of_device_id meson_sar_adc_of_match[] = { + { + .compatible = "amlogic,meson-gxbb-saradc", + .data = &meson_sar_adc_gxbb_data, + }, { + .compatible = "amlogic,meson-gxl-saradc", + .data = &meson_sar_adc_gxl_data, + }, { + .compatible = "amlogic,meson-gxm-saradc", + .data = &meson_sar_adc_gxm_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match); + +static int meson_sar_adc_probe(struct platform_device *pdev) +{ + struct meson_sar_adc_priv *priv; + struct iio_dev *indio_dev; + struct resource *res; + void __iomem *base; + const struct of_device_id *match; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + priv = iio_priv(indio_dev); + + match = of_match_device(meson_sar_adc_of_match, &pdev->dev); + priv->data = match->data; + + indio_dev->name = priv->data->name; + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &meson_sar_adc_iio_info; + + indio_dev->channels = meson_sar_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &meson_sar_adc_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->clkin = devm_clk_get(&pdev->dev, "clkin"); + if (IS_ERR(priv->clkin)) { + dev_err(&pdev->dev, "failed to get clkin\n"); + return PTR_ERR(priv->clkin); + } + + priv->core_clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(priv->core_clk)) { + dev_err(&pdev->dev, "failed to get core clk\n"); + return PTR_ERR(priv->core_clk); + } + + priv->sana_clk = devm_clk_get(&pdev->dev, "sana"); + if (IS_ERR(priv->sana_clk)) { + if (PTR_ERR(priv->sana_clk) == -ENOENT) { + priv->sana_clk = NULL; + } else { + dev_err(&pdev->dev, "failed to get sana clk\n"); + return PTR_ERR(priv->sana_clk); + } + } + + priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(priv->adc_clk)) { + if (PTR_ERR(priv->adc_clk) == -ENOENT) { + priv->adc_clk = NULL; + } else { + dev_err(&pdev->dev, "failed to get adc clk\n"); + return PTR_ERR(priv->adc_clk); + } + } + + priv->adc_sel_clk = devm_clk_get(&pdev->dev, "adc_sel"); + if (IS_ERR(priv->adc_sel_clk)) { + if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) { + priv->adc_sel_clk = NULL; + } else { + dev_err(&pdev->dev, "failed to get adc_sel clk\n"); + return PTR_ERR(priv->adc_sel_clk); + } + } + + /* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */ + if (!priv->adc_clk) { + ret = meson_sar_adc_clk_init(indio_dev, base); + if (ret) + return ret; + } + + priv->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(priv->vref)) { + dev_err(&pdev->dev, "failed to get vref regulator\n"); + return PTR_ERR(priv->vref); + } + + ret = meson_sar_adc_init(indio_dev); + if (ret) + goto err; + + ret = meson_sar_adc_hw_enable(indio_dev); + if (ret) + goto err; + + platform_set_drvdata(pdev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_hw; + + return 0; + +err_hw: + meson_sar_adc_hw_disable(indio_dev); +err: + return ret; +} + +static int meson_sar_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + + return meson_sar_adc_hw_disable(indio_dev); +} + +static int __maybe_unused meson_sar_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return meson_sar_adc_hw_disable(indio_dev); +} + +static int __maybe_unused meson_sar_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return meson_sar_adc_hw_enable(indio_dev); +} + +static SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops, + meson_sar_adc_suspend, meson_sar_adc_resume); + +static struct platform_driver meson_sar_adc_driver = { + .probe = meson_sar_adc_probe, + .remove = meson_sar_adc_remove, + .driver = { + .name = "meson-saradc", + .of_match_table = meson_sar_adc_of_match, + .pm = &meson_sar_adc_pm_ops, + }, +}; + +module_platform_driver(meson_sar_adc_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c index c2babe50a0d8..0a19761d656c 100644 --- a/drivers/iio/adc/qcom-spmi-vadc.c +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -84,7 +84,7 @@ #define VADC_MAX_ADC_CODE 0xa800 #define VADC_ABSOLUTE_RANGE_UV 625000 -#define VADC_RATIOMETRIC_RANGE_UV 1800000 +#define VADC_RATIOMETRIC_RANGE 1800 #define VADC_DEF_PRESCALING 0 /* 1:1 */ #define VADC_DEF_DECIMATION 0 /* 512 */ @@ -100,9 +100,23 @@ #define KELVINMIL_CELSIUSMIL 273150 +#define PMI_CHG_SCALE_1 -138890 +#define PMI_CHG_SCALE_2 391750000000LL + #define VADC_CHAN_MIN VADC_USBIN #define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM +/** + * struct vadc_map_pt - Map the graph representation for ADC channel + * @x: Represent the ADC digitized code. + * @y: Represent the physical data which can be temperature, voltage, + * resistance. + */ +struct vadc_map_pt { + s32 x; + s32 y; +}; + /* * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels. * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for @@ -148,6 +162,9 @@ struct vadc_prescale_ratio { * start of conversion. * @avg_samples: ability to provide single result from the ADC * that is an average of multiple measurements. + * @scale_fn: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + * Referenced from enum vadc_scale_fn_type. */ struct vadc_channel_prop { unsigned int channel; @@ -156,6 +173,7 @@ struct vadc_channel_prop { unsigned int prescale; unsigned int hw_settle_time; unsigned int avg_samples; + unsigned int scale_fn; }; /** @@ -186,6 +204,35 @@ struct vadc_priv { struct mutex lock; }; +/** + * struct vadc_scale_fn - Scaling function prototype + * @scale: Function pointer to one of the scaling functions + * which takes the adc properties, channel properties, + * and returns the physical result. + */ +struct vadc_scale_fn { + int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *, + u16, int *); +}; + +/** + * enum vadc_scale_fn_type - Scaling function to convert ADC code to + * physical scaled units for the channel. + * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV). + * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC. + * Uses a mapping table with 100K pullup. + * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade. + * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC. + * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp + */ +enum vadc_scale_fn_type { + SCALE_DEFAULT = 0, + SCALE_THERM_100K_PULLUP, + SCALE_PMIC_THERM, + SCALE_XOTHERM, + SCALE_PMI_CHG_TEMP, +}; + static const struct vadc_prescale_ratio vadc_prescale_ratios[] = { {.num = 1, .den = 1}, {.num = 1, .den = 3}, @@ -197,6 +244,44 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = { {.num = 1, .den = 10} }; +/* Voltage to temperature */ +static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = { + {1758, -40}, + {1742, -35}, + {1719, -30}, + {1691, -25}, + {1654, -20}, + {1608, -15}, + {1551, -10}, + {1483, -5}, + {1404, 0}, + {1315, 5}, + {1218, 10}, + {1114, 15}, + {1007, 20}, + {900, 25}, + {795, 30}, + {696, 35}, + {605, 40}, + {522, 45}, + {448, 50}, + {383, 55}, + {327, 60}, + {278, 65}, + {237, 70}, + {202, 75}, + {172, 80}, + {146, 85}, + {125, 90}, + {107, 95}, + {92, 100}, + {79, 105}, + {68, 110}, + {59, 115}, + {51, 120}, + {44, 125} +}; + static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data) { return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1); @@ -418,7 +503,7 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc) u16 read_1, read_2; int ret; - vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE_UV; + vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE; vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV; prop = vadc_get_channel(vadc, VADC_REF_1250MV); @@ -468,27 +553,148 @@ err: return ret; } -static s32 vadc_calibrate(struct vadc_priv *vadc, - const struct vadc_channel_prop *prop, u16 adc_code) +static int vadc_map_voltage_temp(const struct vadc_map_pt *pts, + u32 tablesize, s32 input, s64 *output) +{ + bool descending = 1; + u32 i = 0; + + if (!pts) + return -EINVAL; + + /* Check if table is descending or ascending */ + if (tablesize > 1) { + if (pts[0].x < pts[1].x) + descending = 0; + } + + while (i < tablesize) { + if ((descending) && (pts[i].x < input)) { + /* table entry is less than measured*/ + /* value and table is descending, stop */ + break; + } else if ((!descending) && + (pts[i].x > input)) { + /* table entry is greater than measured*/ + /*value and table is ascending, stop */ + break; + } + i++; + } + + if (i == 0) { + *output = pts[0].y; + } else if (i == tablesize) { + *output = pts[tablesize - 1].y; + } else { + /* result is between search_index and search_index-1 */ + /* interpolate linearly */ + *output = (((s32)((pts[i].y - pts[i - 1].y) * + (input - pts[i - 1].x)) / + (pts[i].x - pts[i - 1].x)) + + pts[i - 1].y); + } + + return 0; +} + +static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code, + const struct vadc_channel_prop *prop, + s64 *scale_voltage) +{ + *scale_voltage = (adc_code - + vadc->graph[prop->calibration].gnd); + *scale_voltage *= vadc->graph[prop->calibration].dx; + *scale_voltage = div64_s64(*scale_voltage, + vadc->graph[prop->calibration].dy); + if (prop->calibration == VADC_CALIB_ABSOLUTE) + *scale_voltage += + vadc->graph[prop->calibration].dx; + + if (*scale_voltage < 0) + *scale_voltage = 0; +} + +static int vadc_scale_volt(struct vadc_priv *vadc, + const struct vadc_channel_prop *prop, u16 adc_code, + int *result_uv) { const struct vadc_prescale_ratio *prescale; - s64 voltage; + s64 voltage = 0, result = 0; - voltage = adc_code - vadc->graph[prop->calibration].gnd; - voltage *= vadc->graph[prop->calibration].dx; - voltage = div64_s64(voltage, vadc->graph[prop->calibration].dy); - - if (prop->calibration == VADC_CALIB_ABSOLUTE) - voltage += vadc->graph[prop->calibration].dx; - - if (voltage < 0) - voltage = 0; + vadc_scale_calib(vadc, adc_code, prop, &voltage); prescale = &vadc_prescale_ratios[prop->prescale]; - voltage = voltage * prescale->den; + result = div64_s64(voltage, prescale->num); + *result_uv = result; - return div64_s64(voltage, prescale->num); + return 0; +} + +static int vadc_scale_therm(struct vadc_priv *vadc, + const struct vadc_channel_prop *prop, u16 adc_code, + int *result_mdec) +{ + s64 voltage = 0, result = 0; + + vadc_scale_calib(vadc, adc_code, prop, &voltage); + + if (prop->calibration == VADC_CALIB_ABSOLUTE) + voltage = div64_s64(voltage, 1000); + + vadc_map_voltage_temp(adcmap_100k_104ef_104fb, + ARRAY_SIZE(adcmap_100k_104ef_104fb), + voltage, &result); + result *= 1000; + *result_mdec = result; + + return 0; +} + +static int vadc_scale_die_temp(struct vadc_priv *vadc, + const struct vadc_channel_prop *prop, + u16 adc_code, int *result_mdec) +{ + const struct vadc_prescale_ratio *prescale; + s64 voltage = 0; + u64 temp; /* Temporary variable for do_div */ + + vadc_scale_calib(vadc, adc_code, prop, &voltage); + + if (voltage > 0) { + prescale = &vadc_prescale_ratios[prop->prescale]; + temp = voltage * prescale->den; + do_div(temp, prescale->num * 2); + voltage = temp; + } else { + voltage = 0; + } + + voltage -= KELVINMIL_CELSIUSMIL; + *result_mdec = voltage; + + return 0; +} + +static int vadc_scale_chg_temp(struct vadc_priv *vadc, + const struct vadc_channel_prop *prop, + u16 adc_code, int *result_mdec) +{ + const struct vadc_prescale_ratio *prescale; + s64 voltage = 0, result = 0; + + vadc_scale_calib(vadc, adc_code, prop, &voltage); + + prescale = &vadc_prescale_ratios[prop->prescale]; + voltage = voltage * prescale->den; + voltage = div64_s64(voltage, prescale->num); + voltage = ((PMI_CHG_SCALE_1) * (voltage * 2)); + voltage = (voltage + PMI_CHG_SCALE_2); + result = div64_s64(voltage, 1000000); + *result_mdec = result; + + return 0; } static int vadc_decimation_from_dt(u32 value) @@ -536,6 +742,14 @@ static int vadc_avg_samples_from_dt(u32 value) return __ffs64(value); } +static struct vadc_scale_fn scale_fn[] = { + [SCALE_DEFAULT] = {vadc_scale_volt}, + [SCALE_THERM_100K_PULLUP] = {vadc_scale_therm}, + [SCALE_PMIC_THERM] = {vadc_scale_die_temp}, + [SCALE_XOTHERM] = {vadc_scale_therm}, + [SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp}, +}; + static int vadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -552,11 +766,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev, if (ret) break; - *val = vadc_calibrate(vadc, prop, adc_code); + scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val); - /* 2mV/K, return milli Celsius */ - *val /= 2; - *val -= KELVINMIL_CELSIUSMIL; return IIO_VAL_INT; case IIO_CHAN_INFO_RAW: prop = &vadc->chan_props[chan->address]; @@ -564,12 +775,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev, if (ret) break; - *val = vadc_calibrate(vadc, prop, adc_code); + *val = (int)adc_code; return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = 1000; - return IIO_VAL_INT_PLUS_MICRO; default: ret = -EINVAL; break; @@ -602,22 +809,39 @@ struct vadc_channels { unsigned int prescale_index; enum iio_chan_type type; long info_mask; + unsigned int scale_fn; }; -#define VADC_CHAN(_dname, _type, _mask, _pre) \ +#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \ + [VADC_##_dname] = { \ + .datasheet_name = __stringify(_dname), \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn = _scale \ + }, \ + +#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \ [VADC_##_dname] = { \ .datasheet_name = __stringify(_dname), \ .prescale_index = _pre, \ .type = _type, \ .info_mask = _mask \ - }, \ + }, -#define VADC_CHAN_TEMP(_dname, _pre) \ - VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \ +#define VADC_CHAN_TEMP(_dname, _pre, _scale) \ + VADC_CHAN(_dname, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ -#define VADC_CHAN_VOLT(_dname, _pre) \ +#define VADC_CHAN_VOLT(_dname, _pre, _scale) \ VADC_CHAN(_dname, IIO_VOLTAGE, \ - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ + _pre, _scale) \ + +#define VADC_CHAN_NO_SCALE(_dname, _pre) \ + VADC_NO_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_RAW), \ _pre) \ /* @@ -626,106 +850,106 @@ struct vadc_channels { * gaps in the array should be treated as reserved channels. */ static const struct vadc_channels vadc_chans[] = { - VADC_CHAN_VOLT(USBIN, 4) - VADC_CHAN_VOLT(DCIN, 4) - VADC_CHAN_VOLT(VCHG_SNS, 3) - VADC_CHAN_VOLT(SPARE1_03, 1) - VADC_CHAN_VOLT(USB_ID_MV, 1) - VADC_CHAN_VOLT(VCOIN, 1) - VADC_CHAN_VOLT(VBAT_SNS, 1) - VADC_CHAN_VOLT(VSYS, 1) - VADC_CHAN_TEMP(DIE_TEMP, 0) - VADC_CHAN_VOLT(REF_625MV, 0) - VADC_CHAN_VOLT(REF_1250MV, 0) - VADC_CHAN_VOLT(CHG_TEMP, 0) - VADC_CHAN_VOLT(SPARE1, 0) - VADC_CHAN_VOLT(SPARE2, 0) - VADC_CHAN_VOLT(GND_REF, 0) - VADC_CHAN_VOLT(VDD_VADC, 0) + VADC_CHAN_VOLT(USBIN, 4, SCALE_DEFAULT) + VADC_CHAN_VOLT(DCIN, 4, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(VCHG_SNS, 3) + VADC_CHAN_NO_SCALE(SPARE1_03, 1) + VADC_CHAN_NO_SCALE(USB_ID_MV, 1) + VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(VBAT_SNS, 1) + VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT) + VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM) + VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT) + VADC_CHAN_VOLT(REF_1250MV, 0, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(CHG_TEMP, 0) + VADC_CHAN_NO_SCALE(SPARE1, 0) + VADC_CHAN_TEMP(SPARE2, 0, SCALE_PMI_CHG_TEMP) + VADC_CHAN_VOLT(GND_REF, 0, SCALE_DEFAULT) + VADC_CHAN_VOLT(VDD_VADC, 0, SCALE_DEFAULT) - VADC_CHAN_VOLT(P_MUX1_1_1, 0) - VADC_CHAN_VOLT(P_MUX2_1_1, 0) - VADC_CHAN_VOLT(P_MUX3_1_1, 0) - VADC_CHAN_VOLT(P_MUX4_1_1, 0) - VADC_CHAN_VOLT(P_MUX5_1_1, 0) - VADC_CHAN_VOLT(P_MUX6_1_1, 0) - VADC_CHAN_VOLT(P_MUX7_1_1, 0) - VADC_CHAN_VOLT(P_MUX8_1_1, 0) - VADC_CHAN_VOLT(P_MUX9_1_1, 0) - VADC_CHAN_VOLT(P_MUX10_1_1, 0) - VADC_CHAN_VOLT(P_MUX11_1_1, 0) - VADC_CHAN_VOLT(P_MUX12_1_1, 0) - VADC_CHAN_VOLT(P_MUX13_1_1, 0) - VADC_CHAN_VOLT(P_MUX14_1_1, 0) - VADC_CHAN_VOLT(P_MUX15_1_1, 0) - VADC_CHAN_VOLT(P_MUX16_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX1_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX2_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX3_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX4_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX5_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX6_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX7_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX8_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX9_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX10_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX11_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX12_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX13_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX14_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX15_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX16_1_1, 0) - VADC_CHAN_VOLT(P_MUX1_1_3, 1) - VADC_CHAN_VOLT(P_MUX2_1_3, 1) - VADC_CHAN_VOLT(P_MUX3_1_3, 1) - VADC_CHAN_VOLT(P_MUX4_1_3, 1) - VADC_CHAN_VOLT(P_MUX5_1_3, 1) - VADC_CHAN_VOLT(P_MUX6_1_3, 1) - VADC_CHAN_VOLT(P_MUX7_1_3, 1) - VADC_CHAN_VOLT(P_MUX8_1_3, 1) - VADC_CHAN_VOLT(P_MUX9_1_3, 1) - VADC_CHAN_VOLT(P_MUX10_1_3, 1) - VADC_CHAN_VOLT(P_MUX11_1_3, 1) - VADC_CHAN_VOLT(P_MUX12_1_3, 1) - VADC_CHAN_VOLT(P_MUX13_1_3, 1) - VADC_CHAN_VOLT(P_MUX14_1_3, 1) - VADC_CHAN_VOLT(P_MUX15_1_3, 1) - VADC_CHAN_VOLT(P_MUX16_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX1_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX2_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX3_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX4_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX5_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX6_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX7_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX8_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX9_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX10_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX11_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX12_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX13_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX14_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX15_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX16_1_3, 1) - VADC_CHAN_VOLT(LR_MUX1_BAT_THERM, 0) - VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0) - VADC_CHAN_VOLT(LR_MUX3_XO_THERM, 0) - VADC_CHAN_VOLT(LR_MUX4_AMUX_THM1, 0) - VADC_CHAN_VOLT(LR_MUX5_AMUX_THM2, 0) - VADC_CHAN_VOLT(LR_MUX6_AMUX_THM3, 0) - VADC_CHAN_VOLT(LR_MUX7_HW_ID, 0) - VADC_CHAN_VOLT(LR_MUX8_AMUX_THM4, 0) - VADC_CHAN_VOLT(LR_MUX9_AMUX_THM5, 0) - VADC_CHAN_VOLT(LR_MUX10_USB_ID, 0) - VADC_CHAN_VOLT(AMUX_PU1, 0) - VADC_CHAN_VOLT(AMUX_PU2, 0) - VADC_CHAN_VOLT(LR_MUX3_BUF_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX1_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_USB_ID, 0) + VADC_CHAN_NO_SCALE(AMUX_PU1, 0) + VADC_CHAN_NO_SCALE(AMUX_PU2, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_XO_THERM, 0) - VADC_CHAN_VOLT(LR_MUX1_PU1_BAT_THERM, 0) - VADC_CHAN_VOLT(LR_MUX2_PU1_BAT_ID, 0) - VADC_CHAN_VOLT(LR_MUX3_PU1_XO_THERM, 0) - VADC_CHAN_VOLT(LR_MUX4_PU1_AMUX_THM1, 0) - VADC_CHAN_VOLT(LR_MUX5_PU1_AMUX_THM2, 0) - VADC_CHAN_VOLT(LR_MUX6_PU1_AMUX_THM3, 0) - VADC_CHAN_VOLT(LR_MUX7_PU1_AMUX_HW_ID, 0) - VADC_CHAN_VOLT(LR_MUX8_PU1_AMUX_THM4, 0) - VADC_CHAN_VOLT(LR_MUX9_PU1_AMUX_THM5, 0) - VADC_CHAN_VOLT(LR_MUX10_PU1_AMUX_USB_ID, 0) - VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX1_PU1_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU1_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU1_XO_THERM, 0) + VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_NO_SCALE(LR_MUX7_PU1_AMUX_HW_ID, 0) + VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_NO_SCALE(LR_MUX10_PU1_AMUX_USB_ID, 0) + VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0, SCALE_XOTHERM) - VADC_CHAN_VOLT(LR_MUX1_PU2_BAT_THERM, 0) - VADC_CHAN_VOLT(LR_MUX2_PU2_BAT_ID, 0) - VADC_CHAN_VOLT(LR_MUX3_PU2_XO_THERM, 0) - VADC_CHAN_VOLT(LR_MUX4_PU2_AMUX_THM1, 0) - VADC_CHAN_VOLT(LR_MUX5_PU2_AMUX_THM2, 0) - VADC_CHAN_VOLT(LR_MUX6_PU2_AMUX_THM3, 0) - VADC_CHAN_VOLT(LR_MUX7_PU2_AMUX_HW_ID, 0) - VADC_CHAN_VOLT(LR_MUX8_PU2_AMUX_THM4, 0) - VADC_CHAN_VOLT(LR_MUX9_PU2_AMUX_THM5, 0) - VADC_CHAN_VOLT(LR_MUX10_PU2_AMUX_USB_ID, 0) - VADC_CHAN_VOLT(LR_MUX3_BUF_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX1_PU2_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU2_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_PU2_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_PU2_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_PU2_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_PU2_AMUX_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_PU2_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_PU2_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_PU2_AMUX_USB_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU2_XO_THERM, 0) - VADC_CHAN_VOLT(LR_MUX1_PU1_PU2_BAT_THERM, 0) - VADC_CHAN_VOLT(LR_MUX2_PU1_PU2_BAT_ID, 0) - VADC_CHAN_VOLT(LR_MUX3_PU1_PU2_XO_THERM, 0) - VADC_CHAN_VOLT(LR_MUX4_PU1_PU2_AMUX_THM1, 0) - VADC_CHAN_VOLT(LR_MUX5_PU1_PU2_AMUX_THM2, 0) - VADC_CHAN_VOLT(LR_MUX6_PU1_PU2_AMUX_THM3, 0) - VADC_CHAN_VOLT(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0) - VADC_CHAN_VOLT(LR_MUX8_PU1_PU2_AMUX_THM4, 0) - VADC_CHAN_VOLT(LR_MUX9_PU1_PU2_AMUX_THM5, 0) - VADC_CHAN_VOLT(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0) - VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX1_PU1_PU2_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU1_PU2_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU1_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_PU1_PU2_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_PU1_PU2_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_PU1_PU2_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_PU1_PU2_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_PU1_PU2_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0) }; static int vadc_get_dt_channel_data(struct device *dev, @@ -844,6 +1068,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node) return ret; } + prop.scale_fn = vadc_chans[prop.channel].scale_fn; vadc->chan_props[index] = prop; vadc_chan = &vadc_chans[prop.channel]; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c new file mode 100644 index 000000000000..0c44f72c32a8 --- /dev/null +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -0,0 +1,631 @@ +/* + * Renesas R-Car GyroADC driver + * + * Copyright 2016 Marek Vasut + * + * 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 + +#define DRIVER_NAME "rcar-gyroadc" + +/* GyroADC registers. */ +#define RCAR_GYROADC_MODE_SELECT 0x00 +#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0 +#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1 +#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3 + +#define RCAR_GYROADC_START_STOP 0x04 +#define RCAR_GYROADC_START_STOP_START BIT(0) + +#define RCAR_GYROADC_CLOCK_LENGTH 0x08 +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c + +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4)) +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4)) +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4)) + +#define RCAR_GYROADC_FIFO_STATUS 0x70 +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch))) +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch))) +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch))) + +#define RCAR_GYROADC_INTR 0x74 +#define RCAR_GYROADC_INTR_INT BIT(0) + +#define RCAR_GYROADC_INTENR 0x78 +#define RCAR_GYROADC_INTENR_INTEN BIT(0) + +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */ + +#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000 + +enum rcar_gyroadc_model { + RCAR_GYROADC_MODEL_DEFAULT, + RCAR_GYROADC_MODEL_R8A7792, +}; + +struct rcar_gyroadc { + struct device *dev; + void __iomem *regs; + struct clk *iclk; + struct regulator *vref[8]; + unsigned int num_channels; + enum rcar_gyroadc_model model; + unsigned int mode; + unsigned int sample_width; +}; + +static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv) +{ + const unsigned long clk_mhz = clk_get_rate(priv->iclk) / 1000000; + const unsigned long clk_mul = + (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5; + unsigned long clk_len = clk_mhz * clk_mul; + + /* + * According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014, + * page 77-7, clock length must be even number. If it's odd number, + * add one. + */ + if (clk_len & 1) + clk_len++; + + /* Stop the GyroADC. */ + writel(0, priv->regs + RCAR_GYROADC_START_STOP); + + /* Disable IRQ on V2H. */ + if (priv->model == RCAR_GYROADC_MODEL_R8A7792) + writel(0, priv->regs + RCAR_GYROADC_INTENR); + + /* Set mode and timing. */ + writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT); + writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH); + writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH); +} + +static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv) +{ + /* Start sampling. */ + writel(RCAR_GYROADC_START_STOP_START, + priv->regs + RCAR_GYROADC_START_STOP); + + /* + * Wait for the first conversion to complete. This is longer than + * the 1.25 mS in the datasheet because 1.25 mS is not enough for + * the hardware to deliver the first sample and the hardware does + * then return zeroes instead of valid data. + */ + mdelay(3); +} + +static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv) +{ + /* Stop the GyroADC. */ + writel(0, priv->regs + RCAR_GYROADC_START_STOP); +} + +#define RCAR_GYROADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), +}; + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), + RCAR_GYROADC_CHAN(4), + RCAR_GYROADC_CHAN(5), + RCAR_GYROADC_CHAN(6), + RCAR_GYROADC_CHAN(7), +}; + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), + RCAR_GYROADC_CHAN(4), + RCAR_GYROADC_CHAN(5), + RCAR_GYROADC_CHAN(6), + RCAR_GYROADC_CHAN(7), +}; + +static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on) +{ + struct device *dev = priv->dev; + int ret; + + 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 rcar_gyroadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct regulator *consumer; + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel); + unsigned int vref; + int ret; + + /* + * MB88101 is special in that it has only single regulator for + * all four channels. + */ + if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) + consumer = priv->vref[0]; + else + consumer = priv->vref[chan->channel]; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + /* Channel not connected. */ + if (!consumer) + return -EINVAL; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = rcar_gyroadc_set_power(priv, true); + if (ret < 0) { + iio_device_release_direct_mode(indio_dev); + return ret; + } + + *val = readl(priv->regs + datareg); + *val &= BIT(priv->sample_width) - 1; + + ret = rcar_gyroadc_set_power(priv, false); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Channel not connected. */ + if (!consumer) + return -EINVAL; + + vref = regulator_get_voltage(consumer); + *val = vref / 1000; + *val2 = 1 << priv->sample_width; + + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = RCAR_GYROADC_SAMPLE_RATE; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS; + + if (readval == NULL) + return -EINVAL; + + if (reg % 4) + return -EINVAL; + + /* Handle the V2H case with extra interrupt block. */ + if (priv->model == RCAR_GYROADC_MODEL_R8A7792) + maxreg = RCAR_GYROADC_INTENR; + + if (reg > maxreg) + return -EINVAL; + + *readval = readl(priv->regs + reg); + + return 0; +} + +static const struct iio_info rcar_gyroadc_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = rcar_gyroadc_read_raw, + .debugfs_reg_access = rcar_gyroadc_reg_access, +}; + +static const struct of_device_id rcar_gyroadc_match[] = { + { + /* R-Car compatible GyroADC */ + .compatible = "renesas,rcar-gyroadc", + .data = (void *)RCAR_GYROADC_MODEL_DEFAULT, + }, { + /* R-Car V2H specialty with interrupt registers. */ + .compatible = "renesas,r8a7792-gyroadc", + .data = (void *)RCAR_GYROADC_MODEL_R8A7792, + }, { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(of, rcar_gyroadc_match); + +static const struct of_device_id rcar_gyroadc_child_match[] = { + /* Mode 1 ADCs */ + { + .compatible = "fujitsu,mb88101a", + .data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A, + }, + /* Mode 2 ADCs */ + { + .compatible = "ti,adcs7476", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, { + .compatible = "ti,adc121", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, { + .compatible = "adi,ad7476", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, + /* Mode 3 ADCs */ + { + .compatible = "maxim,max1162", + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162, + }, { + .compatible = "maxim,max11100", + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162, + }, + { /* sentinel */ } +}; + +static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) +{ + const struct of_device_id *of_id; + const struct iio_chan_spec *channels; + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + struct regulator *vref; + unsigned int reg; + unsigned int adcmode, childmode; + unsigned int sample_width; + unsigned int num_channels; + int ret, first = 1; + + for_each_child_of_node(np, child) { + of_id = of_match_node(rcar_gyroadc_child_match, child); + if (!of_id) { + dev_err(dev, "Ignoring unsupported ADC \"%s\".", + child->name); + continue; + } + + childmode = (unsigned int)of_id->data; + switch (childmode) { + case RCAR_GYROADC_MODE_SELECT_1_MB88101A: + sample_width = 12; + channels = rcar_gyroadc_iio_channels_1; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1); + break; + case RCAR_GYROADC_MODE_SELECT_2_ADCS7476: + sample_width = 15; + channels = rcar_gyroadc_iio_channels_2; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2); + break; + case RCAR_GYROADC_MODE_SELECT_3_MAX1162: + sample_width = 16; + channels = rcar_gyroadc_iio_channels_3; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3); + break; + } + + /* + * MB88101 is special in that it's only a single chip taking + * up all the CHS lines. Thus, the DT binding is also special + * and has no reg property. If we run into such ADC, handle + * it here. + */ + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) { + reg = 0; + } else { + ret = of_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, + "Failed to get child reg property of ADC \"%s\".\n", + child->name); + return ret; + } + + /* Channel number is too high. */ + if (reg >= num_channels) { + dev_err(dev, + "Only %i channels supported with %s, but reg = <%i>.\n", + num_channels, child->name, reg); + return ret; + } + } + + /* Child node selected different mode than the rest. */ + if (!first && (adcmode != childmode)) { + dev_err(dev, + "Channel %i uses different ADC mode than the rest.\n", + reg); + return ret; + } + + /* Channel is valid, grab the regulator. */ + dev->of_node = child; + vref = devm_regulator_get(dev, "vref"); + dev->of_node = np; + if (IS_ERR(vref)) { + dev_dbg(dev, "Channel %i 'vref' supply not connected.\n", + reg); + return PTR_ERR(vref); + } + + priv->vref[reg] = vref; + + if (!first) + continue; + + /* First child node which passed sanity tests. */ + adcmode = childmode; + first = 0; + + priv->num_channels = num_channels; + priv->mode = childmode; + priv->sample_width = sample_width; + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + + /* + * MB88101 is special and we only have one such device + * attached to the GyroADC at a time, so if we found it, + * we can stop parsing here. + */ + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) + break; + } + + if (first) { + dev_err(dev, "No valid ADC channels found, aborting.\n"); + return -EINVAL; + } + + return 0; +} + +static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + unsigned int i; + + for (i = 0; i < priv->num_channels; i++) { + if (!priv->vref[i]) + continue; + + regulator_disable(priv->vref[i]); + } +} + +static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + unsigned int i; + int ret; + + for (i = 0; i < priv->num_channels; i++) { + if (!priv->vref[i]) + continue; + + ret = regulator_enable(priv->vref[i]); + if (ret) { + dev_err(dev, "Failed to enable regulator %i (ret=%i)\n", + i, ret); + goto err; + } + } + + return 0; + +err: + rcar_gyroadc_deinit_supplies(indio_dev); + return ret; +} + +static int rcar_gyroadc_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(rcar_gyroadc_match, &pdev->dev); + struct device *dev = &pdev->dev; + struct rcar_gyroadc *priv; + struct iio_dev *indio_dev; + struct resource *mem; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) { + dev_err(dev, "Failed to allocate IIO device.\n"); + return -ENOMEM; + } + + priv = iio_priv(indio_dev); + priv->dev = dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->iclk = devm_clk_get(dev, "if"); + if (IS_ERR(priv->iclk)) { + ret = PTR_ERR(priv->iclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get IF clock (ret=%i)\n", ret); + return ret; + } + + ret = rcar_gyroadc_parse_subdevs(indio_dev); + if (ret) + return ret; + + ret = rcar_gyroadc_init_supplies(indio_dev); + if (ret) + return ret; + + priv->model = (enum rcar_gyroadc_model)of_id->data; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = DRIVER_NAME; + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &rcar_gyroadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = clk_prepare_enable(priv->iclk); + if (ret) { + dev_err(dev, "Could not prepare or enable the IF clock.\n"); + goto err_clk_if_enable; + } + + pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + pm_runtime_get_sync(dev); + rcar_gyroadc_hw_init(priv); + rcar_gyroadc_hw_start(priv); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Couldn't register IIO device.\n"); + goto err_iio_device_register; + } + + pm_runtime_put_sync(dev); + + return 0; + +err_iio_device_register: + rcar_gyroadc_hw_stop(priv); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + clk_disable_unprepare(priv->iclk); +err_clk_if_enable: + rcar_gyroadc_deinit_supplies(indio_dev); + + return ret; +} + +static int rcar_gyroadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + + iio_device_unregister(indio_dev); + pm_runtime_get_sync(dev); + rcar_gyroadc_hw_stop(priv); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + clk_disable_unprepare(priv->iclk); + rcar_gyroadc_deinit_supplies(indio_dev); + + return 0; +} + +#if defined(CONFIG_PM) +static int rcar_gyroadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + + rcar_gyroadc_hw_stop(priv); + + return 0; +} + +static int rcar_gyroadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + + rcar_gyroadc_hw_start(priv); + + return 0; +} +#endif + +static const struct dev_pm_ops rcar_gyroadc_pm_ops = { + SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL) +}; + +static struct platform_driver rcar_gyroadc_driver = { + .probe = rcar_gyroadc_probe, + .remove = rcar_gyroadc_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = rcar_gyroadc_match, + .pm = &rcar_gyroadc_pm_ops, + }, +}; + +module_platform_driver(rcar_gyroadc_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Renesas R-Car GyroADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 4214b0cd6b1b..22b7c9321e78 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -201,6 +201,7 @@ static int stm32_adc_probe(struct platform_device *pdev) priv->common.base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->common.base)) return PTR_ERR(priv->common.base); + priv->common.phys_base = res->start; priv->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(priv->vref)) { diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index 081fa5f55015..2ec7abbfbcaa 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -42,10 +42,12 @@ /** * struct stm32_adc_common - stm32 ADC driver common data (for all instances) * @base: control registers base cpu addr + * @phys_base: control registers base physical addr * @vref_mv: vref voltage (mv) */ struct stm32_adc_common { void __iomem *base; + phys_addr_t phys_base; int vref_mv; }; diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5715e79f4935..9b49a6addc2a 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -21,7 +21,14 @@ #include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include @@ -58,21 +65,71 @@ /* STM32F4_ADC_CR2 - bit fields */ #define STM32F4_SWSTART BIT(30) +#define STM32F4_EXTEN_SHIFT 28 #define STM32F4_EXTEN_MASK GENMASK(29, 28) +#define STM32F4_EXTSEL_SHIFT 24 +#define STM32F4_EXTSEL_MASK GENMASK(27, 24) #define STM32F4_EOCS BIT(10) +#define STM32F4_DDS BIT(9) +#define STM32F4_DMA BIT(8) #define STM32F4_ADON BIT(0) -/* STM32F4_ADC_SQR1 - bit fields */ -#define STM32F4_L_SHIFT 20 -#define STM32F4_L_MASK GENMASK(23, 20) - -/* STM32F4_ADC_SQR3 - bit fields */ -#define STM32F4_SQ1_SHIFT 0 -#define STM32F4_SQ1_MASK GENMASK(4, 0) - +#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ #define STM32_ADC_TIMEOUT_US 100000 #define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000)) +#define STM32_DMA_BUFFER_SIZE PAGE_SIZE + +/* External trigger enable */ +enum stm32_adc_exten { + STM32_EXTEN_SWTRIG, + STM32_EXTEN_HWTRIG_RISING_EDGE, + STM32_EXTEN_HWTRIG_FALLING_EDGE, + STM32_EXTEN_HWTRIG_BOTH_EDGES, +}; + +/* extsel - trigger mux selection value */ +enum stm32_adc_extsel { + STM32_EXT0, + STM32_EXT1, + STM32_EXT2, + STM32_EXT3, + STM32_EXT4, + STM32_EXT5, + STM32_EXT6, + STM32_EXT7, + STM32_EXT8, + STM32_EXT9, + STM32_EXT10, + STM32_EXT11, + STM32_EXT12, + STM32_EXT13, + STM32_EXT14, + STM32_EXT15, +}; + +/** + * struct stm32_adc_trig_info - ADC trigger info + * @name: name of the trigger, corresponding to its source + * @extsel: trigger selection + */ +struct stm32_adc_trig_info { + const char *name; + enum stm32_adc_extsel extsel; +}; + +/** + * stm32_adc_regs - stm32 ADC misc registers & bitfield desc + * @reg: register offset + * @mask: bitfield mask + * @shift: left shift + */ +struct stm32_adc_regs { + int reg; + int mask; + int shift; +}; + /** * struct stm32_adc - private data of each ADC IIO instance * @common: reference to ADC block common data @@ -82,15 +139,29 @@ * @clk: clock for this adc instance * @irq: interrupt for this adc instance * @lock: spinlock + * @bufi: data buffer index + * @num_conv: expected number of scan conversions + * @trigger_polarity: external trigger polarity (e.g. exten) + * @dma_chan: dma channel + * @rx_buf: dma rx buffer cpu address + * @rx_dma_buf: dma rx buffer bus address + * @rx_buf_sz: dma rx buffer size */ struct stm32_adc { struct stm32_adc_common *common; u32 offset; struct completion completion; - u16 *buffer; + u16 buffer[STM32_ADC_MAX_SQ]; struct clk *clk; int irq; spinlock_t lock; /* interrupt lock */ + unsigned int bufi; + unsigned int num_conv; + u32 trigger_polarity; + struct dma_chan *dma_chan; + u8 *rx_buf; + dma_addr_t rx_dma_buf; + unsigned int rx_buf_sz; }; /** @@ -125,6 +196,53 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = { { IIO_VOLTAGE, 15, "in15" }, }; +/** + * stm32f4_sq - describe regular sequence registers + * - L: sequence len (register & bit field) + * - SQ1..SQ16: sequence entries (register & bit field) + */ +static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = { + /* L: len bit field description to be kept as first element */ + { STM32F4_ADC_SQR1, GENMASK(23, 20), 20 }, + /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */ + { STM32F4_ADC_SQR3, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR3, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR3, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR3, GENMASK(19, 15), 15 }, + { STM32F4_ADC_SQR3, GENMASK(24, 20), 20 }, + { STM32F4_ADC_SQR3, GENMASK(29, 25), 25 }, + { STM32F4_ADC_SQR2, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR2, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR2, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR2, GENMASK(19, 15), 15 }, + { STM32F4_ADC_SQR2, GENMASK(24, 20), 20 }, + { STM32F4_ADC_SQR2, GENMASK(29, 25), 25 }, + { STM32F4_ADC_SQR1, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR1, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR1, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR1, GENMASK(19, 15), 15 }, +}; + +/* STM32F4 external trigger sources for all instances */ +static struct stm32_adc_trig_info stm32f4_adc_trigs[] = { + { TIM1_CH1, STM32_EXT0 }, + { TIM1_CH2, STM32_EXT1 }, + { TIM1_CH3, STM32_EXT2 }, + { TIM2_CH2, STM32_EXT3 }, + { TIM2_CH3, STM32_EXT4 }, + { TIM2_CH4, STM32_EXT5 }, + { TIM2_TRGO, STM32_EXT6 }, + { TIM3_CH1, STM32_EXT7 }, + { TIM3_TRGO, STM32_EXT8 }, + { TIM4_CH4, STM32_EXT9 }, + { TIM5_CH1, STM32_EXT10 }, + { TIM5_CH2, STM32_EXT11 }, + { TIM5_CH3, STM32_EXT12 }, + { TIM8_CH1, STM32_EXT13 }, + { TIM8_TRGO, STM32_EXT14 }, + {}, /* sentinel */ +}; + /** * STM32 ADC registers access routines * @adc: stm32 adc instance @@ -187,10 +305,21 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc) /** * stm32_adc_start_conv() - Start conversions for regular channels. * @adc: stm32 adc instance + * @dma: use dma to transfer conversion result + * + * Start conversions for regular channels. + * Also take care of normal or DMA mode. Circular DMA may be used for regular + * conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct + * DR read instead (e.g. read_raw, or triggered buffer mode without DMA). */ -static void stm32_adc_start_conv(struct stm32_adc *adc) +static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma) { stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); + + if (dma) + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, + STM32F4_DMA | STM32F4_DDS); + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON); /* Wait for Power-up time (tSTAB from datasheet) */ @@ -207,9 +336,152 @@ static void stm32_adc_stop_conv(struct stm32_adc *adc) stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT); stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); - stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON); + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, + STM32F4_ADON | STM32F4_DMA | STM32F4_DDS); } +/** + * stm32_adc_conf_scan_seq() - Build regular channels scan sequence + * @indio_dev: IIO device + * @scan_mask: channels to be converted + * + * Conversion sequence : + * Configure ADC scan sequence based on selected channels in scan_mask. + * Add channels to SQR registers, from scan_mask LSB to MSB, then + * program sequence len. + */ +static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + u32 val, bit; + int i = 0; + + for_each_set_bit(bit, scan_mask, indio_dev->masklength) { + chan = indio_dev->channels + bit; + /* + * Assign one channel per SQ entry in regular + * sequence, starting with SQ1. + */ + i++; + if (i > STM32_ADC_MAX_SQ) + return -EINVAL; + + dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n", + __func__, chan->channel, i); + + val = stm32_adc_readl(adc, stm32f4_sq[i].reg); + val &= ~stm32f4_sq[i].mask; + val |= chan->channel << stm32f4_sq[i].shift; + stm32_adc_writel(adc, stm32f4_sq[i].reg, val); + } + + if (!i) + return -EINVAL; + + /* Sequence len */ + val = stm32_adc_readl(adc, stm32f4_sq[0].reg); + val &= ~stm32f4_sq[0].mask; + val |= ((i - 1) << stm32f4_sq[0].shift); + stm32_adc_writel(adc, stm32f4_sq[0].reg, val); + + return 0; +} + +/** + * stm32_adc_get_trig_extsel() - Get external trigger selection + * @trig: trigger + * + * Returns trigger extsel value, if trig matches, -EINVAL otherwise. + */ +static int stm32_adc_get_trig_extsel(struct iio_trigger *trig) +{ + int i; + + /* lookup triggers registered by stm32 timer trigger driver */ + for (i = 0; stm32f4_adc_trigs[i].name; i++) { + /** + * Checking both stm32 timer trigger type and trig name + * should be safe against arbitrary trigger names. + */ + if (is_stm32_timer_trigger(trig) && + !strcmp(stm32f4_adc_trigs[i].name, trig->name)) { + return stm32f4_adc_trigs[i].extsel; + } + } + + return -EINVAL; +} + +/** + * stm32_adc_set_trig() - Set a regular trigger + * @indio_dev: IIO device + * @trig: IIO trigger + * + * Set trigger source/polarity (e.g. SW, or HW with polarity) : + * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw) + * - if HW trigger enabled, set source & polarity + */ +static int stm32_adc_set_trig(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG; + unsigned long flags; + int ret; + + if (trig) { + ret = stm32_adc_get_trig_extsel(trig); + if (ret < 0) + return ret; + + /* set trigger source and polarity (default to rising edge) */ + extsel = ret; + exten = adc->trigger_polarity + STM32_EXTEN_HWTRIG_RISING_EDGE; + } + + spin_lock_irqsave(&adc->lock, flags); + val = stm32_adc_readl(adc, STM32F4_ADC_CR2); + val &= ~(STM32F4_EXTEN_MASK | STM32F4_EXTSEL_MASK); + val |= exten << STM32F4_EXTEN_SHIFT; + val |= extsel << STM32F4_EXTSEL_SHIFT; + stm32_adc_writel(adc, STM32F4_ADC_CR2, val); + spin_unlock_irqrestore(&adc->lock, flags); + + return 0; +} + +static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int type) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + adc->trigger_polarity = type; + + return 0; +} + +static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + return adc->trigger_polarity; +} + +static const char * const stm32_trig_pol_items[] = { + "rising-edge", "falling-edge", "both-edges", +}; + +static const struct iio_enum stm32_adc_trig_pol = { + .items = stm32_trig_pol_items, + .num_items = ARRAY_SIZE(stm32_trig_pol_items), + .get = stm32_adc_get_trig_pol, + .set = stm32_adc_set_trig_pol, +}; + /** * stm32_adc_single_conv() - Performs a single conversion * @indio_dev: IIO device @@ -228,28 +500,27 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, struct stm32_adc *adc = iio_priv(indio_dev); long timeout; u32 val; - u16 result; int ret; reinit_completion(&adc->completion); - adc->buffer = &result; + adc->bufi = 0; - /* Program chan number in regular sequence */ - val = stm32_adc_readl(adc, STM32F4_ADC_SQR3); - val &= ~STM32F4_SQ1_MASK; - val |= chan->channel << STM32F4_SQ1_SHIFT; - stm32_adc_writel(adc, STM32F4_ADC_SQR3, val); + /* Program chan number in regular sequence (SQ1) */ + val = stm32_adc_readl(adc, stm32f4_sq[1].reg); + val &= ~stm32f4_sq[1].mask; + val |= chan->channel << stm32f4_sq[1].shift; + stm32_adc_writel(adc, stm32f4_sq[1].reg, val); /* Set regular sequence len (0 for 1 conversion) */ - stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK); + stm32_adc_clr_bits(adc, stm32f4_sq[0].reg, stm32f4_sq[0].mask); /* Trigger detection disabled (conversion can be launched in SW) */ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK); stm32_adc_conv_irq_enable(adc); - stm32_adc_start_conv(adc); + stm32_adc_start_conv(adc, false); timeout = wait_for_completion_interruptible_timeout( &adc->completion, STM32_ADC_TIMEOUT); @@ -258,7 +529,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, } else if (timeout < 0) { ret = timeout; } else { - *res = result; + *res = adc->buffer[0]; ret = IIO_VAL_INT; } @@ -301,17 +572,73 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, static irqreturn_t stm32_adc_isr(int irq, void *data) { struct stm32_adc *adc = data; + struct iio_dev *indio_dev = iio_priv_to_dev(adc); u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR); if (status & STM32F4_EOC) { - *adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR); - complete(&adc->completion); + /* Reading DR also clears EOC status flag */ + adc->buffer[adc->bufi] = stm32_adc_readw(adc, STM32F4_ADC_DR); + if (iio_buffer_enabled(indio_dev)) { + adc->bufi++; + if (adc->bufi >= adc->num_conv) { + stm32_adc_conv_irq_disable(adc); + iio_trigger_poll(indio_dev->trig); + } + } else { + complete(&adc->completion); + } return IRQ_HANDLED; } return IRQ_NONE; } +/** + * stm32_adc_validate_trigger() - validate trigger for stm32 adc + * @indio_dev: IIO device + * @trig: new trigger + * + * Returns: 0 if trig matches one of the triggers registered by stm32 adc + * driver, -EINVAL otherwise. + */ +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + return stm32_adc_get_trig_extsel(trig) < 0 ? -EINVAL : 0; +} + +static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + unsigned int watermark = STM32_DMA_BUFFER_SIZE / 2; + + /* + * dma cyclic transfers are used, buffer is split into two periods. + * There should be : + * - always one buffer (period) dma is working on + * - one buffer (period) driver can push with iio_trigger_poll(). + */ + watermark = min(watermark, val * (unsigned)(sizeof(u16))); + adc->rx_buf_sz = watermark * 2; + + return 0; +} + +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + + adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength); + + ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask); + if (ret) + return ret; + + return 0; +} + static int stm32_adc_of_xlate(struct iio_dev *indio_dev, const struct of_phandle_args *iiospec) { @@ -350,11 +677,199 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev, static const struct iio_info stm32_adc_iio_info = { .read_raw = stm32_adc_read_raw, + .validate_trigger = stm32_adc_validate_trigger, + .hwfifo_set_watermark = stm32_adc_set_watermark, + .update_scan_mode = stm32_adc_update_scan_mode, .debugfs_reg_access = stm32_adc_debugfs_reg_access, .of_xlate = stm32_adc_of_xlate, .driver_module = THIS_MODULE, }; +static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc) +{ + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(adc->dma_chan, + adc->dma_chan->cookie, + &state); + if (status == DMA_IN_PROGRESS) { + /* Residue is size in bytes from end of buffer */ + unsigned int i = adc->rx_buf_sz - state.residue; + unsigned int size; + + /* Return available bytes */ + if (i >= adc->bufi) + size = i - adc->bufi; + else + size = adc->rx_buf_sz + i - adc->bufi; + + return size; + } + + return 0; +} + +static void stm32_adc_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + + iio_trigger_poll_chained(indio_dev->trig); +} + +static int stm32_adc_dma_start(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + + if (!adc->dma_chan) + return 0; + + dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, + adc->rx_buf_sz, adc->rx_buf_sz / 2); + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(adc->dma_chan, + adc->rx_dma_buf, + adc->rx_buf_sz, adc->rx_buf_sz / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = stm32_adc_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_all(adc->dma_chan); + return ret; + } + + /* Issue pending DMA requests */ + dma_async_issue_pending(adc->dma_chan); + + return 0; +} + +static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + + ret = stm32_adc_set_trig(indio_dev, indio_dev->trig); + if (ret) { + dev_err(&indio_dev->dev, "Can't set trigger\n"); + return ret; + } + + ret = stm32_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "Can't start dma\n"); + goto err_clr_trig; + } + + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret < 0) + goto err_stop_dma; + + /* Reset adc buffer index */ + adc->bufi = 0; + + if (!adc->dma_chan) + stm32_adc_conv_irq_enable(adc); + + stm32_adc_start_conv(adc, !!adc->dma_chan); + + return 0; + +err_stop_dma: + if (adc->dma_chan) + dmaengine_terminate_all(adc->dma_chan); +err_clr_trig: + stm32_adc_set_trig(indio_dev, NULL); + + return ret; +} + +static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + + stm32_adc_stop_conv(adc); + if (!adc->dma_chan) + stm32_adc_conv_irq_disable(adc); + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret < 0) + dev_err(&indio_dev->dev, "predisable failed\n"); + + if (adc->dma_chan) + dmaengine_terminate_all(adc->dma_chan); + + if (stm32_adc_set_trig(indio_dev, NULL)) + dev_err(&indio_dev->dev, "Can't clear trigger\n"); + + return ret; +} + +static const struct iio_buffer_setup_ops stm32_adc_buffer_setup_ops = { + .postenable = &stm32_adc_buffer_postenable, + .predisable = &stm32_adc_buffer_predisable, +}; + +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct stm32_adc *adc = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi); + + if (!adc->dma_chan) { + /* reset buffer index */ + adc->bufi = 0; + iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer, + pf->timestamp); + } else { + int residue = stm32_adc_dma_residue(adc); + + while (residue >= indio_dev->scan_bytes) { + u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi]; + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + pf->timestamp); + residue -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->rx_buf_sz) + adc->bufi = 0; + } + } + + iio_trigger_notify_done(indio_dev->trig); + + /* re-enable eoc irq */ + if (!adc->dma_chan) + stm32_adc_conv_irq_enable(adc); + + return IRQ_HANDLED; +} + +static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = { + IIO_ENUM("trigger_polarity", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol), + { + .name = "trigger_polarity_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&stm32_adc_trig_pol, + }, + {}, +}; + static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_chan_spec *chan, const struct stm32_adc_chan_spec *channel, @@ -370,6 +885,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, chan->scan_type.sign = 'u'; chan->scan_type.realbits = 12; chan->scan_type.storagebits = 16; + chan->ext_info = stm32_adc_ext_info; } static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) @@ -410,6 +926,45 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) return 0; } +static int stm32_adc_dma_request(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct dma_slave_config config; + int ret; + + adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx"); + if (!adc->dma_chan) + return 0; + + adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + &adc->rx_dma_buf, GFP_KERNEL); + if (!adc->rx_buf) { + ret = -ENOMEM; + goto err_release; + } + + /* Configure DMA channel to read data register */ + memset(&config, 0, sizeof(config)); + config.src_addr = (dma_addr_t)adc->common->phys_base; + config.src_addr += adc->offset + STM32F4_ADC_DR; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + ret = dmaengine_slave_config(adc->dma_chan, &config); + if (ret) + goto err_free; + + return 0; + +err_free: + dma_free_coherent(adc->dma_chan->device->dev, STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); +err_release: + dma_release_channel(adc->dma_chan); + + return ret; +} + static int stm32_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -471,14 +1026,37 @@ static int stm32_adc_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_disable; + ret = stm32_adc_dma_request(indio_dev); + if (ret < 0) + goto err_clk_disable; + + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, + &stm32_adc_trigger_handler, + &stm32_adc_buffer_setup_ops); + if (ret) { + dev_err(&pdev->dev, "buffer setup failed\n"); + goto err_dma_disable; + } + ret = iio_device_register(indio_dev); if (ret) { dev_err(&pdev->dev, "iio dev register failed\n"); - goto err_clk_disable; + goto err_buffer_cleanup; } return 0; +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); + +err_dma_disable: + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); + dma_release_channel(adc->dma_chan); + } err_clk_disable: clk_disable_unprepare(adc->clk); @@ -491,6 +1069,13 @@ static int stm32_adc_remove(struct platform_device *pdev) struct iio_dev *indio_dev = iio_priv_to_dev(adc); iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); + dma_release_channel(adc->dma_chan); + } clk_disable_unprepare(adc->clk); return 0; diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c index 7e3645749eaf..be2de48844bc 100644 --- a/drivers/iio/adc/stx104.c +++ b/drivers/iio/adc/stx104.c @@ -76,16 +76,6 @@ struct stx104_gpio { unsigned int out_state; }; -/** - * struct stx104_dev - STX104 device private data structure - * @indio_dev: IIO device - * @chip: instance of the gpio_chip - */ -struct stx104_dev { - struct iio_dev *indio_dev; - struct gpio_chip *chip; -}; - static int stx104_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { @@ -266,12 +256,38 @@ static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, spin_unlock_irqrestore(&stx104gpio->lock, flags); } +#define STX104_NGPIO 8 +static const char *stx104_names[STX104_NGPIO] = { + "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3" +}; + +static void stx104_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + unsigned long flags; + + /* verify masked GPIO are output */ + if (!(*mask & 0xF0)) + return; + + *mask >>= 4; + *bits >>= 4; + + spin_lock_irqsave(&stx104gpio->lock, flags); + + stx104gpio->out_state &= ~*mask; + stx104gpio->out_state |= *mask & *bits; + outb(stx104gpio->out_state, stx104gpio->base); + + spin_unlock_irqrestore(&stx104gpio->lock, flags); +} + static int stx104_probe(struct device *dev, unsigned int id) { struct iio_dev *indio_dev; struct stx104_iio *priv; struct stx104_gpio *stx104gpio; - struct stx104_dev *stx104dev; int err; indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); @@ -282,10 +298,6 @@ static int stx104_probe(struct device *dev, unsigned int id) if (!stx104gpio) return -ENOMEM; - stx104dev = devm_kzalloc(dev, sizeof(*stx104dev), GFP_KERNEL); - if (!stx104dev) - 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", @@ -324,45 +336,26 @@ static int stx104_probe(struct device *dev, unsigned int id) stx104gpio->chip.parent = dev; stx104gpio->chip.owner = THIS_MODULE; stx104gpio->chip.base = -1; - stx104gpio->chip.ngpio = 8; + stx104gpio->chip.ngpio = STX104_NGPIO; + stx104gpio->chip.names = stx104_names; stx104gpio->chip.get_direction = stx104_gpio_get_direction; stx104gpio->chip.direction_input = stx104_gpio_direction_input; stx104gpio->chip.direction_output = stx104_gpio_direction_output; stx104gpio->chip.get = stx104_gpio_get; stx104gpio->chip.set = stx104_gpio_set; + stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; stx104gpio->base = base[id] + 3; stx104gpio->out_state = 0x0; spin_lock_init(&stx104gpio->lock); - stx104dev->indio_dev = indio_dev; - stx104dev->chip = &stx104gpio->chip; - dev_set_drvdata(dev, stx104dev); - - err = gpiochip_add_data(&stx104gpio->chip, stx104gpio); + err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); return err; } - err = iio_device_register(indio_dev); - if (err) { - dev_err(dev, "IIO device registering failed (%d)\n", err); - gpiochip_remove(&stx104gpio->chip); - return err; - } - - return 0; -} - -static int stx104_remove(struct device *dev, unsigned int id) -{ - struct stx104_dev *const stx104dev = dev_get_drvdata(dev); - - iio_device_unregister(stx104dev->indio_dev); - gpiochip_remove(stx104dev->chip); - - return 0; + return devm_iio_device_register(dev, indio_dev); } static struct isa_driver stx104_driver = { @@ -370,7 +363,6 @@ static struct isa_driver stx104_driver = { .driver = { .name = "stx104" }, - .remove = stx104_remove }; module_isa_driver(stx104_driver, num_stx104); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index cde6f130a99a..422b314f5a3f 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -472,14 +472,14 @@ static const struct attribute_group ads1115_attribute_group = { .attrs = ads1115_attributes, }; -static struct iio_info ads1015_info = { +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, }; -static struct iio_info ads1115_info = { +static const struct iio_info ads1115_info = { .driver_module = THIS_MODULE, .read_raw = ads1015_read_raw, .write_raw = ads1015_write_raw, diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c new file mode 100644 index 000000000000..16a06633332c --- /dev/null +++ b/drivers/iio/adc/ti-ads7950.c @@ -0,0 +1,490 @@ +/* + * Texas Instruments ADS7950 SPI ADC driver + * + * Copyright 2016 David Lechner + * + * Based on iio/ad7923.c: + * Copyright 2011 Analog Devices Inc + * Copyright 2012 CS Systemes d'Information + * + * And also on hwmon/ads79xx.c + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ + * Nishanth Menon + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TI_ADS7950_CR_MANUAL BIT(12) +#define TI_ADS7950_CR_WRITE BIT(11) +#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7) +#define TI_ADS7950_CR_RANGE_5V BIT(6) + +#define TI_ADS7950_MAX_CHAN 16 + +#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16)) + +/* val = value, dec = left shift, bits = number of bits of the mask */ +#define TI_ADS7950_EXTRACT(val, dec, bits) \ + (((val) >> (dec)) & ((1 << (bits)) - 1)) + +struct ti_ads7950_state { + struct spi_device *spi; + struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2]; + struct spi_transfer scan_single_xfer[3]; + struct spi_message ring_msg; + struct spi_message scan_single_msg; + + struct regulator *reg; + + unsigned int settings; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE] + ____cacheline_aligned; + __be16 tx_buf[TI_ADS7950_MAX_CHAN]; +}; + +struct ti_ads7950_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum ti_ads7950_id { + TI_ADS7950, + TI_ADS7951, + TI_ADS7952, + TI_ADS7953, + TI_ADS7954, + TI_ADS7955, + TI_ADS7956, + TI_ADS7957, + TI_ADS7958, + TI_ADS7959, + TI_ADS7960, + TI_ADS7961, +}; + +#define TI_ADS7950_V_CHAN(index, bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .datasheet_name = "CH##index", \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + .endianness = IIO_BE, \ + }, \ +} + +#define DECLARE_TI_ADS7950_4_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_TI_ADS7950_8_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +#define DECLARE_TI_ADS7950_12_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + TI_ADS7950_V_CHAN(8, bits), \ + TI_ADS7950_V_CHAN(9, bits), \ + TI_ADS7950_V_CHAN(10, bits), \ + TI_ADS7950_V_CHAN(11, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(12), \ +} + +#define DECLARE_TI_ADS7950_16_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + TI_ADS7950_V_CHAN(8, bits), \ + TI_ADS7950_V_CHAN(9, bits), \ + TI_ADS7950_V_CHAN(10, bits), \ + TI_ADS7950_V_CHAN(11, bits), \ + TI_ADS7950_V_CHAN(12, bits), \ + TI_ADS7950_V_CHAN(13, bits), \ + TI_ADS7950_V_CHAN(14, bits), \ + TI_ADS7950_V_CHAN(15, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(16), \ +} + +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7950, 12); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7951, 12); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7952, 12); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7953, 12); +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7954, 10); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7955, 10); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7956, 10); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7957, 10); +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7958, 8); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8); + +static const struct ti_ads7950_chip_info ti_ads7950_chip_info[] = { + [TI_ADS7950] = { + .channels = ti_ads7950_channels, + .num_channels = ARRAY_SIZE(ti_ads7950_channels), + }, + [TI_ADS7951] = { + .channels = ti_ads7951_channels, + .num_channels = ARRAY_SIZE(ti_ads7951_channels), + }, + [TI_ADS7952] = { + .channels = ti_ads7952_channels, + .num_channels = ARRAY_SIZE(ti_ads7952_channels), + }, + [TI_ADS7953] = { + .channels = ti_ads7953_channels, + .num_channels = ARRAY_SIZE(ti_ads7953_channels), + }, + [TI_ADS7954] = { + .channels = ti_ads7954_channels, + .num_channels = ARRAY_SIZE(ti_ads7954_channels), + }, + [TI_ADS7955] = { + .channels = ti_ads7955_channels, + .num_channels = ARRAY_SIZE(ti_ads7955_channels), + }, + [TI_ADS7956] = { + .channels = ti_ads7956_channels, + .num_channels = ARRAY_SIZE(ti_ads7956_channels), + }, + [TI_ADS7957] = { + .channels = ti_ads7957_channels, + .num_channels = ARRAY_SIZE(ti_ads7957_channels), + }, + [TI_ADS7958] = { + .channels = ti_ads7958_channels, + .num_channels = ARRAY_SIZE(ti_ads7958_channels), + }, + [TI_ADS7959] = { + .channels = ti_ads7959_channels, + .num_channels = ARRAY_SIZE(ti_ads7959_channels), + }, + [TI_ADS7960] = { + .channels = ti_ads7960_channels, + .num_channels = ARRAY_SIZE(ti_ads7960_channels), + }, + [TI_ADS7961] = { + .channels = ti_ads7961_channels, + .num_channels = ARRAY_SIZE(ti_ads7961_channels), + }, +}; + +/* + * ti_ads7950_update_scan_mode() setup the spi transfer buffer for the new + * scan mask + */ +static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + int i, cmd, len; + + len = 0; + for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) { + cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings; + st->tx_buf[len++] = cpu_to_be16(cmd); + } + + /* Data for the 1st channel is not returned until the 3rd transfer */ + len += 2; + for (i = 0; i < len; i++) { + if ((i + 2) < len) + st->ring_xfer[i].tx_buf = &st->tx_buf[i]; + if (i >= 2) + st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2]; + st->ring_xfer[i].len = 2; + st->ring_xfer[i].cs_change = 1; + } + /* make sure last transfer's cs_change is not set */ + st->ring_xfer[len - 1].cs_change = 0; + + spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len); + + return 0; +} + +static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ti_ads7950_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->ring_msg); + if (ret < 0) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ti_ads7950_scan_direct(struct ti_ads7950_state *st, unsigned int ch) +{ + int ret, cmd; + + cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings; + st->tx_buf[0] = cpu_to_be16(cmd); + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return be16_to_cpu(st->rx_buf[0]); +} + +static int ti_ads7950_get_range(struct ti_ads7950_state *st) +{ + int vref; + + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + + if (st->settings & TI_ADS7950_CR_RANGE_5V) + vref *= 2; + + return vref; +} + +static int ti_ads7950_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret < 0) + return ret; + + ret = ti_ads7950_scan_direct(st, chan->address); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + if (chan->address != TI_ADS7950_EXTRACT(ret, 12, 4)) + return -EIO; + + *val = TI_ADS7950_EXTRACT(ret, chan->scan_type.shift, + chan->scan_type.realbits); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = ti_ads7950_get_range(st); + if (ret < 0) + return ret; + + *val = ret; + *val2 = (1 << chan->scan_type.realbits) - 1; + + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static const struct iio_info ti_ads7950_info = { + .read_raw = &ti_ads7950_read_raw, + .update_scan_mode = ti_ads7950_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static int ti_ads7950_probe(struct spi_device *spi) +{ + struct ti_ads7950_state *st; + struct iio_dev *indio_dev; + const struct ti_ads7950_chip_info *info; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + st->settings = TI_ADS7950_CR_MANUAL | TI_ADS7950_CR_RANGE_5V; + + info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &ti_ads7950_info; + + /* + * Setup default message. The sample is read at the end of the first + * transfer, then it takes one full cycle to convert the sample and one + * more cycle to send the value. The conversion process is driven by + * the SPI clock, which is why we have 3 transfers. The middle one is + * just dummy data sent while the chip is converting the sample that + * was read at the end of the first transfer. + */ + + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[1].len = 2; + st->scan_single_xfer[1].cs_change = 1; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init_with_transfers(&st->scan_single_msg, + st->scan_single_xfer, 3); + + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) { + dev_err(&spi->dev, "Failed get get regulator \"vref\"\n"); + return PTR_ERR(st->reg); + } + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n"); + return ret; + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ti_ads7950_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "Failed to setup triggered buffer\n"); + goto error_disable_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device\n"); + goto error_cleanup_ring; + } + + return 0; + +error_cleanup_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int ti_ads7950_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ti_ads7950_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ti_ads7950_id[] = { + { "ads7950", TI_ADS7950 }, + { "ads7951", TI_ADS7951 }, + { "ads7952", TI_ADS7952 }, + { "ads7953", TI_ADS7953 }, + { "ads7954", TI_ADS7954 }, + { "ads7955", TI_ADS7955 }, + { "ads7956", TI_ADS7956 }, + { "ads7957", TI_ADS7957 }, + { "ads7958", TI_ADS7958 }, + { "ads7959", TI_ADS7959 }, + { "ads7960", TI_ADS7960 }, + { "ads7961", TI_ADS7961 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ti_ads7950_id); + +static struct spi_driver ti_ads7950_driver = { + .driver = { + .name = "ads7950", + }, + .probe = ti_ads7950_probe, + .remove = ti_ads7950_remove, + .id_table = ti_ads7950_id, +}; +module_spi_driver(ti_ads7950_driver); + +MODULE_AUTHOR("David Lechner "); +MODULE_DESCRIPTION("TI TI_ADS7950 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c new file mode 100644 index 000000000000..78d91a069ea4 --- /dev/null +++ b/drivers/iio/adc/ti-tlc4541.c @@ -0,0 +1,271 @@ +/* + * TI tlc4541 ADC Driver + * + * Copyright (C) 2017 Phil Reid + * + * Datasheets can be found here: + * http://www.ti.com/lit/gpn/tlc3541 + * http://www.ti.com/lit/gpn/tlc4541 + * + * 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. + * + * The tlc4541 requires 24 clock cycles to start a transfer. + * Conversion then takes 2.94us to complete before data is ready + * Data is returned MSB first. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tlc4541_state { + struct spi_device *spi; + struct regulator *reg; + struct spi_transfer scan_single_xfer[3]; + struct spi_message scan_single_msg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * 2 bytes data + 6 bytes padding + 8 bytes timestamp when + * call iio_push_to_buffers_with_timestamp. + */ + __be16 rx_buf[8] ____cacheline_aligned; +}; + +struct tlc4541_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum tlc4541_id { + TLC3541, + TLC4541, +}; + +#define TLC4541_V_CHAN(bits, bitshift) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = (bitshift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \ +const struct iio_chan_spec name ## _channels[] = { \ + TLC4541_V_CHAN(bits, bitshift), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2); +static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0); + +static const struct tlc4541_chip_info tlc4541_chip_info[] = { + [TLC3541] = { + .channels = tlc3541_channels, + .num_channels = ARRAY_SIZE(tlc3541_channels), + }, + [TLC4541] = { + .channels = tlc4541_channels, + .num_channels = ARRAY_SIZE(tlc4541_channels), + }, +}; + +static irqreturn_t tlc4541_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct tlc4541_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int tlc4541_get_range(struct tlc4541_state *st) +{ + int vref; + + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + + return vref; +} + +static int tlc4541_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret = 0; + struct tlc4541_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = spi_sync(st->spi, &st->scan_single_msg); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + *val = be16_to_cpu(st->rx_buf[0]); + *val = *val >> chan->scan_type.shift; + *val &= GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = tlc4541_get_range(st); + if (ret < 0) + return ret; + *val = ret; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +static const struct iio_info tlc4541_info = { + .read_raw = &tlc4541_read_raw, + .driver_module = THIS_MODULE, +}; + +static int tlc4541_probe(struct spi_device *spi) +{ + struct tlc4541_state *st; + struct iio_dev *indio_dev; + const struct tlc4541_chip_info *info; + int ret; + int8_t device_init = 0; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &tlc4541_info; + + /* perform reset */ + spi_write(spi, &device_init, 1); + + /* Setup default message */ + st->scan_single_xfer[0].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[0].len = 3; + st->scan_single_xfer[1].delay_usecs = 3; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init_with_transfers(&st->scan_single_msg, + st->scan_single_xfer, 3); + + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &tlc4541_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer; + + return 0; + +error_cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int tlc4541_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct tlc4541_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->reg); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id tlc4541_dt_ids[] = { + { .compatible = "ti,tlc3541", }, + { .compatible = "ti,tlc4541", }, + {} +}; +MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); +#endif + +static const struct spi_device_id tlc4541_id[] = { + {"tlc3541", TLC3541}, + {"tlc4541", TLC4541}, + {} +}; +MODULE_DEVICE_TABLE(spi, tlc4541_id); + +static struct spi_driver tlc4541_driver = { + .driver = { + .name = "tlc4541", + .of_match_table = of_match_ptr(tlc4541_dt_ids), + }, + .probe = tlc4541_probe, + .remove = tlc4541_remove, + .id_table = tlc4541_id, +}; +module_spi_driver(tlc4541_driver); + +MODULE_AUTHOR("Phil Reid "); +MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index b8f550e47d3d..4847534700e7 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -10,7 +10,8 @@ #include #include #include -#include +#include +#include #include struct iio_cb_buffer { diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index c5b999f0c519..047fe757ab97 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -5,7 +5,10 @@ #include #include #include +#include +#include #include +#include #include #include diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 7ef94a90ecf7..7afdac42ed42 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -58,6 +58,10 @@ static struct { {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0}, {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000}, + + {HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0}, + {HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND, + 1000000, 0}, }; static int pow_10(unsigned power) @@ -346,6 +350,13 @@ int hid_sensor_format_scale(u32 usage_id, } EXPORT_SYMBOL(hid_sensor_format_scale); +int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st, + int64_t raw_value) +{ + return st->timestamp_ns_scale * raw_value; +} +EXPORT_SYMBOL(hid_sensor_convert_timestamp); + static int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, u32 usage_id, @@ -367,6 +378,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, struct hid_sensor_common *st) { + struct hid_sensor_hub_attribute_info timestamp; hid_sensor_get_reporting_interval(hsdev, usage_id, st); @@ -385,11 +397,25 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, &st->sensitivity); - hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n", - st->poll.index, st->poll.report_id, - st->report_state.index, st->report_state.report_id, - st->power_state.index, st->power_state.report_id, - st->sensitivity.index, st->sensitivity.report_id); + sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, usage_id, + HID_USAGE_SENSOR_TIME_TIMESTAMP, + ×tamp); + if (timestamp.index >= 0 && timestamp.report_id) { + int val0, val1; + + hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP, + ×tamp, &val0, &val1); + st->timestamp_ns_scale = val0; + } else + st->timestamp_ns_scale = 1000000000; + + hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x %x:%x\n", + st->poll.index, st->poll.report_id, + st->report_state.index, st->report_state.report_id, + st->power_state.index, st->power_state.report_id, + st->sensitivity.index, st->sensitivity.report_id, + timestamp.index, timestamp.report_id); return 0; } diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c index a3ae165f8d9f..645f2e3975db 100644 --- a/drivers/iio/common/ssp_sensors/ssp_iio.c +++ b/drivers/iio/common/ssp_sensors/ssp_iio.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index b43aa36031f8..c83df4dbfcd7 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -107,6 +108,25 @@ void st_sensors_of_i2c_probe(struct i2c_client *client, EXPORT_SYMBOL(st_sensors_of_i2c_probe); #endif +#ifdef CONFIG_ACPI +int st_sensors_match_acpi_device(struct device *dev) +{ + const struct acpi_device_id *acpi_id; + kernel_ulong_t driver_data = 0; + + if (ACPI_HANDLE(dev)) { + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!acpi_id) { + dev_err(dev, "No driver data\n"); + return -EINVAL; + } + driver_data = acpi_id->driver_data; + } + return driver_data; +} +EXPORT_SYMBOL(st_sensors_match_acpi_device); +#endif + MODULE_AUTHOR("Denis Ciocca "); MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c index 6eed5b7729be..6a12a3c9d94f 100644 --- a/drivers/iio/dac/ad5592r.c +++ b/drivers/iio/dac/ad5592r.c @@ -13,6 +13,7 @@ #include #include #include +#include #define AD5592R_GPIO_READBACK_EN BIT(10) #define AD5592R_LDAC_READBACK_EN BIT(6) @@ -148,10 +149,17 @@ static const struct of_device_id ad5592r_of_match[] = { }; MODULE_DEVICE_TABLE(of, ad5592r_of_match); +static const struct acpi_device_id ad5592r_acpi_match[] = { + {"ADS5592", }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, ad5592r_acpi_match); + static struct spi_driver ad5592r_spi_driver = { .driver = { .name = "ad5592r", .of_match_table = of_match_ptr(ad5592r_of_match), + .acpi_match_table = ACPI_PTR(ad5592r_acpi_match), }, .probe = ad5592r_spi_probe, .remove = ad5592r_spi_remove, diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index dca158a88f47..fc11ea098f98 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -13,6 +13,7 @@ #include #include #include +#include #define AD5593R_MODE_CONF (0 << 4) #define AD5593R_MODE_DAC_WRITE (1 << 4) @@ -115,10 +116,17 @@ static const struct of_device_id ad5593r_of_match[] = { }; MODULE_DEVICE_TABLE(of, ad5593r_of_match); +static const struct acpi_device_id ad5593r_acpi_match[] = { + {"ADS5593", }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, ad5593r_acpi_match); + static struct i2c_driver ad5593r_driver = { .driver = { .name = "ad5593r", .of_match_table = of_match_ptr(ad5593r_of_match), + .acpi_match_table = ACPI_PTR(ad5593r_acpi_match), }, .probe = ad5593r_i2c_probe, .remove = ad5593r_i2c_remove, diff --git a/drivers/iio/dummy/iio_simple_dummy.h b/drivers/iio/dummy/iio_simple_dummy.h index b9069a180672..f7005c3f5df3 100644 --- a/drivers/iio/dummy/iio_simple_dummy.h +++ b/drivers/iio/dummy/iio_simple_dummy.h @@ -88,11 +88,11 @@ static inline int iio_simple_dummy_events_register(struct iio_dev *indio_dev) { return 0; -}; +} static inline void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) -{ }; +{} #endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/ @@ -119,11 +119,11 @@ void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev); static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) { return 0; -}; +} static inline void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) -{}; +{} #endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */ #endif /* _IIO_SIMPLE_DUMMY_H_ */ diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index b383892a5193..744ca92c3c99 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -20,6 +20,7 @@ #include #include +#include #include #include "iio_simple_dummy.h" @@ -131,9 +132,6 @@ int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) iio_device_attach_buffer(indio_dev, buffer); - /* Enable timestamps by default */ - buffer->scan_timestamp = true; - /* * Tell the core what device type specific functions should * be run on either side of buffer capture enable / disable. diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c index 1f25f406c545..2dacd8e01045 100644 --- a/drivers/iio/gyro/ssp_gyro_sensor.c +++ b/drivers/iio/gyro/ssp_gyro_sensor.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -134,7 +135,7 @@ static int ssp_gyro_probe(struct platform_device *pdev) platform_set_drvdata(pdev, indio_dev); - ret = iio_device_register(indio_dev); + ret = devm_iio_device_register(&pdev->dev, indio_dev); if (ret < 0) return ret; @@ -144,21 +145,11 @@ static int ssp_gyro_probe(struct platform_device *pdev) return 0; } -static int ssp_gyro_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - - return 0; -} - static struct platform_driver ssp_gyro_driver = { .driver = { .name = SSP_GYROSCOPE_NAME, }, .probe = ssp_gyro_probe, - .remove = ssp_gyro_remove, }; module_platform_driver(ssp_gyro_driver); diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 183c14329d6e..f6e283c4d686 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -378,7 +378,7 @@ static int max30100_get_temp(struct max30100_data *data, int *val) if (ret) return ret; - usleep_range(35000, 50000); + msleep(35); return max30100_read_temp(data, val); } diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c index 367ecd509f31..8333c0296c0e 100644 --- a/drivers/iio/humidity/hts221_i2c.c +++ b/drivers/iio/humidity/hts221_i2c.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include "hts221.h" @@ -83,6 +84,12 @@ static int hts221_i2c_probe(struct i2c_client *client, return hts221_probe(iio_dev); } +static const struct acpi_device_id hts221_acpi_match[] = { + {"SMO9100", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, hts221_acpi_match); + static const struct of_device_id hts221_i2c_of_match[] = { { .compatible = "st,hts221", }, {}, @@ -99,6 +106,7 @@ static struct i2c_driver hts221_driver = { .driver = { .name = "hts221_i2c", .of_match_table = of_match_ptr(hts221_i2c_of_match), + .acpi_match_table = ACPI_PTR(hts221_acpi_match), }, .probe = hts221_i2c_probe, .id_table = hts221_i2c_id_table, diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 1f1ad41ef881..156630a21696 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -39,6 +39,7 @@ config KMX61 be called kmx61. source "drivers/iio/imu/inv_mpu6050/Kconfig" +source "drivers/iio/imu/st_lsm6dsx/Kconfig" endmenu diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index c71bcd30dc38..8b563c3323b5 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -17,3 +17,5 @@ obj-y += bmi160/ obj-y += inv_mpu6050/ obj-$(CONFIG_KMX61) += kmx61.o + +obj-y += st_lsm6dsx/ diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index c9e319bff58b..cfd225ed1c8d 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -325,9 +325,9 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type, __le16 sample; enum bmi160_sensor_type t = bmi160_to_sensor(chan_type); - reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16); + reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample); - ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16)); + ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample)); if (ret < 0) return ret; @@ -392,8 +392,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p) for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) { - ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16), - &sample, sizeof(__le16)); + ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample), + &sample, sizeof(sample)); if (ret < 0) goto done; buf[j++] = sample; diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c index 07a179d8fb48..155a31f72445 100644 --- a/drivers/iio/imu/bmi160/bmi160_i2c.c +++ b/drivers/iio/imu/bmi160/bmi160_i2c.c @@ -11,10 +11,11 @@ * - 0x68 if SDO is pulled to GND * - 0x69 if SDO is pulled to VDDIO */ -#include -#include -#include #include +#include +#include +#include +#include #include "bmi160.h" @@ -56,10 +57,19 @@ static const struct acpi_device_id bmi160_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match); +#ifdef CONFIG_OF +static const struct of_device_id bmi160_of_match[] = { + { .compatible = "bosch,bmi160" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bmi160_of_match); +#endif + static struct i2c_driver bmi160_i2c_driver = { .driver = { .name = "bmi160_i2c", .acpi_match_table = ACPI_PTR(bmi160_acpi_match), + .of_match_table = of_match_ptr(bmi160_of_match), }, .probe = bmi160_i2c_probe, .remove = bmi160_i2c_remove, diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c index 1ec8b12bd984..d34dfdfd1a7d 100644 --- a/drivers/iio/imu/bmi160/bmi160_spi.c +++ b/drivers/iio/imu/bmi160/bmi160_spi.c @@ -7,10 +7,11 @@ * the GNU General Public License. See the file COPYING in the main * directory of this archive for more details. */ -#include -#include -#include #include +#include +#include +#include +#include #include "bmi160.h" @@ -47,13 +48,22 @@ static const struct acpi_device_id bmi160_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match); +#ifdef CONFIG_OF +static const struct of_device_id bmi160_of_match[] = { + { .compatible = "bosch,bmi160" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bmi160_of_match); +#endif + static struct spi_driver bmi160_spi_driver = { .probe = bmi160_spi_probe, .remove = bmi160_spi_remove, .id_table = bmi160_spi_id, .driver = { - .acpi_match_table = ACPI_PTR(bmi160_acpi_match), - .name = "bmi160_spi", + .acpi_match_table = ACPI_PTR(bmi160_acpi_match), + .of_match_table = of_match_ptr(bmi160_of_match), + .name = "bmi160_spi", }, }; module_spi_driver(bmi160_spi_driver); diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig new file mode 100644 index 000000000000..935d4cd071a3 --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -0,0 +1,22 @@ + +config IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors" + depends on (I2C || SPI) + select IIO_BUFFER + select IIO_KFIFO_BUF + select IIO_ST_LSM6DSX_I2C if (I2C) + select IIO_ST_LSM6DSX_SPI if (SPI_MASTER) + help + Say yes here to build support for STMicroelectronics LSM6DSx imu + sensor. Supported devices: lsm6ds3, lsm6dsm + + To compile this driver as a module, choose M here: the module + will be called st_lsm6dsx. + +config IIO_ST_LSM6DSX_I2C + tristate + depends on IIO_ST_LSM6DSX + +config IIO_ST_LSM6DSX_SPI + tristate + depends on IIO_ST_LSM6DSX diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile new file mode 100644 index 000000000000..35919febea2a --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/Makefile @@ -0,0 +1,5 @@ +st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o + +obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o +obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o +obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h new file mode 100644 index 000000000000..69deafe1c10d --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -0,0 +1,141 @@ +/* + * STMicroelectronics st_lsm6dsx sensor driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#ifndef ST_LSM6DSX_H +#define ST_LSM6DSX_H + +#include + +#define ST_LSM6DS3_DEV_NAME "lsm6ds3" +#define ST_LSM6DSM_DEV_NAME "lsm6dsm" + +enum st_lsm6dsx_hw_id { + ST_LSM6DS3_ID, + ST_LSM6DSM_ID, +}; + +#define ST_LSM6DSX_CHAN_SIZE 2 +#define ST_LSM6DSX_SAMPLE_SIZE 6 +#define ST_LSM6DSX_SAMPLE_DEPTH (ST_LSM6DSX_SAMPLE_SIZE / \ + ST_LSM6DSX_CHAN_SIZE) + +#if defined(CONFIG_SPI_MASTER) +#define ST_LSM6DSX_RX_MAX_LENGTH 256 +#define ST_LSM6DSX_TX_MAX_LENGTH 8 + +struct st_lsm6dsx_transfer_buffer { + u8 rx_buf[ST_LSM6DSX_RX_MAX_LENGTH]; + u8 tx_buf[ST_LSM6DSX_TX_MAX_LENGTH] ____cacheline_aligned; +}; +#endif /* CONFIG_SPI_MASTER */ + +struct st_lsm6dsx_transfer_function { + int (*read)(struct device *dev, u8 addr, int len, u8 *data); + int (*write)(struct device *dev, u8 addr, int len, u8 *data); +}; + +struct st_lsm6dsx_reg { + u8 addr; + u8 mask; +}; + +struct st_lsm6dsx_settings { + u8 wai; + u16 max_fifo_size; + enum st_lsm6dsx_hw_id id; +}; + +enum st_lsm6dsx_sensor_id { + ST_LSM6DSX_ID_ACC, + ST_LSM6DSX_ID_GYRO, + ST_LSM6DSX_ID_MAX, +}; + +enum st_lsm6dsx_fifo_mode { + ST_LSM6DSX_FIFO_BYPASS = 0x0, + ST_LSM6DSX_FIFO_CONT = 0x6, +}; + +/** + * struct st_lsm6dsx_sensor - ST IMU sensor instance + * @id: Sensor identifier. + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * @gain: Configured sensor sensitivity. + * @odr: Output data rate of the sensor [Hz]. + * @watermark: Sensor watermark level. + * @sip: Number of samples in a given pattern. + * @decimator: FIFO decimation factor. + * @decimator_mask: Sensor mask for decimation register. + * @delta_ts: Delta time between two consecutive interrupts. + * @ts: Latest timestamp from the interrupt handler. + */ +struct st_lsm6dsx_sensor { + enum st_lsm6dsx_sensor_id id; + struct st_lsm6dsx_hw *hw; + + u32 gain; + u16 odr; + + u16 watermark; + u8 sip; + u8 decimator; + u8 decimator_mask; + + s64 delta_ts; + s64 ts; +}; + +/** + * struct st_lsm6dsx_hw - ST IMU MEMS hw instance + * @dev: Pointer to instance of struct device (I2C or SPI). + * @irq: Device interrupt line (I2C or SPI). + * @lock: Mutex to protect read and write operations. + * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO. + * @fifo_mode: FIFO operating mode supported by the device. + * @enable_mask: Enabled sensor bitmask. + * @sip: Total number of samples (acc/gyro) in a given pattern. + * @iio_devs: Pointers to acc/gyro iio_dev instances. + * @settings: Pointer to the specific sensor settings in use. + * @tf: Transfer function structure used by I/O operations. + * @tb: Transfer buffers used by SPI I/O operations. + */ +struct st_lsm6dsx_hw { + struct device *dev; + int irq; + + struct mutex lock; + struct mutex fifo_lock; + + enum st_lsm6dsx_fifo_mode fifo_mode; + u8 enable_mask; + u8 sip; + + struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX]; + + const struct st_lsm6dsx_settings *settings; + + const struct st_lsm6dsx_transfer_function *tf; +#if defined(CONFIG_SPI_MASTER) + struct st_lsm6dsx_transfer_buffer tb; +#endif /* CONFIG_SPI_MASTER */ +}; + +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, + const struct st_lsm6dsx_transfer_function *tf_ops); +int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor); +int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor); +int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask, + u8 val); +int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, + u16 watermark); + +#endif /* ST_LSM6DSX_H */ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c new file mode 100644 index 000000000000..78532ce07449 --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -0,0 +1,454 @@ +/* + * STMicroelectronics st_lsm6dsx FIFO buffer library driver + * + * LSM6DS3/LSM6DSM: The FIFO buffer can be configured to store data + * from gyroscope and accelerometer. Samples are queued without any tag + * according to a specific pattern based on 'FIFO data sets' (6 bytes each): + * - 1st data set is reserved for gyroscope data + * - 2nd data set is reserved for accelerometer data + * The FIFO pattern changes depending on the ODRs and decimation factors + * assigned to the FIFO data sets. The first sequence of data stored in FIFO + * buffer contains the data of all the enabled FIFO data sets + * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the + * value of the decimation factor and ODR set for each FIFO data set. + * FIFO supported modes: + * - BYPASS: FIFO disabled + * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index + * restarts from the beginning and the oldest sample is overwritten + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ +#include +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_REG_FIFO_THL_ADDR 0x06 +#define ST_LSM6DSX_REG_FIFO_THH_ADDR 0x07 +#define ST_LSM6DSX_FIFO_TH_MASK GENMASK(11, 0) +#define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR 0x08 +#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a +#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0) +#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) +#define ST_LSM6DSX_REG_FIFO_DIFFL_ADDR 0x3a +#define ST_LSM6DSX_FIFO_DIFF_MASK GENMASK(11, 0) +#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12) +#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e + +#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 + +struct st_lsm6dsx_decimator_entry { + u8 decimator; + u8 val; +}; + +static const +struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = { + { 0, 0x0 }, + { 1, 0x1 }, + { 2, 0x2 }, + { 3, 0x3 }, + { 4, 0x4 }, + { 8, 0x5 }, + { 16, 0x6 }, + { 32, 0x7 }, +}; + +static int st_lsm6dsx_get_decimator_val(u8 val) +{ + const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table); + int i; + + for (i = 0; i < max_size; i++) + if (st_lsm6dsx_decimator_table[i].decimator == val) + break; + + return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val; +} + +static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw, + u16 *max_odr, u16 *min_odr) +{ + struct st_lsm6dsx_sensor *sensor; + int i; + + *max_odr = 0, *min_odr = ~0; + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + *max_odr = max_t(u16, *max_odr, sensor->odr); + *min_odr = min_t(u16, *min_odr, sensor->odr); + } +} + +static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) +{ + struct st_lsm6dsx_sensor *sensor; + u16 max_odr, min_odr, sip = 0; + int err, i; + u8 data; + + st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr); + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + + /* update fifo decimators and sample in pattern */ + if (hw->enable_mask & BIT(sensor->id)) { + sensor->sip = sensor->odr / min_odr; + sensor->decimator = max_odr / sensor->odr; + data = st_lsm6dsx_get_decimator_val(sensor->decimator); + } else { + sensor->sip = 0; + sensor->decimator = 0; + data = 0; + } + + err = st_lsm6dsx_write_with_mask(hw, + ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR, + sensor->decimator_mask, data); + if (err < 0) + return err; + + sip += sensor->sip; + } + hw->sip = sip; + + return 0; +} + +static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_fifo_mode fifo_mode) +{ + u8 data; + int err; + + switch (fifo_mode) { + case ST_LSM6DSX_FIFO_BYPASS: + data = fifo_mode; + break; + case ST_LSM6DSX_FIFO_CONT: + data = (ST_LSM6DSX_MAX_FIFO_ODR_VAL << + __ffs(ST_LSM6DSX_FIFO_ODR_MASK)) | fifo_mode; + break; + default: + return -EINVAL; + } + + err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_MODE_ADDR, + sizeof(data), &data); + if (err < 0) + return err; + + hw->fifo_mode = fifo_mode; + + return 0; +} + +int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) +{ + u16 fifo_watermark = ~0, cur_watermark, sip = 0; + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *cur_sensor; + __le16 wdata; + int i, err; + u8 data; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + cur_sensor = iio_priv(hw->iio_devs[i]); + + if (!(hw->enable_mask & BIT(cur_sensor->id))) + continue; + + cur_watermark = (cur_sensor == sensor) ? watermark + : cur_sensor->watermark; + + fifo_watermark = min_t(u16, fifo_watermark, cur_watermark); + sip += cur_sensor->sip; + } + + if (!sip) + return 0; + + fifo_watermark = max_t(u16, fifo_watermark, sip); + fifo_watermark = (fifo_watermark / sip) * sip; + fifo_watermark = fifo_watermark * ST_LSM6DSX_SAMPLE_DEPTH; + + mutex_lock(&hw->lock); + + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_THH_ADDR, + sizeof(data), &data); + if (err < 0) + goto out; + + fifo_watermark = ((data & ~ST_LSM6DSX_FIFO_TH_MASK) << 8) | + (fifo_watermark & ST_LSM6DSX_FIFO_TH_MASK); + + wdata = cpu_to_le16(fifo_watermark); + err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_THL_ADDR, + sizeof(wdata), (u8 *)&wdata); +out: + mutex_unlock(&hw->lock); + + return err < 0 ? err : 0; +} + +/** + * st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DSM read FIFO routine + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * + * Read samples from the hw FIFO and push them to IIO buffers. + * + * Return: Number of bytes read from the FIFO + */ +static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) +{ + u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; + int err, acc_sip, gyro_sip, read_len, samples, offset; + struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor; + s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts; + u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)]; + u8 buff[pattern_len]; + __le16 fifo_status; + + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_DIFFL_ADDR, + sizeof(fifo_status), (u8 *)&fifo_status); + if (err < 0) + return err; + + if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK)) + return 0; + + fifo_len = (le16_to_cpu(fifo_status) & ST_LSM6DSX_FIFO_DIFF_MASK) * + ST_LSM6DSX_CHAN_SIZE; + samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE; + fifo_len = (fifo_len / pattern_len) * pattern_len; + + /* + * compute delta timestamp between two consecutive samples + * in order to estimate queueing time of data generated + * by the sensor + */ + acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + acc_ts = acc_sensor->ts - acc_sensor->delta_ts; + acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator, + samples); + + gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); + gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts; + gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator, + samples); + + for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_OUTL_ADDR, + sizeof(buff), buff); + if (err < 0) + return err; + + /* + * Data are written to the FIFO with a specific pattern + * depending on the configured ODRs. The first sequence of data + * stored in FIFO contains the data of all enabled sensors + * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated + * depending on the value of the decimation factor set for each + * sensor. + * + * Supposing the FIFO is storing data from gyroscope and + * accelerometer at different ODRs: + * - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz + * Since the gyroscope ODR is twice the accelerometer one, the + * following pattern is repeated every 9 samples: + * - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz + */ + gyro_sip = gyro_sensor->sip; + acc_sip = acc_sensor->sip; + offset = 0; + + while (acc_sip > 0 || gyro_sip > 0) { + if (gyro_sip-- > 0) { + memcpy(iio_buff, &buff[offset], + ST_LSM6DSX_SAMPLE_SIZE); + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + iio_buff, gyro_ts); + offset += ST_LSM6DSX_SAMPLE_SIZE; + gyro_ts += gyro_delta_ts; + } + + if (acc_sip-- > 0) { + memcpy(iio_buff, &buff[offset], + ST_LSM6DSX_SAMPLE_SIZE); + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + iio_buff, acc_ts); + offset += ST_LSM6DSX_SAMPLE_SIZE; + acc_ts += acc_delta_ts; + } + } + } + + return read_len; +} + +static int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) +{ + int err; + + mutex_lock(&hw->fifo_lock); + + st_lsm6dsx_read_fifo(hw); + err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS); + + mutex_unlock(&hw->fifo_lock); + + return err; +} + +static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + int err; + + if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) { + err = st_lsm6dsx_flush_fifo(hw); + if (err < 0) + return err; + } + + if (enable) { + err = st_lsm6dsx_sensor_enable(sensor); + if (err < 0) + return err; + } else { + err = st_lsm6dsx_sensor_disable(sensor); + if (err < 0) + return err; + } + + err = st_lsm6dsx_update_decimators(hw); + if (err < 0) + return err; + + err = st_lsm6dsx_update_watermark(sensor, sensor->watermark); + if (err < 0) + return err; + + if (hw->enable_mask) { + err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); + if (err < 0) + return err; + + /* + * store enable buffer timestamp as reference to compute + * first delta timestamp + */ + sensor->ts = iio_get_time_ns(iio_dev); + } + + return 0; +} + +static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private) +{ + struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private; + struct st_lsm6dsx_sensor *sensor; + int i; + + if (!hw->sip) + return IRQ_NONE; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + + if (sensor->sip > 0) { + s64 timestamp; + + timestamp = iio_get_time_ns(hw->iio_devs[i]); + sensor->delta_ts = timestamp - sensor->ts; + sensor->ts = timestamp; + } + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) +{ + struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private; + int count; + + mutex_lock(&hw->fifo_lock); + count = st_lsm6dsx_read_fifo(hw); + mutex_unlock(&hw->fifo_lock); + + return !count ? IRQ_NONE : IRQ_HANDLED; +} + +static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev) +{ + return st_lsm6dsx_update_fifo(iio_dev, true); +} + +static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev) +{ + return st_lsm6dsx_update_fifo(iio_dev, false); +} + +static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { + .preenable = st_lsm6dsx_buffer_preenable, + .postdisable = st_lsm6dsx_buffer_postdisable, +}; + +int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) +{ + struct iio_buffer *buffer; + unsigned long irq_type; + int i, err; + + irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); + + switch (irq_type) { + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + break; + default: + dev_info(hw->dev, "mode %lx unsupported\n", irq_type); + return -EINVAL; + } + + err = devm_request_threaded_irq(hw->dev, hw->irq, + st_lsm6dsx_handler_irq, + st_lsm6dsx_handler_thread, + irq_type | IRQF_ONESHOT, + "lsm6dsx", hw); + if (err) { + dev_err(hw->dev, "failed to request trigger irq %d\n", + hw->irq); + return err; + } + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + buffer = devm_iio_kfifo_allocate(hw->dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(hw->iio_devs[i], buffer); + hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE; + hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops; + } + + return 0; +} diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c new file mode 100644 index 000000000000..c92ddcc190e2 --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -0,0 +1,720 @@ +/* + * STMicroelectronics st_lsm6dsx sensor driver + * + * The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer + * and 3D digital gyroscope system-in-package with a digital I2C/SPI serial + * interface standard output. + * LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale + * acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of + * +-125/+-245/+-500/+-1000/+-2000 dps + * LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer + * allowing dynamic batching of sensor data. + * + * Supported sensors: + * - LSM6DS3: + * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 8KB + * + * - LSM6DSM: + * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 4KB + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_REG_ACC_DEC_MASK GENMASK(2, 0) +#define ST_LSM6DSX_REG_GYRO_DEC_MASK GENMASK(5, 3) +#define ST_LSM6DSX_REG_INT1_ADDR 0x0d +#define ST_LSM6DSX_REG_INT2_ADDR 0x0e +#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3) +#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f +#define ST_LSM6DSX_REG_RESET_ADDR 0x12 +#define ST_LSM6DSX_REG_RESET_MASK BIT(0) +#define ST_LSM6DSX_REG_BDU_ADDR 0x12 +#define ST_LSM6DSX_REG_BDU_MASK BIT(6) +#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13 +#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5) +#define ST_LSM6DSX_REG_ROUNDING_ADDR 0x16 +#define ST_LSM6DSX_REG_ROUNDING_MASK BIT(2) +#define ST_LSM6DSX_REG_LIR_ADDR 0x58 +#define ST_LSM6DSX_REG_LIR_MASK BIT(0) + +#define ST_LSM6DSX_REG_ACC_ODR_ADDR 0x10 +#define ST_LSM6DSX_REG_ACC_ODR_MASK GENMASK(7, 4) +#define ST_LSM6DSX_REG_ACC_FS_ADDR 0x10 +#define ST_LSM6DSX_REG_ACC_FS_MASK GENMASK(3, 2) +#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28 +#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a +#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR 0x2c + +#define ST_LSM6DSX_REG_GYRO_ODR_ADDR 0x11 +#define ST_LSM6DSX_REG_GYRO_ODR_MASK GENMASK(7, 4) +#define ST_LSM6DSX_REG_GYRO_FS_ADDR 0x11 +#define ST_LSM6DSX_REG_GYRO_FS_MASK GENMASK(3, 2) +#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR 0x22 +#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24 +#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26 + +#define ST_LSM6DS3_WHOAMI 0x69 +#define ST_LSM6DSM_WHOAMI 0x6a + +#define ST_LSM6DS3_MAX_FIFO_SIZE 8192 +#define ST_LSM6DSM_MAX_FIFO_SIZE 4096 + +#define ST_LSM6DSX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61) +#define ST_LSM6DSX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122) +#define ST_LSM6DSX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244) +#define ST_LSM6DSX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488) + +#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(8750) +#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000) +#define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000) + +struct st_lsm6dsx_odr { + u16 hz; + u8 val; +}; + +#define ST_LSM6DSX_ODR_LIST_SIZE 6 +struct st_lsm6dsx_odr_table_entry { + struct st_lsm6dsx_reg reg; + struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE]; +}; + +static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = ST_LSM6DSX_REG_ACC_ODR_ADDR, + .mask = ST_LSM6DSX_REG_ACC_ODR_MASK, + }, + .odr_avl[0] = { 13, 0x01 }, + .odr_avl[1] = { 26, 0x02 }, + .odr_avl[2] = { 52, 0x03 }, + .odr_avl[3] = { 104, 0x04 }, + .odr_avl[4] = { 208, 0x05 }, + .odr_avl[5] = { 416, 0x06 }, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR, + .mask = ST_LSM6DSX_REG_GYRO_ODR_MASK, + }, + .odr_avl[0] = { 13, 0x01 }, + .odr_avl[1] = { 26, 0x02 }, + .odr_avl[2] = { 52, 0x03 }, + .odr_avl[3] = { 104, 0x04 }, + .odr_avl[4] = { 208, 0x05 }, + .odr_avl[5] = { 416, 0x06 }, + } +}; + +struct st_lsm6dsx_fs { + u32 gain; + u8 val; +}; + +#define ST_LSM6DSX_FS_LIST_SIZE 4 +struct st_lsm6dsx_fs_table_entry { + struct st_lsm6dsx_reg reg; + struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE]; +}; + +static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = ST_LSM6DSX_REG_ACC_FS_ADDR, + .mask = ST_LSM6DSX_REG_ACC_FS_MASK, + }, + .fs_avl[0] = { ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 }, + .fs_avl[1] = { ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 }, + .fs_avl[2] = { ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 }, + .fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 }, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = ST_LSM6DSX_REG_GYRO_FS_ADDR, + .mask = ST_LSM6DSX_REG_GYRO_FS_MASK, + }, + .fs_avl[0] = { ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 }, + .fs_avl[1] = { ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 }, + .fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 }, + .fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 }, + } +}; + +static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { + { + .wai = ST_LSM6DS3_WHOAMI, + .max_fifo_size = ST_LSM6DS3_MAX_FIFO_SIZE, + .id = ST_LSM6DS3_ID, + }, + { + .wai = ST_LSM6DSM_WHOAMI, + .max_fifo_size = ST_LSM6DSM_MAX_FIFO_SIZE, + .id = ST_LSM6DSM_ID, + }, +}; + +#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ +{ \ + .type = chan_type, \ + .address = addr, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = scan_idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ +} + +static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR, + IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR, + IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR, + IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR, + IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR, + IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR, + IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask, + u8 val) +{ + u8 data; + int err; + + mutex_lock(&hw->lock); + + err = hw->tf->read(hw->dev, addr, sizeof(data), &data); + if (err < 0) { + dev_err(hw->dev, "failed to read %02x register\n", addr); + goto out; + } + + data = (data & ~mask) | ((val << __ffs(mask)) & mask); + + err = hw->tf->write(hw->dev, addr, sizeof(data), &data); + if (err < 0) + dev_err(hw->dev, "failed to write %02x register\n", addr); + +out: + mutex_unlock(&hw->lock); + + return err; +} + +static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id) +{ + int err, i; + u8 data; + + for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) { + if (id == st_lsm6dsx_sensor_settings[i].id) + break; + } + + if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) { + dev_err(hw->dev, "unsupported hw id [%02x]\n", id); + return -ENODEV; + } + + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_WHOAMI_ADDR, sizeof(data), + &data); + if (err < 0) { + dev_err(hw->dev, "failed to read whoami register\n"); + return err; + } + + if (data != st_lsm6dsx_sensor_settings[i].wai) { + dev_err(hw->dev, "unsupported whoami [%02x]\n", data); + return -ENODEV; + } + + hw->settings = &st_lsm6dsx_sensor_settings[i]; + + return 0; +} + +static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, + u32 gain) +{ + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, err; + u8 val; + + for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) + if (st_lsm6dsx_fs_table[id].fs_avl[i].gain == gain) + break; + + if (i == ST_LSM6DSX_FS_LIST_SIZE) + return -EINVAL; + + val = st_lsm6dsx_fs_table[id].fs_avl[i].val; + err = st_lsm6dsx_write_with_mask(sensor->hw, + st_lsm6dsx_fs_table[id].reg.addr, + st_lsm6dsx_fs_table[id].reg.mask, + val); + if (err < 0) + return err; + + sensor->gain = gain; + + return 0; +} + +static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr) +{ + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, err; + u8 val; + + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) + if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr) + break; + + if (i == ST_LSM6DSX_ODR_LIST_SIZE) + return -EINVAL; + + val = st_lsm6dsx_odr_table[id].odr_avl[i].val; + err = st_lsm6dsx_write_with_mask(sensor->hw, + st_lsm6dsx_odr_table[id].reg.addr, + st_lsm6dsx_odr_table[id].reg.mask, + val); + if (err < 0) + return err; + + sensor->odr = odr; + + return 0; +} + +int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor) +{ + int err; + + err = st_lsm6dsx_set_odr(sensor, sensor->odr); + if (err < 0) + return err; + + sensor->hw->enable_mask |= BIT(sensor->id); + + return 0; +} + +int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor) +{ + enum st_lsm6dsx_sensor_id id = sensor->id; + int err; + + err = st_lsm6dsx_write_with_mask(sensor->hw, + st_lsm6dsx_odr_table[id].reg.addr, + st_lsm6dsx_odr_table[id].reg.mask, 0); + if (err < 0) + return err; + + sensor->hw->enable_mask &= ~BIT(id); + + return 0; +} + +static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor, + u8 addr, int *val) +{ + int err, delay; + __le16 data; + + err = st_lsm6dsx_sensor_enable(sensor); + if (err < 0) + return err; + + delay = 1000000 / sensor->odr; + usleep_range(delay, 2 * delay); + + err = sensor->hw->tf->read(sensor->hw->dev, addr, sizeof(data), + (u8 *)&data); + if (err < 0) + return err; + + st_lsm6dsx_sensor_disable(sensor); + + *val = (s16)data; + + return IIO_VAL_INT; +} + +static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + break; + + ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val); + iio_device_release_direct_mode(iio_dev); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->odr; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int err; + + err = iio_device_claim_direct_mode(iio_dev); + if (err) + return err; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_lsm6dsx_set_full_scale(sensor, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + err = st_lsm6dsx_set_odr(sensor, val); + break; + default: + err = -EINVAL; + break; + } + + iio_device_release_direct_mode(iio_dev); + + return err; +} + +static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + int err, max_fifo_len; + + max_fifo_len = hw->settings->max_fifo_size / ST_LSM6DSX_SAMPLE_SIZE; + if (val < 1 || val > max_fifo_len) + return -EINVAL; + + err = st_lsm6dsx_update_watermark(sensor, val); + if (err < 0) + return err; + + sensor->watermark = val; + + return 0; +} + +static ssize_t +st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, len = 0; + + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", + st_lsm6dsx_odr_table[id].odr_avl[i].hz); + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, len = 0; + + for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + st_lsm6dsx_fs_table[id].fs_avl[i].gain); + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail); +static IIO_DEVICE_ATTR(in_accel_scale_available, 0444, + st_lsm6dsx_sysfs_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444, + st_lsm6dsx_sysfs_scale_avail, NULL, 0); + +static struct attribute *st_lsm6dsx_acc_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_acc_attribute_group = { + .attrs = st_lsm6dsx_acc_attributes, +}; + +static const struct iio_info st_lsm6dsx_acc_info = { + .driver_module = THIS_MODULE, + .attrs = &st_lsm6dsx_acc_attribute_group, + .read_raw = st_lsm6dsx_read_raw, + .write_raw = st_lsm6dsx_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, +}; + +static struct attribute *st_lsm6dsx_gyro_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_gyro_attribute_group = { + .attrs = st_lsm6dsx_gyro_attributes, +}; + +static const struct iio_info st_lsm6dsx_gyro_info = { + .driver_module = THIS_MODULE, + .attrs = &st_lsm6dsx_gyro_attribute_group, + .read_raw = st_lsm6dsx_read_raw, + .write_raw = st_lsm6dsx_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, +}; + +static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; + +static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin) +{ + struct device_node *np = hw->dev->of_node; + int err; + + if (!np) + return -EINVAL; + + err = of_property_read_u32(np, "st,drdy-int-pin", drdy_pin); + if (err == -ENODATA) { + /* if the property has not been specified use default value */ + *drdy_pin = 1; + err = 0; + } + + return err; +} + +static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg) +{ + int err = 0, drdy_pin; + + if (st_lsm6dsx_of_get_drdy_pin(hw, &drdy_pin) < 0) { + struct st_sensors_platform_data *pdata; + struct device *dev = hw->dev; + + pdata = (struct st_sensors_platform_data *)dev->platform_data; + drdy_pin = pdata ? pdata->drdy_int_pin : 1; + } + + switch (drdy_pin) { + case 1: + *drdy_reg = ST_LSM6DSX_REG_INT1_ADDR; + break; + case 2: + *drdy_reg = ST_LSM6DSX_REG_INT2_ADDR; + break; + default: + dev_err(hw->dev, "unsupported data ready pin\n"); + err = -EINVAL; + break; + } + + return err; +} + +static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) +{ + u8 data, drdy_int_reg; + int err; + + data = ST_LSM6DSX_REG_RESET_MASK; + err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_RESET_ADDR, sizeof(data), + &data); + if (err < 0) + return err; + + msleep(200); + + /* latch interrupts */ + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_LIR_ADDR, + ST_LSM6DSX_REG_LIR_MASK, 1); + if (err < 0) + return err; + + /* enable Block Data Update */ + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_BDU_ADDR, + ST_LSM6DSX_REG_BDU_MASK, 1); + if (err < 0) + return err; + + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_ROUNDING_ADDR, + ST_LSM6DSX_REG_ROUNDING_MASK, 1); + if (err < 0) + return err; + + /* enable FIFO watermak interrupt */ + err = st_lsm6dsx_get_drdy_reg(hw, &drdy_int_reg); + if (err < 0) + return err; + + return st_lsm6dsx_write_with_mask(hw, drdy_int_reg, + ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1); +} + +static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_sensor_id id) +{ + struct st_lsm6dsx_sensor *sensor; + struct iio_dev *iio_dev; + + iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor)); + if (!iio_dev) + return NULL; + + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->dev.parent = hw->dev; + iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks; + + sensor = iio_priv(iio_dev); + sensor->id = id; + sensor->hw = hw; + sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz; + sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain; + sensor->watermark = 1; + + switch (id) { + case ST_LSM6DSX_ID_ACC: + iio_dev->channels = st_lsm6dsx_acc_channels; + iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels); + iio_dev->name = "lsm6dsx_accel"; + iio_dev->info = &st_lsm6dsx_acc_info; + + sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK; + break; + case ST_LSM6DSX_ID_GYRO: + iio_dev->channels = st_lsm6dsx_gyro_channels; + iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels); + iio_dev->name = "lsm6dsx_gyro"; + iio_dev->info = &st_lsm6dsx_gyro_info; + + sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK; + break; + default: + return NULL; + } + + return iio_dev; +} + +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, + const struct st_lsm6dsx_transfer_function *tf_ops) +{ + struct st_lsm6dsx_hw *hw; + int i, err; + + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + dev_set_drvdata(dev, (void *)hw); + + mutex_init(&hw->lock); + mutex_init(&hw->fifo_lock); + + hw->dev = dev; + hw->irq = irq; + hw->tf = tf_ops; + + err = st_lsm6dsx_check_whoami(hw, hw_id); + if (err < 0) + return err; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i); + if (!hw->iio_devs[i]) + return -ENOMEM; + } + + err = st_lsm6dsx_init_device(hw); + if (err < 0) + return err; + + if (hw->irq > 0) { + err = st_lsm6dsx_fifo_setup(hw); + if (err < 0) + return err; + } + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + err = devm_iio_device_register(hw->dev, hw->iio_devs[i]); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL(st_lsm6dsx_probe); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c new file mode 100644 index 000000000000..ea3041186e1e --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -0,0 +1,101 @@ +/* + * STMicroelectronics st_lsm6dsx i2c driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +static int st_lsm6dsx_i2c_read(struct device *dev, u8 addr, int len, u8 *data) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msg[2]; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].len = 1; + msg[0].buf = &addr; + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + return i2c_transfer(client->adapter, msg, 2); +} + +static int st_lsm6dsx_i2c_write(struct device *dev, u8 addr, int len, u8 *data) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msg; + u8 send[len + 1]; + + send[0] = addr; + memcpy(&send[1], data, len * sizeof(u8)); + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = len + 1; + msg.buf = send; + + return i2c_transfer(client->adapter, &msg, 1); +} + +static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = { + .read = st_lsm6dsx_i2c_read, + .write = st_lsm6dsx_i2c_write, +}; + +static int st_lsm6dsx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return st_lsm6dsx_probe(&client->dev, client->irq, + (int)id->driver_data, + &st_lsm6dsx_transfer_fn); +} + +static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { + { + .compatible = "st,lsm6ds3", + .data = (void *)ST_LSM6DS3_ID, + }, + { + .compatible = "st,lsm6dsm", + .data = (void *)ST_LSM6DSM_ID, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); + +static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { + { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, + { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); + +static struct i2c_driver st_lsm6dsx_driver = { + .driver = { + .name = "st_lsm6dsx_i2c", + .of_match_table = of_match_ptr(st_lsm6dsx_i2c_of_match), + }, + .probe = st_lsm6dsx_i2c_probe, + .id_table = st_lsm6dsx_i2c_id_table, +}; +module_i2c_driver(st_lsm6dsx_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c new file mode 100644 index 000000000000..fbe72470ed1e --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -0,0 +1,118 @@ +/* + * STMicroelectronics st_lsm6dsx spi driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +#define SENSORS_SPI_READ BIT(7) + +static int st_lsm6dsx_spi_read(struct device *dev, u8 addr, int len, + u8 *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct st_lsm6dsx_hw *hw = spi_get_drvdata(spi); + int err; + + struct spi_transfer xfers[] = { + { + .tx_buf = hw->tb.tx_buf, + .bits_per_word = 8, + .len = 1, + }, + { + .rx_buf = hw->tb.rx_buf, + .bits_per_word = 8, + .len = len, + } + }; + + hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ; + + err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers)); + if (err < 0) + return err; + + memcpy(data, hw->tb.rx_buf, len * sizeof(u8)); + + return len; +} + +static int st_lsm6dsx_spi_write(struct device *dev, u8 addr, int len, + u8 *data) +{ + struct st_lsm6dsx_hw *hw; + struct spi_device *spi; + + if (len >= ST_LSM6DSX_TX_MAX_LENGTH) + return -ENOMEM; + + spi = to_spi_device(dev); + hw = spi_get_drvdata(spi); + + hw->tb.tx_buf[0] = addr; + memcpy(&hw->tb.tx_buf[1], data, len); + + return spi_write(spi, hw->tb.tx_buf, len + 1); +} + +static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = { + .read = st_lsm6dsx_spi_read, + .write = st_lsm6dsx_spi_write, +}; + +static int st_lsm6dsx_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + return st_lsm6dsx_probe(&spi->dev, spi->irq, + (int)id->driver_data, + &st_lsm6dsx_transfer_fn); +} + +static const struct of_device_id st_lsm6dsx_spi_of_match[] = { + { + .compatible = "st,lsm6ds3", + .data = (void *)ST_LSM6DS3_ID, + }, + { + .compatible = "st,lsm6dsm", + .data = (void *)ST_LSM6DSM_ID, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); + +static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { + { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, + { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, + {}, +}; +MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); + +static struct spi_driver st_lsm6dsx_driver = { + .driver = { + .name = "st_lsm6dsx_spi", + .of_match_table = of_match_ptr(st_lsm6dsx_spi_of_match), + }, + .probe = st_lsm6dsx_spi_probe, + .id_table = st_lsm6dsx_spi_id_table, +}; +module_spi_driver(st_lsm6dsx_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index b12830b09c7d..4972986f6455 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -26,6 +26,7 @@ #include "iio_core.h" #include #include +#include static const char * const iio_endian_prefix[] = { [IIO_BE] = "be", @@ -209,6 +210,18 @@ void iio_buffer_init(struct iio_buffer *buffer) } EXPORT_SYMBOL(iio_buffer_init); +/** + * iio_buffer_set_attrs - Set buffer specific attributes + * @buffer: The buffer for which we are setting attributes + * @attrs: Pointer to a null terminated list of pointers to attributes + */ +void iio_buffer_set_attrs(struct iio_buffer *buffer, + const struct attribute **attrs) +{ + buffer->attrs = attrs; +} +EXPORT_SYMBOL_GPL(iio_buffer_set_attrs); + static ssize_t iio_show_scan_index(struct device *dev, struct device_attribute *attr, char *buf) @@ -346,6 +359,19 @@ static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit) return 0; } +static int iio_scan_mask_query(struct iio_dev *indio_dev, + struct iio_buffer *buffer, int bit) +{ + if (bit > indio_dev->masklength) + return -EINVAL; + + if (!buffer->scan_mask) + return 0; + + /* Ensure return value is 0 or 1. */ + return !!test_bit(bit, buffer->scan_mask); +}; + static ssize_t iio_scan_el_store(struct device *dev, struct device_attribute *attr, const char *buf, @@ -751,6 +777,135 @@ static int iio_verify_update(struct iio_dev *indio_dev, return 0; } +/** + * struct iio_demux_table - table describing demux memcpy ops + * @from: index to copy from + * @to: index to copy to + * @length: how many bytes to copy + * @l: list head used for management + */ +struct iio_demux_table { + unsigned from; + unsigned to; + unsigned length; + struct list_head l; +}; + +static void iio_buffer_demux_free(struct iio_buffer *buffer) +{ + struct iio_demux_table *p, *q; + list_for_each_entry_safe(p, q, &buffer->demux_list, l) { + list_del(&p->l); + kfree(p); + } +} + +static int iio_buffer_add_demux(struct iio_buffer *buffer, + struct iio_demux_table **p, unsigned int in_loc, unsigned int out_loc, + unsigned int length) +{ + + if (*p && (*p)->from + (*p)->length == in_loc && + (*p)->to + (*p)->length == out_loc) { + (*p)->length += length; + } else { + *p = kmalloc(sizeof(**p), GFP_KERNEL); + if (*p == NULL) + return -ENOMEM; + (*p)->from = in_loc; + (*p)->to = out_loc; + (*p)->length = length; + list_add_tail(&(*p)->l, &buffer->demux_list); + } + + return 0; +} + +static int iio_buffer_update_demux(struct iio_dev *indio_dev, + struct iio_buffer *buffer) +{ + int ret, in_ind = -1, out_ind, length; + unsigned in_loc = 0, out_loc = 0; + struct iio_demux_table *p = NULL; + + /* Clear out any old demux */ + iio_buffer_demux_free(buffer); + kfree(buffer->demux_bounce); + buffer->demux_bounce = NULL; + + /* First work out which scan mode we will actually have */ + if (bitmap_equal(indio_dev->active_scan_mask, + buffer->scan_mask, + indio_dev->masklength)) + return 0; + + /* Now we have the two masks, work from least sig and build up sizes */ + for_each_set_bit(out_ind, + buffer->scan_mask, + indio_dev->masklength) { + in_ind = find_next_bit(indio_dev->active_scan_mask, + indio_dev->masklength, + in_ind + 1); + while (in_ind != out_ind) { + in_ind = find_next_bit(indio_dev->active_scan_mask, + indio_dev->masklength, + in_ind + 1); + length = iio_storage_bytes_for_si(indio_dev, in_ind); + /* Make sure we are aligned */ + in_loc = roundup(in_loc, length) + length; + } + 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); + if (ret) + goto error_clear_mux_table; + out_loc += length; + in_loc += length; + } + /* Relies on scan_timestamp being last */ + if (buffer->scan_timestamp) { + 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); + if (ret) + goto error_clear_mux_table; + out_loc += length; + in_loc += length; + } + buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL); + if (buffer->demux_bounce == NULL) { + ret = -ENOMEM; + goto error_clear_mux_table; + } + return 0; + +error_clear_mux_table: + iio_buffer_demux_free(buffer); + + return ret; +} + +static int iio_update_demux(struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + int ret; + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + ret = iio_buffer_update_demux(indio_dev, buffer); + if (ret < 0) + goto error_clear_mux_table; + } + return 0; + +error_clear_mux_table: + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) + iio_buffer_demux_free(buffer); + + return ret; +} + static int iio_enable_buffers(struct iio_dev *indio_dev, struct iio_device_config *config) { @@ -1199,34 +1354,6 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, } EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot); -int iio_scan_mask_query(struct iio_dev *indio_dev, - struct iio_buffer *buffer, int bit) -{ - if (bit > indio_dev->masklength) - return -EINVAL; - - if (!buffer->scan_mask) - return 0; - - /* Ensure return value is 0 or 1. */ - return !!test_bit(bit, buffer->scan_mask); -}; -EXPORT_SYMBOL_GPL(iio_scan_mask_query); - -/** - * struct iio_demux_table - table describing demux memcpy ops - * @from: index to copy from - * @to: index to copy to - * @length: how many bytes to copy - * @l: list head used for management - */ -struct iio_demux_table { - unsigned from; - unsigned to; - unsigned length; - struct list_head l; -}; - static const void *iio_demux(struct iio_buffer *buffer, const void *datain) { @@ -1258,16 +1385,11 @@ static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data) return 0; } -static void iio_buffer_demux_free(struct iio_buffer *buffer) -{ - struct iio_demux_table *p, *q; - list_for_each_entry_safe(p, q, &buffer->demux_list, l) { - list_del(&p->l); - kfree(p); - } -} - - +/** + * iio_push_to_buffers() - push to a registered buffer. + * @indio_dev: iio_dev structure for device. + * @data: Full scan. + */ int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data) { int ret; @@ -1283,113 +1405,6 @@ int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data) } EXPORT_SYMBOL_GPL(iio_push_to_buffers); -static int iio_buffer_add_demux(struct iio_buffer *buffer, - struct iio_demux_table **p, unsigned int in_loc, unsigned int out_loc, - unsigned int length) -{ - - if (*p && (*p)->from + (*p)->length == in_loc && - (*p)->to + (*p)->length == out_loc) { - (*p)->length += length; - } else { - *p = kmalloc(sizeof(**p), GFP_KERNEL); - if (*p == NULL) - return -ENOMEM; - (*p)->from = in_loc; - (*p)->to = out_loc; - (*p)->length = length; - list_add_tail(&(*p)->l, &buffer->demux_list); - } - - return 0; -} - -static int iio_buffer_update_demux(struct iio_dev *indio_dev, - struct iio_buffer *buffer) -{ - int ret, in_ind = -1, out_ind, length; - unsigned in_loc = 0, out_loc = 0; - struct iio_demux_table *p = NULL; - - /* Clear out any old demux */ - iio_buffer_demux_free(buffer); - kfree(buffer->demux_bounce); - buffer->demux_bounce = NULL; - - /* First work out which scan mode we will actually have */ - if (bitmap_equal(indio_dev->active_scan_mask, - buffer->scan_mask, - indio_dev->masklength)) - return 0; - - /* Now we have the two masks, work from least sig and build up sizes */ - for_each_set_bit(out_ind, - buffer->scan_mask, - indio_dev->masklength) { - in_ind = find_next_bit(indio_dev->active_scan_mask, - indio_dev->masklength, - in_ind + 1); - while (in_ind != out_ind) { - in_ind = find_next_bit(indio_dev->active_scan_mask, - indio_dev->masklength, - in_ind + 1); - length = iio_storage_bytes_for_si(indio_dev, in_ind); - /* Make sure we are aligned */ - in_loc = roundup(in_loc, length) + length; - } - 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); - if (ret) - goto error_clear_mux_table; - out_loc += length; - in_loc += length; - } - /* Relies on scan_timestamp being last */ - if (buffer->scan_timestamp) { - 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); - if (ret) - goto error_clear_mux_table; - out_loc += length; - in_loc += length; - } - buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL); - if (buffer->demux_bounce == NULL) { - ret = -ENOMEM; - goto error_clear_mux_table; - } - return 0; - -error_clear_mux_table: - iio_buffer_demux_free(buffer); - - return ret; -} - -int iio_update_demux(struct iio_dev *indio_dev) -{ - struct iio_buffer *buffer; - int ret; - - list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { - ret = iio_buffer_update_demux(indio_dev, buffer); - if (ret < 0) - goto error_clear_mux_table; - } - return 0; - -error_clear_mux_table: - list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) - iio_buffer_demux_free(buffer); - - return ret; -} -EXPORT_SYMBOL_GPL(iio_update_demux); - /** * iio_buffer_release() - Free a buffer's resources * @ref: Pointer to the kref embedded in the iio_buffer struct @@ -1431,3 +1446,19 @@ void iio_buffer_put(struct iio_buffer *buffer) kref_put(&buffer->ref, iio_buffer_release); } EXPORT_SYMBOL_GPL(iio_buffer_put); + +/** + * iio_device_attach_buffer - Attach a buffer to a IIO device + * @indio_dev: The device the buffer should be attached to + * @buffer: The buffer to attach to the device + * + * This function attaches a buffer to a IIO device. The buffer stays attached to + * the device until the device is freed. The function should only be called at + * most once per device. + */ +void iio_device_attach_buffer(struct iio_dev *indio_dev, + struct iio_buffer *buffer) +{ + indio_dev->buffer = iio_buffer_get(buffer); +} +EXPORT_SYMBOL_GPL(iio_device_attach_buffer); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index aaca42862389..d18ded45bedd 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -32,6 +32,7 @@ #include #include #include +#include /* IDA to assign each registered device a unique id */ static DEFINE_IDA(iio_ida); @@ -83,6 +84,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity", [IIO_COUNT] = "count", [IIO_INDEX] = "index", + [IIO_GRAVITY] = "gravity", }; static const char * const iio_modifier_names[] = { diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 978729f6d7c4..978e1592c2a3 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -147,8 +147,7 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name) return NULL; } -static struct iio_trigger *iio_trigger_find_by_name(const char *name, - size_t len) +static struct iio_trigger *iio_trigger_acquire_by_name(const char *name) { struct iio_trigger *trig = NULL, *iter; @@ -156,6 +155,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name, list_for_each_entry(iter, &iio_trigger_list, list) if (sysfs_streq(iter->name, name)) { trig = iter; + iio_trigger_get(trig); break; } mutex_unlock(&iio_trigger_list_lock); @@ -416,20 +416,22 @@ static ssize_t iio_trigger_write_current(struct device *dev, } mutex_unlock(&indio_dev->mlock); - trig = iio_trigger_find_by_name(buf, len); - if (oldtrig == trig) - return len; + trig = iio_trigger_acquire_by_name(buf); + if (oldtrig == trig) { + ret = len; + goto out_trigger_put; + } if (trig && indio_dev->info->validate_trigger) { ret = indio_dev->info->validate_trigger(indio_dev, trig); if (ret) - return ret; + goto out_trigger_put; } if (trig && trig->ops->validate_device) { ret = trig->ops->validate_device(trig, indio_dev); if (ret) - return ret; + goto out_trigger_put; } indio_dev->trig = trig; @@ -441,13 +443,16 @@ static ssize_t iio_trigger_write_current(struct device *dev, iio_trigger_put(oldtrig); } if (indio_dev->trig) { - iio_trigger_get(indio_dev->trig); if (indio_dev->modes & INDIO_EVENT_TRIGGERED) iio_trigger_attach_poll_func(indio_dev->trig, indio_dev->pollfunc_event); } return len; + +out_trigger_put: + iio_trigger_put(trig); + return ret; } static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, @@ -487,7 +492,7 @@ static void iio_trig_release(struct device *device) kfree(trig); } -static struct device_type iio_trig_type = { +static const struct device_type iio_trig_type = { .release = iio_trig_release, .groups = iio_trig_dev_groups, }; @@ -513,46 +518,45 @@ static void iio_trig_subirqunmask(struct irq_data *d) static struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) { struct iio_trigger *trig; + int i; + trig = kzalloc(sizeof *trig, GFP_KERNEL); - if (trig) { - int i; - trig->dev.type = &iio_trig_type; - trig->dev.bus = &iio_bus_type; - device_initialize(&trig->dev); + if (!trig) + return NULL; - mutex_init(&trig->pool_lock); - trig->subirq_base - = irq_alloc_descs(-1, 0, - CONFIG_IIO_CONSUMERS_PER_TRIGGER, - 0); - if (trig->subirq_base < 0) { - kfree(trig); - return NULL; - } + trig->dev.type = &iio_trig_type; + trig->dev.bus = &iio_bus_type; + device_initialize(&trig->dev); - trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); - if (trig->name == NULL) { - irq_free_descs(trig->subirq_base, - CONFIG_IIO_CONSUMERS_PER_TRIGGER); - kfree(trig); - return NULL; - } - trig->subirq_chip.name = trig->name; - trig->subirq_chip.irq_mask = &iio_trig_subirqmask; - trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask; - for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { - irq_set_chip(trig->subirq_base + i, - &trig->subirq_chip); - irq_set_handler(trig->subirq_base + i, - &handle_simple_irq); - irq_modify_status(trig->subirq_base + i, - IRQ_NOREQUEST | IRQ_NOAUTOEN, - IRQ_NOPROBE); - } - get_device(&trig->dev); + mutex_init(&trig->pool_lock); + trig->subirq_base = irq_alloc_descs(-1, 0, + CONFIG_IIO_CONSUMERS_PER_TRIGGER, + 0); + if (trig->subirq_base < 0) + goto free_trig; + + trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); + if (trig->name == NULL) + goto free_descs; + + trig->subirq_chip.name = trig->name; + trig->subirq_chip.irq_mask = &iio_trig_subirqmask; + trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask; + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { + irq_set_chip(trig->subirq_base + i, &trig->subirq_chip); + irq_set_handler(trig->subirq_base + i, &handle_simple_irq); + irq_modify_status(trig->subirq_base + i, + IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); } + get_device(&trig->dev); return trig; + +free_descs: + irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER); +free_trig: + kfree(trig); + return NULL; } struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index b0f4630a163f..7a13535dc3e9 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -601,8 +601,14 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, scale_type = iio_channel_read(chan, &scale_val, &scale_val2, IIO_CHAN_INFO_SCALE); - if (scale_type < 0) - return scale_type; + if (scale_type < 0) { + /* + * Just pass raw values as processed if no scaling is + * available. + */ + *processed = raw; + return 0; + } switch (scale_type) { case IIO_VAL_INT: diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 298ea5081a96..5f731ead9d46 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -115,6 +115,16 @@ config CM3323 To compile this driver as a module, choose M here: the module will be called cm3323. +config CM3605 + tristate "Capella CM3605 ambient light and proximity sensor" + depends on OF + help + Say Y here if you want to build a driver for Capella CM3605 + ambient light and short range proximity sensor. + + To compile this driver as a module, choose M here: the module will + be called cm3605. + config CM36651 depends on I2C tristate "CM36651 driver" diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 4de520036e6e..c13a2399e6df 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_BH1780) += bh1780.o obj-$(CONFIG_CM32181) += cm32181.o obj-$(CONFIG_CM3232) += cm3232.o obj-$(CONFIG_CM3323) += cm3323.o +obj-$(CONFIG_CM3605) += cm3605.o obj-$(CONFIG_CM36651) += cm36651.o obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index fe89b6823217..263e97235ea0 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -119,7 +119,7 @@ static int cm3232_reg_init(struct cm3232_chip *chip) if (ret < 0) dev_err(&chip->client->dev, "Error writing reg_cmd\n"); - return 0; + return ret; } /** diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c new file mode 100644 index 000000000000..980624e9ffb5 --- /dev/null +++ b/drivers/iio/light/cm3605.c @@ -0,0 +1,330 @@ +/* + * CM3605 Ambient Light and Proximity Sensor + * + * Copyright (C) 2016 Linaro Ltd. + * Author: Linus Walleij + * + * This hardware was found in the very first Nexus One handset from Google/HTC + * and an early endavour into mobile light and proximity sensors. + */ + +#include +#include +#include +#include +#include /* To get our ADC channel */ +#include /* To deal with our ADC channel */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CM3605_PROX_CHANNEL 0 +#define CM3605_ALS_CHANNEL 1 +#define CM3605_AOUT_TYP_MAX_MV 1550 +/* It should not go above 1.650V according to the data sheet */ +#define CM3605_AOUT_MAX_MV 1650 + +/** + * struct cm3605 - CM3605 state + * @dev: pointer to parent device + * @vdd: regulator controlling VDD + * @aset: sleep enable GPIO, high = sleep + * @aout: IIO ADC channel to convert the AOUT signal + * @als_max: maximum LUX detection (depends on RSET) + * @dir: proximity direction: start as FALLING + * @led: trigger for the infrared LED used by the proximity sensor + */ +struct cm3605 { + struct device *dev; + struct regulator *vdd; + struct gpio_desc *aset; + struct iio_channel *aout; + s32 als_max; + enum iio_event_direction dir; + struct led_trigger *led; +}; + +static irqreturn_t cm3605_prox_irq(int irq, void *d) +{ + struct iio_dev *indio_dev = d; + struct cm3605 *cm3605 = iio_priv(indio_dev); + u64 ev; + + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, CM3605_PROX_CHANNEL, + IIO_EV_TYPE_THRESH, cm3605->dir); + iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); + + /* Invert the edge for each event */ + if (cm3605->dir == IIO_EV_DIR_RISING) + cm3605->dir = IIO_EV_DIR_FALLING; + else + cm3605->dir = IIO_EV_DIR_RISING; + + return IRQ_HANDLED; +} + +static int cm3605_get_lux(struct cm3605 *cm3605) +{ + int ret, res; + s64 lux; + + ret = iio_read_channel_processed(cm3605->aout, &res); + if (ret < 0) + return ret; + + dev_dbg(cm3605->dev, "read %d mV from ADC\n", res); + + /* + * AOUT has an offset of ~30mV then linear at dark + * then goes from 2.54 up to 650 LUX yielding 1.55V + * (1550 mV) so scale the returned value to this interval + * using simple linear interpolation. + */ + if (res < 30) + return 0; + if (res > CM3605_AOUT_MAX_MV) + dev_err(cm3605->dev, "device out of range\n"); + + /* Remove bias */ + lux = res - 30; + + /* Linear interpolation between 0 and ALS typ max */ + lux *= cm3605->als_max; + lux = div64_s64(lux, CM3605_AOUT_TYP_MAX_MV); + + return lux; +} + +static int cm3605_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cm3605 *cm3605 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + ret = cm3605_get_lux(cm3605); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info cm3605_info = { + .driver_module = THIS_MODULE, + .read_raw = cm3605_read_raw, +}; + +static const struct iio_event_spec cm3605_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec cm3605_channels[] = { + { + .type = IIO_PROXIMITY, + .event_spec = cm3605_events, + .num_event_specs = ARRAY_SIZE(cm3605_events), + }, + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .channel = CM3605_ALS_CHANNEL, + }, +}; + +static int cm3605_probe(struct platform_device *pdev) +{ + struct cm3605 *cm3605; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + enum iio_chan_type ch_type; + u32 rset; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*cm3605)); + if (!indio_dev) + return -ENOMEM; + platform_set_drvdata(pdev, indio_dev); + + cm3605 = iio_priv(indio_dev); + cm3605->dev = dev; + cm3605->dir = IIO_EV_DIR_FALLING; + + ret = of_property_read_u32(np, "capella,aset-resistance-ohms", &rset); + if (ret) { + dev_info(dev, "no RSET specified, assuming 100K\n"); + rset = 100000; + } + switch (rset) { + case 50000: + cm3605->als_max = 650; + break; + case 100000: + cm3605->als_max = 300; + break; + case 300000: + cm3605->als_max = 100; + break; + case 600000: + cm3605->als_max = 50; + break; + default: + dev_info(dev, "non-standard resistance\n"); + return -EINVAL; + } + + cm3605->aout = devm_iio_channel_get(dev, "aout"); + if (IS_ERR(cm3605->aout)) { + if (PTR_ERR(cm3605->aout) == -ENODEV) { + dev_err(dev, "no ADC, deferring...\n"); + return -EPROBE_DEFER; + } + dev_err(dev, "failed to get AOUT ADC channel\n"); + return PTR_ERR(cm3605->aout); + } + ret = iio_get_channel_type(cm3605->aout, &ch_type); + if (ret < 0) + return ret; + if (ch_type != IIO_VOLTAGE) { + dev_err(dev, "wrong type of IIO channel specified for AOUT\n"); + return -EINVAL; + } + + cm3605->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(cm3605->vdd)) { + dev_err(dev, "failed to get VDD regulator\n"); + return PTR_ERR(cm3605->vdd); + } + ret = regulator_enable(cm3605->vdd); + if (ret) { + dev_err(dev, "failed to enable VDD regulator\n"); + return ret; + } + + cm3605->aset = devm_gpiod_get(dev, "aset", GPIOD_OUT_HIGH); + if (IS_ERR(cm3605->aset)) { + dev_err(dev, "no ASET GPIO\n"); + ret = PTR_ERR(cm3605->aset); + goto out_disable_vdd; + } + + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), + cm3605_prox_irq, NULL, 0, "cm3605", indio_dev); + if (ret) { + dev_err(dev, "unable to request IRQ\n"); + goto out_disable_aset; + } + + /* Just name the trigger the same as the driver */ + led_trigger_register_simple("cm3605", &cm3605->led); + led_trigger_event(cm3605->led, LED_FULL); + + indio_dev->dev.parent = dev; + indio_dev->info = &cm3605_info; + indio_dev->name = "cm3605"; + indio_dev->channels = cm3605_channels; + indio_dev->num_channels = ARRAY_SIZE(cm3605_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(indio_dev); + if (ret) + goto out_remove_trigger; + dev_info(dev, "Capella Microsystems CM3605 enabled range 0..%d LUX\n", + cm3605->als_max); + + return 0; + +out_remove_trigger: + led_trigger_event(cm3605->led, LED_OFF); + led_trigger_unregister_simple(cm3605->led); +out_disable_aset: + gpiod_set_value_cansleep(cm3605->aset, 0); +out_disable_vdd: + regulator_disable(cm3605->vdd); + return ret; +} + +static int cm3605_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct cm3605 *cm3605 = iio_priv(indio_dev); + + led_trigger_event(cm3605->led, LED_OFF); + led_trigger_unregister_simple(cm3605->led); + gpiod_set_value_cansleep(cm3605->aset, 0); + iio_device_unregister(indio_dev); + regulator_disable(cm3605->vdd); + + return 0; +} + +static int __maybe_unused cm3605_pm_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct cm3605 *cm3605 = iio_priv(indio_dev); + + led_trigger_event(cm3605->led, LED_OFF); + regulator_disable(cm3605->vdd); + + return 0; +} + +static int __maybe_unused cm3605_pm_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct cm3605 *cm3605 = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(cm3605->vdd); + if (ret) + dev_err(dev, "failed to enable regulator in resume path\n"); + led_trigger_event(cm3605->led, LED_FULL); + + return 0; +} + +static const struct dev_pm_ops cm3605_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cm3605_pm_suspend, + cm3605_pm_resume) +}; + +static const struct of_device_id cm3605_of_match[] = { + {.compatible = "capella,cm3605"}, + { }, +}; +MODULE_DEVICE_TABLE(of, cm3605_of_match); + +static struct platform_driver cm3605_driver = { + .driver = { + .name = "cm3605", + .of_match_table = cm3605_of_match, + .pm = &cm3605_dev_pm_ops, + }, + .probe = cm3605_probe, + .remove = cm3605_remove, +}; +module_platform_driver(cm3605_driver); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 8bb1f90ecd51..059d964772c7 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -31,13 +31,17 @@ #include #include "../common/hid-sensors/hid-sensor-trigger.h" -#define CHANNEL_SCAN_INDEX_ILLUM 0 +enum { + CHANNEL_SCAN_INDEX_INTENSITY = 0, + CHANNEL_SCAN_INDEX_ILLUM = 1, + CHANNEL_SCAN_INDEX_MAX +}; struct als_state { struct hid_sensor_hub_callbacks callbacks; struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info als_illum; - u32 illum; + u32 illum[CHANNEL_SCAN_INDEX_MAX]; int scale_pre_decml; int scale_post_decml; int scale_precision; @@ -55,6 +59,15 @@ static const struct iio_chan_spec als_channels[] = { BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_HYSTERESIS), + .scan_index = CHANNEL_SCAN_INDEX_INTENSITY, + }, + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS), .scan_index = CHANNEL_SCAN_INDEX_ILLUM, } }; @@ -86,6 +99,7 @@ static int als_read_raw(struct iio_dev *indio_dev, switch (mask) { case 0: switch (chan->scan_index) { + case CHANNEL_SCAN_INDEX_INTENSITY: case CHANNEL_SCAN_INDEX_ILLUM: report_id = als_state->als_illum.report_id; address = @@ -202,10 +216,12 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); int ret = -EINVAL; + u32 sample_data = *(u32 *)raw_data; switch (usage_id) { case HID_USAGE_SENSOR_LIGHT_ILLUM: - als_state->illum = *(u32 *)raw_data; + als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data; + als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data; ret = 0; break; default: @@ -230,6 +246,8 @@ static int als_parse_report(struct platform_device *pdev, &st->als_illum); if (ret < 0) return ret; + als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_INTENSITY, + st->als_illum.size); als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM, st->als_illum.size); diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 78c9b3a6453a..b91ebc3483ce 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -840,6 +840,7 @@ static const struct of_device_id opt3001_of_match[] = { { .compatible = "ti,opt3001" }, { } }; +MODULE_DEVICE_TABLE(of, opt3001_of_match); static struct i2c_driver opt3001_driver = { .probe = opt3001_probe, diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index ce09d771c1fb..6dd8cbd7ce95 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -278,13 +278,9 @@ static int ak8974_await_drdy(struct ak8974 *ak8974) if (val & AK8974_STATUS_DRDY) return 0; } while (--timeout); - if (!timeout) { - dev_err(&ak8974->i2c->dev, - "timeout waiting for DRDY\n"); - return -ETIMEDOUT; - } - return 0; + dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY\n"); + return -ETIMEDOUT; } static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result) diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index f2b3bd7bf862..b4f643fb3b1e 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -222,29 +222,39 @@ static int mag3110_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct mag3110_data *data = iio_priv(indio_dev); - int rate; + int rate, ret; - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: rate = mag3110_get_samp_freq_index(data, val, val2); - if (rate < 0) - return -EINVAL; + if (rate < 0) { + ret = -EINVAL; + break; + } data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK; data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; - return i2c_smbus_write_byte_data(data->client, + ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, data->ctrl_reg1); + break; case IIO_CHAN_INFO_CALIBBIAS: - if (val < -10000 || val > 10000) - return -EINVAL; - return i2c_smbus_write_word_swapped(data->client, + if (val < -10000 || val > 10000) { + ret = -EINVAL; + break; + } + ret = i2c_smbus_write_word_swapped(data->client, MAG3110_OFF_X + 2 * chan->scan_index, val << 1); + break; default: - return -EINVAL; + ret = -EINVAL; + break; } + iio_device_release_direct_mode(indio_dev); + return ret; } static irqreturn_t mag3110_trigger_handler(int irq, void *p) diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig index 2e9da1cf3297..8bf282510be6 100644 --- a/drivers/iio/potentiometer/Kconfig +++ b/drivers/iio/potentiometer/Kconfig @@ -15,6 +15,17 @@ config DS1803 To compile this driver as a module, choose M here: the module will be called ds1803. +config MAX5481 + tristate "Maxim MAX5481-MAX5484 Digital Potentiometer driver" + depends on SPI + help + Say yes here to build support for the Maxim + MAX5481, MAX5482, MAX5483, MAX5484 digital potentiometer + chips. + + To compile this driver as a module, choose M here: the + module will be called max5481. + config MAX5487 tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver" depends on SPI diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile index 8adb58f38c0b..2260d40e0936 100644 --- a/drivers/iio/potentiometer/Makefile +++ b/drivers/iio/potentiometer/Makefile @@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_DS1803) += ds1803.o +obj-$(CONFIG_MAX5481) += max5481.o obj-$(CONFIG_MAX5487) += max5487.o obj-$(CONFIG_MCP4131) += mcp4131.o obj-$(CONFIG_MCP4531) += mcp4531.o diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c new file mode 100644 index 000000000000..926554991244 --- /dev/null +++ b/drivers/iio/potentiometer/max5481.c @@ -0,0 +1,223 @@ +/* + * Maxim Integrated MAX5481-MAX5484 digital potentiometer driver + * Copyright 2016 Rockwell Collins + * + * Datasheet: + * http://datasheets.maximintegrated.com/en/ds/MAX5481-MAX5484.pdf + * + * 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 +#include +#include +#include +#include +#include + +/* write wiper reg */ +#define MAX5481_WRITE_WIPER (0 << 4) +/* copy wiper reg to NV reg */ +#define MAX5481_COPY_AB_TO_NV (2 << 4) +/* copy NV reg to wiper reg */ +#define MAX5481_COPY_NV_TO_AB (3 << 4) + +#define MAX5481_MAX_POS 1023 + +enum max5481_variant { + max5481, + max5482, + max5483, + max5484, +}; + +struct max5481_cfg { + int kohms; +}; + +static const struct max5481_cfg max5481_cfg[] = { + [max5481] = { .kohms = 10, }, + [max5482] = { .kohms = 50, }, + [max5483] = { .kohms = 10, }, + [max5484] = { .kohms = 50, }, +}; + +struct max5481_data { + struct spi_device *spi; + const struct max5481_cfg *cfg; + u8 msg[3] ____cacheline_aligned; +}; + +#define MAX5481_CHANNEL { \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .output = 1, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec max5481_channels[] = { + MAX5481_CHANNEL, +}; + +static int max5481_write_cmd(struct max5481_data *data, u8 cmd, u16 val) +{ + struct spi_device *spi = data->spi; + + data->msg[0] = cmd; + + switch (cmd) { + case MAX5481_WRITE_WIPER: + data->msg[1] = val >> 2; + data->msg[2] = (val & 0x3) << 6; + return spi_write(spi, data->msg, 3); + + case MAX5481_COPY_AB_TO_NV: + case MAX5481_COPY_NV_TO_AB: + return spi_write(spi, data->msg, 1); + + default: + return -EIO; + } +} + +static int max5481_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max5481_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_SCALE) + return -EINVAL; + + *val = 1000 * data->cfg->kohms; + *val2 = MAX5481_MAX_POS; + + return IIO_VAL_FRACTIONAL; +} + +static int max5481_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct max5481_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + if (val < 0 || val > MAX5481_MAX_POS) + return -EINVAL; + + return max5481_write_cmd(data, MAX5481_WRITE_WIPER, val); +} + +static const struct iio_info max5481_info = { + .read_raw = max5481_read_raw, + .write_raw = max5481_write_raw, + .driver_module = THIS_MODULE, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id max5481_match[] = { + { .compatible = "maxim,max5481", .data = &max5481_cfg[max5481] }, + { .compatible = "maxim,max5482", .data = &max5481_cfg[max5482] }, + { .compatible = "maxim,max5483", .data = &max5481_cfg[max5483] }, + { .compatible = "maxim,max5484", .data = &max5481_cfg[max5484] }, + { } +}; +MODULE_DEVICE_TABLE(of, max5481_match); +#endif + +static int max5481_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max5481_data *data; + const struct spi_device_id *id = spi_get_device_id(spi); + const struct of_device_id *match; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, indio_dev); + data = iio_priv(indio_dev); + + data->spi = spi; + + match = of_match_device(of_match_ptr(max5481_match), &spi->dev); + if (match) + data->cfg = of_device_get_match_data(&spi->dev); + else + data->cfg = &max5481_cfg[id->driver_data]; + + indio_dev->name = id->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* variant specific configuration */ + indio_dev->info = &max5481_info; + indio_dev->channels = max5481_channels; + indio_dev->num_channels = ARRAY_SIZE(max5481_channels); + + /* restore wiper from NV */ + ret = max5481_write_cmd(data, MAX5481_COPY_NV_TO_AB, 0); + if (ret < 0) + return ret; + + return iio_device_register(indio_dev); +} + +static int max5481_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + struct max5481_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + /* save wiper reg to NV reg */ + return max5481_write_cmd(data, MAX5481_COPY_AB_TO_NV, 0); +} + +static const struct spi_device_id max5481_id_table[] = { + { "max5481", max5481 }, + { "max5482", max5482 }, + { "max5483", max5483 }, + { "max5484", max5484 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max5481_id_table); + +#if defined(CONFIG_ACPI) +static const struct acpi_device_id max5481_acpi_match[] = { + { "max5481", max5481 }, + { "max5482", max5482 }, + { "max5483", max5483 }, + { "max5484", max5484 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, max5481_acpi_match); +#endif + +static struct spi_driver max5481_driver = { + .driver = { + .name = "max5481", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max5481_match), + .acpi_match_table = ACPI_PTR(max5481_acpi_match), + }, + .probe = max5481_probe, + .remove = max5481_remove, + .id_table = max5481_id_table, +}; + +module_spi_driver(max5481_driver); + +MODULE_AUTHOR("Maury Anderson "); +MODULE_DESCRIPTION("max5481 SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index 0d1bcf89ae17..314353d7ab59 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -284,6 +284,7 @@ static const struct of_device_id mcp4531_of_match[] = { MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104), { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mcp4531_of_match); #endif static int mcp4531_probe(struct i2c_client *client, diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index bd8d96b96771..5d16b252ab6b 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -42,6 +42,16 @@ config BMP280_SPI depends on SPI_MASTER select REGMAP +config IIO_CROS_EC_BARO + tristate "ChromeOS EC Barometer Sensor" + depends on IIO_CROS_EC_SENSORS_CORE + help + Say yes here to build support for the Barometer sensor when + presented by the ChromeOS EC Sensor hub. + + To compile this driver as a module, choose M here: the module + will be called cros_ec_baro. + config HID_SENSOR_PRESS depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index de3dbc81dc5a..838642789389 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BMP280) += bmp280.o bmp280-objs := bmp280-core.o bmp280-regmap.o obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o +obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_HP03) += hp03.o obj-$(CONFIG_MPL115) += mpl115.o diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index e5a533cbd53f..4d18826ac63c 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -65,7 +65,7 @@ struct bmp280_data { struct bmp180_calib calib; struct regulator *vddd; struct regulator *vdda; - unsigned int start_up_time; /* in milliseconds */ + unsigned int start_up_time; /* in microseconds */ /* log of base 2 of oversampling rate */ u8 oversampling_press; @@ -935,14 +935,14 @@ int bmp280_common_probe(struct device *dev, data->chip_info = &bmp180_chip_info; data->oversampling_press = ilog2(8); data->oversampling_temp = ilog2(1); - data->start_up_time = 10; + data->start_up_time = 10000; break; case BMP280_CHIP_ID: indio_dev->num_channels = 2; data->chip_info = &bmp280_chip_info; data->oversampling_press = ilog2(16); data->oversampling_temp = ilog2(2); - data->start_up_time = 2; + data->start_up_time = 2000; break; case BME280_CHIP_ID: indio_dev->num_channels = 3; @@ -950,7 +950,7 @@ int bmp280_common_probe(struct device *dev, data->oversampling_press = ilog2(16); data->oversampling_humid = ilog2(16); data->oversampling_temp = ilog2(2); - data->start_up_time = 2; + data->start_up_time = 2000; break; default: return -EINVAL; @@ -979,7 +979,7 @@ int bmp280_common_probe(struct device *dev, goto out_disable_vddd; } /* Wait to make sure we started up properly */ - mdelay(data->start_up_time); + usleep_range(data->start_up_time, data->start_up_time + 100); /* Bring chip out of reset if there is an assigned GPIO line */ gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); @@ -1038,7 +1038,7 @@ int bmp280_common_probe(struct device *dev, * Set autosuspend to two orders of magnitude larger than the * start-up time. */ - pm_runtime_set_autosuspend_delay(dev, data->start_up_time *100); + pm_runtime_set_autosuspend_delay(dev, data->start_up_time / 10); pm_runtime_use_autosuspend(dev); pm_runtime_put(dev); @@ -1101,7 +1101,7 @@ static int bmp280_runtime_resume(struct device *dev) ret = regulator_enable(data->vdda); if (ret) return ret; - msleep(data->start_up_time); + usleep_range(data->start_up_time, data->start_up_time + 100); return data->chip_info->chip_config(data); } #endif /* CONFIG_PM */ diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c new file mode 100644 index 000000000000..48b2a30f57ae --- /dev/null +++ b/drivers/iio/pressure/cros_ec_baro.c @@ -0,0 +1,220 @@ +/* + * cros_ec_baro - Driver for barometer sensor behind CrosEC. + * + * Copyright (C) 2017 Google, 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 +#include +#include +#include +#include +#include +#include + +#include "../common/cros_ec_sensors/cros_ec_sensors_core.h" + +/* + * One channel for pressure, the other for timestamp. + */ +#define CROS_EC_BARO_MAX_CHANNELS (1 + 1) + +/* State data for ec_sensors iio driver. */ +struct cros_ec_baro_state { + /* Shared by all sensors */ + struct cros_ec_sensors_core_state core; + + struct iio_chan_spec channels[CROS_EC_BARO_MAX_CHANNELS]; +}; + +static int cros_ec_baro_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cros_ec_baro_state *st = iio_priv(indio_dev); + u16 data = 0; + int ret = IIO_VAL_INT; + int idx = chan->scan_index; + + mutex_lock(&st->core.cmd_lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx, + (s16 *)&data) < 0) + ret = -EIO; + *val = data; + break; + case IIO_CHAN_INFO_SCALE: + st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE; + st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE; + + if (cros_ec_motion_send_host_cmd(&st->core, 0)) { + ret = -EIO; + break; + } + *val = st->core.resp->sensor_range.ret; + + /* scale * in_pressure_raw --> kPa */ + *val2 = 10 << CROS_EC_SENSOR_BITS; + ret = IIO_VAL_FRACTIONAL; + break; + default: + ret = cros_ec_sensors_core_read(&st->core, chan, val, val2, + mask); + break; + } + + mutex_unlock(&st->core.cmd_lock); + + return ret; +} + +static int cros_ec_baro_write(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct cros_ec_baro_state *st = iio_priv(indio_dev); + int ret = 0; + + mutex_lock(&st->core.cmd_lock); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE; + st->core.param.sensor_range.data = val; + + /* Always roundup, so caller gets at least what it asks for. */ + st->core.param.sensor_range.roundup = 1; + + if (cros_ec_motion_send_host_cmd(&st->core, 0)) + ret = -EIO; + break; + default: + ret = cros_ec_sensors_core_write(&st->core, chan, val, val2, + mask); + break; + } + + mutex_unlock(&st->core.cmd_lock); + + return ret; +} + +static const struct iio_info cros_ec_baro_info = { + .read_raw = &cros_ec_baro_read, + .write_raw = &cros_ec_baro_write, + .driver_module = THIS_MODULE, +}; + +static int cros_ec_baro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); + struct cros_ec_device *ec_device; + struct iio_dev *indio_dev; + struct cros_ec_baro_state *state; + struct iio_chan_spec *channel; + int ret; + + if (!ec_dev || !ec_dev->ec_dev) { + dev_warn(dev, "No CROS EC device found.\n"); + return -EINVAL; + } + ec_device = ec_dev->ec_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + ret = cros_ec_sensors_core_init(pdev, indio_dev, true); + if (ret) + return ret; + + indio_dev->info = &cros_ec_baro_info; + state = iio_priv(indio_dev); + state->core.type = state->core.resp->info.type; + state->core.loc = state->core.resp->info.location; + channel = state->channels; + /* Common part */ + channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + channel->info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_FREQUENCY); + channel->scan_type.realbits = CROS_EC_SENSOR_BITS; + channel->scan_type.storagebits = CROS_EC_SENSOR_BITS; + channel->scan_type.shift = 0; + channel->scan_index = 0; + channel->ext_info = cros_ec_sensors_ext_info; + channel->scan_type.sign = 'u'; + + state->core.calib[0] = 0; + + /* Sensor specific */ + switch (state->core.type) { + case MOTIONSENSE_TYPE_BARO: + channel->type = IIO_PRESSURE; + break; + default: + dev_warn(dev, "Unknown motion sensor\n"); + return -EINVAL; + } + + /* Timestamp */ + channel++; + channel->type = IIO_TIMESTAMP; + channel->channel = -1; + channel->scan_index = 1; + channel->scan_type.sign = 's'; + channel->scan_type.realbits = 64; + channel->scan_type.storagebits = 64; + + indio_dev->channels = state->channels; + indio_dev->num_channels = CROS_EC_BARO_MAX_CHANNELS; + + state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + cros_ec_sensors_capture, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id cros_ec_baro_ids[] = { + { + .name = "cros-ec-baro", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, cros_ec_baro_ids); + +static struct platform_driver cros_ec_baro_platform_driver = { + .driver = { + .name = "cros-ec-baro", + }, + .probe = cros_ec_baro_probe, + .id_table = cros_ec_baro_ids, +}; +module_platform_driver(cros_ec_baro_platform_driver); + +MODULE_DESCRIPTION("ChromeOS EC barometer sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 73f2f0c46e62..8f2bce213248 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -137,6 +137,7 @@ static const struct iio_chan_spec mpl115_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, }; diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index cc3f84139157..525644a7442d 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -190,7 +190,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { { .type = IIO_PRESSURE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .scan_index = 0, .scan_type = { .sign = 'u', @@ -203,7 +203,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .scan_index = 1, .scan_type = { .sign = 's', diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 6bd53e702667..2a77a2f15752 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -308,6 +308,7 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, { struct ms5611_state *st = iio_priv(indio_dev); const struct ms5611_osr *osr = NULL; + int ret; if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) return -EINVAL; @@ -321,12 +322,11 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, if (!osr) return -EINVAL; - mutex_lock(&st->lock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&st->lock); - return -EBUSY; - } + mutex_lock(&st->lock); if (chan->type == IIO_TEMP) st->temp_osr = osr; @@ -334,6 +334,8 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, st->pressure_osr = osr; mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + return 0; } diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index 903a21e46874..7d995937adba 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -14,6 +14,14 @@ #include #include +enum st_press_type { + LPS001WP, + LPS25H, + LPS331AP, + LPS22HB, + ST_PRESS_MAX, +}; + #define LPS001WP_PRESS_DEV_NAME "lps001wp" #define LPS25H_PRESS_DEV_NAME "lps25h" #define LPS331AP_PRESS_DEV_NAME "lps331ap" diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index e19e0787864c..5f2680855552 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -136,20 +136,21 @@ static const struct iio_chan_spec st_press_1_channels[] = { .address = ST_PRESS_1_OUT_XL_ADDR, .scan_index = 0, .scan_type = { - .sign = 'u', + .sign = 's', .realbits = 24, .storagebits = 32, .endianness = IIO_LE, }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_TEMP, .address = ST_TEMP_1_OUT_L_ADDR, .scan_index = 1, .scan_type = { - .sign = 'u', + .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_LE, @@ -158,6 +159,7 @@ static const struct iio_chan_spec st_press_1_channels[] = { BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, IIO_CHAN_SOFT_TIMESTAMP(2) }; @@ -168,7 +170,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = { .address = ST_PRESS_LPS001WP_OUT_L_ADDR, .scan_index = 0, .scan_type = { - .sign = 'u', + .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_LE, @@ -182,7 +184,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = { .address = ST_TEMP_LPS001WP_OUT_L_ADDR, .scan_index = 1, .scan_type = { - .sign = 'u', + .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_LE, @@ -200,7 +202,7 @@ static const struct iio_chan_spec st_press_lps22hb_channels[] = { .address = ST_PRESS_1_OUT_XL_ADDR, .scan_index = 0, .scan_type = { - .sign = 'u', + .sign = 's', .realbits = 24, .storagebits = 32, .endianness = IIO_LE, diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index ed18701c68c9..17417a4d5a5f 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -43,25 +44,56 @@ MODULE_DEVICE_TABLE(of, st_press_of_match); #define st_press_of_match NULL #endif +#ifdef CONFIG_ACPI +static const struct acpi_device_id st_press_acpi_match[] = { + {"SNO9210", LPS22HB}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, st_press_acpi_match); +#else +#define st_press_acpi_match NULL +#endif + +static const struct i2c_device_id st_press_id_table[] = { + { LPS001WP_PRESS_DEV_NAME, LPS001WP }, + { LPS25H_PRESS_DEV_NAME, LPS25H }, + { LPS331AP_PRESS_DEV_NAME, LPS331AP }, + { LPS22HB_PRESS_DEV_NAME, LPS22HB }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_press_id_table); + static int st_press_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct st_sensor_data *press_data; - int err; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*press_data)); if (!indio_dev) return -ENOMEM; press_data = iio_priv(indio_dev); - st_sensors_of_i2c_probe(client, st_press_of_match); + + if (client->dev.of_node) { + st_sensors_of_i2c_probe(client, st_press_of_match); + } else if (ACPI_HANDLE(&client->dev)) { + ret = st_sensors_match_acpi_device(&client->dev); + if ((ret < 0) || (ret >= ST_PRESS_MAX)) + return -ENODEV; + + strncpy(client->name, st_press_id_table[ret].name, + sizeof(client->name)); + client->name[sizeof(client->name) - 1] = '\0'; + } else if (!id) + return -ENODEV; st_sensors_i2c_configure(indio_dev, client, press_data); - err = st_press_common_probe(indio_dev); - if (err < 0) - return err; + ret = st_press_common_probe(indio_dev); + if (ret < 0) + return ret; return 0; } @@ -73,18 +105,11 @@ static int st_press_i2c_remove(struct i2c_client *client) return 0; } -static const struct i2c_device_id st_press_id_table[] = { - { LPS001WP_PRESS_DEV_NAME }, - { LPS25H_PRESS_DEV_NAME }, - { LPS331AP_PRESS_DEV_NAME }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, st_press_id_table); - static struct i2c_driver st_press_driver = { .driver = { .name = "st-press-i2c", .of_match_table = of_match_ptr(st_press_of_match), + .acpi_match_table = ACPI_PTR(st_press_acpi_match), }, .probe = st_press_i2c_probe, .remove = st_press_i2c_remove, diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index ef4c73db5b53..ab96cb7a0054 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -18,7 +18,7 @@ config AS3935 endmenu -menu "Proximity sensors" +menu "Proximity and distance sensors" config LIDAR_LITE_V2 tristate "PulsedLight LIDAR sensor" @@ -45,4 +45,15 @@ config SX9500 To compile this driver as a module, choose M here: the module will be called sx9500. +config SRF08 + tristate "Devantech SRF08 ultrasonic ranger sensor" + depends on I2C + help + Say Y here to build a driver for Devantech SRF08 ultrasonic + ranger sensor. This driver can be used to measure the distance + of objects. + + To compile this driver as a module, choose M here: the + module will be called srf08. + endmenu diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 9aadd9a8ee99..e914c2a5dd49 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -5,4 +5,5 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o +obj-$(CONFIG_SRF08) += srf08.o obj-$(CONFIG_SX9500) += sx9500.o diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 1fa9eefa0982..20c16a08c9d9 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -326,12 +326,14 @@ static int lidar_remove(struct i2c_client *client) static const struct i2c_device_id lidar_id[] = { {"lidar-lite-v2", 0}, + {"lidar-lite-v3", 0}, { }, }; MODULE_DEVICE_TABLE(i2c, lidar_id); static const struct of_device_id lidar_dt_ids[] = { { .compatible = "pulsedlight,lidar-lite-v2" }, + { .compatible = "grmn,lidar-lite-v3" }, { } }; MODULE_DEVICE_TABLE(of, lidar_dt_ids); diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c new file mode 100644 index 000000000000..49316cbf7c60 --- /dev/null +++ b/drivers/iio/proximity/srf08.c @@ -0,0 +1,398 @@ +/* + * srf08.c - Support for Devantech SRF08 ultrasonic ranger + * + * Copyright (c) 2016 Andreas Klinger + * + * 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. + * + * For details about the device see: + * http://www.robot-electronics.co.uk/htm/srf08tech.html + */ + +#include +#include +#include +#include +#include +#include +#include + +/* registers of SRF08 device */ +#define SRF08_WRITE_COMMAND 0x00 /* Command Register */ +#define SRF08_WRITE_MAX_GAIN 0x01 /* Max Gain Register: 0 .. 31 */ +#define SRF08_WRITE_RANGE 0x02 /* Range Register: 0 .. 255 */ +#define SRF08_READ_SW_REVISION 0x00 /* Software Revision */ +#define SRF08_READ_LIGHT 0x01 /* Light Sensor during last echo */ +#define SRF08_READ_ECHO_1_HIGH 0x02 /* Range of first echo received */ +#define SRF08_READ_ECHO_1_LOW 0x03 /* Range of first echo received */ + +#define SRF08_CMD_RANGING_CM 0x51 /* Ranging Mode - Result in cm */ + +#define SRF08_DEFAULT_GAIN 1025 /* default analogue value of Gain */ +#define SRF08_DEFAULT_RANGE 6020 /* default value of Range in mm */ + +struct srf08_data { + struct i2c_client *client; + int sensitivity; /* Gain */ + int range_mm; /* max. Range in mm */ + struct mutex lock; +}; + +/* + * in the documentation one can read about the "Gain" of the device + * which is used here for amplifying the signal and filtering out unwanted + * ones. + * But with ADC's this term is already used differently and that's why it + * is called "Sensitivity" here. + */ +static const int srf08_sensitivity[] = { + 94, 97, 100, 103, 107, 110, 114, 118, + 123, 128, 133, 139, 145, 152, 159, 168, + 177, 187, 199, 212, 227, 245, 265, 288, + 317, 352, 395, 450, 524, 626, 777, 1025 }; + +static int srf08_read_ranging(struct srf08_data *data) +{ + struct i2c_client *client = data->client; + int ret, i; + int waittime; + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, + SRF08_WRITE_COMMAND, SRF08_CMD_RANGING_CM); + if (ret < 0) { + dev_err(&client->dev, "write command - err: %d\n", ret); + mutex_unlock(&data->lock); + return ret; + } + + /* + * we read here until a correct version number shows up as + * suggested by the documentation + * + * with an ultrasonic speed of 343 m/s and a roundtrip of it + * sleep the expected duration and try to read from the device + * if nothing useful is read try it in a shorter grid + * + * polling for not more than 20 ms should be enough + */ + waittime = 1 + data->range_mm / 172; + msleep(waittime); + for (i = 0; i < 4; i++) { + ret = i2c_smbus_read_byte_data(data->client, + SRF08_READ_SW_REVISION); + + /* check if a valid version number is read */ + if (ret < 255 && ret > 0) + break; + msleep(5); + } + + if (ret >= 255 || ret <= 0) { + dev_err(&client->dev, "device not ready\n"); + mutex_unlock(&data->lock); + return -EIO; + } + + ret = i2c_smbus_read_word_swapped(data->client, + SRF08_READ_ECHO_1_HIGH); + if (ret < 0) { + dev_err(&client->dev, "cannot read distance: ret=%d\n", ret); + mutex_unlock(&data->lock); + return ret; + } + + mutex_unlock(&data->lock); + + return ret; +} + +static int srf08_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct srf08_data *data = iio_priv(indio_dev); + int ret; + + if (channel->type != IIO_DISTANCE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = srf08_read_ranging(data); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* 1 LSB is 1 cm */ + *val = 0; + *val2 = 10000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static ssize_t srf08_show_range_mm_available(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "[0.043 0.043 11.008]\n"); +} + +static IIO_DEVICE_ATTR(sensor_max_range_available, S_IRUGO, + srf08_show_range_mm_available, NULL, 0); + +static ssize_t srf08_show_range_mm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct srf08_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%d.%03d\n", data->range_mm / 1000, + data->range_mm % 1000); +} + +/* + * set the range of the sensor to an even multiple of 43 mm + * which corresponds to 1 LSB in the register + * + * register value corresponding range + * 0x00 43 mm + * 0x01 86 mm + * 0x02 129 mm + * ... + * 0xFF 11008 mm + */ +static ssize_t srf08_write_range_mm(struct srf08_data *data, unsigned int val) +{ + int ret; + struct i2c_client *client = data->client; + unsigned int mod; + u8 regval; + + ret = val / 43 - 1; + mod = val % 43; + + if (mod || (ret < 0) || (ret > 255)) + return -EINVAL; + + regval = ret; + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_byte_data(client, SRF08_WRITE_RANGE, regval); + if (ret < 0) { + dev_err(&client->dev, "write_range - err: %d\n", ret); + mutex_unlock(&data->lock); + return ret; + } + + data->range_mm = val; + + mutex_unlock(&data->lock); + + return 0; +} + +static ssize_t srf08_store_range_mm(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct srf08_data *data = iio_priv(indio_dev); + int ret; + int integer, fract; + + ret = iio_str_to_fixpoint(buf, 100, &integer, &fract); + if (ret) + return ret; + + ret = srf08_write_range_mm(data, integer * 1000 + fract); + if (ret < 0) + return ret; + + return len; +} + +static IIO_DEVICE_ATTR(sensor_max_range, S_IRUGO | S_IWUSR, + srf08_show_range_mm, srf08_store_range_mm, 0); + +static ssize_t srf08_show_sensitivity_available(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++) + len += sprintf(buf + len, "%d ", srf08_sensitivity[i]); + + len += sprintf(buf + len, "\n"); + + return len; +} + +static IIO_DEVICE_ATTR(sensor_sensitivity_available, S_IRUGO, + srf08_show_sensitivity_available, NULL, 0); + +static ssize_t srf08_show_sensitivity(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct srf08_data *data = iio_priv(indio_dev); + int len; + + len = sprintf(buf, "%d\n", data->sensitivity); + + return len; +} + +static ssize_t srf08_write_sensitivity(struct srf08_data *data, + unsigned int val) +{ + struct i2c_client *client = data->client; + int ret, i; + u8 regval; + + for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++) + if (val == srf08_sensitivity[i]) { + regval = i; + break; + } + + if (i >= ARRAY_SIZE(srf08_sensitivity)) + return -EINVAL; + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_byte_data(client, + SRF08_WRITE_MAX_GAIN, regval); + if (ret < 0) { + dev_err(&client->dev, "write_sensitivity - err: %d\n", ret); + mutex_unlock(&data->lock); + return ret; + } + + data->sensitivity = val; + + mutex_unlock(&data->lock); + + return 0; +} + +static ssize_t srf08_store_sensitivity(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct srf08_data *data = iio_priv(indio_dev); + int ret; + unsigned int val; + + ret = kstrtouint(buf, 10, &val); + if (ret) + return ret; + + ret = srf08_write_sensitivity(data, val); + if (ret < 0) + return ret; + + return len; +} + +static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR, + srf08_show_sensitivity, srf08_store_sensitivity, 0); + +static struct attribute *srf08_attributes[] = { + &iio_dev_attr_sensor_max_range.dev_attr.attr, + &iio_dev_attr_sensor_max_range_available.dev_attr.attr, + &iio_dev_attr_sensor_sensitivity.dev_attr.attr, + &iio_dev_attr_sensor_sensitivity_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group srf08_attribute_group = { + .attrs = srf08_attributes, +}; + +static const struct iio_chan_spec srf08_channels[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_info srf08_info = { + .read_raw = srf08_read_raw, + .attrs = &srf08_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int srf08_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct srf08_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->name = "srf08"; + indio_dev->dev.parent = &client->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &srf08_info; + indio_dev->channels = srf08_channels; + indio_dev->num_channels = ARRAY_SIZE(srf08_channels); + + mutex_init(&data->lock); + + /* + * set default values of device here + * these register values cannot be read from the hardware + * therefore set driver specific default values + */ + ret = srf08_write_range_mm(data, SRF08_DEFAULT_RANGE); + if (ret < 0) + return ret; + + ret = srf08_write_sensitivity(data, SRF08_DEFAULT_GAIN); + if (ret < 0) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id srf08_id[] = { + { "srf08", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, srf08_id); + +static struct i2c_driver srf08_driver = { + .driver = { + .name = "srf08", + }, + .probe = srf08_probe, + .id_table = srf08_id, +}; +module_i2c_driver(srf08_driver); + +MODULE_AUTHOR("Andreas Klinger "); +MODULE_DESCRIPTION("Devantech SRF08 ultrasonic ranger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 1f06282ec793..9ea147f1a50d 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -387,14 +387,18 @@ static int sx9500_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct sx9500_data *data = iio_priv(indio_dev); + int ret; switch (chan->type) { case IIO_PROXIMITY: switch (mask) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - return sx9500_read_proximity(data, chan, val); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = sx9500_read_proximity(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; case IIO_CHAN_INFO_SAMP_FREQ: return sx9500_read_samp_freq(data, val, val2); default: diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index 5ea77a7e261d..3089e8d0a32d 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -39,6 +39,16 @@ config TMP006 This driver can also be built as a module. If so, the module will be called tmp006. +config TMP007 + tristate "TMP007 infrared thermopile sensor with Integrated Math Engine" + depends on I2C + help + If you say yes here you get support for the Texas Instruments + TMP007 infrared thermopile sensor with Integrated Math Engine. + + This driver can also be built as a module. If so, the module will + be called tmp007. + config TSYS01 tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 78c3de0dc3f0..4c4377480726 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_TMP006) += tmp006.o +obj-$(CONFIG_TMP007) += tmp007.o obj-$(CONFIG_TSYS01) += tsys01.o obj-$(CONFIG_TSYS02D) += tsys02d.o diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c new file mode 100644 index 000000000000..f04d0d1f6ac8 --- /dev/null +++ b/drivers/iio/temperature/tmp007.c @@ -0,0 +1,345 @@ +/* + * tmp007.c - Support for TI TMP007 IR thermopile sensor with integrated math engine + * + * Copyright (c) 2017 Manivannan Sadhasivam + * + * 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. + * + * Driver for the Texas Instruments I2C 16-bit IR thermopile sensor + * + * (7-bit I2C slave address (0x40 - 0x47), changeable via ADR pins) + * + * Note: This driver assumes that the sensor has been calibrated beforehand + * + * TODO: ALERT irq, limit threshold events + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TMP007_TDIE 0x01 +#define TMP007_CONFIG 0x02 +#define TMP007_TOBJECT 0x03 +#define TMP007_STATUS 0x04 +#define TMP007_STATUS_MASK 0x05 +#define TMP007_MANUFACTURER_ID 0x1e +#define TMP007_DEVICE_ID 0x1f + +#define TMP007_CONFIG_CONV_EN BIT(12) +#define TMP007_CONFIG_COMP_EN BIT(5) +#define TMP007_CONFIG_TC_EN BIT(6) +#define TMP007_CONFIG_CR_MASK GENMASK(11, 9) +#define TMP007_CONFIG_CR_SHIFT 9 + +#define TMP007_STATUS_CONV_READY BIT(14) +#define TMP007_STATUS_DATA_VALID BIT(9) + +#define TMP007_MANUFACTURER_MAGIC 0x5449 +#define TMP007_DEVICE_MAGIC 0x0078 + +#define TMP007_TEMP_SHIFT 2 + +struct tmp007_data { + struct i2c_client *client; + u16 config; +}; + +static const int tmp007_avgs[5][2] = { {4, 0}, {2, 0}, {1, 0}, + {0, 500000}, {0, 250000} }; + +static int tmp007_read_temperature(struct tmp007_data *data, u8 reg) +{ + s32 ret; + int tries = 50; + + while (tries-- > 0) { + ret = i2c_smbus_read_word_swapped(data->client, + TMP007_STATUS); + if (ret < 0) + return ret; + if ((ret & TMP007_STATUS_CONV_READY) && + !(ret & TMP007_STATUS_DATA_VALID)) + break; + msleep(100); + } + + if (tries < 0) + return -EIO; + + return i2c_smbus_read_word_swapped(data->client, reg); +} + +static int tmp007_powerdown(struct tmp007_data *data) +{ + return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, + data->config & ~TMP007_CONFIG_CONV_EN); +} + +static int tmp007_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct tmp007_data *data = iio_priv(indio_dev); + s32 ret; + int conv_rate; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (channel->channel2) { + case IIO_MOD_TEMP_AMBIENT: /* LSB: 0.03125 degree Celsius */ + ret = i2c_smbus_read_word_swapped(data->client, TMP007_TDIE); + if (ret < 0) + return ret; + break; + case IIO_MOD_TEMP_OBJECT: + ret = tmp007_read_temperature(data, TMP007_TOBJECT); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + *val = sign_extend32(ret, 15) >> TMP007_TEMP_SHIFT; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 31; + *val2 = 250000; + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + conv_rate = (data->config & TMP007_CONFIG_CR_MASK) + >> TMP007_CONFIG_CR_SHIFT; + *val = tmp007_avgs[conv_rate][0]; + *val2 = tmp007_avgs[conv_rate][1]; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int tmp007_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct tmp007_data *data = iio_priv(indio_dev); + int i; + u16 tmp; + + if (mask == IIO_CHAN_INFO_SAMP_FREQ) { + for (i = 0; i < ARRAY_SIZE(tmp007_avgs); i++) { + if ((val == tmp007_avgs[i][0]) && + (val2 == tmp007_avgs[i][1])) { + tmp = data->config & ~TMP007_CONFIG_CR_MASK; + tmp |= (i << TMP007_CONFIG_CR_SHIFT); + + return i2c_smbus_write_word_swapped(data->client, + TMP007_CONFIG, + data->config = tmp); + } + } + } + + return -EINVAL; +} + +static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25"); + +static struct attribute *tmp007_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group tmp007_attribute_group = { + .attrs = tmp007_attributes, +}; + +static const struct iio_chan_spec tmp007_channels[] = { + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_OBJECT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + } +}; + +static const struct iio_info tmp007_info = { + .read_raw = tmp007_read_raw, + .write_raw = tmp007_write_raw, + .attrs = &tmp007_attribute_group, + .driver_module = THIS_MODULE, +}; + +static bool tmp007_identify(struct i2c_client *client) +{ + int manf_id, dev_id; + + manf_id = i2c_smbus_read_word_swapped(client, TMP007_MANUFACTURER_ID); + if (manf_id < 0) + return false; + + dev_id = i2c_smbus_read_word_swapped(client, TMP007_DEVICE_ID); + if (dev_id < 0) + return false; + + return (manf_id == TMP007_MANUFACTURER_MAGIC && dev_id == TMP007_DEVICE_MAGIC); +} + +static int tmp007_probe(struct i2c_client *client, + const struct i2c_device_id *tmp007_id) +{ + struct tmp007_data *data; + struct iio_dev *indio_dev; + int ret; + u16 status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + if (!tmp007_identify(client)) { + dev_err(&client->dev, "TMP007 not found\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = "tmp007"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tmp007_info; + + indio_dev->channels = tmp007_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp007_channels); + + /* + * Set Configuration register: + * 1. Conversion ON + * 2. Comparator mode + * 3. Transient correction enable + */ + + ret = i2c_smbus_read_word_swapped(data->client, TMP007_CONFIG); + if (ret < 0) + return ret; + + data->config = ret; + data->config |= (TMP007_CONFIG_CONV_EN | TMP007_CONFIG_COMP_EN | TMP007_CONFIG_TC_EN); + + ret = i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, + data->config); + if (ret < 0) + return ret; + + /* + * Set Status Mask register: + * 1. Conversion ready enable + * 2. Data valid enable + */ + + ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK); + if (ret < 0) + goto error_powerdown; + + status = ret; + status |= (TMP007_STATUS_CONV_READY | TMP007_STATUS_DATA_VALID); + + ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, status); + if (ret < 0) + goto error_powerdown; + + return iio_device_register(indio_dev); + +error_powerdown: + tmp007_powerdown(data); + + return ret; +} + +static int tmp007_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct tmp007_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + tmp007_powerdown(data); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tmp007_suspend(struct device *dev) +{ + struct tmp007_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return tmp007_powerdown(data); +} + +static int tmp007_resume(struct device *dev) +{ + struct tmp007_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, + data->config | TMP007_CONFIG_CONV_EN); +} +#endif + +static SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume); + +static const struct of_device_id tmp007_of_match[] = { + { .compatible = "ti,tmp007", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tmp007_of_match); + +static const struct i2c_device_id tmp007_id[] = { + { "tmp007", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp007_id); + +static struct i2c_driver tmp007_driver = { + .driver = { + .name = "tmp007", + .of_match_table = of_match_ptr(tmp007_of_match), + .pm = &tmp007_pm_ops, + }, + .probe = tmp007_probe, + .remove = tmp007_remove, + .id_table = tmp007_id, +}; +module_i2c_driver(tmp007_driver); + +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_DESCRIPTION("TI TMP007 IR thermopile sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index 809b2e7d58fa..e4d4e63434db 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -24,6 +24,15 @@ config IIO_INTERRUPT_TRIGGER To compile this driver as a module, choose M here: the module will be called iio-trig-interrupt. +config IIO_STM32_TIMER_TRIGGER + tristate "STM32 Timer Trigger" + depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST + help + Select this option to enable STM32 Timer Trigger + + To compile this driver as a module, choose M here: the + module will be called stm32-timer-trigger. + config IIO_TIGHTLOOP_TRIGGER tristate "A kthread based hammering loop trigger" depends on IIO_SW_TRIGGER diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile index aab4dc23303d..5c4ecd380653 100644 --- a/drivers/iio/trigger/Makefile +++ b/drivers/iio/trigger/Makefile @@ -6,5 +6,6 @@ obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o +obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 572bc6f02ca8..e18f12b74610 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -58,7 +58,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); if (!trig_info) { ret = -ENOMEM; - goto error_put_trigger; + goto error_free_trigger; } iio_trigger_set_drvdata(trig, trig_info); trig_info->irq = irq; @@ -83,8 +83,8 @@ error_release_irq: free_irq(irq, trig); error_free_trig_info: kfree(trig_info); -error_put_trigger: - iio_trigger_put(trig); +error_free_trigger: + iio_trigger_free(trig); error_ret: return ret; } @@ -99,7 +99,7 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev) iio_trigger_unregister(trig); free_irq(trig_info->irq, trig); kfree(trig_info); - iio_trigger_put(trig); + iio_trigger_free(trig); return 0; } diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 3dfab2bc6d69..202e8b89caf2 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -174,7 +174,7 @@ static int iio_sysfs_trigger_probe(int id) return 0; out2: - iio_trigger_put(t->trig); + iio_trigger_free(t->trig); free_t: kfree(t); out1: diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c new file mode 100644 index 000000000000..994b96d19750 --- /dev/null +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) STMicroelectronics 2016 + * + * Author: Benjamin Gaignard + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TRIGGERS 6 + +/* List the triggers created by each timer */ +static const void *triggers_table[][MAX_TRIGGERS] = { + { TIM1_TRGO, TIM1_CH1, TIM1_CH2, TIM1_CH3, TIM1_CH4,}, + { TIM2_TRGO, TIM2_CH1, TIM2_CH2, TIM2_CH3, TIM2_CH4,}, + { TIM3_TRGO, TIM3_CH1, TIM3_CH2, TIM3_CH3, TIM3_CH4,}, + { TIM4_TRGO, TIM4_CH1, TIM4_CH2, TIM4_CH3, TIM4_CH4,}, + { TIM5_TRGO, TIM5_CH1, TIM5_CH2, TIM5_CH3, TIM5_CH4,}, + { TIM6_TRGO,}, + { TIM7_TRGO,}, + { TIM8_TRGO, TIM8_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,}, + { TIM9_TRGO, TIM9_CH1, TIM9_CH2,}, + { }, /* timer 10 */ + { }, /* timer 11 */ + { TIM12_TRGO, TIM12_CH1, TIM12_CH2,}, +}; + +struct stm32_timer_trigger { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + u32 max_arr; + const void *triggers; +}; + +static int stm32_timer_start(struct stm32_timer_trigger *priv, + unsigned int frequency) +{ + unsigned long long prd, div; + int prescaler = 0; + u32 ccer, cr1; + + /* Period and prescaler values depends of clock rate */ + div = (unsigned long long)clk_get_rate(priv->clk); + + do_div(div, frequency); + + prd = div; + + /* + * Increase prescaler value until we get a result that fit + * with auto reload register maximum value. + */ + while (div > priv->max_arr) { + prescaler++; + div = prd; + do_div(div, (prescaler + 1)); + } + prd = div; + + if (prescaler > MAX_TIM_PSC) { + dev_err(priv->dev, "prescaler exceeds the maximum value\n"); + return -EINVAL; + } + + /* Check if nobody else use the timer */ + regmap_read(priv->regmap, TIM_CCER, &ccer); + if (ccer & TIM_CCER_CCXE) + return -EBUSY; + + regmap_read(priv->regmap, TIM_CR1, &cr1); + if (!(cr1 & TIM_CR1_CEN)) + clk_enable(priv->clk); + + regmap_write(priv->regmap, TIM_PSC, prescaler); + regmap_write(priv->regmap, TIM_ARR, prd - 1); + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + + /* Force master mode to update mode */ + regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 0x20); + + /* Make sure that registers are updated */ + regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); + + /* Enable controller */ + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); + + return 0; +} + +static void stm32_timer_stop(struct stm32_timer_trigger *priv) +{ + u32 ccer, cr1; + + regmap_read(priv->regmap, TIM_CCER, &ccer); + if (ccer & TIM_CCER_CCXE) + return; + + regmap_read(priv->regmap, TIM_CR1, &cr1); + if (cr1 & TIM_CR1_CEN) + clk_disable(priv->clk); + + /* Stop timer */ + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + regmap_write(priv->regmap, TIM_PSC, 0); + regmap_write(priv->regmap, TIM_ARR, 0); + + /* Make sure that registers are updated */ + regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); +} + +static ssize_t stm32_tt_store_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig); + unsigned int freq; + int ret; + + ret = kstrtouint(buf, 10, &freq); + if (ret) + return ret; + + if (freq == 0) { + stm32_timer_stop(priv); + } else { + ret = stm32_timer_start(priv, freq); + if (ret) + return ret; + } + + return len; +} + +static ssize_t stm32_tt_read_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig); + u32 psc, arr, cr1; + unsigned long long freq = 0; + + regmap_read(priv->regmap, TIM_CR1, &cr1); + regmap_read(priv->regmap, TIM_PSC, &psc); + regmap_read(priv->regmap, TIM_ARR, &arr); + + if (psc && arr && (cr1 & TIM_CR1_CEN)) { + freq = (unsigned long long)clk_get_rate(priv->clk); + do_div(freq, psc); + do_div(freq, arr); + } + + return sprintf(buf, "%d\n", (unsigned int)freq); +} + +static IIO_DEV_ATTR_SAMP_FREQ(0660, + stm32_tt_read_frequency, + stm32_tt_store_frequency); + +static char *master_mode_table[] = { + "reset", + "enable", + "update", + "compare_pulse", + "OC1REF", + "OC2REF", + "OC3REF", + "OC4REF" +}; + +static ssize_t stm32_tt_show_master_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 cr2; + + regmap_read(priv->regmap, TIM_CR2, &cr2); + cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT; + + return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]); +} + +static ssize_t stm32_tt_store_master_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + int i; + + for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) { + if (!strncmp(master_mode_table[i], buf, + strlen(master_mode_table[i]))) { + regmap_update_bits(priv->regmap, TIM_CR2, + TIM_CR2_MMS, i << TIM_CR2_MMS_SHIFT); + /* Make sure that registers are updated */ + regmap_update_bits(priv->regmap, TIM_EGR, + TIM_EGR_UG, TIM_EGR_UG); + return len; + } + } + + return -EINVAL; +} + +static IIO_CONST_ATTR(master_mode_available, + "reset enable update compare_pulse OC1REF OC2REF OC3REF OC4REF"); + +static IIO_DEVICE_ATTR(master_mode, 0660, + stm32_tt_show_master_mode, + stm32_tt_store_master_mode, + 0); + +static struct attribute *stm32_trigger_attrs[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_dev_attr_master_mode.dev_attr.attr, + &iio_const_attr_master_mode_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group stm32_trigger_attr_group = { + .attrs = stm32_trigger_attrs, +}; + +static const struct attribute_group *stm32_trigger_attr_groups[] = { + &stm32_trigger_attr_group, + NULL, +}; + +static const struct iio_trigger_ops timer_trigger_ops = { + .owner = THIS_MODULE, +}; + +static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) +{ + int ret; + const char * const *cur = priv->triggers; + + while (cur && *cur) { + struct iio_trigger *trig; + + trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur); + if (!trig) + return -ENOMEM; + + trig->dev.parent = priv->dev->parent; + trig->ops = &timer_trigger_ops; + + /* + * sampling frequency and master mode attributes + * should only be available on trgo trigger which + * is always the first in the list. + */ + if (cur == priv->triggers) + trig->dev.groups = stm32_trigger_attr_groups; + + iio_trigger_set_drvdata(trig, priv); + + ret = devm_iio_trigger_register(priv->dev, trig); + if (ret) + return ret; + cur++; + } + + return 0; +} + +/** + * is_stm32_timer_trigger + * @trig: trigger to be checked + * + * return true if the trigger is a valid stm32 iio timer trigger + * either return false + */ +bool is_stm32_timer_trigger(struct iio_trigger *trig) +{ + return (trig->ops == &timer_trigger_ops); +} +EXPORT_SYMBOL(is_stm32_timer_trigger); + +static int stm32_timer_trigger_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stm32_timer_trigger *priv; + struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); + unsigned int index; + int ret; + + if (of_property_read_u32(dev->of_node, "reg", &index)) + return -EINVAL; + + if (index >= ARRAY_SIZE(triggers_table)) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->regmap = ddata->regmap; + priv->clk = ddata->clk; + priv->max_arr = ddata->max_arr; + priv->triggers = triggers_table[index]; + + ret = stm32_setup_iio_triggers(priv); + if (ret) + return ret; + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static const struct of_device_id stm32_trig_of_match[] = { + { .compatible = "st,stm32-timer-trigger", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_trig_of_match); + +static struct platform_driver stm32_timer_trigger_driver = { + .probe = stm32_timer_trigger_probe, + .driver = { + .name = "stm32-timer-trigger", + .of_match_table = stm32_trig_of_match, + }, +}; +module_platform_driver(stm32_timer_trigger_driver); + +MODULE_ALIAS("platform: stm32-timer-trigger"); +MODULE_DESCRIPTION("STMicroelectronics STM32 Timer Trigger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 4ce3b6f11830..d0c14b88b24e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1621,6 +1621,17 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series. +config MFD_STM32_TIMERS + tristate "Support for STM32 Timers" + depends on (ARCH_STM32 && OF) || COMPILE_TEST + select MFD_CORE + select REGMAP + select REGMAP_MMIO + help + Select this option to enable STM32 timers driver used + for PWM and IIO Timer. This driver allow to share the + registers between the others drivers. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index dda4d4f73ad7..876ca8600c51 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -212,3 +212,5 @@ obj-$(CONFIG_MFD_MT6397) += mt6397-core.o obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o + +obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c new file mode 100644 index 000000000000..41bd9017f3d0 --- /dev/null +++ b/drivers/mfd/stm32-timers.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) STMicroelectronics 2016 + * + * Author: Benjamin Gaignard + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include + +static const struct regmap_config stm32_timers_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x400, +}; + +static void stm32_timers_get_arr_size(struct stm32_timers *ddata) +{ + /* + * Only the available bits will be written so when readback + * we get the maximum value of auto reload register + */ + regmap_write(ddata->regmap, TIM_ARR, ~0L); + regmap_read(ddata->regmap, TIM_ARR, &ddata->max_arr); + regmap_write(ddata->regmap, TIM_ARR, 0x0); +} + +static int stm32_timers_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stm32_timers *ddata; + struct resource *res; + void __iomem *mmio; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + ddata->regmap = devm_regmap_init_mmio_clk(dev, "int", mmio, + &stm32_timers_regmap_cfg); + if (IS_ERR(ddata->regmap)) + return PTR_ERR(ddata->regmap); + + ddata->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ddata->clk)) + return PTR_ERR(ddata->clk); + + stm32_timers_get_arr_size(ddata); + + platform_set_drvdata(pdev, ddata); + + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); +} + +static const struct of_device_id stm32_timers_of_match[] = { + { .compatible = "st,stm32-timers", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_timers_of_match); + +static struct platform_driver stm32_timers_driver = { + .probe = stm32_timers_probe, + .driver = { + .name = "stm32-timers", + .of_match_table = stm32_timers_of_match, + }, +}; +module_platform_driver(stm32_timers_driver); + +MODULE_DESCRIPTION("STMicroelectronics STM32 Timers"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index 47268ecedc4d..6f09da4dadb8 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -328,6 +328,9 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec) case MOTIONSENSE_TYPE_ACCEL: sensor_cells[id].name = "cros-ec-accel"; break; + case MOTIONSENSE_TYPE_BARO: + sensor_cells[id].name = "cros-ec-baro"; + break; case MOTIONSENSE_TYPE_GYRO: sensor_cells[id].name = "cros-ec-gyro"; break; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index f92dd41b0395..2d0cfaa6d84c 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -397,6 +397,15 @@ config PWM_STI To compile this driver as a module, choose M here: the module will be called pwm-sti. +config PWM_STM32 + tristate "STMicroelectronics STM32 PWM" + depends on MFD_STM32_TIMERS || COMPILE_TEST + help + Generic PWM framework driver for STM32 SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-stm32. + config PWM_STMPE bool "STMPE expander PWM export" depends on MFD_STMPE diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a48bdb517792..346a83b00f28 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_STI) += pwm-sti.o +obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c new file mode 100644 index 000000000000..6139512aab7b --- /dev/null +++ b/drivers/pwm/pwm-stm32.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) STMicroelectronics 2016 + * + * Author: Gerald Baeza + * + * License terms: GNU General Public License (GPL), version 2 + * + * Inspired by timer-stm32.c from Maxime Coquelin + * pwm-atmel.c from Bo Shen + */ + +#include +#include +#include +#include +#include + +#define CCMR_CHANNEL_SHIFT 8 +#define CCMR_CHANNEL_MASK 0xFF +#define MAX_BREAKINPUT 2 + +struct stm32_pwm { + struct pwm_chip chip; + struct device *dev; + struct clk *clk; + struct regmap *regmap; + u32 max_arr; + bool have_complementary_output; +}; + +struct stm32_breakinput { + u32 index; + u32 level; + u32 filter; +}; + +static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip) +{ + return container_of(chip, struct stm32_pwm, chip); +} + +static u32 active_channels(struct stm32_pwm *dev) +{ + u32 ccer; + + regmap_read(dev->regmap, TIM_CCER, &ccer); + + return ccer & TIM_CCER_CCXE; +} + +static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value) +{ + switch (ch) { + case 0: + return regmap_write(dev->regmap, TIM_CCR1, value); + case 1: + return regmap_write(dev->regmap, TIM_CCR2, value); + case 2: + return regmap_write(dev->regmap, TIM_CCR3, value); + case 3: + return regmap_write(dev->regmap, TIM_CCR4, value); + } + return -EINVAL; +} + +static int stm32_pwm_config(struct stm32_pwm *priv, int ch, + int duty_ns, int period_ns) +{ + unsigned long long prd, div, dty; + unsigned int prescaler = 0; + u32 ccmr, mask, shift; + + /* Period and prescaler values depends on clock rate */ + div = (unsigned long long)clk_get_rate(priv->clk) * period_ns; + + do_div(div, NSEC_PER_SEC); + prd = div; + + while (div > priv->max_arr) { + prescaler++; + div = prd; + do_div(div, prescaler + 1); + } + + prd = div; + + if (prescaler > MAX_TIM_PSC) + return -EINVAL; + + /* + * All channels share the same prescaler and counter so when two + * channels are active at the same time we can't change them + */ + if (active_channels(priv) & ~(1 << ch * 4)) { + u32 psc, arr; + + regmap_read(priv->regmap, TIM_PSC, &psc); + regmap_read(priv->regmap, TIM_ARR, &arr); + + if ((psc != prescaler) || (arr != prd - 1)) + return -EBUSY; + } + + regmap_write(priv->regmap, TIM_PSC, prescaler); + regmap_write(priv->regmap, TIM_ARR, prd - 1); + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + + /* Calculate the duty cycles */ + dty = prd * duty_ns; + do_div(dty, period_ns); + + write_ccrx(priv, ch, dty); + + /* Configure output mode */ + shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; + ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; + mask = CCMR_CHANNEL_MASK << shift; + + if (ch < 2) + regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr); + else + regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr); + + regmap_update_bits(priv->regmap, TIM_BDTR, + TIM_BDTR_MOE | TIM_BDTR_AOE, + TIM_BDTR_MOE | TIM_BDTR_AOE); + + return 0; +} + +static int stm32_pwm_set_polarity(struct stm32_pwm *priv, int ch, + enum pwm_polarity polarity) +{ + u32 mask; + + mask = TIM_CCER_CC1P << (ch * 4); + if (priv->have_complementary_output) + mask |= TIM_CCER_CC1NP << (ch * 4); + + regmap_update_bits(priv->regmap, TIM_CCER, mask, + polarity == PWM_POLARITY_NORMAL ? 0 : mask); + + return 0; +} + +static int stm32_pwm_enable(struct stm32_pwm *priv, int ch) +{ + u32 mask; + int ret; + + ret = clk_enable(priv->clk); + if (ret) + return ret; + + /* Enable channel */ + mask = TIM_CCER_CC1E << (ch * 4); + if (priv->have_complementary_output) + mask |= TIM_CCER_CC1NE << (ch * 4); + + regmap_update_bits(priv->regmap, TIM_CCER, mask, mask); + + /* Make sure that registers are updated */ + regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); + + /* Enable controller */ + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); + + return 0; +} + +static void stm32_pwm_disable(struct stm32_pwm *priv, int ch) +{ + u32 mask; + + /* Disable channel */ + mask = TIM_CCER_CC1E << (ch * 4); + if (priv->have_complementary_output) + mask |= TIM_CCER_CC1NE << (ch * 4); + + regmap_update_bits(priv->regmap, TIM_CCER, mask, 0); + + /* When all channels are disabled, we can disable the controller */ + if (!active_channels(priv)) + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + + clk_disable(priv->clk); +} + +static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + bool enabled; + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + int ret; + + enabled = pwm->state.enabled; + + if (enabled && !state->enabled) { + stm32_pwm_disable(priv, pwm->hwpwm); + return 0; + } + + if (state->polarity != pwm->state.polarity) + stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity); + + ret = stm32_pwm_config(priv, pwm->hwpwm, + state->duty_cycle, state->period); + if (ret) + return ret; + + if (!enabled && state->enabled) + ret = stm32_pwm_enable(priv, pwm->hwpwm); + + return ret; +} + +static const struct pwm_ops stm32pwm_ops = { + .owner = THIS_MODULE, + .apply = stm32_pwm_apply, +}; + +static int stm32_pwm_set_breakinput(struct stm32_pwm *priv, + int index, int level, int filter) +{ + u32 bke = (index == 0) ? TIM_BDTR_BKE : TIM_BDTR_BK2E; + int shift = (index == 0) ? TIM_BDTR_BKF_SHIFT : TIM_BDTR_BK2F_SHIFT; + u32 mask = (index == 0) ? TIM_BDTR_BKE | TIM_BDTR_BKP | TIM_BDTR_BKF + : TIM_BDTR_BK2E | TIM_BDTR_BK2P | TIM_BDTR_BK2F; + u32 bdtr = bke; + + /* + * The both bits could be set since only one will be wrote + * due to mask value. + */ + if (level) + bdtr |= TIM_BDTR_BKP | TIM_BDTR_BK2P; + + bdtr |= (filter & TIM_BDTR_BKF_MASK) << shift; + + regmap_update_bits(priv->regmap, TIM_BDTR, mask, bdtr); + + regmap_read(priv->regmap, TIM_BDTR, &bdtr); + + return (bdtr & bke) ? 0 : -EINVAL; +} + +static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv, + struct device_node *np) +{ + struct stm32_breakinput breakinput[MAX_BREAKINPUT]; + int nb, ret, i, array_size; + + nb = of_property_count_elems_of_size(np, "st,breakinput", + sizeof(struct stm32_breakinput)); + + /* + * Because "st,breakinput" parameter is optional do not make probe + * failed if it doesn't exist. + */ + if (nb <= 0) + return 0; + + if (nb > MAX_BREAKINPUT) + return -EINVAL; + + array_size = nb * sizeof(struct stm32_breakinput) / sizeof(u32); + ret = of_property_read_u32_array(np, "st,breakinput", + (u32 *)breakinput, array_size); + if (ret) + return ret; + + for (i = 0; i < nb && !ret; i++) { + ret = stm32_pwm_set_breakinput(priv, + breakinput[i].index, + breakinput[i].level, + breakinput[i].filter); + } + + return ret; +} + +static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) +{ + u32 ccer; + + /* + * If complementary bit doesn't exist writing 1 will have no + * effect so we can detect it. + */ + regmap_update_bits(priv->regmap, + TIM_CCER, TIM_CCER_CC1NE, TIM_CCER_CC1NE); + regmap_read(priv->regmap, TIM_CCER, &ccer); + regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CC1NE, 0); + + priv->have_complementary_output = (ccer != 0); +} + +static int stm32_pwm_detect_channels(struct stm32_pwm *priv) +{ + u32 ccer; + int npwm = 0; + + /* + * If channels enable bits don't exist writing 1 will have no + * effect so we can detect and count them. + */ + regmap_update_bits(priv->regmap, + TIM_CCER, TIM_CCER_CCXE, TIM_CCER_CCXE); + regmap_read(priv->regmap, TIM_CCER, &ccer); + regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE, 0); + + if (ccer & TIM_CCER_CC1E) + npwm++; + + if (ccer & TIM_CCER_CC2E) + npwm++; + + if (ccer & TIM_CCER_CC3E) + npwm++; + + if (ccer & TIM_CCER_CC4E) + npwm++; + + return npwm; +} + +static int stm32_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); + struct stm32_pwm *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = ddata->regmap; + priv->clk = ddata->clk; + priv->max_arr = ddata->max_arr; + + if (!priv->regmap || !priv->clk) + return -EINVAL; + + ret = stm32_pwm_apply_breakinputs(priv, np); + if (ret) + return ret; + + stm32_pwm_detect_complementary(priv); + + priv->chip.base = -1; + priv->chip.dev = dev; + priv->chip.ops = &stm32pwm_ops; + priv->chip.npwm = stm32_pwm_detect_channels(priv); + + ret = pwmchip_add(&priv->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static int stm32_pwm_remove(struct platform_device *pdev) +{ + struct stm32_pwm *priv = platform_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < priv->chip.npwm; i++) + pwm_disable(&priv->chip.pwms[i]); + + pwmchip_remove(&priv->chip); + + return 0; +} + +static const struct of_device_id stm32_pwm_of_match[] = { + { .compatible = "st,stm32-pwm", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_pwm_of_match); + +static struct platform_driver stm32_pwm_driver = { + .probe = stm32_pwm_probe, + .remove = stm32_pwm_remove, + .driver = { + .name = "stm32-pwm", + .of_match_table = stm32_pwm_of_match, + }, +}; +module_platform_driver(stm32_pwm_driver); + +MODULE_ALIAS("platform:stm32-pwm"); +MODULE_DESCRIPTION("STMicroelectronics STM32 PWM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index cd005cd41413..4c360f8071a8 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -96,12 +96,12 @@ source "drivers/staging/wilc1000/Kconfig" source "drivers/staging/most/Kconfig" -source "drivers/staging/i4l/Kconfig" - source "drivers/staging/ks7010/Kconfig" source "drivers/staging/greybus/Kconfig" source "drivers/staging/vc04_services/Kconfig" +source "drivers/staging/bcm2835-audio/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 831e2e891989..29cec5aa2945 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -37,7 +37,8 @@ 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/ obj-$(CONFIG_KS7010) += ks7010/ obj-$(CONFIG_GREYBUS) += greybus/ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/ +obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/ + diff --git a/drivers/staging/android/ion/ion-ioctl.c b/drivers/staging/android/ion/ion-ioctl.c index 7e7431d8d49f..9ff815ad1cb1 100644 --- a/drivers/staging/android/ion/ion-ioctl.c +++ b/drivers/staging/android/ion/ion-ioctl.c @@ -111,7 +111,8 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct ion_handle *handle; mutex_lock(&client->lock); - handle = ion_handle_get_by_id_nolock(client, data.handle.handle); + handle = ion_handle_get_by_id_nolock(client, + data.handle.handle); if (IS_ERR(handle)) { mutex_unlock(&client->lock); return PTR_ERR(handle); diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c index 6c7de74bc7ab..6c4068523781 100644 --- a/drivers/staging/android/ion/ion_cma_heap.c +++ b/drivers/staging/android/ion/ion_cma_heap.c @@ -24,8 +24,6 @@ #include "ion.h" #include "ion_priv.h" -#define ION_CMA_ALLOCATE_FAILED -1 - struct ion_cma_heap { struct ion_heap heap; struct device *dev; @@ -57,9 +55,9 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, if (align > PAGE_SIZE) return -EINVAL; - info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL); + info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) - return ION_CMA_ALLOCATE_FAILED; + return -ENOMEM; info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), GFP_HIGHUSER | __GFP_ZERO); @@ -69,7 +67,7 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, goto err; } - info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + info->table = kmalloc(sizeof(*info->table), GFP_KERNEL); if (!info->table) goto free_mem; @@ -88,7 +86,7 @@ free_mem: dma_free_coherent(dev, len, info->cpu_addr, info->handle); err: kfree(info); - return ION_CMA_ALLOCATE_FAILED; + return -ENOMEM; } static void ion_cma_free(struct ion_buffer *buffer) @@ -142,7 +140,7 @@ struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) { struct ion_cma_heap *cma_heap; - cma_heap = kzalloc(sizeof(struct ion_cma_heap), GFP_KERNEL); + cma_heap = kzalloc(sizeof(*cma_heap), GFP_KERNEL); if (!cma_heap) return ERR_PTR(-ENOMEM); diff --git a/drivers/staging/android/ion/ion_of.c b/drivers/staging/android/ion/ion_of.c index 46b2bb99bfd6..7791c705ea31 100644 --- a/drivers/staging/android/ion/ion_of.c +++ b/drivers/staging/android/ion/ion_of.c @@ -161,7 +161,6 @@ static int rmem_ion_device_init(struct reserved_mem *rmem, struct device *dev) static void rmem_ion_device_release(struct reserved_mem *rmem, struct device *dev) { - return; } static const struct reserved_mem_ops rmem_dma_ops = { diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h index 3c3b3245275d..5b3059c78431 100644 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -54,7 +54,7 @@ * handle, used for debugging * @pid: pid of last client to reference this buffer in a * handle, used for debugging -*/ + */ struct ion_buffer { struct kref ref; union { @@ -287,10 +287,10 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap); * some helpers for common operations on buffers using the sg_table * and vaddr fields */ -void *ion_heap_map_kernel(struct ion_heap *, struct ion_buffer *); -void ion_heap_unmap_kernel(struct ion_heap *, struct ion_buffer *); -int ion_heap_map_user(struct ion_heap *, struct ion_buffer *, - struct vm_area_struct *); +void *ion_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer); +void ion_heap_unmap_kernel(struct ion_heap *heap, struct ion_buffer *buffer); +int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma); int ion_heap_buffer_zero(struct ion_buffer *buffer); int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot); @@ -371,21 +371,21 @@ size_t ion_heap_freelist_size(struct ion_heap *heap); * heaps as appropriate. */ -struct ion_heap *ion_heap_create(struct ion_platform_heap *); -void ion_heap_destroy(struct ion_heap *); -struct ion_heap *ion_system_heap_create(struct ion_platform_heap *); -void ion_system_heap_destroy(struct ion_heap *); +struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data); +void ion_heap_destroy(struct ion_heap *heap); +struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused); +void ion_system_heap_destroy(struct ion_heap *heap); -struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *); -void ion_system_contig_heap_destroy(struct ion_heap *); +struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *heap); +void ion_system_contig_heap_destroy(struct ion_heap *heap); -struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *); -void ion_carveout_heap_destroy(struct ion_heap *); +struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data); +void ion_carveout_heap_destroy(struct ion_heap *heap); -struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *); -void ion_chunk_heap_destroy(struct ion_heap *); -struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *); -void ion_cma_heap_destroy(struct ion_heap *); +struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data); +void ion_chunk_heap_destroy(struct ion_heap *heap); +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data); +void ion_cma_heap_destroy(struct ion_heap *heap); /** * functions for creating and destroying a heap pool -- allows you @@ -427,9 +427,9 @@ struct ion_page_pool { struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order, bool cached); -void ion_page_pool_destroy(struct ion_page_pool *); -struct page *ion_page_pool_alloc(struct ion_page_pool *); -void ion_page_pool_free(struct ion_page_pool *, struct page *); +void ion_page_pool_destroy(struct ion_page_pool *pool); +struct page *ion_page_pool_alloc(struct ion_page_pool *pool); +void ion_page_pool_free(struct ion_page_pool *pool, struct page *page); /** ion_page_pool_shrink - shrinks the size of the memory cached in the pool * @pool: the pool diff --git a/drivers/staging/bcm2835-audio/Kconfig b/drivers/staging/bcm2835-audio/Kconfig new file mode 100644 index 000000000000..32a2ff9ef9b2 --- /dev/null +++ b/drivers/staging/bcm2835-audio/Kconfig @@ -0,0 +1,7 @@ +config SND_BCM2835 + tristate "BCM2835 ALSA driver" + depends on ARCH_BCM2835 && BCM2835_VCHIQ && SND + select SND_PCM + help + Say Y or M if you want to support BCM2835 Alsa pcm card driver + diff --git a/drivers/staging/bcm2835-audio/Makefile b/drivers/staging/bcm2835-audio/Makefile new file mode 100644 index 000000000000..d7b88d164d15 --- /dev/null +++ b/drivers/staging/bcm2835-audio/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o +snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o + +ccflags-y += -Idrivers/staging/vc04_services -D__VCCOREVER__=0x04000000 + diff --git a/drivers/staging/bcm2835-audio/TODO b/drivers/staging/bcm2835-audio/TODO new file mode 100644 index 000000000000..73d41fa631ac --- /dev/null +++ b/drivers/staging/bcm2835-audio/TODO @@ -0,0 +1,29 @@ +***************************************************************************** +* * +* TODO: BCM2835-AUDIO * +* * +***************************************************************************** + + +1) Document the device tree node + +The downstream tree(the tree that the driver was imported from) at +http://www.github.com/raspberrypi/linux uses this node: + +audio: audio { + compatible = "brcm,bcm2835-audio"; + brcm,pwm-channels = <8>; +}; + +Since the driver requires the use of VCHIQ, it may be useful to have a link +in the device tree to the VCHIQ driver. + +2) Gracefully handle the case where VCHIQ is missing from the device tree or +it has not been initialized yet. + +3) Review error handling and remove duplicate code. + +4) Cleanup the logging mechanism. The driver should probably be using the +standard kernel logging mechanisms such as dev_info, dev_dbg, and friends. + +5) Fix the remaining checkpatch.pl errors and warnings. diff --git a/drivers/staging/bcm2835-audio/bcm2835-ctl.c b/drivers/staging/bcm2835-audio/bcm2835-ctl.c new file mode 100644 index 000000000000..a4ffa1bf53e5 --- /dev/null +++ b/drivers/staging/bcm2835-audio/bcm2835-ctl.c @@ -0,0 +1,345 @@ +/***************************************************************************** + * Copyright 2011 Broadcom Corporation. All rights reserved. + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2, available at + * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a + * license other than the GPL, without Broadcom's express prior written + * consent. + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm2835.h" + +/* volume maximum and minimum in terms of 0.01dB */ +#define CTRL_VOL_MAX 400 +#define CTRL_VOL_MIN -10239 /* originally -10240 */ + +static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + audio_info(" ... IN\n"); + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = CTRL_VOL_MIN; + uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */ + } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = AUDIO_DEST_MAX - 1; + } + audio_info(" ... OUT\n"); + return 0; +} + +/* toggles mute on or off depending on the value of nmute, and returns + * 1 if the mute value was changed, otherwise 0 + */ +static int toggle_mute(struct bcm2835_chip *chip, int nmute) +{ + /* if settings are ok, just return 0 */ + if (chip->mute == nmute) + return 0; + + /* if the sound is muted then we need to unmute */ + if (chip->mute == CTRL_VOL_MUTE) { + chip->volume = chip->old_volume; /* copy the old volume back */ + audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); + } else /* otherwise we mute */ { + chip->old_volume = chip->volume; + chip->volume = 26214; /* set volume to minimum level AKA mute */ + audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); + } + + chip->mute = nmute; + return 1; +} + +static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + + if (mutex_lock_interruptible(&chip->audio_mutex)) + return -EINTR; + + BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); + + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) + ucontrol->value.integer.value[0] = chip2alsa(chip->volume); + else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) + ucontrol->value.integer.value[0] = chip->mute; + else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) + ucontrol->value.integer.value[0] = chip->dest; + + mutex_unlock(&chip->audio_mutex); + return 0; +} + +static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + + if (mutex_lock_interruptible(&chip->audio_mutex)) + return -EINTR; + + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { + audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int) ucontrol->value.integer.value[0]); + if (chip->mute == CTRL_VOL_MUTE) { + /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ + changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ + goto unlock; + } + if (changed + || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { + + chip->volume = alsa2chip(ucontrol->value.integer.value[0]); + changed = 1; + } + + } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { + /* Now implemented */ + audio_info(" Mute attempted\n"); + changed = toggle_mute(chip, ucontrol->value.integer.value[0]); + + } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { + if (ucontrol->value.integer.value[0] != chip->dest) { + chip->dest = ucontrol->value.integer.value[0]; + changed = 1; + } + } + + if (changed) { + if (bcm2835_audio_set_ctls(chip)) + printk(KERN_ERR "Failed to set ALSA controls..\n"); + } + +unlock: + mutex_unlock(&chip->audio_mutex); + return changed; +} + +static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); + +static struct snd_kcontrol_new snd_bcm2835_ctl[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .private_value = PCM_PLAYBACK_VOLUME, + .info = snd_bcm2835_ctl_info, + .get = snd_bcm2835_ctl_get, + .put = snd_bcm2835_ctl_put, + .count = 1, + .tlv = {.p = snd_bcm2835_db_scale} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = PCM_PLAYBACK_MUTE, + .info = snd_bcm2835_ctl_info, + .get = snd_bcm2835_ctl_get, + .put = snd_bcm2835_ctl_put, + .count = 1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Route", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = PCM_PLAYBACK_DEVICE, + .info = snd_bcm2835_ctl_info, + .get = snd_bcm2835_ctl_get, + .put = snd_bcm2835_ctl_put, + .count = 1, + }, +}; + +static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + int i; + + if (mutex_lock_interruptible(&chip->audio_mutex)) + return -EINTR; + + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = + (chip->spdif_status >> (i * 8)) & 0xff; + + mutex_unlock(&chip->audio_mutex); + return 0; +} + +static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + unsigned int val = 0; + int i, change; + + if (mutex_lock_interruptible(&chip->audio_mutex)) + return -EINTR; + + for (i = 0; i < 4; i++) + val |= (unsigned int) ucontrol->value.iec958.status[i] << (i * 8); + + change = val != chip->spdif_status; + chip->spdif_status = val; + + mutex_unlock(&chip->audio_mutex); + return change; +} + +static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* bcm2835 supports only consumer mode and sets all other format flags + * automatically. So the only thing left is signalling non-audio + * content */ + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; + return 0; +} + +static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + int i; + + if (mutex_lock_interruptible(&chip->audio_mutex)) + return -EINTR; + + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = + (chip->spdif_status >> (i * 8)) & 0xff; + + mutex_unlock(&chip->audio_mutex); + return 0; +} + +static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + unsigned int val = 0; + int i, change; + + if (mutex_lock_interruptible(&chip->audio_mutex)) + return -EINTR; + + for (i = 0; i < 4; i++) + val |= (unsigned int) ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->spdif_status; + chip->spdif_status = val; + + mutex_unlock(&chip->audio_mutex); + return change; +} + +static struct snd_kcontrol_new snd_bcm2835_spdif[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_bcm2835_spdif_default_info, + .get = snd_bcm2835_spdif_default_get, + .put = snd_bcm2835_spdif_default_put + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), + .info = snd_bcm2835_spdif_mask_info, + .get = snd_bcm2835_spdif_mask_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .info = snd_bcm2835_spdif_stream_info, + .get = snd_bcm2835_spdif_stream_get, + .put = snd_bcm2835_spdif_stream_put, + }, +}; + +int snd_bcm2835_new_ctl(struct bcm2835_chip *chip) +{ + int err; + unsigned int idx; + + strcpy(chip->card->mixername, "Broadcom Mixer"); + for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&snd_bcm2835_ctl[idx], chip)); + if (err < 0) + return err; + } + for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&snd_bcm2835_spdif[idx], chip)); + if (err < 0) + return err; + } + return 0; +} diff --git a/drivers/staging/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/bcm2835-audio/bcm2835-pcm.c new file mode 100644 index 000000000000..16127e062661 --- /dev/null +++ b/drivers/staging/bcm2835-audio/bcm2835-pcm.c @@ -0,0 +1,554 @@ +/***************************************************************************** + * Copyright 2011 Broadcom Corporation. All rights reserved. + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2, available at + * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a + * license other than the GPL, without Broadcom's express prior written + * consent. + *****************************************************************************/ + +#include +#include + +#include + +#include "bcm2835.h" + +/* hardware definition */ +static struct snd_pcm_hardware snd_bcm2835_playback_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 1 * 1024, + .period_bytes_max = 128 * 1024, + .periods_min = 1, + .periods_max = 128, +}; + +static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 1 * 1024, + .period_bytes_max = 128 * 1024, + .periods_min = 1, + .periods_max = 128, +}; + +static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) +{ + audio_info("Freeing up alsa stream here ..\n"); + if (runtime->private_data) + kfree(runtime->private_data); + runtime->private_data = NULL; +} + +void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream) +{ + unsigned int consumed = 0; + int new_period = 0; + + audio_info(" .. IN\n"); + + audio_info("alsa_stream=%p substream=%p\n", alsa_stream, + alsa_stream ? alsa_stream->substream : 0); + + if (alsa_stream->open) + consumed = bcm2835_audio_retrieve_buffers(alsa_stream); + + /* We get called only if playback was triggered, So, the number of buffers we retrieve in + * each iteration are the buffers that have been played out already + */ + + if (alsa_stream->period_size) { + if ((alsa_stream->pos / alsa_stream->period_size) != + ((alsa_stream->pos + consumed) / alsa_stream->period_size)) + new_period = 1; + } + audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n", + alsa_stream->pos, + consumed, + alsa_stream->buffer_size, + (int) (alsa_stream->period_size * alsa_stream->substream->runtime->periods), + frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr), + new_period); + if (alsa_stream->buffer_size) { + alsa_stream->pos += consumed &~(1 << 30); + alsa_stream->pos %= alsa_stream->buffer_size; + } + + if (alsa_stream->substream) { + if (new_period) + snd_pcm_period_elapsed(alsa_stream->substream); + } else { + audio_warning(" unexpected NULL substream\n"); + } + audio_info(" .. OUT\n"); +} + +/* open callback */ +static int snd_bcm2835_playback_open_generic( + struct snd_pcm_substream *substream, int spdif) +{ + struct bcm2835_chip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm2835_alsa_stream *alsa_stream; + int idx; + int err; + + audio_info(" .. IN (%d)\n", substream->number); + + if (mutex_lock_interruptible(&chip->audio_mutex)) { + audio_error("Interrupted whilst waiting for lock\n"); + return -EINTR; + } + audio_info("Alsa open (%d)\n", substream->number); + idx = substream->number; + + if (spdif && chip->opened) { + err = -EBUSY; + goto out; + } else if (!spdif && (chip->opened & (1 << idx))) { + err = -EBUSY; + goto out; + } + if (idx >= MAX_SUBSTREAMS) { + audio_error + ("substream(%d) device doesn't exist max(%d) substreams allowed\n", + idx, MAX_SUBSTREAMS); + err = -ENODEV; + goto out; + } + + /* Check if we are ready */ + if (!(chip->avail_substreams & (1 << idx))) { + /* We are not ready yet */ + audio_error("substream(%d) device is not ready yet\n", idx); + err = -EAGAIN; + goto out; + } + + alsa_stream = kzalloc(sizeof(*alsa_stream), GFP_KERNEL); + if (!alsa_stream) { + err = -ENOMEM; + goto out; + } + + /* Initialise alsa_stream */ + alsa_stream->chip = chip; + alsa_stream->substream = substream; + alsa_stream->idx = idx; + + sema_init(&alsa_stream->buffers_update_sem, 0); + sema_init(&alsa_stream->control_sem, 0); + spin_lock_init(&alsa_stream->lock); + + err = bcm2835_audio_open(alsa_stream); + if (err) { + kfree(alsa_stream); + goto out; + } + runtime->private_data = alsa_stream; + runtime->private_free = snd_bcm2835_playback_free; + if (spdif) { + runtime->hw = snd_bcm2835_playback_spdif_hw; + } else { + /* clear spdif status, as we are not in spdif mode */ + chip->spdif_status = 0; + runtime->hw = snd_bcm2835_playback_hw; + } + /* minimum 16 bytes alignment (for vchiq bulk transfers) */ + snd_pcm_hw_constraint_step(runtime, + 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + 16); + + chip->alsa_stream[idx] = alsa_stream; + + chip->opened |= (1 << idx); + alsa_stream->open = 1; + alsa_stream->draining = 1; + +out: + mutex_unlock(&chip->audio_mutex); + + audio_info(" .. OUT =%d\n", err); + + return err; +} + +static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) +{ + return snd_bcm2835_playback_open_generic(substream, 0); +} + +static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream) +{ + return snd_bcm2835_playback_open_generic(substream, 1); +} + +/* close callback */ +static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) +{ + /* the hardware-specific codes will be here */ + + struct bcm2835_chip *chip; + struct snd_pcm_runtime *runtime; + struct bcm2835_alsa_stream *alsa_stream; + + audio_info(" .. IN\n"); + + chip = snd_pcm_substream_chip(substream); + if (mutex_lock_interruptible(&chip->audio_mutex)) { + audio_error("Interrupted whilst waiting for lock\n"); + return -EINTR; + } + runtime = substream->runtime; + alsa_stream = runtime->private_data; + + audio_info("Alsa close\n"); + + /* + * Call stop if it's still running. This happens when app + * is force killed and we don't get a stop trigger. + */ + if (alsa_stream->running) { + int err; + err = bcm2835_audio_stop(alsa_stream); + alsa_stream->running = 0; + if (err) + audio_error(" Failed to STOP alsa device\n"); + } + + alsa_stream->period_size = 0; + alsa_stream->buffer_size = 0; + + if (alsa_stream->open) { + alsa_stream->open = 0; + bcm2835_audio_close(alsa_stream); + } + if (alsa_stream->chip) + alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; + /* + * Do not free up alsa_stream here, it will be freed up by + * runtime->private_free callback we registered in *_open above + */ + + chip->opened &= ~(1 << substream->number); + + mutex_unlock(&chip->audio_mutex); + audio_info(" .. OUT\n"); + + return 0; +} + +/* hw_params callback */ +static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; + int err; + + audio_info(" .. IN\n"); + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) { + audio_error + (" pcm_lib_malloc failed to allocated pages for buffers\n"); + return err; + } + + alsa_stream->channels = params_channels(params); + alsa_stream->params_rate = params_rate(params); + alsa_stream->pcm_format_width = snd_pcm_format_width(params_format(params)); + audio_info(" .. OUT\n"); + + return err; +} + +/* hw_free callback */ +static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream) +{ + audio_info(" .. IN\n"); + return snd_pcm_lib_free_pages(substream); +} + +/* prepare callback */ +static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct bcm2835_chip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; + int channels; + int err; + + audio_info(" .. IN\n"); + + if (mutex_lock_interruptible(&chip->audio_mutex)) + return -EINTR; + + /* notify the vchiq that it should enter spdif passthrough mode by + * setting channels=0 (see + * https://github.com/raspberrypi/linux/issues/528) */ + if (chip->spdif_status & IEC958_AES0_NONAUDIO) + channels = 0; + else + channels = alsa_stream->channels; + + err = bcm2835_audio_set_params(alsa_stream, channels, + alsa_stream->params_rate, + alsa_stream->pcm_format_width); + if (err < 0) { + audio_error(" error setting hw params\n"); + } + + bcm2835_audio_setup(alsa_stream); + + /* in preparation of the stream, set the controls (volume level) of the stream */ + bcm2835_audio_set_ctls(alsa_stream->chip); + + + memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect)); + + alsa_stream->pcm_indirect.hw_buffer_size = + alsa_stream->pcm_indirect.sw_buffer_size = + snd_pcm_lib_buffer_bytes(substream); + + alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); + alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); + alsa_stream->pos = 0; + + audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", + alsa_stream->buffer_size, alsa_stream->period_size, + alsa_stream->pos, runtime->frame_bits); + + mutex_unlock(&chip->audio_mutex); + audio_info(" .. OUT\n"); + return 0; +} + +static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream, + struct snd_pcm_indirect *rec, size_t bytes) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; + void *src = (void *) (substream->runtime->dma_area + rec->sw_data); + int err; + + err = bcm2835_audio_write(alsa_stream, bytes, src); + if (err) + audio_error(" Failed to transfer to alsa device (%d)\n", err); + +} + +static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; + struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; + + pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max; + snd_pcm_indirect_playback_transfer(substream, pcm_indirect, + snd_bcm2835_pcm_transfer); + return 0; +} + +/* trigger callback */ +static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; + int err = 0; + + audio_info(" .. IN\n"); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n", + alsa_stream->running); + if (!alsa_stream->running) { + err = bcm2835_audio_start(alsa_stream); + if (!err) { + alsa_stream->pcm_indirect.hw_io = + alsa_stream->pcm_indirect.hw_data = + bytes_to_frames(runtime, + alsa_stream->pos); + substream->ops->ack(substream); + alsa_stream->running = 1; + alsa_stream->draining = 1; + } else { + audio_error(" Failed to START alsa device (%d)\n", err); + } + } + break; + case SNDRV_PCM_TRIGGER_STOP: + audio_debug + ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n", + alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING); + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + audio_info("DRAINING\n"); + alsa_stream->draining = 1; + } else { + audio_info("DROPPING\n"); + alsa_stream->draining = 0; + } + if (alsa_stream->running) { + err = bcm2835_audio_stop(alsa_stream); + if (err != 0) + audio_error(" Failed to STOP alsa device (%d)\n", err); + alsa_stream->running = 0; + } + break; + default: + err = -EINVAL; + } + + audio_info(" .. OUT\n"); + return err; +} + +/* pointer callback */ +static snd_pcm_uframes_t +snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; + + audio_info(" .. IN\n"); + + audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0, + frames_to_bytes(runtime, runtime->status->hw_ptr), + frames_to_bytes(runtime, runtime->control->appl_ptr), + alsa_stream->pos); + + audio_info(" .. OUT\n"); + return snd_pcm_indirect_playback_pointer(substream, + &alsa_stream->pcm_indirect, + alsa_stream->pos); +} + +static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int ret = snd_pcm_lib_ioctl(substream, cmd, arg); + + audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream, + cmd, arg, arg ? *(unsigned *) arg : 0, ret); + return ret; +} + +/* operators */ +static struct snd_pcm_ops snd_bcm2835_playback_ops = { + .open = snd_bcm2835_playback_open, + .close = snd_bcm2835_playback_close, + .ioctl = snd_bcm2835_pcm_lib_ioctl, + .hw_params = snd_bcm2835_pcm_hw_params, + .hw_free = snd_bcm2835_pcm_hw_free, + .prepare = snd_bcm2835_pcm_prepare, + .trigger = snd_bcm2835_pcm_trigger, + .pointer = snd_bcm2835_pcm_pointer, + .ack = snd_bcm2835_pcm_ack, +}; + +static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = { + .open = snd_bcm2835_playback_spdif_open, + .close = snd_bcm2835_playback_close, + .ioctl = snd_bcm2835_pcm_lib_ioctl, + .hw_params = snd_bcm2835_pcm_hw_params, + .hw_free = snd_bcm2835_pcm_hw_free, + .prepare = snd_bcm2835_pcm_prepare, + .trigger = snd_bcm2835_pcm_trigger, + .pointer = snd_bcm2835_pcm_pointer, + .ack = snd_bcm2835_pcm_ack, +}; + +/* create a pcm device */ +int snd_bcm2835_new_pcm(struct bcm2835_chip *chip) +{ + struct snd_pcm *pcm; + int err; + + audio_info(" .. IN\n"); + mutex_init(&chip->audio_mutex); + if (mutex_lock_interruptible(&chip->audio_mutex)) { + audio_error("Interrupted whilst waiting for lock\n"); + return -EINTR; + } + err = snd_pcm_new(chip->card, "bcm2835 ALSA", 0, MAX_SUBSTREAMS, 0, &pcm); + if (err < 0) + goto out; + pcm->private_data = chip; + strcpy(pcm->name, "bcm2835 ALSA"); + chip->pcm = pcm; + chip->dest = AUDIO_DEST_AUTO; + chip->volume = alsa2chip(0); + chip->mute = CTRL_VOL_UNMUTE; /*disable mute on startup */ + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_bcm2835_playback_ops); + + /* pre-allocation of buffers */ + /* NOTE: this may fail */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + snd_bcm2835_playback_hw.buffer_bytes_max, + snd_bcm2835_playback_hw.buffer_bytes_max); + + +out: + mutex_unlock(&chip->audio_mutex); + audio_info(" .. OUT\n"); + + return 0; +} + +int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip) +{ + struct snd_pcm *pcm; + int err; + + audio_info(" .. IN\n"); + if (mutex_lock_interruptible(&chip->audio_mutex)) { + audio_error("Interrupted whilst waiting for lock\n"); + return -EINTR; + } + err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm); + if (err < 0) + goto out; + + pcm->private_data = chip; + strcpy(pcm->name, "bcm2835 IEC958/HDMI"); + chip->pcm_spdif = pcm; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_bcm2835_playback_spdif_ops); + + /* pre-allocation of buffers */ + /* NOTE: this may fail */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + snd_bcm2835_playback_spdif_hw.buffer_bytes_max, snd_bcm2835_playback_spdif_hw.buffer_bytes_max); +out: + mutex_unlock(&chip->audio_mutex); + audio_info(" .. OUT\n"); + + return 0; +} diff --git a/drivers/staging/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/bcm2835-audio/bcm2835-vchiq.c new file mode 100644 index 000000000000..fa23a13f8d95 --- /dev/null +++ b/drivers/staging/bcm2835-audio/bcm2835-vchiq.c @@ -0,0 +1,912 @@ +/***************************************************************************** + * Copyright 2011 Broadcom Corporation. All rights reserved. + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2, available at + * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a + * license other than the GPL, without Broadcom's express prior written + * consent. + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm2835.h" + +/* ---- Include Files -------------------------------------------------------- */ + +#include "interface/vchi/vchi.h" +#include "vc_vchi_audioserv_defs.h" + +/* ---- Private Constants and Types ------------------------------------------ */ + +#define BCM2835_AUDIO_STOP 0 +#define BCM2835_AUDIO_START 1 +#define BCM2835_AUDIO_WRITE 2 + +/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */ +#ifdef AUDIO_DEBUG_ENABLE +#define LOG_ERR(fmt, arg...) pr_err("%s:%d " fmt, __func__, __LINE__, ##arg) +#define LOG_WARN(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) +#define LOG_INFO(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) +#define LOG_DBG(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) +#else +#define LOG_ERR(fmt, arg...) pr_err("%s:%d " fmt, __func__, __LINE__, ##arg) +#define LOG_WARN(fmt, arg...) no_printk(fmt, ##arg) +#define LOG_INFO(fmt, arg...) no_printk(fmt, ##arg) +#define LOG_DBG(fmt, arg...) no_printk(fmt, ##arg) +#endif + +struct bcm2835_audio_instance { + unsigned int num_connections; + VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; + struct completion msg_avail_comp; + struct mutex vchi_mutex; + struct bcm2835_alsa_stream *alsa_stream; + int result; + short peer_version; +}; + +static bool force_bulk; + +/* ---- Private Variables ---------------------------------------------------- */ + +/* ---- Private Function Prototypes ------------------------------------------ */ + +/* ---- Private Functions ---------------------------------------------------- */ + +static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream); +static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream); +static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream, + unsigned int count, void *src); + +// Routine to send a message across a service + +static int +bcm2835_vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, + void *data, + unsigned int size) +{ + return vchi_queue_kernel_message(handle, + data, + size); +} + +static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 | + 'M' << 8 | 'A'); +static const u32 BCM2835_AUDIO_WRITE_COOKIE2 = ('D' << 24 | 'A' << 16 | + 'T' << 8 | 'A'); + +struct bcm2835_audio_work { + struct work_struct my_work; + struct bcm2835_alsa_stream *alsa_stream; + int cmd; + void *src; + unsigned int count; +}; + +static void my_wq_function(struct work_struct *work) +{ + struct bcm2835_audio_work *w = + container_of(work, struct bcm2835_audio_work, my_work); + int ret = -9; + + LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd); + switch (w->cmd) { + case BCM2835_AUDIO_START: + ret = bcm2835_audio_start_worker(w->alsa_stream); + break; + case BCM2835_AUDIO_STOP: + ret = bcm2835_audio_stop_worker(w->alsa_stream); + break; + case BCM2835_AUDIO_WRITE: + ret = bcm2835_audio_write_worker(w->alsa_stream, w->count, + w->src); + break; + default: + LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd); + break; + } + kfree((void *) work); + LOG_DBG(" .. OUT %d\n", ret); +} + +int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream) +{ + int ret = -1; + + LOG_DBG(" .. IN\n"); + if (alsa_stream->my_wq) { + struct bcm2835_audio_work *work; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + /*--- Queue some work (item 1) ---*/ + if (work) { + INIT_WORK(&work->my_work, my_wq_function); + work->alsa_stream = alsa_stream; + work->cmd = BCM2835_AUDIO_START; + if (queue_work(alsa_stream->my_wq, &work->my_work)) + ret = 0; + } else + LOG_ERR(" .. Error: NULL work kmalloc\n"); + } + LOG_DBG(" .. OUT %d\n", ret); + return ret; +} + +int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream) +{ + int ret = -1; + + LOG_DBG(" .. IN\n"); + if (alsa_stream->my_wq) { + struct bcm2835_audio_work *work; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + /*--- Queue some work (item 1) ---*/ + if (work) { + INIT_WORK(&work->my_work, my_wq_function); + work->alsa_stream = alsa_stream; + work->cmd = BCM2835_AUDIO_STOP; + if (queue_work(alsa_stream->my_wq, &work->my_work)) + ret = 0; + } else + LOG_ERR(" .. Error: NULL work kmalloc\n"); + } + LOG_DBG(" .. OUT %d\n", ret); + return ret; +} + +int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, + unsigned int count, void *src) +{ + int ret = -1; + + LOG_DBG(" .. IN\n"); + if (alsa_stream->my_wq) { + struct bcm2835_audio_work *work; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + /*--- Queue some work (item 1) ---*/ + if (work) { + INIT_WORK(&work->my_work, my_wq_function); + work->alsa_stream = alsa_stream; + work->cmd = BCM2835_AUDIO_WRITE; + work->src = src; + work->count = count; + if (queue_work(alsa_stream->my_wq, &work->my_work)) + ret = 0; + } else + LOG_ERR(" .. Error: NULL work kmalloc\n"); + } + LOG_DBG(" .. OUT %d\n", ret); + return ret; +} + +static void my_workqueue_init(struct bcm2835_alsa_stream *alsa_stream) +{ + alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1); + return; +} + +static void my_workqueue_quit(struct bcm2835_alsa_stream *alsa_stream) +{ + if (alsa_stream->my_wq) { + flush_workqueue(alsa_stream->my_wq); + destroy_workqueue(alsa_stream->my_wq); + alsa_stream->my_wq = NULL; + } + return; +} + +static void audio_vchi_callback(void *param, + const VCHI_CALLBACK_REASON_T reason, + void *msg_handle) +{ + struct bcm2835_audio_instance *instance = param; + int status; + int msg_len; + struct vc_audio_msg m; + + LOG_DBG(" .. IN instance=%p, handle=%p, alsa=%p, reason=%d, handle=%p\n", + instance, instance ? instance->vchi_handle[0] : NULL, instance ? instance->alsa_stream : NULL, reason, msg_handle); + + if (reason != VCHI_CALLBACK_MSG_AVAILABLE) { + return; + } + if (!instance) { + LOG_ERR(" .. instance is null\n"); + BUG(); + return; + } + if (!instance->vchi_handle[0]) { + LOG_ERR(" .. instance->vchi_handle[0] is null\n"); + BUG(); + return; + } + status = vchi_msg_dequeue(instance->vchi_handle[0], + &m, sizeof(m), &msg_len, VCHI_FLAGS_NONE); + if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { + LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n", + instance, m.u.result.success); + instance->result = m.u.result.success; + complete(&instance->msg_avail_comp); + } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) { + struct bcm2835_alsa_stream *alsa_stream = instance->alsa_stream; + + LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n", + instance, m.u.complete.count); + if (m.u.complete.cookie1 != BCM2835_AUDIO_WRITE_COOKIE1 || + m.u.complete.cookie2 != BCM2835_AUDIO_WRITE_COOKIE2) + LOG_ERR(" .. response is corrupt\n"); + else if (alsa_stream) { + atomic_add(m.u.complete.count, + &alsa_stream->retrieved); + bcm2835_playback_fifo(alsa_stream); + } else { + LOG_ERR(" .. unexpected alsa_stream=%p\n", + alsa_stream); + } + } else { + LOG_ERR(" .. unexpected m.type=%d\n", m.type); + } + LOG_DBG(" .. OUT\n"); +} + +static struct bcm2835_audio_instance * +vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance, + VCHI_CONNECTION_T **vchi_connections, + unsigned int num_connections) +{ + unsigned int i; + struct bcm2835_audio_instance *instance; + int status; + + LOG_DBG("%s: start", __func__); + + if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { + LOG_ERR("%s: unsupported number of connections %u (max=%u)\n", + __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); + + return NULL; + } + /* Allocate memory for this instance */ + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) + return NULL; + + memset(instance, 0, sizeof(*instance)); + instance->num_connections = num_connections; + + /* Create a lock for exclusive, serialized VCHI connection access */ + mutex_init(&instance->vchi_mutex); + /* Open the VCHI service connections */ + for (i = 0; i < num_connections; i++) { + SERVICE_CREATION_T params = { + VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), + VC_AUDIO_SERVER_NAME, // 4cc service code + vchi_connections[i], // passed in fn pointers + 0, // rx fifo size (unused) + 0, // tx fifo size (unused) + audio_vchi_callback, // service callback + instance, // service callback parameter + 1, //TODO: remove VCOS_FALSE, // unaligned bulk recieves + 1, //TODO: remove VCOS_FALSE, // unaligned bulk transmits + 0 // want crc check on bulk transfers + }; + + LOG_DBG("%s: about to open %i\n", __func__, i); + status = vchi_service_open(vchi_instance, ¶ms, + &instance->vchi_handle[i]); + LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status); + if (status) { + LOG_ERR("%s: failed to open VCHI service connection (status=%d)\n", + __func__, status); + + goto err_close_services; + } + /* Finished with the service for now */ + vchi_service_release(instance->vchi_handle[i]); + } + + LOG_DBG("%s: okay\n", __func__); + return instance; + +err_close_services: + for (i = 0; i < instance->num_connections; i++) { + LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]); + if (instance->vchi_handle[i]) + vchi_service_close(instance->vchi_handle[i]); + } + + kfree(instance); + LOG_ERR("%s: error\n", __func__); + + return NULL; +} + +static int vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance) +{ + unsigned int i; + + LOG_DBG(" .. IN\n"); + + if (!instance) { + LOG_ERR("%s: invalid handle %p\n", __func__, instance); + + return -1; + } + + LOG_DBG(" .. about to lock (%d)\n", instance->num_connections); + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", + instance->num_connections); + return -EINTR; + } + + /* Close all VCHI service connections */ + for (i = 0; i < instance->num_connections; i++) { + int status; + + LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]); + vchi_service_use(instance->vchi_handle[i]); + + status = vchi_service_close(instance->vchi_handle[i]); + if (status) { + LOG_DBG("%s: failed to close VCHI service connection (status=%d)\n", + __func__, status); + } + } + + mutex_unlock(&instance->vchi_mutex); + + kfree(instance); + + LOG_DBG(" .. OUT\n"); + + return 0; +} + +static int bcm2835_audio_open_connection(struct bcm2835_alsa_stream *alsa_stream) +{ + static VCHI_INSTANCE_T vchi_instance; + static VCHI_CONNECTION_T *vchi_connection; + static int initted; + struct bcm2835_audio_instance *instance = + (struct bcm2835_audio_instance *)alsa_stream->instance; + int ret; + + LOG_DBG(" .. IN\n"); + + LOG_INFO("%s: start\n", __func__); + BUG_ON(instance); + if (instance) { + LOG_ERR("%s: VCHI instance already open (%p)\n", + __func__, instance); + instance->alsa_stream = alsa_stream; + alsa_stream->instance = instance; + ret = 0; // xxx todo -1; + goto err_free_mem; + } + + /* Initialize and create a VCHI connection */ + if (!initted) { + ret = vchi_initialise(&vchi_instance); + if (ret) { + LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n", + __func__, ret); + + ret = -EIO; + goto err_free_mem; + } + ret = vchi_connect(NULL, 0, vchi_instance); + if (ret) { + LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n", + __func__, ret); + + ret = -EIO; + goto err_free_mem; + } + initted = 1; + } + + /* Initialize an instance of the audio service */ + instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1); + + if (!instance) { + LOG_ERR("%s: failed to initialize audio service\n", __func__); + + ret = -EPERM; + goto err_free_mem; + } + + instance->alsa_stream = alsa_stream; + alsa_stream->instance = instance; + + LOG_DBG(" success !\n"); + ret = 0; +err_free_mem: + LOG_DBG(" .. OUT\n"); + + return ret; +} + +int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream) +{ + struct bcm2835_audio_instance *instance; + struct vc_audio_msg m; + int status; + int ret; + + LOG_DBG(" .. IN\n"); + + my_workqueue_init(alsa_stream); + + ret = bcm2835_audio_open_connection(alsa_stream); + if (ret) { + ret = -1; + goto exit; + } + instance = alsa_stream->instance; + LOG_DBG(" instance (%p)\n", instance); + + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections); + return -EINTR; + } + vchi_service_use(instance->vchi_handle[0]); + + m.type = VC_AUDIO_MSG_TYPE_OPEN; + + /* Send the message to the videocore */ + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + &m, sizeof(m)); + + if (status) { + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", + __func__, status); + + ret = -1; + goto unlock; + } + + ret = 0; + +unlock: + vchi_service_release(instance->vchi_handle[0]); + mutex_unlock(&instance->vchi_mutex); +exit: + LOG_DBG(" .. OUT\n"); + return ret; +} + +static int bcm2835_audio_set_ctls_chan(struct bcm2835_alsa_stream *alsa_stream, + struct bcm2835_chip *chip) +{ + struct vc_audio_msg m; + struct bcm2835_audio_instance *instance = alsa_stream->instance; + int status; + int ret; + + LOG_DBG(" .. IN\n"); + + LOG_INFO(" Setting ALSA dest(%d), volume(%d)\n", + chip->dest, chip->volume); + + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", + instance->num_connections); + return -EINTR; + } + vchi_service_use(instance->vchi_handle[0]); + + instance->result = -1; + + m.type = VC_AUDIO_MSG_TYPE_CONTROL; + m.u.control.dest = chip->dest; + m.u.control.volume = chip->volume; + + /* Create the message available completion */ + init_completion(&instance->msg_avail_comp); + + /* Send the message to the videocore */ + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + &m, sizeof(m)); + + if (status) { + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", + __func__, status); + + ret = -1; + goto unlock; + } + + /* We are expecting a reply from the videocore */ + wait_for_completion(&instance->msg_avail_comp); + + if (instance->result) { + LOG_ERR("%s: result=%d\n", __func__, instance->result); + + ret = -1; + goto unlock; + } + + ret = 0; + +unlock: + vchi_service_release(instance->vchi_handle[0]); + mutex_unlock(&instance->vchi_mutex); + + LOG_DBG(" .. OUT\n"); + return ret; +} + +int bcm2835_audio_set_ctls(struct bcm2835_chip *chip) +{ + int i; + int ret = 0; + + LOG_DBG(" .. IN\n"); + LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); + + /* change ctls for all substreams */ + for (i = 0; i < MAX_SUBSTREAMS; i++) { + if (chip->avail_substreams & (1 << i)) { + if (!chip->alsa_stream[i]) { + LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams); + ret = 0; + } else if (bcm2835_audio_set_ctls_chan(chip->alsa_stream[i], chip) != 0) { + LOG_ERR("Couldn't set the controls for stream %d\n", i); + ret = -1; + } else { + LOG_DBG(" Controls set for stream %d\n", i); + } + } + } + LOG_DBG(" .. OUT ret=%d\n", ret); + return ret; +} + +int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream, + unsigned int channels, unsigned int samplerate, + unsigned int bps) +{ + struct vc_audio_msg m; + struct bcm2835_audio_instance *instance = alsa_stream->instance; + int status; + int ret; + + LOG_DBG(" .. IN\n"); + + LOG_INFO(" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n", + channels, samplerate, bps); + + /* resend ctls - alsa_stream may not have been open when first send */ + ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip); + if (ret) { + LOG_ERR(" Alsa controls not supported\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections); + return -EINTR; + } + vchi_service_use(instance->vchi_handle[0]); + + instance->result = -1; + + m.type = VC_AUDIO_MSG_TYPE_CONFIG; + m.u.config.channels = channels; + m.u.config.samplerate = samplerate; + m.u.config.bps = bps; + + /* Create the message available completion */ + init_completion(&instance->msg_avail_comp); + + /* Send the message to the videocore */ + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + &m, sizeof(m)); + + if (status) { + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", + __func__, status); + + ret = -1; + goto unlock; + } + + /* We are expecting a reply from the videocore */ + wait_for_completion(&instance->msg_avail_comp); + + if (instance->result) { + LOG_ERR("%s: result=%d", __func__, instance->result); + + ret = -1; + goto unlock; + } + + ret = 0; + +unlock: + vchi_service_release(instance->vchi_handle[0]); + mutex_unlock(&instance->vchi_mutex); + + LOG_DBG(" .. OUT\n"); + return ret; +} + +int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream) +{ + LOG_DBG(" .. IN\n"); + + LOG_DBG(" .. OUT\n"); + + return 0; +} + +static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream) +{ + struct vc_audio_msg m; + struct bcm2835_audio_instance *instance = alsa_stream->instance; + int status; + int ret; + + LOG_DBG(" .. IN\n"); + + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", + instance->num_connections); + return -EINTR; + } + vchi_service_use(instance->vchi_handle[0]); + + m.type = VC_AUDIO_MSG_TYPE_START; + + /* Send the message to the videocore */ + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + &m, sizeof(m)); + + if (status) { + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", + __func__, status); + + ret = -1; + goto unlock; + } + + ret = 0; + +unlock: + vchi_service_release(instance->vchi_handle[0]); + mutex_unlock(&instance->vchi_mutex); + LOG_DBG(" .. OUT\n"); + return ret; +} + +static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream) +{ + struct vc_audio_msg m; + struct bcm2835_audio_instance *instance = alsa_stream->instance; + int status; + int ret; + + LOG_DBG(" .. IN\n"); + + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", + instance->num_connections); + return -EINTR; + } + vchi_service_use(instance->vchi_handle[0]); + + m.type = VC_AUDIO_MSG_TYPE_STOP; + m.u.stop.draining = alsa_stream->draining; + + /* Send the message to the videocore */ + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + &m, sizeof(m)); + + if (status) { + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", + __func__, status); + + ret = -1; + goto unlock; + } + + ret = 0; + +unlock: + vchi_service_release(instance->vchi_handle[0]); + mutex_unlock(&instance->vchi_mutex); + LOG_DBG(" .. OUT\n"); + return ret; +} + +int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream) +{ + struct vc_audio_msg m; + struct bcm2835_audio_instance *instance = alsa_stream->instance; + int status; + int ret; + + LOG_DBG(" .. IN\n"); + + my_workqueue_quit(alsa_stream); + + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", + instance->num_connections); + return -EINTR; + } + vchi_service_use(instance->vchi_handle[0]); + + m.type = VC_AUDIO_MSG_TYPE_CLOSE; + + /* Create the message available completion */ + init_completion(&instance->msg_avail_comp); + + /* Send the message to the videocore */ + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + &m, sizeof(m)); + + if (status) { + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", + __func__, status); + ret = -1; + goto unlock; + } + + /* We are expecting a reply from the videocore */ + wait_for_completion(&instance->msg_avail_comp); + + if (instance->result) { + LOG_ERR("%s: failed result (result=%d)\n", + __func__, instance->result); + + ret = -1; + goto unlock; + } + + ret = 0; + +unlock: + vchi_service_release(instance->vchi_handle[0]); + mutex_unlock(&instance->vchi_mutex); + + /* Stop the audio service */ + vc_vchi_audio_deinit(instance); + alsa_stream->instance = NULL; + + LOG_DBG(" .. OUT\n"); + return ret; +} + +static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream, + unsigned int count, void *src) +{ + struct vc_audio_msg m; + struct bcm2835_audio_instance *instance = alsa_stream->instance; + int status; + int ret; + + LOG_DBG(" .. IN\n"); + + LOG_INFO(" Writing %d bytes from %p\n", count, src); + + if (mutex_lock_interruptible(&instance->vchi_mutex)) { + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", + instance->num_connections); + return -EINTR; + } + vchi_service_use(instance->vchi_handle[0]); + + if (instance->peer_version == 0 && vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0) { + LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version); + } + m.type = VC_AUDIO_MSG_TYPE_WRITE; + m.u.write.count = count; + // old version uses bulk, new version uses control + m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0 : 4000; + m.u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1; + m.u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2; + m.u.write.silence = src == NULL; + + /* Send the message to the videocore */ + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + &m, sizeof(m)); + + if (status) { + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", + __func__, status); + + ret = -1; + goto unlock; + } + if (!m.u.write.silence) { + if (!m.u.write.max_packet) { + /* Send the message to the videocore */ + status = vchi_bulk_queue_transmit(instance->vchi_handle[0], + src, count, + 0 * + VCHI_FLAGS_BLOCK_UNTIL_QUEUED + + + 1 * + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, + NULL); + } else { + while (count > 0) { + int bytes = min((int) m.u.write.max_packet, (int) count); + + status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], + src, bytes); + src = (char *)src + bytes; + count -= bytes; + } + } + if (status) { + LOG_ERR("%s: failed on vchi_bulk_queue_transmit (status=%d)\n", + __func__, status); + + ret = -1; + goto unlock; + } + } + ret = 0; + +unlock: + vchi_service_release(instance->vchi_handle[0]); + mutex_unlock(&instance->vchi_mutex); + LOG_DBG(" .. OUT\n"); + return ret; +} + +/** + * Returns all buffers from arm->vc + */ +void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream) +{ + LOG_DBG(" .. IN\n"); + LOG_DBG(" .. OUT\n"); + return; +} + +/** + * Forces VC to flush(drop) its filled playback buffers and + * return them the us. (VC->ARM) + */ +void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream) +{ + LOG_DBG(" .. IN\n"); + LOG_DBG(" .. OUT\n"); +} + +unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream) +{ + unsigned int count = atomic_read(&alsa_stream->retrieved); + + atomic_sub(count, &alsa_stream->retrieved); + return count; +} + +module_param(force_bulk, bool, 0444); +MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); diff --git a/drivers/staging/bcm2835-audio/bcm2835.c b/drivers/staging/bcm2835-audio/bcm2835.c new file mode 100644 index 000000000000..3a5e528e0ec6 --- /dev/null +++ b/drivers/staging/bcm2835-audio/bcm2835.c @@ -0,0 +1,250 @@ +/***************************************************************************** + * Copyright 2011 Broadcom Corporation. All rights reserved. + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2, available at + * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a + * license other than the GPL, without Broadcom's express prior written + * consent. + *****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "bcm2835.h" + +/* HACKY global pointers needed for successive probes to work : ssp + * But compared against the changes we will have to do in VC audio_ipc code + * to export 8 audio_ipc devices as a single IPC device and then monitor all + * four devices in a thread, this gets things done quickly and should be easier + * to debug if we run into issues + */ + +static struct snd_card *g_card; +static struct bcm2835_chip *g_chip; + +static int snd_bcm2835_free(struct bcm2835_chip *chip) +{ + kfree(chip); + return 0; +} + +/* component-destructor + * (see "Management of Cards and Components") + */ +static int snd_bcm2835_dev_free(struct snd_device *device) +{ + return snd_bcm2835_free(device->device_data); +} + +/* chip-specific constructor + * (see "Management of Cards and Components") + */ +static int snd_bcm2835_create(struct snd_card *card, + struct platform_device *pdev, + struct bcm2835_chip **rchip) +{ + struct bcm2835_chip *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_bcm2835_dev_free, + }; + + *rchip = NULL; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->card = card; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + snd_bcm2835_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int snd_bcm2835_alsa_probe_dt(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm2835_chip *chip; + struct snd_card *card; + u32 numchans; + int err, i; + + err = of_property_read_u32(dev->of_node, "brcm,pwm-channels", + &numchans); + if (err) { + dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'"); + return err; + } + + if (numchans == 0 || numchans > MAX_SUBSTREAMS) { + numchans = MAX_SUBSTREAMS; + dev_warn(dev, "Illegal 'brcm,pwm-channels' value, will use %u\n", + numchans); + } + + err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); + if (err) { + dev_err(dev, "Failed to create soundcard structure\n"); + return err; + } + + snd_card_set_dev(card, dev); + strcpy(card->driver, "bcm2835"); + strcpy(card->shortname, "bcm2835 ALSA"); + sprintf(card->longname, "%s", card->shortname); + + err = snd_bcm2835_create(card, pdev, &chip); + if (err < 0) { + dev_err(dev, "Failed to create bcm2835 chip\n"); + goto err_free; + } + + err = snd_bcm2835_new_pcm(chip); + if (err < 0) { + dev_err(dev, "Failed to create new bcm2835 pcm device\n"); + goto err_free; + } + + err = snd_bcm2835_new_spdif_pcm(chip); + if (err < 0) { + dev_err(dev, "Failed to create new bcm2835 spdif pcm device\n"); + goto err_free; + } + + err = snd_bcm2835_new_ctl(chip); + if (err < 0) { + dev_err(dev, "Failed to create new bcm2835 ctl\n"); + goto err_free; + } + + for (i = 0; i < numchans; i++) { + chip->avail_substreams |= (1 << i); + chip->pdev[i] = pdev; + } + + err = snd_card_register(card); + if (err) { + dev_err(dev, "Failed to register bcm2835 ALSA card\n"); + goto err_free; + } + + g_card = card; + g_chip = chip; + platform_set_drvdata(pdev, card); + audio_info("bcm2835 ALSA card created with %u channels\n", numchans); + + return 0; + +err_free: + snd_card_free(card); + + return err; +} + +static int snd_bcm2835_alsa_remove(struct platform_device *pdev) +{ + int idx; + void *drv_data; + + drv_data = platform_get_drvdata(pdev); + + if (drv_data == (void *)g_card) { + /* This is the card device */ + snd_card_free((struct snd_card *)drv_data); + g_card = NULL; + g_chip = NULL; + } else { + idx = (int)(long)drv_data; + if (g_card) { + BUG_ON(!g_chip); + /* We pass chip device numbers in audio ipc devices + * other than the one we registered our card with + */ + idx = (int)(long)drv_data; + BUG_ON(!idx || idx > MAX_SUBSTREAMS); + g_chip->avail_substreams &= ~(1 << idx); + /* There should be atleast one substream registered + * after we are done here, as it wil be removed when + * the *remove* is called for the card device + */ + BUG_ON(!g_chip->avail_substreams); + } + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM + +static int snd_bcm2835_alsa_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int snd_bcm2835_alsa_resume(struct platform_device *pdev) +{ + return 0; +} + +#endif + +static const struct of_device_id snd_bcm2835_of_match_table[] = { + { .compatible = "brcm,bcm2835-audio",}, + {}, +}; +MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table); + +static struct platform_driver bcm2835_alsa0_driver = { + .probe = snd_bcm2835_alsa_probe_dt, + .remove = snd_bcm2835_alsa_remove, +#ifdef CONFIG_PM + .suspend = snd_bcm2835_alsa_suspend, + .resume = snd_bcm2835_alsa_resume, +#endif + .driver = { + .name = "bcm2835_AUD0", + .owner = THIS_MODULE, + .of_match_table = snd_bcm2835_of_match_table, + }, +}; + +static int bcm2835_alsa_device_init(void) +{ + int retval; + + retval = platform_driver_register(&bcm2835_alsa0_driver); + if (retval) + pr_err("Error registering bcm2835_alsa0_driver %d .\n", retval); + + return retval; +} + +static void bcm2835_alsa_device_exit(void) +{ + platform_driver_unregister(&bcm2835_alsa0_driver); +} + +late_initcall(bcm2835_alsa_device_init); +module_exit(bcm2835_alsa_device_exit); + +MODULE_AUTHOR("Dom Cobley"); +MODULE_DESCRIPTION("Alsa driver for BCM2835 chip"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/bcm2835-audio/bcm2835.h b/drivers/staging/bcm2835-audio/bcm2835.h new file mode 100644 index 000000000000..36e3ef80e60c --- /dev/null +++ b/drivers/staging/bcm2835-audio/bcm2835.h @@ -0,0 +1,167 @@ +/***************************************************************************** + * Copyright 2011 Broadcom Corporation. All rights reserved. + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2, available at + * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a + * license other than the GPL, without Broadcom's express prior written + * consent. + *****************************************************************************/ + +#ifndef __SOUND_ARM_BCM2835_H +#define __SOUND_ARM_BCM2835_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +#define AUDIO_DEBUG_ENABLE +#define AUDIO_VERBOSE_DEBUG_ENABLE + */ + +/* Debug macros */ + +#ifdef AUDIO_DEBUG_ENABLE +#ifdef AUDIO_VERBOSE_DEBUG_ENABLE + +#define audio_debug(fmt, arg...) \ + printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) + +#define audio_info(fmt, arg...) \ + printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) + +#else + +#define audio_debug(fmt, arg...) + +#define audio_info(fmt, arg...) + +#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */ + +#else + +#define audio_debug(fmt, arg...) + +#define audio_info(fmt, arg...) + +#endif /* AUDIO_DEBUG_ENABLE */ + +#define audio_error(fmt, arg...) \ + printk(KERN_ERR"%s:%d " fmt, __func__, __LINE__, ##arg) + +#define audio_warning(fmt, arg...) \ + printk(KERN_WARNING"%s:%d " fmt, __func__, __LINE__, ##arg) + +#define audio_alert(fmt, arg...) \ + printk(KERN_ALERT"%s:%d " fmt, __func__, __LINE__, ##arg) + +#define MAX_SUBSTREAMS (8) +#define AVAIL_SUBSTREAMS_MASK (0xff) + +enum { + CTRL_VOL_MUTE, + CTRL_VOL_UNMUTE +}; + +/* macros for alsa2chip and chip2alsa, instead of functions */ + +#define alsa2chip(vol) (uint)(-((vol << 8) / 100)) /* convert alsa to chip volume (defined as macro rather than function call) */ +#define chip2alsa(vol) -((vol * 100) >> 8) /* convert chip to alsa volume */ + +/* Some constants for values .. */ +enum snd_bcm2835_route { + AUDIO_DEST_AUTO = 0, + AUDIO_DEST_HEADPHONES = 1, + AUDIO_DEST_HDMI = 2, + AUDIO_DEST_MAX, +}; + +enum snd_bcm2835_ctrl { + PCM_PLAYBACK_VOLUME, + PCM_PLAYBACK_MUTE, + PCM_PLAYBACK_DEVICE, +}; + +/* definition of the chip-specific record */ +struct bcm2835_chip { + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm *pcm_spdif; + /* Bitmat for valid reg_base and irq numbers */ + unsigned int avail_substreams; + struct platform_device *pdev[MAX_SUBSTREAMS]; + struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS]; + + int volume; + int old_volume; /* stores the volume value whist muted */ + int dest; + int mute; + + unsigned int opened; + unsigned int spdif_status; + struct mutex audio_mutex; +}; + +struct bcm2835_alsa_stream { + struct bcm2835_chip *chip; + struct snd_pcm_substream *substream; + struct snd_pcm_indirect pcm_indirect; + + struct semaphore buffers_update_sem; + struct semaphore control_sem; + spinlock_t lock; + volatile unsigned int control; + volatile unsigned int status; + + int open; + int running; + int draining; + + int channels; + int params_rate; + int pcm_format_width; + + unsigned int pos; + unsigned int buffer_size; + unsigned int period_size; + + atomic_t retrieved; + struct bcm2835_audio_instance *instance; + struct workqueue_struct *my_wq; + int idx; +}; + +int snd_bcm2835_new_ctl(struct bcm2835_chip *chip); +int snd_bcm2835_new_pcm(struct bcm2835_chip *chip); +int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip); + +int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream); +int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream); +int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream, + unsigned int channels, unsigned int samplerate, + unsigned int bps); +int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream); +int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream); +int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream); +int bcm2835_audio_set_ctls(struct bcm2835_chip *chip); +int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, + unsigned int count, + void *src); +void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream); +unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream); +void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream); +void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream); + +#endif /* __SOUND_ARM_BCM2835_H */ diff --git a/drivers/staging/bcm2835-audio/vc_vchi_audioserv_defs.h b/drivers/staging/bcm2835-audio/vc_vchi_audioserv_defs.h new file mode 100644 index 000000000000..da96f1bc2516 --- /dev/null +++ b/drivers/staging/bcm2835-audio/vc_vchi_audioserv_defs.h @@ -0,0 +1,108 @@ +/***************************************************************************** + * Copyright 2011 Broadcom Corporation. All rights reserved. + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2, available at + * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a + * license other than the GPL, without Broadcom's express prior written + * consent. + *****************************************************************************/ + +#ifndef _VC_AUDIO_DEFS_H_ +#define _VC_AUDIO_DEFS_H_ + +#define VC_AUDIOSERV_MIN_VER 1 +#define VC_AUDIOSERV_VER 2 + +/* FourCC code used for VCHI connection */ +#define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") + +/* + * List of screens that are currently supported + * All message types supported for HOST->VC direction + */ + +enum vc_audio_msg_type { + VC_AUDIO_MSG_TYPE_RESULT, // Generic result + VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result + VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio + VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio + VC_AUDIO_MSG_TYPE_OPEN, // Configure audio + VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio + VC_AUDIO_MSG_TYPE_START, // Configure audio + VC_AUDIO_MSG_TYPE_STOP, // Configure audio + VC_AUDIO_MSG_TYPE_WRITE, // Configure audio + VC_AUDIO_MSG_TYPE_MAX +}; + +/* configure the audio */ + +struct vc_audio_config { + u32 channels; + u32 samplerate; + u32 bps; +}; + +struct vc_audio_control { + u32 volume; + u32 dest; +}; + +struct vc_audio_open { + u32 dummy; +}; + +struct vc_audio_close { + u32 dummy; +}; + +struct vc_audio_start { + u32 dummy; +}; + +struct vc_audio_stop { + u32 draining; +}; + +/* configure the write audio samples */ +struct vc_audio_write { + u32 count; // in bytes + u32 cookie1; + u32 cookie2; + s16 silence; + s16 max_packet; +}; + +/* Generic result for a request (VC->HOST) */ +struct vc_audio_result { + s32 success; // Success value +}; + +/* Generic result for a request (VC->HOST) */ +struct vc_audio_complete { + s32 count; // Success value + u32 cookie1; + u32 cookie2; +}; + +/* Message header for all messages in HOST->VC direction */ +struct vc_audio_msg { + s32 type; /* Message type (VC_AUDIO_MSG_TYPE) */ + union { + struct vc_audio_config config; + struct vc_audio_control control; + struct vc_audio_open open; + struct vc_audio_close close; + struct vc_audio_start start; + struct vc_audio_stop stop; + struct vc_audio_write write; + struct vc_audio_result result; + struct vc_audio_complete complete; + } u; +}; + +#endif /* _VC_AUDIO_DEFS_H_ */ diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index e7255f811611..942507754cab 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -1025,7 +1025,7 @@ config COMEDI_NI_660X select COMEDI_NI_TIOCMD ---help--- Enable support for National Instruments PCI-6601 (ni_660x), PCI-6602, - PXI-6602, PXI-6608 and PXI-6624. + PXI-6602, PXI-6608, PCI-6624, and PXI-6624. To compile this driver as a module, choose M here: the module will be called ni_660x. @@ -1070,9 +1070,11 @@ config COMEDI_NI_PCIMIO PCI-MIO-16E-4, PCI-6014, PCI-6040E, 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, PCI-6254, PCI-6259, PCIe-6259, - PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289, PCI-6711, PXI-6711, + PCI-6220, PXI-6220, PCI-6221, PXI-6221, PCI-6224, PXI-6224, PCI-6225, + PXI-6225, PCI-6229, PXI-6229, PCI-6250, PXI-6250, PCI-6251, PXI-6251, + PCIe-6251, PXIe-6251, PCI-6254, PXI-6254, PCI-6259, PXI-6259, + PCIe-6259, PXIe-6259, PCI-6280, PXI-6280, PCI-6281, PXI-6281, + PCI-6284, PXI-6284, PCI-6289, PXI-6289, PCI-6711, PXI-6711, PCI-6713, PXI-6713, PXI-6071E, PCI-6070E, PXI-6070E, PXI-6052E, PCI-6036E, PCI-6731, PCI-6733, PXI-6733, PCI-6143, PXI-6143 diff --git a/drivers/staging/comedi/comedi_compat32.h b/drivers/staging/comedi/comedi_compat32.h index 5ce77f3e8c22..0127c1f98bbf 100644 --- a/drivers/staging/comedi/comedi_compat32.h +++ b/drivers/staging/comedi/comedi_compat32.h @@ -25,7 +25,8 @@ #ifdef CONFIG_COMPAT struct file; -long comedi_compat_ioctl(struct file *, unsigned int cmd, unsigned long arg); +long comedi_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); #else /* CONFIG_COMPAT */ diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 64b3966c5f1f..57e8599b54e6 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -28,15 +28,11 @@ #include #include #include -#include #include -#include #include -#include #include #include "comedidev.h" #include -#include #include #include @@ -2898,9 +2894,6 @@ static int __init comedi_init(void) comedi_class->dev_groups = comedi_dev_groups; - /* XXX requires /proc interface */ - comedi_proc_init(); - /* create devices files for legacy/manual use */ for (i = 0; i < comedi_num_legacy_minors; i++) { struct comedi_device *dev; @@ -2917,6 +2910,9 @@ static int __init comedi_init(void) mutex_unlock(&dev->mutex); } + /* XXX requires /proc interface */ + comedi_proc_init(); + return 0; } module_init(comedi_init); diff --git a/drivers/staging/comedi/comedi_internal.h b/drivers/staging/comedi/comedi_internal.h index 3f2c88ae6470..534415e331b6 100644 --- a/drivers/staging/comedi/comedi_internal.h +++ b/drivers/staging/comedi/comedi_internal.h @@ -44,11 +44,12 @@ extern unsigned int comedi_default_buf_maxsize_kb; extern struct comedi_driver *comedi_drivers; extern struct mutex comedi_drivers_list_lock; -int insn_inval(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, unsigned int *); +int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); -void comedi_device_detach(struct comedi_device *); -int comedi_device_attach(struct comedi_device *, struct comedi_devconfig *); +void comedi_device_detach(struct comedi_device *dev); +int comedi_device_attach(struct comedi_device *dev, + struct comedi_devconfig *it); #ifdef CONFIG_PROC_FS diff --git a/drivers/staging/comedi/comedi_pci.h b/drivers/staging/comedi/comedi_pci.h index 4005cc9cf7f1..7dfd892c74b0 100644 --- a/drivers/staging/comedi/comedi_pci.h +++ b/drivers/staging/comedi/comedi_pci.h @@ -34,18 +34,20 @@ #define PCI_VENDOR_ID_RTD 0x1435 #define PCI_VENDOR_ID_HUMUSOFT 0x186c -struct pci_dev *comedi_to_pci_dev(struct comedi_device *); +struct pci_dev *comedi_to_pci_dev(struct comedi_device *dev); -int comedi_pci_enable(struct comedi_device *); -void comedi_pci_disable(struct comedi_device *); -void comedi_pci_detach(struct comedi_device *); +int comedi_pci_enable(struct comedi_device *dev); +void comedi_pci_disable(struct comedi_device *dev); +void comedi_pci_detach(struct comedi_device *dev); -int comedi_pci_auto_config(struct pci_dev *, struct comedi_driver *, +int comedi_pci_auto_config(struct pci_dev *pcidev, struct comedi_driver *driver, unsigned long context); -void comedi_pci_auto_unconfig(struct pci_dev *); +void comedi_pci_auto_unconfig(struct pci_dev *pcidev); -int comedi_pci_driver_register(struct comedi_driver *, struct pci_driver *); -void comedi_pci_driver_unregister(struct comedi_driver *, struct pci_driver *); +int comedi_pci_driver_register(struct comedi_driver *comedi_driver, + struct pci_driver *pci_driver); +void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver, + struct pci_driver *pci_driver); /** * module_comedi_pci_driver() - Helper macro for registering a comedi PCI driver diff --git a/drivers/staging/comedi/comedi_pcmcia.c b/drivers/staging/comedi/comedi_pcmcia.c index d7072a5c1647..cd4742851c08 100644 --- a/drivers/staging/comedi/comedi_pcmcia.c +++ b/drivers/staging/comedi/comedi_pcmcia.c @@ -78,7 +78,8 @@ static int comedi_pcmcia_conf_check(struct pcmcia_device *link, * or a negative error number from pcmcia_enable_device() if it fails. */ int comedi_pcmcia_enable(struct comedi_device *dev, - int (*conf_check)(struct pcmcia_device *, void *)) + int (*conf_check)(struct pcmcia_device *p_dev, + void *priv_data)) { struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); int ret; diff --git a/drivers/staging/comedi/comedi_pcmcia.h b/drivers/staging/comedi/comedi_pcmcia.h index 5a572c200a8b..9e45c7c93278 100644 --- a/drivers/staging/comedi/comedi_pcmcia.h +++ b/drivers/staging/comedi/comedi_pcmcia.h @@ -24,19 +24,21 @@ #include "comedidev.h" -struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *); +struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *dev); -int comedi_pcmcia_enable(struct comedi_device *, - int (*conf_check)(struct pcmcia_device *, void *)); -void comedi_pcmcia_disable(struct comedi_device *); +int comedi_pcmcia_enable(struct comedi_device *dev, + int (*conf_check)(struct pcmcia_device *p_dev, + void *priv_data)); +void comedi_pcmcia_disable(struct comedi_device *dev); -int comedi_pcmcia_auto_config(struct pcmcia_device *, struct comedi_driver *); -void comedi_pcmcia_auto_unconfig(struct pcmcia_device *); +int comedi_pcmcia_auto_config(struct pcmcia_device *link, + struct comedi_driver *driver); +void comedi_pcmcia_auto_unconfig(struct pcmcia_device *link); -int comedi_pcmcia_driver_register(struct comedi_driver *, - struct pcmcia_driver *); -void comedi_pcmcia_driver_unregister(struct comedi_driver *, - struct pcmcia_driver *); +int comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver, + struct pcmcia_driver *pcmcia_driver); +void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver, + struct pcmcia_driver *pcmcia_driver); /** * module_comedi_pcmcia_driver() - Helper macro for registering a comedi diff --git a/drivers/staging/comedi/comedi_usb.h b/drivers/staging/comedi/comedi_usb.h index 721128bece3c..132154ec792f 100644 --- a/drivers/staging/comedi/comedi_usb.h +++ b/drivers/staging/comedi/comedi_usb.h @@ -23,15 +23,17 @@ #include "comedidev.h" -struct usb_interface *comedi_to_usb_interface(struct comedi_device *); -struct usb_device *comedi_to_usb_dev(struct comedi_device *); +struct usb_interface *comedi_to_usb_interface(struct comedi_device *dev); +struct usb_device *comedi_to_usb_dev(struct comedi_device *dev); -int comedi_usb_auto_config(struct usb_interface *, struct comedi_driver *, - unsigned long context); -void comedi_usb_auto_unconfig(struct usb_interface *); +int comedi_usb_auto_config(struct usb_interface *intf, + struct comedi_driver *driver, unsigned long context); +void comedi_usb_auto_unconfig(struct usb_interface *intf); -int comedi_usb_driver_register(struct comedi_driver *, struct usb_driver *); -void comedi_usb_driver_unregister(struct comedi_driver *, struct usb_driver *); +int comedi_usb_driver_register(struct comedi_driver *comedi_driver, + struct usb_driver *usb_driver); +void comedi_usb_driver_unregister(struct comedi_driver *comedi_driver, + struct usb_driver *usb_driver); /** * module_comedi_usb_driver() - Helper macro for registering a comedi USB driver diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h index 0c7c37a8ff33..1bb9986f865e 100644 --- a/drivers/staging/comedi/comedidev.h +++ b/drivers/staging/comedi/comedidev.h @@ -612,12 +612,6 @@ extern const struct comedi_lrange range_unknown; #define range_digital range_unipolar5 -#if __GNUC__ >= 3 -#define GCC_ZERO_LENGTH_ARRAY -#else -#define GCC_ZERO_LENGTH_ARRAY 0 -#endif - /** * struct comedi_lrange - Describes a COMEDI range table * @length: Number of entries in the range table. @@ -631,7 +625,7 @@ extern const struct comedi_lrange range_unknown; */ struct comedi_lrange { int length; - struct comedi_krange range[GCC_ZERO_LENGTH_ARRAY]; + struct comedi_krange range[]; }; /** @@ -982,19 +976,21 @@ unsigned int comedi_buf_read_samples(struct comedi_subdevice *s, #define COMEDI_TIMEOUT_MS 1000 -int comedi_timeout(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, - int (*cb)(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, unsigned long context), +int comedi_timeout(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, + int (*cb)(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned long context), unsigned long context); unsigned int comedi_handle_events(struct comedi_device *dev, struct comedi_subdevice *s); -int comedi_dio_insn_config(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, unsigned int *data, +int comedi_dio_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data, unsigned int mask); -unsigned int comedi_dio_update_state(struct comedi_subdevice *, +unsigned int comedi_dio_update_state(struct comedi_subdevice *s, unsigned int *data); unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s); unsigned int comedi_nscans_left(struct comedi_subdevice *s, @@ -1004,32 +1000,33 @@ unsigned int comedi_nsamples_left(struct comedi_subdevice *s, void comedi_inc_scan_progress(struct comedi_subdevice *s, unsigned int num_bytes); -void *comedi_alloc_devpriv(struct comedi_device *, size_t); -int comedi_alloc_subdevices(struct comedi_device *, int); -int comedi_alloc_subdev_readback(struct comedi_subdevice *); +void *comedi_alloc_devpriv(struct comedi_device *dev, size_t size); +int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices); +int comedi_alloc_subdev_readback(struct comedi_subdevice *s); -int comedi_readback_insn_read(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, unsigned int *data); +int comedi_readback_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); -int comedi_load_firmware(struct comedi_device *, struct device *, +int comedi_load_firmware(struct comedi_device *dev, struct device *hw_dev, const char *name, - int (*cb)(struct comedi_device *, + int (*cb)(struct comedi_device *dev, const u8 *data, size_t size, unsigned long context), unsigned long context); -int __comedi_request_region(struct comedi_device *, +int __comedi_request_region(struct comedi_device *dev, unsigned long start, unsigned long len); -int comedi_request_region(struct comedi_device *, +int comedi_request_region(struct comedi_device *dev, unsigned long start, unsigned long len); -void comedi_legacy_detach(struct comedi_device *); +void comedi_legacy_detach(struct comedi_device *dev); -int comedi_auto_config(struct device *, struct comedi_driver *, - unsigned long context); -void comedi_auto_unconfig(struct device *); +int comedi_auto_config(struct device *hardware_device, + struct comedi_driver *driver, unsigned long context); +void comedi_auto_unconfig(struct device *hardware_device); -int comedi_driver_register(struct comedi_driver *); -void comedi_driver_unregister(struct comedi_driver *); +int comedi_driver_register(struct comedi_driver *driver); +void comedi_driver_unregister(struct comedi_driver *driver); /** * module_comedi_driver() - Helper macro for registering a comedi driver diff --git a/drivers/staging/comedi/drivers/addi_apci_3501.c b/drivers/staging/comedi/drivers/addi_apci_3501.c index 57f0f46de0be..1fdc0f8d7e1a 100644 --- a/drivers/staging/comedi/drivers/addi_apci_3501.c +++ b/drivers/staging/comedi/drivers/addi_apci_3501.c @@ -94,7 +94,7 @@ struct apci3501_private { unsigned char timer_mode; }; -static struct comedi_lrange apci3501_ao_range = { +static const struct comedi_lrange apci3501_ao_range = { 2, { BIP_RANGE(10), UNI_RANGE(10) diff --git a/drivers/staging/comedi/drivers/addi_watchdog.h b/drivers/staging/comedi/drivers/addi_watchdog.h index 3f8e7388bbca..b049cfba9813 100644 --- a/drivers/staging/comedi/drivers/addi_watchdog.h +++ b/drivers/staging/comedi/drivers/addi_watchdog.h @@ -4,6 +4,6 @@ struct comedi_subdevice; void addi_watchdog_reset(unsigned long iobase); -int addi_watchdog_init(struct comedi_subdevice *, unsigned long iobase); +int addi_watchdog_init(struct comedi_subdevice *s, unsigned long iobase); #endif diff --git a/drivers/staging/comedi/drivers/adl_pci9118.c b/drivers/staging/comedi/drivers/adl_pci9118.c index 86450c08f291..1cc9b7ef1ff9 100644 --- a/drivers/staging/comedi/drivers/adl_pci9118.c +++ b/drivers/staging/comedi/drivers/adl_pci9118.c @@ -1279,9 +1279,8 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev, } else { arg = cmd->convert_arg * cmd->chanlist_len; } - err |= comedi_check_trigger_arg_min(&cmd-> - scan_begin_arg, - arg); + err |= comedi_check_trigger_arg_min( + &cmd->scan_begin_arg, arg); } } diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c index cb9c2699277e..efbf27730d71 100644 --- a/drivers/staging/comedi/drivers/cb_pcidas64.c +++ b/drivers/staging/comedi/drivers/cb_pcidas64.c @@ -238,7 +238,7 @@ enum daq_atrig_low_4020_contents { EXT_START_TRIG_BNC_BIT = 0x2000, }; -static inline uint16_t analog_trig_low_threshold_bits(uint16_t threshold) +static inline u16 analog_trig_low_threshold_bits(u16 threshold) { return threshold & 0xfff; } @@ -280,17 +280,17 @@ enum adc_control1_contents { ADC_MODE_MASK = 0xf000, }; -static inline uint16_t adc_lo_chan_4020_bits(unsigned int channel) +static inline u16 adc_lo_chan_4020_bits(unsigned int channel) { return (channel & 0x3) << 8; }; -static inline uint16_t adc_hi_chan_4020_bits(unsigned int channel) +static inline u16 adc_hi_chan_4020_bits(unsigned int channel) { return (channel & 0x3) << 10; }; -static inline uint16_t adc_mode_bits(unsigned int mode) +static inline u16 adc_mode_bits(unsigned int mode) { return (mode & 0xf) << 12; }; @@ -318,12 +318,12 @@ enum calibration_contents { * 7 : dac channel 1 */ -static inline uint16_t adc_src_bits(unsigned int source) +static inline u16 adc_src_bits(unsigned int source) { return (source & 0xf) << 3; }; -static inline uint16_t adc_convert_chan_4020_bits(unsigned int channel) +static inline u16 adc_convert_chan_4020_bits(unsigned int channel) { return (channel & 0x3) << 8; }; @@ -337,7 +337,7 @@ enum adc_queue_load_contents { QUEUE_EOSCAN_BIT = 0x8000, /* queue end of scan */ }; -static inline uint16_t adc_chan_bits(unsigned int channel) +static inline u16 adc_chan_bits(unsigned int channel) { return channel & 0x3f; }; @@ -384,22 +384,22 @@ enum hw_status_contents { ADC_STOP_BIT = 0x200, }; -static inline uint16_t pipe_full_bits(uint16_t hw_status_bits) +static inline u16 pipe_full_bits(u16 hw_status_bits) { return (hw_status_bits >> 10) & 0x3; }; -static inline unsigned int dma_chain_flag_bits(uint16_t prepost_bits) +static inline unsigned int dma_chain_flag_bits(u16 prepost_bits) { return (prepost_bits >> 6) & 0x3; } -static inline unsigned int adc_upper_read_ptr_code(uint16_t prepost_bits) +static inline unsigned int adc_upper_read_ptr_code(u16 prepost_bits) { return (prepost_bits >> 12) & 0x3; } -static inline unsigned int adc_upper_write_ptr_code(uint16_t prepost_bits) +static inline unsigned int adc_upper_write_ptr_code(u16 prepost_bits) { return (prepost_bits >> 14) & 0x3; } @@ -418,12 +418,12 @@ enum range_cal_i2c_contents { BNC_TRIG_THRESHOLD_0V_BIT = 0x80, }; -static inline uint8_t adc_src_4020_bits(unsigned int source) +static inline u8 adc_src_4020_bits(unsigned int source) { return (source << 4) & ADC_SRC_4020_MASK; }; -static inline uint8_t attenuate_bit(unsigned int channel) +static inline u8 attenuate_bit(unsigned int channel) { /* attenuate channel (+-5V input range) */ return 1 << (channel & 0x3); @@ -443,7 +443,7 @@ static const struct comedi_lrange ai_ranges_64xx = { } }; -static const uint8_t ai_range_code_64xx[8] = { +static const u8 ai_range_code_64xx[8] = { 0x0, 0x1, 0x2, 0x3, /* bipolar 10, 5, 2,5, 1.25 */ 0x8, 0x9, 0xa, 0xb /* unipolar 10, 5, 2.5, 1.25 */ }; @@ -461,7 +461,7 @@ static const struct comedi_lrange ai_ranges_64_mx = { } }; -static const uint8_t ai_range_code_64_mx[7] = { +static const u8 ai_range_code_64_mx[7] = { 0x0, 0x1, 0x2, 0x3, /* bipolar 5, 2.5, 1.25, 0.625 */ 0x9, 0xa, 0xb /* unipolar 5, 2.5, 1.25 */ }; @@ -476,7 +476,7 @@ static const struct comedi_lrange ai_ranges_60xx = { } }; -static const uint8_t ai_range_code_60xx[4] = { +static const u8 ai_range_code_60xx[4] = { 0x0, 0x1, 0x4, 0x7 /* bipolar 10, 5, 0.5, 0.05 */ }; @@ -500,7 +500,7 @@ static const struct comedi_lrange ai_ranges_6030 = { } }; -static const uint8_t ai_range_code_6030[14] = { +static const u8 ai_range_code_6030[14] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, /* bip 10, 5, 2, 1, 0.5, 0.2, 0.1 */ 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* uni 10, 5, 2, 1, 0.5, 0.2, 0.1 */ }; @@ -526,7 +526,7 @@ static const struct comedi_lrange ai_ranges_6052 = { } }; -static const uint8_t ai_range_code_6052[15] = { +static const u8 ai_range_code_6052[15] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, /* bipolar 10 ... 0.05 */ 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* unipolar 10 ... 0.1 */ }; @@ -594,7 +594,7 @@ struct hw_fifo_info { unsigned int num_segments; unsigned int max_segment_length; unsigned int sample_packing_ratio; - uint16_t fifo_size_reg_mask; + u16 fifo_size_reg_mask; }; enum pcidas64_boardid { @@ -634,7 +634,7 @@ struct pcidas64_board { int ai_bits; /* analog input resolution */ int ai_speed; /* fastest conversion period in ns */ const struct comedi_lrange *ai_range_table; - const uint8_t *ai_range_code; + const u8 *ai_range_code; int ao_nchan; /* number of analog out channels */ int ao_bits; /* analog output resolution */ int ao_scan_speed; /* analog output scan speed */ @@ -1132,10 +1132,10 @@ struct pcidas64_private { void __iomem *plx9080_iobase; void __iomem *main_iobase; /* local address (used by dma controller) */ - uint32_t local0_iobase; - uint32_t local1_iobase; + u32 local0_iobase; + u32 local1_iobase; /* dma buffers for analog input */ - uint16_t *ai_buffer[MAX_AI_DMA_RING_COUNT]; + u16 *ai_buffer[MAX_AI_DMA_RING_COUNT]; /* physical addresses of ai dma buffers */ dma_addr_t ai_buffer_bus_addr[MAX_AI_DMA_RING_COUNT]; /* @@ -1151,7 +1151,7 @@ struct pcidas64_private { */ unsigned int ai_dma_index; /* dma buffers for analog output */ - uint16_t *ao_buffer[AO_DMA_RING_COUNT]; + u16 *ao_buffer[AO_DMA_RING_COUNT]; /* physical addresses of ao dma buffers */ dma_addr_t ao_buffer_bus_addr[AO_DMA_RING_COUNT]; struct plx_dma_desc *ao_dma_desc; @@ -1162,20 +1162,20 @@ struct pcidas64_private { /* last bits sent to INTR_ENABLE_REG register */ unsigned int intr_enable_bits; /* last bits sent to ADC_CONTROL1_REG register */ - uint16_t adc_control1_bits; + u16 adc_control1_bits; /* last bits sent to FIFO_SIZE_REG register */ - uint16_t fifo_size_bits; + u16 fifo_size_bits; /* last bits sent to HW_CONFIG_REG register */ - uint16_t hw_config_bits; - uint16_t dac_control1_bits; + u16 hw_config_bits; + u16 dac_control1_bits; /* last bits written to plx9080 control register */ - uint32_t plx_control_bits; + u32 plx_control_bits; /* last bits written to plx interrupt control and status register */ - uint32_t plx_intcsr_bits; + u32 plx_intcsr_bits; /* index of calibration source readable through ai ch0 */ int calibration_source; /* bits written to i2c calibration/range register */ - uint8_t i2c_cal_range_bits; + u8 i2c_cal_range_bits; /* configure digital triggers to trigger on falling edge */ unsigned int ext_trig_falling; short ai_cmd_running; @@ -1193,7 +1193,7 @@ static unsigned int ai_range_bits_6xxx(const struct comedi_device *dev, } static unsigned int hw_revision(const struct comedi_device *dev, - uint16_t hw_status_bits) + u16 hw_status_bits) { const struct pcidas64_board *board = dev->board_ptr; @@ -1204,7 +1204,7 @@ static unsigned int hw_revision(const struct comedi_device *dev, } static void set_dac_range_bits(struct comedi_device *dev, - uint16_t *bits, unsigned int channel, + u16 *bits, unsigned int channel, unsigned int range) { const struct pcidas64_board *board = dev->board_ptr; @@ -1266,7 +1266,7 @@ static void enable_ai_interrupts(struct comedi_device *dev, { const struct pcidas64_board *board = dev->board_ptr; struct pcidas64_private *devpriv = dev->private; - uint32_t bits; + u32 bits; unsigned long flags; bits = EN_ADC_OVERRUN_BIT | EN_ADC_DONE_INTR_BIT | @@ -1292,7 +1292,7 @@ static void init_plx9080(struct comedi_device *dev) { const struct pcidas64_board *board = dev->board_ptr; struct pcidas64_private *devpriv = dev->private; - uint32_t bits; + u32 bits; void __iomem *plx_iobase = devpriv->plx9080_iobase; devpriv->plx_control_bits = @@ -1378,7 +1378,7 @@ static int set_ai_fifo_segment_length(struct comedi_device *dev, static const int increment_size = 0x100; const struct hw_fifo_info *const fifo = board->ai_fifo; unsigned int num_increments; - uint16_t bits; + u16 bits; if (num_entries < increment_size) num_entries = increment_size; @@ -1437,7 +1437,7 @@ static void init_stc_registers(struct comedi_device *dev) { const struct pcidas64_board *board = dev->board_ptr; struct pcidas64_private *devpriv = dev->private; - uint16_t bits; + u16 bits; unsigned long flags; spin_lock_irqsave(&dev->spinlock, flags); @@ -1657,9 +1657,9 @@ static void i2c_set_scl(struct comedi_device *dev, int state) } } -static void i2c_write_byte(struct comedi_device *dev, uint8_t byte) +static void i2c_write_byte(struct comedi_device *dev, u8 byte) { - uint8_t bit; + u8 bit; unsigned int num_bits = 8; for (bit = 1 << (num_bits - 1); bit; bit >>= 1) { @@ -1700,11 +1700,11 @@ static void i2c_stop(struct comedi_device *dev) } static void i2c_write(struct comedi_device *dev, unsigned int address, - const uint8_t *data, unsigned int length) + const u8 *data, unsigned int length) { struct pcidas64_private *devpriv = dev->private; unsigned int i; - uint8_t bitstream; + u8 bitstream; static const int read_bit = 0x1; /* @@ -1831,7 +1831,7 @@ static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, /* set start channel, and rest of settings */ writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG); } else { - uint8_t old_cal_range_bits = devpriv->i2c_cal_range_bits; + u8 old_cal_range_bits = devpriv->i2c_cal_range_bits; devpriv->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK; if (insn->chanspec & CR_ALT_SOURCE) { @@ -1850,7 +1850,7 @@ static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, * as it is very slow */ if (old_cal_range_bits != devpriv->i2c_cal_range_bits) { - uint8_t i2c_data = devpriv->i2c_cal_range_bits; + u8 i2c_data = devpriv->i2c_cal_range_bits; i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data, sizeof(i2c_data)); @@ -2273,23 +2273,23 @@ static inline unsigned int dma_transfer_size(struct comedi_device *dev) num_samples = devpriv->ai_fifo_segment_length * board->ai_fifo->sample_packing_ratio; - if (num_samples > DMA_BUFFER_SIZE / sizeof(uint16_t)) - num_samples = DMA_BUFFER_SIZE / sizeof(uint16_t); + if (num_samples > DMA_BUFFER_SIZE / sizeof(u16)) + num_samples = DMA_BUFFER_SIZE / sizeof(u16); return num_samples; } -static uint32_t ai_convert_counter_6xxx(const struct comedi_device *dev, +static u32 ai_convert_counter_6xxx(const struct comedi_device *dev, const struct comedi_cmd *cmd) { /* supposed to load counter with desired divisor minus 3 */ return cmd->convert_arg / TIMER_BASE - 3; } -static uint32_t ai_scan_counter_6xxx(struct comedi_device *dev, +static u32 ai_scan_counter_6xxx(struct comedi_device *dev, struct comedi_cmd *cmd) { - uint32_t count; + u32 count; /* figure out how long we need to delay at end of scan */ switch (cmd->scan_begin_src) { @@ -2307,7 +2307,7 @@ static uint32_t ai_scan_counter_6xxx(struct comedi_device *dev, return count - 3; } -static uint32_t ai_convert_counter_4020(struct comedi_device *dev, +static u32 ai_convert_counter_4020(struct comedi_device *dev, struct comedi_cmd *cmd) { struct pcidas64_private *devpriv = dev->private; @@ -2382,7 +2382,7 @@ static void set_ai_pacing(struct comedi_device *dev, struct comedi_cmd *cmd) { const struct pcidas64_board *board = dev->board_ptr; struct pcidas64_private *devpriv = dev->private; - uint32_t convert_counter = 0, scan_counter = 0; + u32 convert_counter = 0, scan_counter = 0; check_adc_timing(dev, cmd); @@ -2529,7 +2529,7 @@ static int setup_channel_queue(struct comedi_device *dev, * as it is very slow */ if (old_cal_range_bits != devpriv->i2c_cal_range_bits) { - uint8_t i2c_data = devpriv->i2c_cal_range_bits; + u8 i2c_data = devpriv->i2c_cal_range_bits; i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data, sizeof(i2c_data)); @@ -2572,7 +2572,7 @@ static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) struct pcidas64_private *devpriv = dev->private; struct comedi_async *async = s->async; struct comedi_cmd *cmd = &async->cmd; - uint32_t bits; + u32 bits; unsigned int i; unsigned long flags; int retval; @@ -2634,7 +2634,7 @@ static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) for (i = 0; i < ai_dma_ring_count(board); i++) devpriv->ai_dma_desc[i].transfer_size = cpu_to_le32(dma_transfer_size(dev) * - sizeof(uint16_t)); + sizeof(u16)); /* give location of first dma descriptor */ load_first_dma_descriptor(dev, 1, @@ -2691,7 +2691,7 @@ static void pio_drain_ai_fifo_16(struct comedi_device *dev) struct pcidas64_private *devpriv = dev->private; struct comedi_subdevice *s = dev->read_subdev; unsigned int i; - uint16_t prepost_bits; + u16 prepost_bits; int read_segment, read_index, write_segment, write_index; int num_samples; @@ -2754,7 +2754,7 @@ static void pio_drain_ai_fifo_32(struct comedi_device *dev) struct comedi_subdevice *s = dev->read_subdev; unsigned int nsamples; unsigned int i; - uint32_t fifo_data; + u32 fifo_data; int write_code = readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG) & 0x7fff; int read_code = @@ -2794,7 +2794,7 @@ static void drain_dma_buffers(struct comedi_device *dev, unsigned int channel) const struct pcidas64_board *board = dev->board_ptr; struct pcidas64_private *devpriv = dev->private; struct comedi_subdevice *s = dev->read_subdev; - uint32_t next_transfer_addr; + u32 next_transfer_addr; int j; int num_samples = 0; void __iomem *pci_addr_reg; @@ -2831,7 +2831,7 @@ static void handle_ai_interrupt(struct comedi_device *dev, struct comedi_subdevice *s = dev->read_subdev; struct comedi_async *async = s->async; struct comedi_cmd *cmd = &async->cmd; - uint8_t dma1_status; + u8 dma1_status; unsigned long flags; /* check for fifo overrun */ @@ -3008,7 +3008,7 @@ static void handle_ao_interrupt(struct comedi_device *dev, struct comedi_subdevice *s = dev->write_subdev; struct comedi_async *async; struct comedi_cmd *cmd; - uint8_t dma0_status; + u8 dma0_status; unsigned long flags; /* board might not support ao, in which case write_subdev is NULL */ @@ -3056,8 +3056,8 @@ static irqreturn_t handle_interrupt(int irq, void *d) struct comedi_device *dev = d; struct pcidas64_private *devpriv = dev->private; unsigned short status; - uint32_t plx_status; - uint32_t plx_bits; + u32 plx_status; + u32 plx_bits; plx_status = readl(devpriv->plx9080_iobase + PLX_REG_INTCSR); status = readw(devpriv->main_iobase + HW_STATUS_REG); @@ -3180,7 +3180,7 @@ static void set_dac_select_reg(struct comedi_device *dev, const struct comedi_cmd *cmd) { struct pcidas64_private *devpriv = dev->private; - uint16_t bits; + u16 bits; unsigned int first_channel, last_channel; first_channel = CR_CHAN(cmd->chanlist[0]); @@ -3523,7 +3523,7 @@ static int dio_60xx_wbits(struct comedi_device *dev, */ static int caldac_8800_write(struct comedi_device *dev, unsigned int address, - uint8_t value) + u8 value) { struct pcidas64_private *devpriv = dev->private; static const int num_caldac_channels = 8; @@ -3558,8 +3558,8 @@ static int caldac_8800_write(struct comedi_device *dev, unsigned int address, static int caldac_i2c_write(struct comedi_device *dev, unsigned int caldac_channel, unsigned int value) { - uint8_t serial_bytes[3]; - uint8_t i2c_addr; + u8 serial_bytes[3]; + u8 i2c_addr; enum pointer_bits { /* manual has gain and offset bits switched */ OFFSET_0_2 = 0x1, @@ -3708,7 +3708,7 @@ static int cb_pcidas64_ad8402_insn_write(struct comedi_device *dev, return insn->n; } -static uint16_t read_eeprom(struct comedi_device *dev, uint8_t address) +static u16 read_eeprom(struct comedi_device *dev, u8 address) { struct pcidas64_private *devpriv = dev->private; static const int bitstream_length = 11; @@ -3717,7 +3717,7 @@ static uint16_t read_eeprom(struct comedi_device *dev, uint8_t address) unsigned int bit; void __iomem * const plx_control_addr = devpriv->plx9080_iobase + PLX_REG_CNTRL; - uint16_t value; + u16 value; static const int value_length = 16; static const int eeprom_udelay = 1; @@ -3813,7 +3813,7 @@ static int setup_subdevices(struct comedi_device *dev) s->do_cmdtest = ai_cmdtest; s->cancel = ai_cancel; if (board->layout == LAYOUT_4020) { - uint8_t data; + u8 data; /* * set adc to read from inputs * (not internal calibration sources) @@ -3975,7 +3975,7 @@ static int auto_attach(struct comedi_device *dev, struct pci_dev *pcidev = comedi_to_pci_dev(dev); const struct pcidas64_board *board = NULL; struct pcidas64_private *devpriv; - uint32_t local_range, local_decode; + u32 local_range, local_decode; int retval; if (context < ARRAY_SIZE(pcidas64_boards)) @@ -4013,13 +4013,13 @@ static int auto_attach(struct comedi_device *dev, PLX_LASRR_MEM_MASK; local_decode = readl(devpriv->plx9080_iobase + PLX_REG_LAS0BA) & local_range & PLX_LASBA_MEM_MASK; - devpriv->local0_iobase = ((uint32_t)devpriv->main_phys_iobase & + devpriv->local0_iobase = ((u32)devpriv->main_phys_iobase & ~local_range) | local_decode; local_range = readl(devpriv->plx9080_iobase + PLX_REG_LAS1RR) & PLX_LASRR_MEM_MASK; local_decode = readl(devpriv->plx9080_iobase + PLX_REG_LAS1BA) & local_range & PLX_LASBA_MEM_MASK; - devpriv->local1_iobase = ((uint32_t)devpriv->dio_counter_phys_iobase & + devpriv->local1_iobase = ((u32)devpriv->dio_counter_phys_iobase & ~local_range) | local_decode; retval = alloc_and_init_dma_members(dev); diff --git a/drivers/staging/comedi/drivers/comedi_8254.h b/drivers/staging/comedi/drivers/comedi_8254.h index a12c29455d9d..326bd44b063e 100644 --- a/drivers/staging/comedi/drivers/comedi_8254.h +++ b/drivers/staging/comedi/drivers/comedi_8254.h @@ -100,34 +100,36 @@ struct comedi_8254 { unsigned int gate_src[3]; bool busy[3]; - int (*insn_config)(struct comedi_device *, struct comedi_subdevice *s, - struct comedi_insn *, unsigned int *data); + int (*insn_config)(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); }; -unsigned int comedi_8254_status(struct comedi_8254 *, unsigned int counter); -unsigned int comedi_8254_read(struct comedi_8254 *, unsigned int counter); -void comedi_8254_write(struct comedi_8254 *, +unsigned int comedi_8254_status(struct comedi_8254 *i8254, + unsigned int counter); +unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter); +void comedi_8254_write(struct comedi_8254 *i8254, unsigned int counter, unsigned int val); -int comedi_8254_set_mode(struct comedi_8254 *, +int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter, unsigned int mode); -int comedi_8254_load(struct comedi_8254 *, +int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter, unsigned int val, unsigned int mode); -void comedi_8254_pacer_enable(struct comedi_8254 *, +void comedi_8254_pacer_enable(struct comedi_8254 *i8254, unsigned int counter1, unsigned int counter2, bool enable); -void comedi_8254_update_divisors(struct comedi_8254 *); -void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *, +void comedi_8254_update_divisors(struct comedi_8254 *i8254); +void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254, unsigned int *nanosec, unsigned int flags); -void comedi_8254_ns_to_timer(struct comedi_8254 *, +void comedi_8254_ns_to_timer(struct comedi_8254 *i8254, unsigned int *nanosec, unsigned int flags); -void comedi_8254_set_busy(struct comedi_8254 *, +void comedi_8254_set_busy(struct comedi_8254 *i8254, unsigned int counter, bool busy); -void comedi_8254_subdevice_init(struct comedi_subdevice *, - struct comedi_8254 *); +void comedi_8254_subdevice_init(struct comedi_subdevice *s, + struct comedi_8254 *i8254); struct comedi_8254 *comedi_8254_init(unsigned long iobase, unsigned int osc_base, diff --git a/drivers/staging/comedi/drivers/comedi_isadma.h b/drivers/staging/comedi/drivers/comedi_isadma.h index 2fb6573ba9e4..a193d3e8d185 100644 --- a/drivers/staging/comedi/drivers/comedi_isadma.h +++ b/drivers/staging/comedi/drivers/comedi_isadma.h @@ -63,18 +63,18 @@ struct comedi_isadma { #if IS_ENABLED(CONFIG_ISA_DMA_API) -void comedi_isadma_program(struct comedi_isadma_desc *); +void comedi_isadma_program(struct comedi_isadma_desc *desc); unsigned int comedi_isadma_disable(unsigned int dma_chan); unsigned int comedi_isadma_disable_on_sample(unsigned int dma_chan, unsigned int size); -unsigned int comedi_isadma_poll(struct comedi_isadma *); -void comedi_isadma_set_mode(struct comedi_isadma_desc *, char dma_dir); +unsigned int comedi_isadma_poll(struct comedi_isadma *dma); +void comedi_isadma_set_mode(struct comedi_isadma_desc *desc, char dma_dir); -struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *, +struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *dev, int n_desc, unsigned int dma_chan1, unsigned int dma_chan2, unsigned int maxsize, char dma_dir); -void comedi_isadma_free(struct comedi_isadma *); +void comedi_isadma_free(struct comedi_isadma *dma); #else /* !IS_ENABLED(CONFIG_ISA_DMA_API) */ diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c index ec5b9a23494d..2a063f07fe7b 100644 --- a/drivers/staging/comedi/drivers/comedi_test.c +++ b/drivers/staging/comedi/drivers/comedi_test.c @@ -36,7 +36,15 @@ * generate sample waveforms on systems that don't have data acquisition * hardware. * - * Configuration options: + * Auto-configuration is the default mode if no parameter is supplied during + * module loading. Manual configuration requires COMEDI userspace tool. + * To disable auto-configuration mode, pass "noauto=1" parameter for module + * loading. Refer modinfo or MODULE_PARM_DESC description below for details. + * + * Auto-configuration options: + * Refer modinfo or MODULE_PARM_DESC description below for details. + * + * Manual configuration options: * [0] - Amplitude in microvolts for fake waveforms (default 1 volt) * [1] - Period in microseconds for fake waveforms (default 0.1 sec) * @@ -53,8 +61,27 @@ #include #include #include +#include +#include #define N_CHANS 8 +#define DEV_NAME "comedi_testd" +#define CLASS_NAME "comedi_test" + +static bool config_mode; +static unsigned int set_amplitude; +static unsigned int set_period; +static struct class *ctcls; +static struct device *ctdev; + +module_param_named(noauto, config_mode, bool, 0444); +MODULE_PARM_DESC(noauto, "Disable auto-configuration: (1=disable [defaults to enable])"); + +module_param_named(amplitude, set_amplitude, uint, 0444); +MODULE_PARM_DESC(amplitude, "Set auto mode wave amplitude in microvolts: (defaults to 1 volt)"); + +module_param_named(period, set_period, uint, 0444); +MODULE_PARM_DESC(period, "Set auto mode wave period in microseconds: (defaults to 0.1 sec)"); /* Data unique to this driver */ struct waveform_private { @@ -607,13 +634,11 @@ static int waveform_ao_insn_write(struct comedi_device *dev, return insn->n; } -static int waveform_attach(struct comedi_device *dev, - struct comedi_devconfig *it) +static int waveform_common_attach(struct comedi_device *dev, + int amplitude, int period) { struct waveform_private *devpriv; struct comedi_subdevice *s; - int amplitude = it->options[0]; - int period = it->options[1]; int i; int ret; @@ -621,12 +646,6 @@ static int waveform_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - /* set default amplitude and period */ - if (amplitude <= 0) - amplitude = 1000000; /* 1 volt */ - if (period <= 0) - period = 100000; /* 0.1 sec */ - devpriv->wf_amplitude = amplitude; devpriv->wf_period = period; @@ -678,6 +697,36 @@ static int waveform_attach(struct comedi_device *dev, return 0; } +static int waveform_attach(struct comedi_device *dev, + struct comedi_devconfig *it) +{ + int amplitude = it->options[0]; + int period = it->options[1]; + + /* set default amplitude and period */ + if (amplitude <= 0) + amplitude = 1000000; /* 1 volt */ + if (period <= 0) + period = 100000; /* 0.1 sec */ + + return waveform_common_attach(dev, amplitude, period); +} + +static int waveform_auto_attach(struct comedi_device *dev, + unsigned long context_unused) +{ + int amplitude = set_amplitude; + int period = set_period; + + /* set default amplitude and period */ + if (!amplitude) + amplitude = 1000000; /* 1 volt */ + if (!period) + period = 100000; /* 0.1 sec */ + + return waveform_common_attach(dev, amplitude, period); +} + static void waveform_detach(struct comedi_device *dev) { struct waveform_private *devpriv = dev->private; @@ -692,9 +741,71 @@ static struct comedi_driver waveform_driver = { .driver_name = "comedi_test", .module = THIS_MODULE, .attach = waveform_attach, + .auto_attach = waveform_auto_attach, .detach = waveform_detach, }; -module_comedi_driver(waveform_driver); + +/* + * For auto-configuration, a device is created to stand in for a + * real hardware device. + */ +static int __init comedi_test_init(void) +{ + int ret; + + ret = comedi_driver_register(&waveform_driver); + if (ret) { + pr_err("comedi_test: unable to register driver\n"); + return ret; + } + + if (!config_mode) { + ctcls = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(ctcls)) { + pr_warn("comedi_test: unable to create class\n"); + goto clean3; + } + + ctdev = device_create(ctcls, NULL, MKDEV(0, 0), NULL, DEV_NAME); + if (IS_ERR(ctdev)) { + pr_warn("comedi_test: unable to create device\n"); + goto clean2; + } + + ret = comedi_auto_config(ctdev, &waveform_driver, 0); + if (ret) { + pr_warn("comedi_test: unable to auto-configure device\n"); + goto clean; + } + } + + return 0; + +clean: + device_destroy(ctcls, MKDEV(0, 0)); +clean2: + class_destroy(ctcls); + ctdev = NULL; +clean3: + ctcls = NULL; + + return 0; +} +module_init(comedi_test_init); + +static void __exit comedi_test_exit(void) +{ + if (ctdev) + comedi_auto_unconfig(ctdev); + + if (ctcls) { + device_destroy(ctcls, MKDEV(0, 0)); + class_destroy(ctcls); + } + + comedi_driver_unregister(&waveform_driver); +} +module_exit(comedi_test_exit); MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); diff --git a/drivers/staging/comedi/drivers/daqboard2000.c b/drivers/staging/comedi/drivers/daqboard2000.c index 0f4eb954aa80..32dd8a857b6b 100644 --- a/drivers/staging/comedi/drivers/daqboard2000.c +++ b/drivers/staging/comedi/drivers/daqboard2000.c @@ -109,28 +109,11 @@ #include "../comedi_pci.h" #include "8255.h" +#include "plx9080.h" -#define DAQBOARD2000_FIRMWARE "daqboard2000_firmware.bin" +#define DB2K_FIRMWARE "daqboard2000_firmware.bin" -#define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */ -#define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */ - -/* Initialization bits for the Serial EEPROM Control Register */ -#define DB2K_SECR_PROG_PIN_HI 0x8001767e -#define DB2K_SECR_PROG_PIN_LO 0x8000767e -#define DB2K_SECR_LOCAL_BUS_HI 0xc000767e -#define DB2K_SECR_LOCAL_BUS_LO 0x8000767e -#define DB2K_SECR_RELOAD_HI 0xa000767e -#define DB2K_SECR_RELOAD_LO 0x8000767e - -/* SECR status bits */ -#define DAQBOARD2000_EEPROM_PRESENT 0x10000000 - -/* CPLD status bits */ -#define DAQBOARD2000_CPLD_INIT 0x0002 -#define DAQBOARD2000_CPLD_DONE 0x0004 - -static const struct comedi_lrange range_daqboard2000_ai = { +static const struct comedi_lrange db2k_ai_range = { 13, { BIP_RANGE(10), BIP_RANGE(5), @@ -183,6 +166,10 @@ static const struct comedi_lrange range_daqboard2000_ai = { #define DB2K_REG_TRIG_DACS 0xbc /* u16 */ #define DB2K_REG_DIO_P2_EXP_IO_16_BIT(x) (0xc0 + (x) * 2) /* s16 */ +/* CPLD registers */ +#define DB2K_REG_CPLD_STATUS 0x1000 /* u16 (r) */ +#define DB2K_REG_CPLD_WDATA 0x1000 /* u16 (w) */ + /* Scan Sequencer programming */ #define DB2K_ACQ_CONTROL_SEQ_START_SCAN_LIST 0x0011 #define DB2K_ACQ_CONTROL_SEQ_STOP_SCAN_LIST 0x0010 @@ -248,33 +235,45 @@ static const struct comedi_lrange range_daqboard2000_ai = { #define DB2K_REF_DACS_SELECT_POS_REF 0x0100 #define DB2K_REF_DACS_SELECT_NEG_REF 0x0000 -struct daq200_boardtype { +/* CPLD status bits */ +#define DB2K_CPLD_STATUS_INIT 0x0002 +#define DB2K_CPLD_STATUS_TXREADY 0x0004 +#define DB2K_CPLD_VERSION_MASK 0xf000 +/* "New CPLD" signature. */ +#define DB2K_CPLD_VERSION_NEW 0x5000 + +enum db2k_boardid { + BOARD_DAQBOARD2000, + BOARD_DAQBOARD2001 +}; + +struct db2k_boardtype { const char *name; - int id; + bool has_2_ao:1; /* false: 4 AO chans; true: 2 AO chans */ }; -static const struct daq200_boardtype boardtypes[] = { - {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2}, - {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4}, +static const struct db2k_boardtype db2k_boardtypes[] = { + [BOARD_DAQBOARD2000] = { + .name = "daqboard2000", + .has_2_ao = true, + }, + [BOARD_DAQBOARD2001] = { + .name = "daqboard2001", + }, }; -struct daqboard2000_private { - enum { - card_daqboard_2000 - } card; +struct db2k_private { void __iomem *plx; }; -static void daqboard2000_write_acq_scan_list_entry(struct comedi_device *dev, - u16 entry) +static void db2k_write_acq_scan_list_entry(struct comedi_device *dev, u16 entry) { writew(entry & 0x00ff, dev->mmio + DB2K_REG_ACQ_SCAN_LIST_FIFO); writew((entry >> 8) & 0x00ff, dev->mmio + DB2K_REG_ACQ_SCAN_LIST_FIFO); } -static void daqboard2000_setup_sampling(struct comedi_device *dev, int chan, - int gain) +static void db2k_setup_sampling(struct comedi_device *dev, int chan, int gain) { u16 word0, word1, word2, word3; @@ -308,16 +307,14 @@ static void daqboard2000_setup_sampling(struct comedi_device *dev, int chan, /* These should be read from EEPROM */ word2 |= 0x0800; /* offset */ word3 |= 0xc000; /* gain */ - daqboard2000_write_acq_scan_list_entry(dev, word0); - daqboard2000_write_acq_scan_list_entry(dev, word1); - daqboard2000_write_acq_scan_list_entry(dev, word2); - daqboard2000_write_acq_scan_list_entry(dev, word3); + db2k_write_acq_scan_list_entry(dev, word0); + db2k_write_acq_scan_list_entry(dev, word1); + db2k_write_acq_scan_list_entry(dev, word2); + db2k_write_acq_scan_list_entry(dev, word3); } -static int daqboard2000_ai_status(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) +static int db2k_ai_status(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned long context) { unsigned int status; @@ -327,10 +324,9 @@ static int daqboard2000_ai_status(struct comedi_device *dev, return -EBUSY; } -static int daqboard2000_ai_insn_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) +static int db2k_ai_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data) { int gain, chan; int ret; @@ -359,12 +355,12 @@ static int daqboard2000_ai_insn_read(struct comedi_device *dev, * forced to fix it. --ds */ for (i = 0; i < insn->n; i++) { - daqboard2000_setup_sampling(dev, chan, gain); + db2k_setup_sampling(dev, chan, gain); /* Enable reading from the scanlist FIFO */ writew(DB2K_ACQ_CONTROL_SEQ_START_SCAN_LIST, dev->mmio + DB2K_REG_ACQ_CONTROL); - ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status, + ret = comedi_timeout(dev, s, insn, db2k_ai_status, DB2K_ACQ_STATUS_CONFIG_PIPE_FULL); if (ret) return ret; @@ -372,13 +368,13 @@ static int daqboard2000_ai_insn_read(struct comedi_device *dev, writew(DB2K_ACQ_CONTROL_ADC_PACER_ENABLE, dev->mmio + DB2K_REG_ACQ_CONTROL); - ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status, + ret = comedi_timeout(dev, s, insn, db2k_ai_status, DB2K_ACQ_STATUS_LOGIC_SCANNING); if (ret) return ret; ret = - comedi_timeout(dev, s, insn, daqboard2000_ai_status, + comedi_timeout(dev, s, insn, db2k_ai_status, DB2K_ACQ_STATUS_RESULTS_FIFO_HAS_DATA); if (ret) return ret; @@ -393,10 +389,8 @@ static int daqboard2000_ai_insn_read(struct comedi_device *dev, return i; } -static int daqboard2000_ao_eoc(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) +static int db2k_ao_eoc(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned long context) { unsigned int chan = CR_CHAN(insn->chanspec); unsigned int status; @@ -407,10 +401,9 @@ static int daqboard2000_ao_eoc(struct comedi_device *dev, return -EBUSY; } -static int daqboard2000_ao_insn_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) +static int db2k_ao_insn_write(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data) { unsigned int chan = CR_CHAN(insn->chanspec); int i; @@ -421,7 +414,7 @@ static int daqboard2000_ao_insn_write(struct comedi_device *dev, writew(val, dev->mmio + DB2K_REG_DAC_SETTING(chan)); - ret = comedi_timeout(dev, s, insn, daqboard2000_ao_eoc, 0); + ret = comedi_timeout(dev, s, insn, db2k_ao_eoc, 0); if (ret) return ret; @@ -431,49 +424,62 @@ static int daqboard2000_ao_insn_write(struct comedi_device *dev, return insn->n; } -static void daqboard2000_reset_local_bus(struct comedi_device *dev) +static void db2k_reset_local_bus(struct comedi_device *dev) { - struct daqboard2000_private *devpriv = dev->private; + struct db2k_private *devpriv = dev->private; + u32 cntrl; - writel(DB2K_SECR_LOCAL_BUS_HI, devpriv->plx + 0x6c); + cntrl = readl(devpriv->plx + PLX_REG_CNTRL); + cntrl |= PLX_CNTRL_RESET; + writel(cntrl, devpriv->plx + PLX_REG_CNTRL); mdelay(10); - writel(DB2K_SECR_LOCAL_BUS_LO, devpriv->plx + 0x6c); + cntrl &= ~PLX_CNTRL_RESET; + writel(cntrl, devpriv->plx + PLX_REG_CNTRL); mdelay(10); } -static void daqboard2000_reload_plx(struct comedi_device *dev) +static void db2k_reload_plx(struct comedi_device *dev) { - struct daqboard2000_private *devpriv = dev->private; + struct db2k_private *devpriv = dev->private; + u32 cntrl; - writel(DB2K_SECR_RELOAD_LO, devpriv->plx + 0x6c); + cntrl = readl(devpriv->plx + PLX_REG_CNTRL); + cntrl &= ~PLX_CNTRL_EERELOAD; + writel(cntrl, devpriv->plx + PLX_REG_CNTRL); mdelay(10); - writel(DB2K_SECR_RELOAD_HI, devpriv->plx + 0x6c); + cntrl |= PLX_CNTRL_EERELOAD; + writel(cntrl, devpriv->plx + PLX_REG_CNTRL); mdelay(10); - writel(DB2K_SECR_RELOAD_LO, devpriv->plx + 0x6c); + cntrl &= ~PLX_CNTRL_EERELOAD; + writel(cntrl, devpriv->plx + PLX_REG_CNTRL); mdelay(10); } -static void daqboard2000_pulse_prog_pin(struct comedi_device *dev) +static void db2k_pulse_prog_pin(struct comedi_device *dev) { - struct daqboard2000_private *devpriv = dev->private; + struct db2k_private *devpriv = dev->private; + u32 cntrl; - writel(DB2K_SECR_PROG_PIN_HI, devpriv->plx + 0x6c); + cntrl = readl(devpriv->plx + PLX_REG_CNTRL); + cntrl |= PLX_CNTRL_USERO; + writel(cntrl, devpriv->plx + PLX_REG_CNTRL); mdelay(10); - writel(DB2K_SECR_PROG_PIN_LO, devpriv->plx + 0x6c); + cntrl &= ~PLX_CNTRL_USERO; + writel(cntrl, devpriv->plx + PLX_REG_CNTRL); mdelay(10); /* Not in the original code, but I like symmetry... */ } -static int daqboard2000_poll_cpld(struct comedi_device *dev, int mask) +static int db2k_wait_cpld_init(struct comedi_device *dev) { - int result = 0; + int result = -ETIMEDOUT; int i; - int cpld; + u16 cpld; /* timeout after 50 tries -> 5ms */ for (i = 0; i < 50; i++) { - cpld = readw(dev->mmio + 0x1000); - if ((cpld & mask) == mask) { - result = 1; + cpld = readw(dev->mmio + DB2K_REG_CPLD_STATUS); + if (cpld & DB2K_CPLD_STATUS_INIT) { + result = 0; break; } usleep_range(100, 1000); @@ -482,67 +488,123 @@ static int daqboard2000_poll_cpld(struct comedi_device *dev, int mask) return result; } -static int daqboard2000_write_cpld(struct comedi_device *dev, int data) +static int db2k_wait_cpld_txready(struct comedi_device *dev) +{ + int i; + + for (i = 0; i < 100; i++) { + if (readw(dev->mmio + DB2K_REG_CPLD_STATUS) & + DB2K_CPLD_STATUS_TXREADY) { + return 0; + } + udelay(1); + } + return -ETIMEDOUT; +} + +static int db2k_write_cpld(struct comedi_device *dev, u16 data, bool new_cpld) { int result = 0; - usleep_range(10, 20); - writew(data, dev->mmio + 0x1000); - if ((readw(dev->mmio + 0x1000) & DAQBOARD2000_CPLD_INIT) == - DAQBOARD2000_CPLD_INIT) { - result = 1; + if (new_cpld) { + result = db2k_wait_cpld_txready(dev); + if (result) + return result; + } else { + usleep_range(10, 20); } + writew(data, dev->mmio + DB2K_REG_CPLD_WDATA); + if (!(readw(dev->mmio + DB2K_REG_CPLD_STATUS) & DB2K_CPLD_STATUS_INIT)) + result = -EIO; + return result; } -static int daqboard2000_load_firmware(struct comedi_device *dev, - const u8 *cpld_array, size_t len, - unsigned long context) +static int db2k_wait_fpga_programmed(struct comedi_device *dev) { - struct daqboard2000_private *devpriv = dev->private; + struct db2k_private *devpriv = dev->private; + int i; + + /* Time out after 200 tries -> 20ms */ + for (i = 0; i < 200; i++) { + u32 cntrl = readl(devpriv->plx + PLX_REG_CNTRL); + /* General Purpose Input (USERI) set on FPGA "DONE". */ + if (cntrl & PLX_CNTRL_USERI) + return 0; + + usleep_range(100, 1000); + } + return -ETIMEDOUT; +} + +static int db2k_load_firmware(struct comedi_device *dev, const u8 *cpld_array, + size_t len, unsigned long context) +{ + struct db2k_private *devpriv = dev->private; int result = -EIO; - /* Read the serial EEPROM control register */ - int secr; + u32 cntrl; int retry; size_t i; + bool new_cpld; + + /* Look for FPGA start sequence in firmware. */ + for (i = 0; i + 1 < len; i++) { + if (cpld_array[i] == 0xff && cpld_array[i + 1] == 0x20) + break; + } + if (i + 1 >= len) { + dev_err(dev->class_dev, "bad firmware - no start sequence\n"); + return -EINVAL; + } + /* Check length is even. */ + if ((len - i) & 1) { + dev_err(dev->class_dev, + "bad firmware - odd length (%zu = %zu - %zu)\n", + len - i, len, i); + return -EINVAL; + } + /* Strip firmware header. */ + cpld_array += i; + len -= i; /* Check to make sure the serial eeprom is present on the board */ - secr = readl(devpriv->plx + 0x6c); - if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) + cntrl = readl(devpriv->plx + PLX_REG_CNTRL); + if (!(cntrl & PLX_CNTRL_EEPRESENT)) return -EIO; for (retry = 0; retry < 3; retry++) { - daqboard2000_reset_local_bus(dev); - daqboard2000_reload_plx(dev); - daqboard2000_pulse_prog_pin(dev); - if (daqboard2000_poll_cpld(dev, DAQBOARD2000_CPLD_INIT)) { - for (i = 0; i < len; i++) { - if (cpld_array[i] == 0xff && - cpld_array[i + 1] == 0x20) - break; - } - for (; i < len; i += 2) { - int data = - (cpld_array[i] << 8) + cpld_array[i + 1]; - if (!daqboard2000_write_cpld(dev, data)) - break; - } - if (i >= len) { - daqboard2000_reset_local_bus(dev); - daqboard2000_reload_plx(dev); - result = 0; + db2k_reset_local_bus(dev); + db2k_reload_plx(dev); + db2k_pulse_prog_pin(dev); + result = db2k_wait_cpld_init(dev); + if (result) + continue; + + new_cpld = (readw(dev->mmio + DB2K_REG_CPLD_STATUS) & + DB2K_CPLD_VERSION_MASK) == DB2K_CPLD_VERSION_NEW; + for (; i < len; i += 2) { + u16 data = (cpld_array[i] << 8) + cpld_array[i + 1]; + + result = db2k_write_cpld(dev, data, new_cpld); + if (result) break; - } + } + if (result == 0) + result = db2k_wait_fpga_programmed(dev); + if (result == 0) { + db2k_reset_local_bus(dev); + db2k_reload_plx(dev); + break; } } return result; } -static void daqboard2000_adc_stop_dma_transfer(struct comedi_device *dev) +static void db2k_adc_stop_dma_transfer(struct comedi_device *dev) { } -static void daqboard2000_adc_disarm(struct comedi_device *dev) +static void db2k_adc_disarm(struct comedi_device *dev) { /* Disable hardware triggers */ udelay(2); @@ -563,10 +625,10 @@ static void daqboard2000_adc_disarm(struct comedi_device *dev) dev->mmio + DB2K_REG_ACQ_CONTROL); /* Stop the input dma (abort channel 1) */ - daqboard2000_adc_stop_dma_transfer(dev); + db2k_adc_stop_dma_transfer(dev); } -static void daqboard2000_activate_reference_dacs(struct comedi_device *dev) +static void db2k_activate_reference_dacs(struct comedi_device *dev) { unsigned int val; int timeout; @@ -592,34 +654,33 @@ static void daqboard2000_activate_reference_dacs(struct comedi_device *dev) } } -static void daqboard2000_initialize_ctrs(struct comedi_device *dev) +static void db2k_initialize_ctrs(struct comedi_device *dev) { } -static void daqboard2000_initialize_tmrs(struct comedi_device *dev) +static void db2k_initialize_tmrs(struct comedi_device *dev) { } -static void daqboard2000_dac_disarm(struct comedi_device *dev) +static void db2k_dac_disarm(struct comedi_device *dev) { } -static void daqboard2000_initialize_adc(struct comedi_device *dev) +static void db2k_initialize_adc(struct comedi_device *dev) { - daqboard2000_adc_disarm(dev); - daqboard2000_activate_reference_dacs(dev); - daqboard2000_initialize_ctrs(dev); - daqboard2000_initialize_tmrs(dev); + db2k_adc_disarm(dev); + db2k_activate_reference_dacs(dev); + db2k_initialize_ctrs(dev); + db2k_initialize_tmrs(dev); } -static void daqboard2000_initialize_dac(struct comedi_device *dev) +static void db2k_initialize_dac(struct comedi_device *dev) { - daqboard2000_dac_disarm(dev); + db2k_dac_disarm(dev); } -static int daqboard2000_8255_cb(struct comedi_device *dev, - int dir, int port, int data, - unsigned long iobase) +static int db2k_8255_cb(struct comedi_device *dev, int dir, int port, int data, + unsigned long iobase) { if (dir) { writew(data, dev->mmio + iobase + port * 2); @@ -628,34 +689,18 @@ static int daqboard2000_8255_cb(struct comedi_device *dev, return readw(dev->mmio + iobase + port * 2); } -static const void *daqboard2000_find_boardinfo(struct comedi_device *dev, - struct pci_dev *pcidev) -{ - const struct daq200_boardtype *board; - int i; - - if (pcidev->subsystem_vendor != PCI_VENDOR_ID_IOTECH) - return NULL; - - for (i = 0; i < ARRAY_SIZE(boardtypes); i++) { - board = &boardtypes[i]; - if (pcidev->subsystem_device == board->id) - return board; - } - return NULL; -} - -static int daqboard2000_auto_attach(struct comedi_device *dev, - unsigned long context_unused) +static int db2k_auto_attach(struct comedi_device *dev, unsigned long context) { struct pci_dev *pcidev = comedi_to_pci_dev(dev); - const struct daq200_boardtype *board; - struct daqboard2000_private *devpriv; + const struct db2k_boardtype *board; + struct db2k_private *devpriv; struct comedi_subdevice *s; int result; - board = daqboard2000_find_boardinfo(dev, pcidev); - if (!board) + if (context >= ARRAY_SIZE(db2k_boardtypes)) + return -ENODEV; + board = &db2k_boardtypes[context]; + if (!board->name) return -ENODEV; dev->board_ptr = board; dev->board_name = board->name; @@ -677,16 +722,13 @@ static int daqboard2000_auto_attach(struct comedi_device *dev, if (result) return result; - readl(devpriv->plx + 0x6c); - result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev, - DAQBOARD2000_FIRMWARE, - daqboard2000_load_firmware, 0); + DB2K_FIRMWARE, db2k_load_firmware, 0); if (result < 0) return result; - daqboard2000_initialize_adc(dev); - daqboard2000_initialize_dac(dev); + db2k_initialize_adc(dev); + db2k_initialize_dac(dev); s = &dev->subdevices[0]; /* ai subdevice */ @@ -694,16 +736,16 @@ static int daqboard2000_auto_attach(struct comedi_device *dev, s->subdev_flags = SDF_READABLE | SDF_GROUND; s->n_chan = 24; s->maxdata = 0xffff; - s->insn_read = daqboard2000_ai_insn_read; - s->range_table = &range_daqboard2000_ai; + s->insn_read = db2k_ai_insn_read; + s->range_table = &db2k_ai_range; s = &dev->subdevices[1]; /* ao subdevice */ s->type = COMEDI_SUBD_AO; s->subdev_flags = SDF_WRITABLE; - s->n_chan = 2; + s->n_chan = board->has_2_ao ? 2 : 4; s->maxdata = 0xffff; - s->insn_write = daqboard2000_ao_insn_write; + s->insn_write = db2k_ao_insn_write; s->range_table = &range_bipolar10; result = comedi_alloc_subdev_readback(s); @@ -711,48 +753,49 @@ static int daqboard2000_auto_attach(struct comedi_device *dev, return result; s = &dev->subdevices[2]; - return subdev_8255_init(dev, s, daqboard2000_8255_cb, + return subdev_8255_init(dev, s, db2k_8255_cb, DB2K_REG_DIO_P2_EXP_IO_8_BIT); } -static void daqboard2000_detach(struct comedi_device *dev) +static void db2k_detach(struct comedi_device *dev) { - struct daqboard2000_private *devpriv = dev->private; + struct db2k_private *devpriv = dev->private; if (devpriv && devpriv->plx) iounmap(devpriv->plx); comedi_pci_detach(dev); } -static struct comedi_driver daqboard2000_driver = { +static struct comedi_driver db2k_driver = { .driver_name = "daqboard2000", .module = THIS_MODULE, - .auto_attach = daqboard2000_auto_attach, - .detach = daqboard2000_detach, + .auto_attach = db2k_auto_attach, + .detach = db2k_detach, }; -static int daqboard2000_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) +static int db2k_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { - return comedi_pci_auto_config(dev, &daqboard2000_driver, - id->driver_data); + return comedi_pci_auto_config(dev, &db2k_driver, id->driver_data); } -static const struct pci_device_id daqboard2000_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) }, +static const struct pci_device_id db2k_pci_table[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_IOTECH, 0x0409, PCI_VENDOR_ID_IOTECH, + 0x0002), .driver_data = BOARD_DAQBOARD2000, }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_IOTECH, 0x0409, PCI_VENDOR_ID_IOTECH, + 0x0004), .driver_data = BOARD_DAQBOARD2001, }, { 0 } }; -MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table); +MODULE_DEVICE_TABLE(pci, db2k_pci_table); -static struct pci_driver daqboard2000_pci_driver = { +static struct pci_driver db2k_pci_driver = { .name = "daqboard2000", - .id_table = daqboard2000_pci_table, - .probe = daqboard2000_pci_probe, + .id_table = db2k_pci_table, + .probe = db2k_pci_probe, .remove = comedi_pci_auto_unconfig, }; -module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver); +module_comedi_pci_driver(db2k_driver, db2k_pci_driver); MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE); +MODULE_FIRMWARE(DB2K_FIRMWARE); diff --git a/drivers/staging/comedi/drivers/dmm32at.c b/drivers/staging/comedi/drivers/dmm32at.c index b8606ded0623..771cceb71069 100644 --- a/drivers/staging/comedi/drivers/dmm32at.c +++ b/drivers/staging/comedi/drivers/dmm32at.c @@ -510,7 +510,7 @@ static int dmm32at_reset(struct comedi_device *dev) outb(DMM32AT_CTRL_RESETA, dev->iobase + DMM32AT_CTRL_REG); /* allow a millisecond to reset */ - udelay(1000); + usleep_range(1000, 3000); /* zero scan and fifo control */ outb(0x0, dev->iobase + DMM32AT_FIFO_CTRL_REG); @@ -526,7 +526,7 @@ static int dmm32at_reset(struct comedi_device *dev) outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AI_CFG_REG); /* should take 10 us to settle, here's a hundred */ - udelay(100); + usleep_range(100, 200); /* read back the values */ ailo = inb(dev->iobase + DMM32AT_AI_LO_CHAN_REG); diff --git a/drivers/staging/comedi/drivers/dt2801.c b/drivers/staging/comedi/drivers/dt2801.c index c2ce1eb87385..30805797a957 100644 --- a/drivers/staging/comedi/drivers/dt2801.c +++ b/drivers/staging/comedi/drivers/dt2801.c @@ -343,7 +343,7 @@ static int dt2801_reset(struct comedi_device *dev) outb_p(DT_C_STOP, dev->iobase + DT2801_CMD); /* dt2801_wait_for_ready(dev); */ - udelay(100); + usleep_range(100, 200); timeout = 10000; do { stat = inb_p(dev->iobase + DT2801_STATUS); @@ -358,7 +358,7 @@ static int dt2801_reset(struct comedi_device *dev) outb_p(DT_C_RESET, dev->iobase + DT2801_CMD); /* dt2801_writecmd(dev,DT_C_RESET); */ - udelay(100); + usleep_range(100, 200); timeout = 10000; do { stat = inb_p(dev->iobase + DT2801_STATUS); diff --git a/drivers/staging/comedi/drivers/dt2814.c b/drivers/staging/comedi/drivers/dt2814.c index 2f903bedcefa..09984a66dba3 100644 --- a/drivers/staging/comedi/drivers/dt2814.c +++ b/drivers/staging/comedi/drivers/dt2814.c @@ -245,7 +245,7 @@ static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it) return ret; outb(0, dev->iobase + DT2814_CSR); - udelay(100); + usleep_range(100, 200); if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) { dev_err(dev->class_dev, "reset error (fatal)\n"); return -EIO; diff --git a/drivers/staging/comedi/drivers/dt2815.c b/drivers/staging/comedi/drivers/dt2815.c index 0be77cc40a79..ce5571971194 100644 --- a/drivers/staging/comedi/drivers/dt2815.c +++ b/drivers/staging/comedi/drivers/dt2815.c @@ -188,7 +188,7 @@ static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it) /* This is incredibly slow (approx 20 ms) */ unsigned int status; - udelay(1000); + usleep_range(1000, 3000); status = inb(dev->iobase + DT2815_STATUS); if (status == 4) { unsigned int program; diff --git a/drivers/staging/comedi/drivers/dyna_pci10xx.c b/drivers/staging/comedi/drivers/dyna_pci10xx.c index c9eb26fab44e..bab7ac9e6237 100644 --- a/drivers/staging/comedi/drivers/dyna_pci10xx.c +++ b/drivers/staging/comedi/drivers/dyna_pci10xx.c @@ -89,7 +89,7 @@ static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev, /* trigger conversion */ smp_mb(); outw_p(0x0000 + range + chan, dev->iobase + 2); - udelay(10); + usleep_range(10, 20); ret = comedi_timeout(dev, s, insn, dyna_pci10xx_ai_eoc, 0); if (ret) @@ -125,7 +125,7 @@ static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev, smp_mb(); /* trigger conversion and write data */ outw_p(data[n], dev->iobase); - udelay(10); + usleep_range(10, 20); } mutex_unlock(&devpriv->mutex); return n; @@ -143,7 +143,7 @@ static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev, mutex_lock(&devpriv->mutex); smp_mb(); d = inw_p(devpriv->BADR3); - udelay(10); + usleep_range(10, 100); /* on return the data[0] contains output and data[1] contains input */ data[1] = d; @@ -163,7 +163,7 @@ static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev, if (comedi_dio_update_state(s, data)) { smp_mb(); outw_p(s->state, devpriv->BADR3); - udelay(10); + usleep_range(10, 100); } data[1] = s->state; diff --git a/drivers/staging/comedi/drivers/mite.h b/drivers/staging/comedi/drivers/mite.h index b6349aed97d0..02a627d3969d 100644 --- a/drivers/staging/comedi/drivers/mite.h +++ b/drivers/staging/comedi/drivers/mite.h @@ -60,35 +60,36 @@ struct mite { spinlock_t lock; }; -u32 mite_bytes_in_transit(struct mite_channel *); +u32 mite_bytes_in_transit(struct mite_channel *mite_chan); -void mite_sync_dma(struct mite_channel *, struct comedi_subdevice *); -void mite_ack_linkc(struct mite_channel *, struct comedi_subdevice *s, +void mite_sync_dma(struct mite_channel *mite_chan, struct comedi_subdevice *s); +void mite_ack_linkc(struct mite_channel *mite_chan, struct comedi_subdevice *s, bool sync); -int mite_done(struct mite_channel *); +int mite_done(struct mite_channel *mite_chan); -void mite_dma_arm(struct mite_channel *); -void mite_dma_disarm(struct mite_channel *); +void mite_dma_arm(struct mite_channel *mite_chan); +void mite_dma_disarm(struct mite_channel *mite_chan); -void mite_prep_dma(struct mite_channel *, +void mite_prep_dma(struct mite_channel *mite_chan, unsigned int num_device_bits, unsigned int num_memory_bits); -struct mite_channel *mite_request_channel_in_range(struct mite *, - struct mite_ring *, +struct mite_channel *mite_request_channel_in_range(struct mite *mite, + struct mite_ring *ring, unsigned int min_channel, unsigned int max_channel); -struct mite_channel *mite_request_channel(struct mite *, struct mite_ring *); -void mite_release_channel(struct mite_channel *); +struct mite_channel *mite_request_channel(struct mite *mite, + struct mite_ring *ring); +void mite_release_channel(struct mite_channel *mite_chan); -int mite_init_ring_descriptors(struct mite_ring *, struct comedi_subdevice *, - unsigned int nbytes); -int mite_buf_change(struct mite_ring *, struct comedi_subdevice *); +int mite_init_ring_descriptors(struct mite_ring *ring, + struct comedi_subdevice *s, unsigned int nbytes); +int mite_buf_change(struct mite_ring *ring, struct comedi_subdevice *s); -struct mite_ring *mite_alloc_ring(struct mite *); -void mite_free_ring(struct mite_ring *); +struct mite_ring *mite_alloc_ring(struct mite *mite); +void mite_free_ring(struct mite_ring *ring); -struct mite *mite_attach(struct comedi_device *, bool use_win1); -void mite_detach(struct mite *); +struct mite *mite_attach(struct comedi_device *dev, bool use_win1); +void mite_detach(struct mite *mite); /* * Mite registers (used outside of the mite driver) diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c index 0dcb826a9f1f..6aa755ad3953 100644 --- a/drivers/staging/comedi/drivers/ni_660x.c +++ b/drivers/staging/comedi/drivers/ni_660x.c @@ -16,13 +16,13 @@ * Driver: ni_660x * Description: National Instruments 660x counter/timer boards * Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602, - * PXI-6608, PXI-6624 + * PXI-6608, PCI-6624, PXI-6624 * Author: J.P. Mellor , * Herman.Bruyninckx@mech.kuleuven.ac.be, * Wim.Meeussen@mech.kuleuven.ac.be, * Klaas.Gadeyne@mech.kuleuven.ac.be, * Frank Mori Hess - * Updated: Fri, 15 Mar 2013 10:47:56 +0000 + * Updated: Mon, 16 Jan 2017 14:00:43 +0000 * Status: experimental * * Encoders work. PulseGeneration (both single pulse and pulse train) @@ -211,6 +211,7 @@ enum ni_660x_boardid { BOARD_PCI6602, BOARD_PXI6602, BOARD_PXI6608, + BOARD_PCI6624, BOARD_PXI6624 }; @@ -236,6 +237,10 @@ static const struct ni_660x_board ni_660x_boards[] = { .name = "PXI-6608", .n_chips = 2, }, + [BOARD_PCI6624] = { + .name = "PCI-6624", + .n_chips = 2, + }, [BOARD_PXI6624] = { .name = "PXI-6624", .n_chips = 2, @@ -920,6 +925,7 @@ static const struct pci_device_id ni_660x_pci_table[] = { { PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 }, { PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 }, { PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 }, + { PCI_VDEVICE(NI, 0x1e30), BOARD_PCI6624 }, { PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 }, { 0 } }; diff --git a/drivers/staging/comedi/drivers/ni_670x.c b/drivers/staging/comedi/drivers/ni_670x.c index 74911dbb2561..1d3ff60efcb8 100644 --- a/drivers/staging/comedi/drivers/ni_670x.c +++ b/drivers/staging/comedi/drivers/ni_670x.c @@ -141,7 +141,7 @@ static int ni_670x_dio_insn_config(struct comedi_device *dev, /* ripped from mite.h and mite_setup2() to avoid mite dependency */ #define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */ -#define WENAB (1 << 7) /* window enable */ +#define WENAB BIT(7) /* window enable */ static int ni_670x_mite_init(struct pci_dev *pcidev) { diff --git a/drivers/staging/comedi/drivers/ni_at_a2150.c b/drivers/staging/comedi/drivers/ni_at_a2150.c index 5a4dcc6e61d8..c69cd676f357 100644 --- a/drivers/staging/comedi/drivers/ni_at_a2150.c +++ b/drivers/staging/comedi/drivers/ni_at_a2150.c @@ -757,7 +757,7 @@ static int a2150_attach(struct comedi_device *dev, struct comedi_devconfig *it) for (i = 0; i < timeout; i++) { if ((DCAL_BIT & inw(dev->iobase + STATUS_REG)) == 0) break; - udelay(1000); + usleep_range(1000, 3000); } if (i == timeout) { dev_err(dev->class_dev, diff --git a/drivers/staging/comedi/drivers/ni_at_ao.c b/drivers/staging/comedi/drivers/ni_at_ao.c index f27aa0e82234..158fa29374fc 100644 --- a/drivers/staging/comedi/drivers/ni_at_ao.c +++ b/drivers/staging/comedi/drivers/ni_at_ao.c @@ -49,43 +49,43 @@ #define ATAO_CFG2_REG 0x02 #define ATAO_CFG2_CALLD_NOP (0 << 14) #define ATAO_CFG2_CALLD(x) ((((x) >> 3) + 1) << 14) -#define ATAO_CFG2_FFRTEN (1 << 13) +#define ATAO_CFG2_FFRTEN BIT(13) #define ATAO_CFG2_DACS(x) (1 << (((x) / 2) + 8)) #define ATAO_CFG2_LDAC(x) (1 << (((x) / 2) + 3)) -#define ATAO_CFG2_PROMEN (1 << 2) -#define ATAO_CFG2_SCLK (1 << 1) -#define ATAO_CFG2_SDATA (1 << 0) +#define ATAO_CFG2_PROMEN BIT(2) +#define ATAO_CFG2_SCLK BIT(1) +#define ATAO_CFG2_SDATA BIT(0) #define ATAO_CFG3_REG 0x04 -#define ATAO_CFG3_DMAMODE (1 << 6) -#define ATAO_CFG3_CLKOUT (1 << 5) -#define ATAO_CFG3_RCLKEN (1 << 4) -#define ATAO_CFG3_DOUTEN2 (1 << 3) -#define ATAO_CFG3_DOUTEN1 (1 << 2) -#define ATAO_CFG3_EN2_5V (1 << 1) -#define ATAO_CFG3_SCANEN (1 << 0) +#define ATAO_CFG3_DMAMODE BIT(6) +#define ATAO_CFG3_CLKOUT BIT(5) +#define ATAO_CFG3_RCLKEN BIT(4) +#define ATAO_CFG3_DOUTEN2 BIT(3) +#define ATAO_CFG3_DOUTEN1 BIT(2) +#define ATAO_CFG3_EN2_5V BIT(1) +#define ATAO_CFG3_SCANEN BIT(0) #define ATAO_82C53_BASE 0x06 #define ATAO_CFG1_REG 0x0a -#define ATAO_CFG1_EXTINT2EN (1 << 15) -#define ATAO_CFG1_EXTINT1EN (1 << 14) -#define ATAO_CFG1_CNTINT2EN (1 << 13) -#define ATAO_CFG1_CNTINT1EN (1 << 12) -#define ATAO_CFG1_TCINTEN (1 << 11) -#define ATAO_CFG1_CNT1SRC (1 << 10) -#define ATAO_CFG1_CNT2SRC (1 << 9) -#define ATAO_CFG1_FIFOEN (1 << 8) -#define ATAO_CFG1_GRP2WR (1 << 7) -#define ATAO_CFG1_EXTUPDEN (1 << 6) -#define ATAO_CFG1_DMARQ (1 << 5) -#define ATAO_CFG1_DMAEN (1 << 4) +#define ATAO_CFG1_EXTINT2EN BIT(15) +#define ATAO_CFG1_EXTINT1EN BIT(14) +#define ATAO_CFG1_CNTINT2EN BIT(13) +#define ATAO_CFG1_CNTINT1EN BIT(12) +#define ATAO_CFG1_TCINTEN BIT(11) +#define ATAO_CFG1_CNT1SRC BIT(10) +#define ATAO_CFG1_CNT2SRC BIT(9) +#define ATAO_CFG1_FIFOEN BIT(8) +#define ATAO_CFG1_GRP2WR BIT(7) +#define ATAO_CFG1_EXTUPDEN BIT(6) +#define ATAO_CFG1_DMARQ BIT(5) +#define ATAO_CFG1_DMAEN BIT(4) #define ATAO_CFG1_CH(x) (((x) & 0xf) << 0) #define ATAO_STATUS_REG 0x0a -#define ATAO_STATUS_FH (1 << 6) -#define ATAO_STATUS_FE (1 << 5) -#define ATAO_STATUS_FF (1 << 4) -#define ATAO_STATUS_INT2 (1 << 3) -#define ATAO_STATUS_INT1 (1 << 2) -#define ATAO_STATUS_TCINT (1 << 1) -#define ATAO_STATUS_PROMOUT (1 << 0) +#define ATAO_STATUS_FH BIT(6) +#define ATAO_STATUS_FE BIT(5) +#define ATAO_STATUS_FF BIT(4) +#define ATAO_STATUS_INT2 BIT(3) +#define ATAO_STATUS_INT1 BIT(2) +#define ATAO_STATUS_TCINT BIT(1) +#define ATAO_STATUS_PROMOUT BIT(0) #define ATAO_FIFO_WRITE_REG 0x0c #define ATAO_FIFO_CLEAR_REG 0x0c #define ATAO_AO_REG(x) (0x0c + ((x) * 2)) @@ -95,7 +95,7 @@ #define ATAO_2_INT1CLR_REG 0x02 #define ATAO_2_INT2CLR_REG 0x04 #define ATAO_2_RTSISHFT_REG 0x06 -#define ATAO_2_RTSISHFT_RSI (1 << 0) +#define ATAO_2_RTSISHFT_RSI BIT(0) #define ATAO_2_RTSISTRB_REG 0x07 struct atao_board { diff --git a/drivers/staging/comedi/drivers/ni_labpc.h b/drivers/staging/comedi/drivers/ni_labpc.h index be8d5cd3f7f0..c2edadc7b3c6 100644 --- a/drivers/staging/comedi/drivers/ni_labpc.h +++ b/drivers/staging/comedi/drivers/ni_labpc.h @@ -52,8 +52,8 @@ struct labpc_private { * function pointers so we can use inb/outb or readb/writeb as * appropriate */ - unsigned int (*read_byte)(struct comedi_device *, unsigned long reg); - void (*write_byte)(struct comedi_device *, + unsigned int (*read_byte)(struct comedi_device *dev, unsigned long reg); + void (*write_byte)(struct comedi_device *dev, unsigned int byte, unsigned long reg); }; diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c index daeb4ad7a75f..b27345abebe1 100644 --- a/drivers/staging/comedi/drivers/ni_pcidio.c +++ b/drivers/staging/comedi/drivers/ni_pcidio.c @@ -65,7 +65,7 @@ #define WindowAddressStatus_mask 0x7c #define Master_DMA_And_Interrupt_Control 5 /* W */ -#define InterruptLine(x) ((x)&3) +#define InterruptLine(x) ((x) & 3) #define OpenInt BIT(2) #define Group_Status 5 /* R */ #define DataLeft BIT(0) @@ -100,38 +100,38 @@ #define Chip_ID_I 25 #define Chip_ID_O 26 #define Chip_Version 27 -#define Port_IO(x) (28+(x)) -#define Port_Pin_Directions(x) (32+(x)) -#define Port_Pin_Mask(x) (36+(x)) -#define Port_Pin_Polarities(x) (40+(x)) +#define Port_IO(x) (28 + (x)) +#define Port_Pin_Directions(x) (32 + (x)) +#define Port_Pin_Mask(x) (36 + (x)) +#define Port_Pin_Polarities(x) (40 + (x)) #define Master_Clock_Routing 45 -#define RTSIClocking(x) (((x)&3)<<4) +#define RTSIClocking(x) (((x) & 3) << 4) #define Group_1_Second_Clear 46 /* W */ #define Group_2_Second_Clear 47 /* W */ #define ClearExpired BIT(0) -#define Port_Pattern(x) (48+(x)) +#define Port_Pattern(x) (48 + (x)) #define Data_Path 64 #define FIFOEnableA BIT(0) #define FIFOEnableB BIT(1) #define FIFOEnableC BIT(2) #define FIFOEnableD BIT(3) -#define Funneling(x) (((x)&3)<<4) +#define Funneling(x) (((x) & 3) << 4) #define GroupDirection BIT(7) #define Protocol_Register_1 65 #define OpMode Protocol_Register_1 -#define RunMode(x) ((x)&7) +#define RunMode(x) ((x) & 7) #define Numbered BIT(3) #define Protocol_Register_2 66 #define ClockReg Protocol_Register_2 -#define ClockLine(x) (((x)&3)<<5) +#define ClockLine(x) (((x) & 3) << 5) #define InvertStopTrig BIT(7) -#define DataLatching(x) (((x)&3)<<5) +#define DataLatching(x) (((x) & 3) << 5) #define Protocol_Register_3 67 #define Sequence Protocol_Register_3 @@ -141,13 +141,13 @@ #define Protocol_Register_4 70 #define ReqReg Protocol_Register_4 -#define ReqConditioning(x) (((x)&7)<<3) +#define ReqConditioning(x) (((x) & 7) << 3) #define Protocol_Register_5 71 #define BlockMode Protocol_Register_5 #define FIFO_Control 72 -#define ReadyLevel(x) ((x)&7) +#define ReadyLevel(x) ((x) & 7) #define Protocol_Register_6 73 #define LinePolarities Protocol_Register_6 @@ -160,7 +160,7 @@ #define Protocol_Register_7 74 #define AckSer Protocol_Register_7 -#define AckLine(x) (((x)&3)<<2) +#define AckLine(x) (((x) & 3) << 2) #define ExchangePins BIT(7) #define Interrupt_Control 75 @@ -180,15 +180,15 @@ static inline unsigned int secondary_DMAChannel_bits(unsigned int channel) } #define Transfer_Size_Control 77 -#define TransferWidth(x) ((x)&3) -#define TransferLength(x) (((x)&3)<<3) +#define TransferWidth(x) ((x) & 3) +#define TransferLength(x) (((x) & 3) << 3) #define RequireRLevel BIT(5) #define Protocol_Register_15 79 #define DAQOptions Protocol_Register_15 -#define StartSource(x) ((x)&0x3) +#define StartSource(x) ((x) & 0x3) #define InvertStart BIT(2) -#define StopSource(x) (((x)&0x3)<<3) +#define StopSource(x) (((x) & 0x3) << 3) #define ReqStart BIT(6) #define PreStart BIT(7) @@ -230,6 +230,7 @@ enum pci_6534_firmware_registers { /* 16 bit */ Firmware_Mask_Register = 0x10c, Firmware_Debug_Register = 0x110, }; + /* main fpga registers (32 bit)*/ enum pci_6534_fpga_registers { FPGA_Control1_Register = 0x200, @@ -246,6 +247,7 @@ enum pci_6534_fpga_registers { FPGA_ELC_Read_Register = 0x2b8, FPGA_ELC_Write_Register = 0x2bc, }; + enum FPGA_Control_Bits { FPGA_Enable_Bit = 0x8000, }; @@ -253,9 +255,9 @@ enum FPGA_Control_Bits { #define TIMER_BASE 50 /* nanoseconds */ #ifdef USE_DMA -#define IntEn (CountExpired|Waited|PrimaryTC|SecondaryTC) +#define IntEn (CountExpired | Waited | PrimaryTC | SecondaryTC) #else -#define IntEn (TransferReady|CountExpired|Waited|PrimaryTC|SecondaryTC) +#define IntEn (TransferReady | CountExpired | Waited | PrimaryTC | SecondaryTC) #endif enum nidio_boardid { diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c index f13a2f7360b3..3a96913c025e 100644 --- a/drivers/staging/comedi/drivers/ni_pcimio.c +++ b/drivers/staging/comedi/drivers/ni_pcimio.c @@ -25,17 +25,16 @@ * PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1, PCI-MIO-16E-4, PCI-6014, * PCI-6040E, 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, 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, - * PXI-6071E, PCI-6070E, PXI-6070E, + * PCI-6035E, PCI-6052E, PCI-6110, PCI-6111, PCI-6220, PXI-6220, + * PCI-6221, PXI-6221, PCI-6224, PXI-6224, PCI-6225, PXI-6225, + * PCI-6229, PXI-6229, PCI-6250, PXI-6250, PCI-6251, PXI-6251, + * PCIe-6251, PXIe-6251, PCI-6254, PXI-6254, PCI-6259, PXI-6259, + * PCIe-6259, PXIe-6259, PCI-6280, PXI-6280, PCI-6281, PXI-6281, + * PCI-6284, PXI-6284, PCI-6289, PXI-6289, PCI-6711, PXI-6711, + * PCI-6713, PXI-6713, PXI-6071E, PCI-6070E, PXI-6070E, * PXI-6052E, PCI-6036E, PCI-6731, PCI-6733, PXI-6733, * PCI-6143, PXI-6143 - * Updated: Mon, 09 Jan 2012 14:52:48 +0000 + * Updated: Mon, 16 Jan 2017 12:56:04 +0000 * * These boards are almost identical to the AT-MIO E series, except that * they use the PCI bus instead of ISA (i.e., AT). See the notes for the @@ -181,26 +180,36 @@ enum ni_pcimio_boardid { BOARD_PXI6031E, BOARD_PCI6036E, BOARD_PCI6220, + BOARD_PXI6220, BOARD_PCI6221, BOARD_PCI6221_37PIN, + BOARD_PXI6221, BOARD_PCI6224, BOARD_PXI6224, BOARD_PCI6225, BOARD_PXI6225, BOARD_PCI6229, + BOARD_PXI6229, BOARD_PCI6250, + BOARD_PXI6250, BOARD_PCI6251, BOARD_PXI6251, BOARD_PCIE6251, BOARD_PXIE6251, BOARD_PCI6254, + BOARD_PXI6254, BOARD_PCI6259, + BOARD_PXI6259, BOARD_PCIE6259, + BOARD_PXIE6259, BOARD_PCI6280, + BOARD_PXI6280, BOARD_PCI6281, BOARD_PXI6281, BOARD_PCI6284, + BOARD_PXI6284, BOARD_PCI6289, + BOARD_PXI6289, BOARD_PCI6143, BOARD_PXI6143, }; @@ -684,6 +693,16 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_622x, .caldac = { caldac_none }, }, + [BOARD_PXI6220] = { + .name = "pxi-6220", + .n_adchan = 16, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 512, /* FIXME: guess */ + .gainlkup = ai_gain_622x, + .ai_speed = 4000, + .reg_type = ni_reg_622x, + .caldac = { caldac_none }, + }, [BOARD_PCI6221] = { .name = "pci-6221", .n_adchan = 16, @@ -714,6 +733,21 @@ static const struct ni_board_struct ni_boards[] = { .ao_speed = 1200, .caldac = { caldac_none }, }, + [BOARD_PXI6221] = { + .name = "pxi-6221", + .n_adchan = 16, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_622x, + .ai_speed = 4000, + .n_aochan = 2, + .ao_maxdata = 0xffff, + .ao_fifo_depth = 8191, + .ao_range_table = &range_bipolar10, + .reg_type = ni_reg_622x, + .ao_speed = 1200, + .caldac = { caldac_none }, + }, [BOARD_PCI6224] = { .name = "pci-6224", .n_adchan = 32, @@ -784,6 +818,22 @@ static const struct ni_board_struct ni_boards[] = { .has_32dio_chan = 1, .caldac = { caldac_none }, }, + [BOARD_PXI6229] = { + .name = "pxi-6229", + .n_adchan = 32, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_622x, + .ai_speed = 4000, + .n_aochan = 4, + .ao_maxdata = 0xffff, + .ao_fifo_depth = 8191, + .ao_range_table = &range_bipolar10, + .reg_type = ni_reg_622x, + .ao_speed = 1200, + .has_32dio_chan = 1, + .caldac = { caldac_none }, + }, [BOARD_PCI6250] = { .name = "pci-6250", .n_adchan = 16, @@ -794,6 +844,16 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_625x, .caldac = { caldac_none }, }, + [BOARD_PXI6250] = { + .name = "pxi-6250", + .n_adchan = 16, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_628x, + .ai_speed = 800, + .reg_type = ni_reg_625x, + .caldac = { caldac_none }, + }, [BOARD_PCI6251] = { .name = "pci-6251", .n_adchan = 16, @@ -865,6 +925,17 @@ static const struct ni_board_struct ni_boards[] = { .has_32dio_chan = 1, .caldac = { caldac_none }, }, + [BOARD_PXI6254] = { + .name = "pxi-6254", + .n_adchan = 32, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_628x, + .ai_speed = 800, + .reg_type = ni_reg_625x, + .has_32dio_chan = 1, + .caldac = { caldac_none }, + }, [BOARD_PCI6259] = { .name = "pci-6259", .n_adchan = 32, @@ -881,6 +952,22 @@ static const struct ni_board_struct ni_boards[] = { .has_32dio_chan = 1, .caldac = { caldac_none }, }, + [BOARD_PXI6259] = { + .name = "pxi-6259", + .n_adchan = 32, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_628x, + .ai_speed = 800, + .n_aochan = 4, + .ao_maxdata = 0xffff, + .ao_fifo_depth = 8191, + .ao_range_table = &range_ni_M_625x_ao, + .reg_type = ni_reg_625x, + .ao_speed = 350, + .has_32dio_chan = 1, + .caldac = { caldac_none }, + }, [BOARD_PCIE6259] = { .name = "pcie-6259", .n_adchan = 32, @@ -897,6 +984,22 @@ static const struct ni_board_struct ni_boards[] = { .has_32dio_chan = 1, .caldac = { caldac_none }, }, + [BOARD_PXIE6259] = { + .name = "pxie-6259", + .n_adchan = 32, + .ai_maxdata = 0xffff, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_628x, + .ai_speed = 800, + .n_aochan = 4, + .ao_maxdata = 0xffff, + .ao_fifo_depth = 8191, + .ao_range_table = &range_ni_M_625x_ao, + .reg_type = ni_reg_625x, + .ao_speed = 350, + .has_32dio_chan = 1, + .caldac = { caldac_none }, + }, [BOARD_PCI6280] = { .name = "pci-6280", .n_adchan = 16, @@ -908,6 +1011,17 @@ static const struct ni_board_struct ni_boards[] = { .reg_type = ni_reg_628x, .caldac = { caldac_none }, }, + [BOARD_PXI6280] = { + .name = "pxi-6280", + .n_adchan = 16, + .ai_maxdata = 0x3ffff, + .ai_fifo_depth = 2047, + .gainlkup = ai_gain_628x, + .ai_speed = 1600, + .ao_fifo_depth = 8191, + .reg_type = ni_reg_628x, + .caldac = { caldac_none }, + }, [BOARD_PCI6281] = { .name = "pci-6281", .n_adchan = 16, @@ -949,6 +1063,17 @@ static const struct ni_board_struct ni_boards[] = { .has_32dio_chan = 1, .caldac = { caldac_none }, }, + [BOARD_PXI6284] = { + .name = "pxi-6284", + .n_adchan = 32, + .ai_maxdata = 0x3ffff, + .ai_fifo_depth = 2047, + .gainlkup = ai_gain_628x, + .ai_speed = 1600, + .reg_type = ni_reg_628x, + .has_32dio_chan = 1, + .caldac = { caldac_none }, + }, [BOARD_PCI6289] = { .name = "pci-6289", .n_adchan = 32, @@ -965,6 +1090,22 @@ static const struct ni_board_struct ni_boards[] = { .has_32dio_chan = 1, .caldac = { caldac_none }, }, + [BOARD_PXI6289] = { + .name = "pxi-6289", + .n_adchan = 32, + .ai_maxdata = 0x3ffff, + .ai_fifo_depth = 2047, + .gainlkup = ai_gain_628x, + .ai_speed = 1600, + .n_aochan = 4, + .ao_maxdata = 0xffff, + .ao_fifo_depth = 8191, + .ao_range_table = &range_ni_M_628x_ao, + .reg_type = ni_reg_628x, + .ao_speed = 350, + .has_32dio_chan = 1, + .caldac = { caldac_none }, + }, [BOARD_PCI6143] = { .name = "pci-6143", .n_adchan = 8, @@ -1061,8 +1202,6 @@ static void m_series_init_eeprom_buffer(struct comedi_device *dev) resource_size_t daq_phys_addr; static const int Start_Cal_EEPROM = 0x400; static const unsigned int window_size = 10; - static const int serial_number_eeprom_offset = 0x4; - static const int serial_number_eeprom_length = 0x4; unsigned int old_iodwbsr_bits; unsigned int old_iodwbsr1_bits; unsigned int old_iodwcr1_bits; @@ -1080,13 +1219,6 @@ static void m_series_init_eeprom_buffer(struct comedi_device *dev) writel(0x1 | old_iodwcr1_bits, mite->mmio + MITE_IODWCR_1); writel(0xf, mite->mmio + 0x30); - BUG_ON(serial_number_eeprom_length > sizeof(devpriv->serial_number)); - for (i = 0; i < serial_number_eeprom_length; ++i) { - char *byte_ptr = (char *)&devpriv->serial_number + i; - *byte_ptr = ni_readb(dev, serial_number_eeprom_offset + i); - } - devpriv->serial_number = be32_to_cpu(devpriv->serial_number); - for (i = 0; i < M_SERIES_EEPROM_SIZE; ++i) devpriv->eeprom_buffer[i] = ni_readb(dev, Start_Cal_EEPROM + i); @@ -1284,14 +1416,24 @@ static const struct pci_device_id ni_pcimio_pci_table[] = { { PCI_VDEVICE(NI, 0x70aa), BOARD_PCI6229 }, { PCI_VDEVICE(NI, 0x70ab), BOARD_PCI6259 }, { PCI_VDEVICE(NI, 0x70ac), BOARD_PCI6289 }, + { PCI_VDEVICE(NI, 0x70ad), BOARD_PXI6251 }, + { PCI_VDEVICE(NI, 0x70ae), BOARD_PXI6220 }, { PCI_VDEVICE(NI, 0x70af), BOARD_PCI6221 }, { PCI_VDEVICE(NI, 0x70b0), BOARD_PCI6220 }, + { PCI_VDEVICE(NI, 0x70b1), BOARD_PXI6229 }, + { PCI_VDEVICE(NI, 0x70b2), BOARD_PXI6259 }, + { PCI_VDEVICE(NI, 0x70b3), BOARD_PXI6289 }, { PCI_VDEVICE(NI, 0x70b4), BOARD_PCI6250 }, + { PCI_VDEVICE(NI, 0x70b5), BOARD_PXI6221 }, { PCI_VDEVICE(NI, 0x70b6), BOARD_PCI6280 }, { PCI_VDEVICE(NI, 0x70b7), BOARD_PCI6254 }, { PCI_VDEVICE(NI, 0x70b8), BOARD_PCI6251 }, + { PCI_VDEVICE(NI, 0x70b9), BOARD_PXI6250 }, + { PCI_VDEVICE(NI, 0x70ba), BOARD_PXI6254 }, + { PCI_VDEVICE(NI, 0x70bb), BOARD_PXI6280 }, { PCI_VDEVICE(NI, 0x70bc), BOARD_PCI6284 }, { PCI_VDEVICE(NI, 0x70bd), BOARD_PCI6281 }, + { PCI_VDEVICE(NI, 0x70be), BOARD_PXI6284 }, { PCI_VDEVICE(NI, 0x70bf), BOARD_PXI6281 }, { PCI_VDEVICE(NI, 0x70c0), BOARD_PCI6143 }, { PCI_VDEVICE(NI, 0x70f2), BOARD_PCI6224 }, @@ -1299,11 +1441,11 @@ static const struct pci_device_id ni_pcimio_pci_table[] = { { PCI_VDEVICE(NI, 0x710d), BOARD_PXI6143 }, { PCI_VDEVICE(NI, 0x716c), BOARD_PCI6225 }, { PCI_VDEVICE(NI, 0x716d), BOARD_PXI6225 }, + { PCI_VDEVICE(NI, 0x717d), BOARD_PCIE6251 }, { PCI_VDEVICE(NI, 0x717f), BOARD_PCIE6259 }, { 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 }, + { PCI_VDEVICE(NI, 0x72e9), BOARD_PXIE6259 }, { 0 } }; MODULE_DEVICE_TABLE(pci, ni_pcimio_pci_table); diff --git a/drivers/staging/comedi/drivers/ni_stc.h b/drivers/staging/comedi/drivers/ni_stc.h index f27b545f83eb..61138e86a455 100644 --- a/drivers/staging/comedi/drivers/ni_stc.h +++ b/drivers/staging/comedi/drivers/ni_stc.h @@ -1031,7 +1031,6 @@ struct ni_private { unsigned short ai_fifo_buffer[0x2000]; u8 eeprom_buffer[M_SERIES_EEPROM_SIZE]; - __be32 serial_number; struct mite *mite; struct mite_channel *ai_mite_chan; diff --git a/drivers/staging/comedi/drivers/ni_tio.h b/drivers/staging/comedi/drivers/ni_tio.h index 4978358f9b13..2012033414d3 100644 --- a/drivers/staging/comedi/drivers/ni_tio.h +++ b/drivers/staging/comedi/drivers/ni_tio.h @@ -110,9 +110,9 @@ struct ni_gpct { struct ni_gpct_device { struct comedi_device *dev; - void (*write)(struct ni_gpct *, unsigned int value, + void (*write)(struct ni_gpct *counter, unsigned int value, enum ni_gpct_register); - unsigned int (*read)(struct ni_gpct *, enum ni_gpct_register); + unsigned int (*read)(struct ni_gpct *counter, enum ni_gpct_register); enum ni_gpct_variant variant; struct ni_gpct *counters; unsigned int num_counters; @@ -121,28 +121,30 @@ struct ni_gpct_device { }; struct ni_gpct_device * -ni_gpct_device_construct(struct comedi_device *, - void (*write)(struct ni_gpct *, +ni_gpct_device_construct(struct comedi_device *dev, + void (*write)(struct ni_gpct *counter, unsigned int value, enum ni_gpct_register), - unsigned int (*read)(struct ni_gpct *, + unsigned int (*read)(struct ni_gpct *counter, enum ni_gpct_register), enum ni_gpct_variant, unsigned int num_counters); -void ni_gpct_device_destroy(struct ni_gpct_device *); -void ni_tio_init_counter(struct ni_gpct *); -int ni_tio_insn_read(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, unsigned int *data); -int ni_tio_insn_config(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, unsigned int *data); -int ni_tio_insn_write(struct comedi_device *, struct comedi_subdevice *, - struct comedi_insn *, unsigned int *data); -int ni_tio_cmd(struct comedi_device *, struct comedi_subdevice *); -int ni_tio_cmdtest(struct comedi_device *, struct comedi_subdevice *, - struct comedi_cmd *); -int ni_tio_cancel(struct ni_gpct *); -void ni_tio_handle_interrupt(struct ni_gpct *, struct comedi_subdevice *); -void ni_tio_set_mite_channel(struct ni_gpct *, struct mite_channel *); -void ni_tio_acknowledge(struct ni_gpct *); +void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev); +void ni_tio_init_counter(struct ni_gpct *counter); +int ni_tio_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +int ni_tio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +int ni_tio_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +int ni_tio_cmd(struct comedi_device *dev, struct comedi_subdevice *s); +int ni_tio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_cmd *cmd); +int ni_tio_cancel(struct ni_gpct *counter); +void ni_tio_handle_interrupt(struct ni_gpct *counter, + struct comedi_subdevice *s); +void ni_tio_set_mite_channel(struct ni_gpct *counter, + struct mite_channel *mite_chan); +void ni_tio_acknowledge(struct ni_gpct *counter); #endif /* _COMEDI_NI_TIO_H */ diff --git a/drivers/staging/comedi/drivers/ni_tio_internal.h b/drivers/staging/comedi/drivers/ni_tio_internal.h index b15b10833c42..4e024eb5656b 100644 --- a/drivers/staging/comedi/drivers/ni_tio_internal.h +++ b/drivers/staging/comedi/drivers/ni_tio_internal.h @@ -160,8 +160,9 @@ #define GI_TC_INTERRUPT_ENABLE(x) (((x) % 2) ? BIT(9) : BIT(6)) #define GI_GATE_INTERRUPT_ENABLE(x) (((x) % 2) ? BIT(10) : BIT(8)) -void ni_tio_write(struct ni_gpct *, unsigned int value, enum ni_gpct_register); -unsigned int ni_tio_read(struct ni_gpct *, enum ni_gpct_register); +void ni_tio_write(struct ni_gpct *counter, unsigned int value, + enum ni_gpct_register); +unsigned int ni_tio_read(struct ni_gpct *counter, enum ni_gpct_register); static inline bool ni_tio_counting_mode_registers_present(const struct ni_gpct_device *counter_dev) @@ -170,12 +171,13 @@ ni_tio_counting_mode_registers_present(const struct ni_gpct_device *counter_dev) return counter_dev->variant != ni_gpct_variant_e_series; } -void ni_tio_set_bits(struct ni_gpct *, enum ni_gpct_register reg, +void ni_tio_set_bits(struct ni_gpct *counter, enum ni_gpct_register reg, unsigned int mask, unsigned int value); -unsigned int ni_tio_get_soft_copy(const struct ni_gpct *, +unsigned int ni_tio_get_soft_copy(const struct ni_gpct *counter, enum ni_gpct_register reg); -int ni_tio_arm(struct ni_gpct *, bool arm, unsigned int start_trigger); -int ni_tio_set_gate_src(struct ni_gpct *, unsigned int gate, unsigned int src); +int ni_tio_arm(struct ni_gpct *counter, bool arm, unsigned int start_trigger); +int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned int gate, + unsigned int src); #endif /* _COMEDI_NI_TIO_INTERNAL_H */ diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c index 0dd5fe286855..97939b42cc00 100644 --- a/drivers/staging/comedi/drivers/s626.c +++ b/drivers/staging/comedi/drivers/s626.c @@ -1513,7 +1513,7 @@ static int s626_ai_insn_read(struct comedi_device *dev, for (n = 0; n < insn->n; n++) { /* Delay 10 microseconds for analog input settling. */ - udelay(10); + usleep_range(10, 20); /* Start ADC by pulsing GPIO1 low */ gpio_image = readl(dev->mmio + S626_P_GPIO); diff --git a/drivers/staging/comedi/proc.c b/drivers/staging/comedi/proc.c index 91dea25b5724..2644dd4d6143 100644 --- a/drivers/staging/comedi/proc.c +++ b/drivers/staging/comedi/proc.c @@ -80,15 +80,17 @@ static int comedi_proc_open(struct inode *inode, struct file *file) } static const struct file_operations comedi_proc_fops = { + .owner = THIS_MODULE, .open = comedi_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -void comedi_proc_init(void) +void __init comedi_proc_init(void) { - proc_create("comedi", 0644, NULL, &comedi_proc_fops); + if (!proc_create("comedi", 0444, NULL, &comedi_proc_fops)) + pr_warn("comedi: unable to create proc entry\n"); } void comedi_proc_cleanup(void) diff --git a/drivers/staging/dgnc/TODO b/drivers/staging/dgnc/TODO index 0e0825bd70ae..e26d1d6a5941 100644 --- a/drivers/staging/dgnc/TODO +++ b/drivers/staging/dgnc/TODO @@ -1,10 +1,9 @@ -* checkpatch fixes * remove unnecessary comments * remove unnecessary error messages. Example kzalloc() has its own error message. Adding an extra one is useless. * use goto statements for error handling when appropriate * there is a lot of unnecessary code in the driver. It was - originally a standalone driver. Remove uneeded code. + originally a standalone driver. Remove unneeded code. Please send patches to Greg Kroah-Hartman and Cc: Lidza Louina diff --git a/drivers/staging/dgnc/dgnc_tty.c b/drivers/staging/dgnc/dgnc_tty.c index 1e10c0fe4745..c63e591631f6 100644 --- a/drivers/staging/dgnc/dgnc_tty.c +++ b/drivers/staging/dgnc/dgnc_tty.c @@ -971,9 +971,10 @@ static int dgnc_tty_open(struct tty_struct *tty, struct file *file) * touched safely, the close routine will signal the * ch_flags_wait to wake us back up. */ - rc = wait_event_interruptible(ch->ch_flags_wait, - (((ch->ch_tun.un_flags | - ch->ch_pun.un_flags) & UN_CLOSING) == 0)); + rc = wait_event_interruptible( + ch->ch_flags_wait, + (((ch->ch_tun.un_flags | + ch->ch_pun.un_flags) & UN_CLOSING) == 0)); /* If ret is non-zero, user ctrl-c'ed us */ if (rc) @@ -1193,7 +1194,8 @@ static int dgnc_block_til_ready(struct tty_struct *tty, (old_flags != (ch->ch_tun.un_flags | ch->ch_pun.un_flags))); else - retval = wait_event_interruptible(ch->ch_flags_wait, + retval = wait_event_interruptible( + ch->ch_flags_wait, (old_flags != ch->ch_flags)); /* diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c index 3f42fa8b0bf3..77b242e09932 100644 --- a/drivers/staging/emxx_udc/emxx_udc.c +++ b/drivers/staging/emxx_udc/emxx_udc.c @@ -553,28 +553,22 @@ static void _nbu2ss_dma_unmap_single( /*-------------------------------------------------------------------------*/ /* Endpoint 0 OUT Transfer (PIO) */ -static int EP0_out_PIO(struct nbu2ss_udc *udc, u8 *pBuf, u32 length) +static int ep0_out_pio(struct nbu2ss_udc *udc, u8 *buf, u32 length) { u32 i; - int nret = 0; - u32 iWordLength = 0; - union usb_reg_access *pBuf32 = (union usb_reg_access *)pBuf; + u32 numreads = length / sizeof(u32); + union usb_reg_access *buf32 = (union usb_reg_access *)buf; - /*------------------------------------------------------------*/ - /* Read Length */ - iWordLength = length / sizeof(u32); + if (!numreads) + return 0; - /*------------------------------------------------------------*/ /* PIO Read */ - if (iWordLength) { - for (i = 0; i < iWordLength; i++) { - pBuf32->dw = _nbu2ss_readl(&udc->p_regs->EP0_READ); - pBuf32++; - } - nret = iWordLength * sizeof(u32); + for (i = 0; i < numreads; i++) { + buf32->dw = _nbu2ss_readl(&udc->p_regs->EP0_READ); + buf32++; } - return nret; + return numreads * sizeof(u32); } /*-------------------------------------------------------------------------*/ @@ -758,7 +752,7 @@ static int _nbu2ss_ep0_out_transfer( pBuffer = (u8 *)req->req.buf; pBuffer += req->req.actual; - result = EP0_out_PIO(udc, pBuffer + result = ep0_out_pio(udc, pBuffer , min(iRemainSize, iRecvLength)); if (result < 0) return result; @@ -3137,7 +3131,7 @@ static const struct { }; /*-------------------------------------------------------------------------*/ -static void __init nbu2ss_drv_ep_init(struct nbu2ss_udc *udc) +static void nbu2ss_drv_ep_init(struct nbu2ss_udc *udc) { int i; @@ -3168,7 +3162,7 @@ static void __init nbu2ss_drv_ep_init(struct nbu2ss_udc *udc) /*-------------------------------------------------------------------------*/ /* platform_driver */ -static int __init nbu2ss_drv_contest_init( +static int nbu2ss_drv_contest_init( struct platform_device *pdev, struct nbu2ss_udc *udc) { diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c index a6e3af74a904..4ee76dbd30b5 100644 --- a/drivers/staging/fbtft/fb_agm1264k-fl.c +++ b/drivers/staging/fbtft/fb_agm1264k-fl.c @@ -185,9 +185,9 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) buf[i] = (u8)va_arg(args, unsigned int); va_end(args); - fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, - par->info->device, u8, buf, len, "%s: ", __func__); - } + fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, + u8, buf, len, "%s: ", __func__); +} va_start(args, len); @@ -246,7 +246,7 @@ static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) static void construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src, - int xs, int xe, int y) + int xs, int xe, int y) { int x, i; @@ -361,7 +361,8 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) /* left half of display */ if (addr_win.xs < par->info->var.xres / 2) { construct_line_bitmap(par, buf, convert_buf, - addr_win.xs, par->info->var.xres / 2, y); + addr_win.xs, + par->info->var.xres / 2, y); len = par->info->var.xres / 2 - addr_win.xs; @@ -382,8 +383,9 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) /* right half of display */ if (addr_win.xe >= par->info->var.xres / 2) { construct_line_bitmap(par, buf, - convert_buf, par->info->var.xres / 2, - addr_win.xe + 1, y); + convert_buf, + par->info->var.xres / 2, + addr_win.xe + 1, y); len = addr_win.xe + 1 - par->info->var.xres / 2; @@ -413,7 +415,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) static int write(struct fbtft_par *par, void *buf, size_t len) { fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, - "%s(len=%d): ", __func__, len); + "%s(len=%d): ", __func__, len); gpio_set_value(par->RW, 0); /* set write mode */ diff --git a/drivers/staging/fbtft/fb_hx8340bn.c b/drivers/staging/fbtft/fb_hx8340bn.c index 9970ed74bb38..1ca1fcd353d6 100644 --- a/drivers/staging/fbtft/fb_hx8340bn.c +++ b/drivers/staging/fbtft/fb_hx8340bn.c @@ -37,7 +37,7 @@ "3 3 17 8 4 7 05 7 6 0 3 1 6 0 0 " static bool emulate; -module_param(emulate, bool, 0); +module_param(emulate, bool, 0000); MODULE_PARM_DESC(emulate, "Force emulation in 9-bit mode"); static int init_display(struct fbtft_par *par) @@ -158,7 +158,7 @@ static int set_var(struct fbtft_par *par) * ON0 ON1 CN0 CN1 CN2 CN3 CN4 MN0 MN1 MN2 MN3 MN4 MN5 XXXX GC */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long mask[] = { 0x0f, 0x0f, 0x1f, 0x0f, 0x0f, 0x0f, 0x1f, 0x07, 0x07, 0x07, diff --git a/drivers/staging/fbtft/fb_hx8347d.c b/drivers/staging/fbtft/fb_hx8347d.c index 450a61e3f99c..bbf78f8644a8 100644 --- a/drivers/staging/fbtft/fb_hx8347d.c +++ b/drivers/staging/fbtft/fb_hx8347d.c @@ -102,7 +102,7 @@ static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) * VRN0 VRN1 VRN2 VRN3 VRN4 VRN5 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 CGM */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long mask[] = { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x7f, 0x7f, 0x1f, 0x1f, diff --git a/drivers/staging/fbtft/fb_hx8353d.c b/drivers/staging/fbtft/fb_hx8353d.c index 72e4ff8c5553..2c18051a44b3 100644 --- a/drivers/staging/fbtft/fb_hx8353d.c +++ b/drivers/staging/fbtft/fb_hx8353d.c @@ -118,7 +118,7 @@ static int set_var(struct fbtft_par *par) } /* gamma string format: */ -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { write_reg(par, 0xE0, curves[0], curves[1], curves[2], curves[3], diff --git a/drivers/staging/fbtft/fb_ili9163.c b/drivers/staging/fbtft/fb_ili9163.c index 6b8f8b17e9a3..579e17734612 100644 --- a/drivers/staging/fbtft/fb_ili9163.c +++ b/drivers/staging/fbtft/fb_ili9163.c @@ -202,7 +202,7 @@ static int set_var(struct fbtft_par *par) #ifdef GAMMA_ADJ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int gamma_adj(struct fbtft_par *par, unsigned long *curves) +static int gamma_adj(struct fbtft_par *par, u32 *curves) { unsigned long mask[] = { 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, diff --git a/drivers/staging/fbtft/fb_ili9320.c b/drivers/staging/fbtft/fb_ili9320.c index 278e4c7e95e5..20ba86da028b 100644 --- a/drivers/staging/fbtft/fb_ili9320.c +++ b/drivers/staging/fbtft/fb_ili9320.c @@ -221,7 +221,7 @@ static int set_var(struct fbtft_par *par) * VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5 */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long mask[] = { 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, diff --git a/drivers/staging/fbtft/fb_ili9325.c b/drivers/staging/fbtft/fb_ili9325.c index 19e33bab9cac..7189de5ae4b3 100644 --- a/drivers/staging/fbtft/fb_ili9325.c +++ b/drivers/staging/fbtft/fb_ili9325.c @@ -215,7 +215,7 @@ static int set_var(struct fbtft_par *par) * VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5 */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long mask[] = { 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, diff --git a/drivers/staging/fbtft/fb_ili9341.c b/drivers/staging/fbtft/fb_ili9341.c index ff35c8624ca3..21a98e9e1a14 100644 --- a/drivers/staging/fbtft/fb_ili9341.c +++ b/drivers/staging/fbtft/fb_ili9341.c @@ -121,7 +121,7 @@ static int set_var(struct fbtft_par *par) * Negative: Par1 Par2 [...] Par15 */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { int i; diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c index a4710dc067ef..87f678a314cc 100644 --- a/drivers/staging/fbtft/fb_pcd8544.c +++ b/drivers/staging/fbtft/fb_pcd8544.c @@ -33,11 +33,11 @@ #define DEFAULT_GAMMA "40" /* gamma controls the contrast in this driver */ static unsigned int tc; -module_param(tc, uint, 0); +module_param(tc, uint, 0000); MODULE_PARM_DESC(tc, "TC[1:0] Temperature coefficient: 0-3 (default: 0)"); static unsigned int bs = 4; -module_param(bs, uint, 0); +module_param(bs, uint, 0000); MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)"); static int init_display(struct fbtft_par *par) @@ -137,7 +137,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) return ret; } -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { /* apply mask */ curves[0] &= 0x7F; diff --git a/drivers/staging/fbtft/fb_ra8875.c b/drivers/staging/fbtft/fb_ra8875.c index 308a244972aa..89d36d6d0cd3 100644 --- a/drivers/staging/fbtft/fb_ra8875.c +++ b/drivers/staging/fbtft/fb_ra8875.c @@ -33,7 +33,7 @@ static int write_spi(struct fbtft_par *par, void *buf, size_t len) struct spi_message m; fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, - "%s(len=%d): ", __func__, len); + "%s(len=%d): ", __func__, len); if (!par->spi) { dev_err(par->info->device, @@ -42,10 +42,6 @@ static int write_spi(struct fbtft_par *par, void *buf, size_t len) } spi_message_init(&m); - if (par->txbuf.dma && buf == par->txbuf.buf) { - t.tx_dma = par->txbuf.dma; - m.is_dma_mapped = 1; - } spi_message_add_tail(&t, &m); return spi_sync(par->spi, &m); } @@ -55,9 +51,9 @@ static int init_display(struct fbtft_par *par) gpio_set_value(par->gpio.dc, 1); fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, - "%s()\n", __func__); + "%s()\n", __func__); fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, - "display size %dx%d\n", + "display size %dx%d\n", par->info->var.xres, par->info->var.yres); @@ -215,7 +211,7 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) buf[i] = (u8)va_arg(args, unsigned int); va_end(args); fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, - u8, buf, len, "%s: ", __func__); + u8, buf, len, "%s: ", __func__); } va_start(args, len); @@ -266,7 +262,7 @@ static int write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len) size_t startbyte_size = 0; fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", - __func__, offset, len); + __func__, offset, len); remain = len / 2; vmem16 = (u16 *)(par->info->screen_buffer + offset); diff --git a/drivers/staging/fbtft/fb_s6d1121.c b/drivers/staging/fbtft/fb_s6d1121.c index 9b1d70b218df..3b36ed50d491 100644 --- a/drivers/staging/fbtft/fb_s6d1121.c +++ b/drivers/staging/fbtft/fb_s6d1121.c @@ -130,7 +130,7 @@ static int set_var(struct fbtft_par *par) * PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 PKN6 PKN7 PRN8 PRN9 PRN10 PRN11 VRN0 VRN1 */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long mask[] = { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, diff --git a/drivers/staging/fbtft/fb_ssd1289.c b/drivers/staging/fbtft/fb_ssd1289.c index 25f9fbe1e76f..c603e1516e64 100644 --- a/drivers/staging/fbtft/fb_ssd1289.c +++ b/drivers/staging/fbtft/fb_ssd1289.c @@ -30,7 +30,7 @@ "02 03 2 5 7 5 4 2 4 2" static unsigned int reg11 = 0x6040; -module_param(reg11, uint, 0); +module_param(reg11, uint, 0000); MODULE_PARM_DESC(reg11, "Register 11h value"); static int init_display(struct fbtft_par *par) @@ -136,7 +136,7 @@ static int set_var(struct fbtft_par *par) * VRN0 VRN1 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long mask[] = { 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, diff --git a/drivers/staging/fbtft/fb_ssd1305.c b/drivers/staging/fbtft/fb_ssd1305.c index 4b38c3fadd60..33c03872ca84 100644 --- a/drivers/staging/fbtft/fb_ssd1305.c +++ b/drivers/staging/fbtft/fb_ssd1305.c @@ -148,7 +148,7 @@ static int blank(struct fbtft_par *par, bool on) } /* Gamma is used to control Contrast */ -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { curves[0] &= 0xFF; /* Set Contrast Control for BANK0 */ diff --git a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c index 80fc57029fee..96c58de85288 100644 --- a/drivers/staging/fbtft/fb_ssd1306.c +++ b/drivers/staging/fbtft/fb_ssd1306.c @@ -62,6 +62,8 @@ static int init_display(struct fbtft_par *par) write_reg(par, 0xA8); if (par->info->var.yres == 64) write_reg(par, 0x3F); + else if (par->info->var.yres == 48) + write_reg(par, 0x2F); else write_reg(par, 0x1F); @@ -82,7 +84,7 @@ static int init_display(struct fbtft_par *par) /* Vertical addressing mode */ write_reg(par, 0x01); - /*Set Segment Re-map */ + /* Set Segment Re-map */ /* column address 127 is mapped to SEG0 */ write_reg(par, 0xA0 | 0x1); @@ -95,6 +97,9 @@ static int init_display(struct fbtft_par *par) if (par->info->var.yres == 64) /* A[4]=1b, Alternative COM pin configuration */ write_reg(par, 0x12); + else if (par->info->var.yres == 48) + /* A[4]=1b, Alternative COM pin configuration */ + write_reg(par, 0x12); else /* A[4]=0b, Sequential COM pin configuration */ write_reg(par, 0x02); @@ -124,6 +129,19 @@ static int init_display(struct fbtft_par *par) return 0; } +static void set_addr_win_64x48(struct fbtft_par *par) +{ + /* Set Column Address */ + write_reg(par, 0x21); + write_reg(par, 0x20); + write_reg(par, 0x5F); + + /* Set Page Address */ + write_reg(par, 0x22); + write_reg(par, 0x0); + write_reg(par, 0x5); +} + static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) { /* Set Lower Column Start Address for Page Addressing Mode */ @@ -132,12 +150,15 @@ static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) write_reg(par, 0x10 | 0x0); /* Set Display Start Line */ write_reg(par, 0x40 | 0x0); + + if (par->info->var.xres == 64 && par->info->var.yres == 48) + set_addr_win_64x48(par); } static int blank(struct fbtft_par *par, bool on) { fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n", - __func__, on ? "true" : "false"); + __func__, on ? "true" : "false"); if (on) write_reg(par, 0xAE); @@ -147,7 +168,7 @@ static int blank(struct fbtft_par *par, bool on) } /* Gamma is used to control Contrast */ -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { /* apply mask */ curves[0] &= 0xFF; @@ -162,26 +183,24 @@ static int set_gamma(struct fbtft_par *par, unsigned long *curves) static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) { u16 *vmem16 = (u16 *)par->info->screen_buffer; + u32 xres = par->info->var.xres; + u32 yres = par->info->var.yres; u8 *buf = par->txbuf.buf; int x, y, i; int ret = 0; - for (x = 0; x < par->info->var.xres; x++) { - for (y = 0; y < par->info->var.yres/8; y++) { + for (x = 0; x < xres; x++) { + for (y = 0; y < yres / 8; y++) { *buf = 0x00; for (i = 0; i < 8; i++) - *buf |= (vmem16[(y * 8 + i) * - par->info->var.xres + x] ? - 1 : 0) << i; + *buf |= (vmem16[(y * 8 + i) * xres + x] ? 1 : 0) << i; buf++; } } /* Write data */ gpio_set_value(par->gpio.dc, 1); - ret = par->fbtftops.write(par, par->txbuf.buf, - par->info->var.xres * par->info->var.yres / - 8); + ret = par->fbtftops.write(par, par->txbuf.buf, xres * yres / 8); if (ret < 0) dev_err(par->info->device, "write failed and returned: %d\n", ret); diff --git a/drivers/staging/fbtft/fb_ssd1325.c b/drivers/staging/fbtft/fb_ssd1325.c index 15078bf2aa4b..b7e40c24f58e 100644 --- a/drivers/staging/fbtft/fb_ssd1325.c +++ b/drivers/staging/fbtft/fb_ssd1325.c @@ -116,7 +116,7 @@ static int blank(struct fbtft_par *par, bool on) * 0 = Setting of GS1 < Setting of GS2 < Setting of GS3.....< * Setting of GS14 < Setting of GS15 */ -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { int i; diff --git a/drivers/staging/fbtft/fb_ssd1331.c b/drivers/staging/fbtft/fb_ssd1331.c index 1d74ac1343a8..26f24e32d979 100644 --- a/drivers/staging/fbtft/fb_ssd1331.c +++ b/drivers/staging/fbtft/fb_ssd1331.c @@ -122,7 +122,7 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) * Setting of GS63 has to be > Setting of GS62 +1 * */ -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long tmp[GAMMA_NUM * GAMMA_LEN]; int i, acc = 0; @@ -145,14 +145,16 @@ static int set_gamma(struct fbtft_par *par, unsigned long *curves) } write_reg(par, 0xB8, - tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], - tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15], - tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23], - tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31], - tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39], - tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47], - tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55], - tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]); + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], + tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], + tmp[14], tmp[15], tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], + tmp[21], tmp[22], tmp[23], tmp[24], tmp[25], tmp[26], tmp[27], + tmp[28], tmp[29], tmp[30], tmp[31], tmp[32], tmp[33], tmp[34], + tmp[35], tmp[36], tmp[37], tmp[38], tmp[39], tmp[40], tmp[41], + tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47], tmp[48], + tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55], + tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], + tmp[62]); return 0; } @@ -160,7 +162,7 @@ static int set_gamma(struct fbtft_par *par, unsigned long *curves) static int blank(struct fbtft_par *par, bool on) { fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n", - __func__, on ? "true" : "false"); + __func__, on ? "true" : "false"); if (on) write_reg(par, 0xAE); else diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c index 200aa9ba98f9..e62235d4d9e7 100644 --- a/drivers/staging/fbtft/fb_ssd1351.c +++ b/drivers/staging/fbtft/fb_ssd1351.c @@ -71,8 +71,8 @@ static int set_var(struct fbtft_par *par) if (par->fbtftops.init_display != init_display) { /* don't risk messing up register A0h */ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, - "%s: skipping since custom init_display() is used\n", - __func__); + "%s: skipping since custom init_display() is used\n", + __func__); return 0; } @@ -117,7 +117,7 @@ static int set_var(struct fbtft_par *par) * Setting of GS63 has to be > Setting of GS62 +1 * */ -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { unsigned long tmp[GAMMA_NUM * GAMMA_LEN]; int i, acc = 0; diff --git a/drivers/staging/fbtft/fb_st7735r.c b/drivers/staging/fbtft/fb_st7735r.c index 710b74bbba97..24d17cdc71ab 100644 --- a/drivers/staging/fbtft/fb_st7735r.c +++ b/drivers/staging/fbtft/fb_st7735r.c @@ -143,7 +143,7 @@ static int set_var(struct fbtft_par *par) * VRF0N VOS0N PK0N PK1N PK2N PK3N PK4N PK5N PK6N PK7N PK8N PK9N SELV0N SELV1N SELV62N SELV63N */ #define CURVE(num, idx) curves[num * par->gamma.num_values + idx] -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { int i, j; diff --git a/drivers/staging/fbtft/fb_st7789v.c b/drivers/staging/fbtft/fb_st7789v.c index 085e9872c46d..8935a97ec048 100644 --- a/drivers/staging/fbtft/fb_st7789v.c +++ b/drivers/staging/fbtft/fb_st7789v.c @@ -178,7 +178,7 @@ static int set_var(struct fbtft_par *par) * * Return: 0 on success, < 0 if error occurred. */ -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { int i; int j; diff --git a/drivers/staging/fbtft/fb_tls8204.c b/drivers/staging/fbtft/fb_tls8204.c index ea2ddacb9468..4302e822de3b 100644 --- a/drivers/staging/fbtft/fb_tls8204.c +++ b/drivers/staging/fbtft/fb_tls8204.c @@ -36,7 +36,7 @@ #define DEFAULT_GAMMA "40" static unsigned int bs = 4; -module_param(bs, uint, 0); +module_param(bs, uint, 0000); MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)"); static int init_display(struct fbtft_par *par) @@ -130,7 +130,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) return ret; } -static int set_gamma(struct fbtft_par *par, unsigned long *curves) +static int set_gamma(struct fbtft_par *par, u32 *curves) { /* apply mask */ curves[0] &= 0x7F; diff --git a/drivers/staging/fbtft/fb_uc1611.c b/drivers/staging/fbtft/fb_uc1611.c index b33b73f17da4..48e3b3fd9fed 100644 --- a/drivers/staging/fbtft/fb_uc1611.c +++ b/drivers/staging/fbtft/fb_uc1611.c @@ -42,30 +42,30 @@ /* BR -> actual ratio: 0-3 -> 5, 10, 11, 13 */ static unsigned int ratio = 2; -module_param(ratio, uint, 0); +module_param(ratio, uint, 0000); MODULE_PARM_DESC(ratio, "BR[1:0] Bias voltage ratio: 0-3 (default: 2)"); static unsigned int gain = 3; -module_param(gain, uint, 0); +module_param(gain, uint, 0000); MODULE_PARM_DESC(gain, "GN[1:0] Bias voltage gain: 0-3 (default: 3)"); static unsigned int pot = 16; -module_param(pot, uint, 0); +module_param(pot, uint, 0000); MODULE_PARM_DESC(pot, "PM[6:0] Bias voltage pot.: 0-63 (default: 16)"); /* TC -> % compensation per deg C: 0-3 -> -.05, -.10, -.015, -.20 */ static unsigned int temp; -module_param(temp, uint, 0); +module_param(temp, uint, 0000); MODULE_PARM_DESC(temp, "TC[1:0] Temperature compensation: 0-3 (default: 0)"); /* PC[1:0] -> LCD capacitance: 0-3 -> <20nF, 20-28 nF, 29-40 nF, 40-56 nF */ static unsigned int load = 1; -module_param(load, uint, 0); +module_param(load, uint, 0000); MODULE_PARM_DESC(load, "PC[1:0] Panel Loading: 0-3 (default: 1)"); /* PC[3:2] -> V_LCD: 0, 1, 3 -> ext., int. with ratio = 5, int. standard */ static unsigned int pump = 3; -module_param(pump, uint, 0); +module_param(pump, uint, 0000); MODULE_PARM_DESC(pump, "PC[3:2] Pump control: 0,1,3 (default: 3)"); static int init_display(struct fbtft_par *par) diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c index a52e28a48825..429304546b44 100644 --- a/drivers/staging/fbtft/fb_watterott.c +++ b/drivers/staging/fbtft/fb_watterott.c @@ -40,7 +40,7 @@ #define COLOR_RGB565 16 static short mode = 565; -module_param(mode, short, 0); +module_param(mode, short, 0000); MODULE_PARM_DESC(mode, "RGB color transfer mode: 332, 565 (default)"); static void write_reg8_bus8(struct fbtft_par *par, int len, ...) diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index bbe89c9c4fb9..7c8af29cdb75 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include