Staging/IIO driver patches for 5.12-rc1

Here is the "big" set of staging and IIO driver patches for 5.12-rc1.
 
 Nothing really huge in here, the number of staging tree patches has gone
 down for a bit, maybe there's only so much churn to happen in here at
 the moment.
 
 The IIO changes are:
 	- new drivers
 	- new DT bindings
 	- new iio driver features
 with full details in the shortlog.
 
 The staging driver patches are just a lot of tiny coding style cleanups,
 along with some semi-larger hikey driver cleanups as those are _almost_
 good enough to get out of the staging tree, but will probably have to
 wait until 5.13 to have happen.
 
 All of these have been in linux-next with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYCqelQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymIDQCguWtPGy6U1sgaL3GAK/ROt2aet3wAn3TP1WgB
 GeKAKKPshu3cskYQzlou
 =UPZR
 -----END PGP SIGNATURE-----

Merge tag 'staging-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging and IIO driver updates from Greg KH:
 "Here is the "big" set of staging and IIO driver patches for 5.12-rc1.

  Nothing really huge in here, the number of staging tree patches has
  gone down for a bit, maybe there's only so much churn to happen in
  here at the moment.

  The IIO changes are:

   - new drivers

   - new DT bindings

   - new iio driver features

  with full details in the shortlog.

  The staging driver patches are just a lot of tiny coding style
  cleanups, along with some semi-larger hikey driver cleanups as those
  are _almost_ good enough to get out of the staging tree, but will
  probably have to wait until 5.13 to have happen.

  All of these have been in linux-next with no reported issues"

* tag 'staging-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (189 commits)
  staging: hikey9xx: Fix alignment of function parameters
  staging: greybus: Fixed a misspelling in hid.c
  staging: wimax/i2400m: fix some byte order issues found by sparse
  staging: wimax: i2400m: fix some incorrect type warnings
  staging: greybus: minor code style fix
  staging:wlan-ng: use memdup_user instead of kmalloc/copy_from_user
  staging:r8188eu: use IEEE80211_FCTL_* kernel definitions
  staging: rtl8192e: remove multiple blank lines
  staging: greybus: Fixed alignment issue in hid.c
  staging: wfx: remove unused included header files
  staging: nvec: minor coding style fix
  staging: wimax: Fix some coding style problem
  staging: fbtft: add tearing signal detect
  staging: vt6656: Fixed issue with alignment in rf.c
  staging: qlge: Remove duplicate word in comment
  staging: rtl8723bs: remove obsolete commented out code
  staging: rtl8723bs: fix function comments to follow kernel-doc
  staging: wfx: avoid defining array of flexible struct
  staging: rtl8723bs: Replace one-element array with flexible-array member in struct ndis_80211_var_ie
  staging: Replace lkml.org links with lore
  ...
This commit is contained in:
Linus Torvalds 2021-02-20 21:36:51 -08:00
commit 5d99aa093b
206 changed files with 7064 additions and 4423 deletions

View File

@ -198,6 +198,7 @@ Description:
Units after application of scale and offset are m/s^2.
What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw
What: /sys/bus/iio/devices/iio:deviceX/in_anglY_raw
KernelVersion: 4.17
Contact: linux-iio@vger.kernel.org
Description:
@ -1812,3 +1813,13 @@ Contact: linux-iio@vger.kernel.org
Description:
Unscaled light intensity according to CIE 1931/DIN 5033 color space.
Units after application of scale are nano nanowatts per square meter.
What: /sys/bus/iio/devices/iio:deviceX/in_anglY_label
KernelVersion: 5.12
Contact: linux-iio@vger.kernel.org
Description:
Optional symbolic label for channel Y.
For Intel hid hinge sensor, the label values are:
hinge, keyboard, screen. It means the three channels
each correspond respectively to hinge angle, keyboard angle,
and screen angle.

View File

@ -0,0 +1,31 @@
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_enable
KernelVersion: 5.12
Contact: linux-iio@vger.kernel.org
Description:
Dither enable. Write 1 to enable dither or 0 to disable it.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_invert
KernelVersion: 5.12
Contact: linux-iio@vger.kernel.org
Description:
Inverts the dither applied to the selected DAC channel. Dither is not
inverted by default. Write "1" to invert dither.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_scale_available
KernelVersion: 5.12
Contact: linux-iio@vger.kernel.org
Description:
Returns possible scalings available for the current channel.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_scale
KernelVersion: 5.12
Contact: linux-iio@vger.kernel.org
Description:
Scales the dither before it is applied to the selected channel.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_source
KernelVersion: 5.12
Contact: linux-iio@vger.kernel.org
Description:
Selects dither source applied to the selected channel. Write "0" to
select N0 source, write "1" to select N1 source.

View File

@ -20,6 +20,9 @@ properties:
reg:
maxItems: 1
vdd-supply: true
vddio-supply: true
mount-matrix:
description: an optional 3x3 mounting rotation matrix.

View File

@ -1,13 +1,22 @@
Xilinx XADC device driver
This binding document describes the bindings for both of them since the
bindings are very similar. The Xilinx XADC is a ADC that can be found in the
series 7 FPGAs from Xilinx. The XADC has a DRP interface for communication.
Currently two different frontends for the DRP interface exist. One that is only
available on the ZYNQ family as a hardmacro in the SoC portion of the ZYNQ. The
other one is available on all series 7 platforms and is a softmacro with a AXI
interface. This binding document describes the bindings for both of them since
the bindings are very similar.
This binding document describes the bindings for the Xilinx 7 Series XADC as well
as the UltraScale/UltraScale+ System Monitor.
The Xilinx XADC is an ADC that can be found in the Series 7 FPGAs from Xilinx.
The XADC has a DRP interface for communication. Currently two different
frontends for the DRP interface exist. One that is only available on the ZYNQ
family as a hardmacro in the SoC portion of the ZYNQ. The other one is available
on all series 7 platforms and is a softmacro with a AXI interface. This binding
document describes the bindings for both of them since the bindings are very
similar.
The Xilinx System Monitor is an ADC that is found in the UltraScale and
UltraScale+ FPGAs from Xilinx. The System Monitor provides a DRP interface for
communication. Xilinx provides a standard IP core that can be used to access the
System Monitor through an AXI interface in the FPGA fabric. This IP core is
called the Xilinx System Management Wizard. This document describes the bindings
for this IP.
Required properties:
- compatible: Should be one of
@ -15,11 +24,14 @@ Required properties:
configuration interface to interface to the XADC hardmacro.
* "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
interface to the XADC hardmacro.
* "xlnx,system-management-wiz-1.3": When using the
Xilinx System Management Wizard fabric IP core to access the
UltraScale and UltraScale+ System Monitor.
- reg: Address and length of the register set for the device
- interrupts: Interrupt for the XADC control interface.
- clocks: When using the ZYNQ this must be the ZYNQ PCAP clock,
when using the AXI-XADC pcore this must be the clock that provides the
clock to the AXI bus interface of the core.
when using the axi-xadc or the axi-system-management-wizard this must be
the clock that provides the clock to the AXI bus interface of the core.
Optional properties:
- xlnx,external-mux:
@ -110,3 +122,20 @@ Examples:
};
};
};
adc@80000000 {
compatible = "xlnx,system-management-wiz-1.3";
reg = <0x80000000 0x1000>;
interrupts = <0 81 4>;
interrupt-parent = <&gic>;
clocks = <&fpga1_clk>;
xlnx,channels {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
xlnx,bipolar;
};
};
};

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2020 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/adi,ad5766.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD5766 DAC device driver
maintainers:
- Cristian Pop <cristian.pop@analog.com>
description: |
Bindings for the Analog Devices AD5766 current DAC device. Datasheet can be
found here:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad5766-5767.pdf
properties:
compatible:
enum:
- adi,ad5766
- adi,ad5767
output-range-microvolts:
description: Select converter output range.
reg:
maxItems: 1
spi-max-frequency:
maximum: 1000000
spi-cpol: true
reset-gpios:
description: GPIO spec for the RESET pin. As the line is active low, it
should be marked GPIO_ACTIVE_LOW.
maxItems: 1
required:
- compatible
- output-range-microvolts
- reg
- spi-max-frequency
- spi-cpol
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
ad5766@0 {
compatible = "adi,ad5766";
output-range-microvolts = <(-5000) 5000>;
reg = <0>;
spi-cpol;
spi-max-frequency = <1000000>;
reset-gpios = <&gpio 22 0>;
};
};

View File

@ -39,20 +39,39 @@ properties:
allOf:
- if:
not:
properties:
compatible:
contains:
const: microchip,mcp4726
properties:
compatible:
contains:
const: microchip,mcp4725
then:
properties:
vref-supply: false
required:
- vdd-supply
- if:
properties:
compatible:
contains:
const: microchip,mcp4726
then:
anyOf:
- required:
- vdd-supply
- required:
- vref-supply
- if:
not:
required:
- vref-supply
then:
properties:
microchip,vref-buffered: false
required:
- compatible
- reg
- vdd-supply
additionalProperties: false

View File

@ -19,6 +19,9 @@ properties:
reg:
maxItems: 1
vdd-supply: true
vddio-supply: true
interrupts:
minItems: 1
description:

View File

@ -1,45 +0,0 @@
Invensense MPU-3050 Gyroscope device tree bindings
Required properties:
- compatible : should be "invensense,mpu3050"
- reg : the I2C address of the sensor
Optional properties:
- interrupts : interrupt mapping for the trigger interrupt from the
internal oscillator. The following IRQ modes are supported:
IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
for the desired interrupt type.
- vdd-supply : supply regulator for the main power voltage.
- vlogic-supply : supply regulator for the signal voltage.
- mount-matrix : see iio/mount-matrix.txt
Optional subnodes:
- The MPU-3050 will pass through and forward the I2C signals from the
incoming I2C bus, alternatively drive traffic to a slave device (usually
an accelerometer) on its own initiative. Therefore is supports a subnode
i2c gate node. For details see: i2c/i2c-gate.txt
Example:
mpu3050@68 {
compatible = "invensense,mpu3050";
reg = <0x68>;
interrupt-parent = <&foo>;
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&bar>;
vlogic-supply = <&baz>;
/* External I2C interface */
i2c-gate {
#address-cells = <1>;
#size-cells = <0>;
fnord@18 {
compatible = "fnord";
reg = <0x18>;
interrupt-parent = <&foo>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
};
};
};

View File

@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/gyroscope/invensense,mpu3050.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Invensense MPU-3050 Gyroscope
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
properties:
compatible:
const: invensense,mpu3050
reg:
maxItems: 1
vdd-supply: true
vlogic-supply: true
interrupts:
minItems: 1
description:
Interrupt mapping for the trigger interrupt from the internal oscillator.
mount-matrix: true
i2c-gate:
$ref: /schemas/i2c/i2c-controller.yaml
unevaluatedProperties: false
description: |
The MPU-3050 will pass through and forward the I2C signals from the
incoming I2C bus, alternatively drive traffic to a slave device (usually
an accelerometer) on its own initiative. Therefore is supports an
i2c-gate subnode.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
gyroscope@68 {
compatible = "invensense,mpu3050";
reg = <0x68>;
interrupt-parent = <&foo>;
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&bar>;
vlogic-supply = <&baz>;
i2c-gate {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@c {
compatible = "ak,ak8975";
reg = <0x0c>;
};
};
};
};
...

View File

@ -1,67 +0,0 @@
InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
http://www.invensense.com/mems/gyro/mpu6050.html
Required properties:
- compatible : should be one of
"invensense,mpu6000"
"invensense,mpu6050"
"invensense,mpu6500"
"invensense,mpu6515"
"invensense,mpu9150"
"invensense,mpu9250"
"invensense,mpu9255"
"invensense,icm20608"
"invensense,icm20609"
"invensense,icm20689"
"invensense,icm20602"
"invensense,icm20690"
"invensense,iam20680"
- reg : the I2C address of the sensor
- interrupts: interrupt mapping for IRQ. It should be configured with flags
IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
IRQ_TYPE_EDGE_FALLING.
Refer to interrupt-controller/interrupts.txt for generic interrupt client node
bindings.
Optional properties:
- vdd-supply: regulator phandle for VDD supply
- vddio-supply: regulator phandle for VDDIO supply
- mount-matrix: an optional 3x3 mounting rotation matrix
- i2c-gate node. These devices also support an auxiliary i2c bus. This is
simple enough to be described using the i2c-gate binding. See
i2c/i2c-gate.txt for more details.
Example:
mpu6050@68 {
compatible = "invensense,mpu6050";
reg = <0x68>;
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_RISING>;
mount-matrix = "-0.984807753012208", /* x0 */
"0", /* y0 */
"-0.173648177666930", /* z0 */
"0", /* x1 */
"-1", /* y1 */
"0", /* z1 */
"-0.173648177666930", /* x2 */
"0", /* y2 */
"0.984807753012208"; /* z2 */
};
mpu9250@68 {
compatible = "invensense,mpu9250";
reg = <0x68>;
interrupt-parent = <&gpio3>;
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
i2c-gate {
#address-cells = <1>;
#size-cells = <0>;
ax8975@c {
compatible = "ak,ak8975";
reg = <0x0c>;
};
};
};

View File

@ -0,0 +1,104 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/imu/invensense,mpu6050.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
maintainers:
- Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
description: |
These devices support both I2C and SPI bus interfaces.
properties:
compatible:
enum:
- invensense,iam20680
- invensense,icm20608
- invensense,icm20609
- invensense,icm20689
- invensense,icm20602
- invensense,icm20690
- invensense,mpu6000
- invensense,mpu6050
- invensense,mpu6500
- invensense,mpu6515
- invensense,mpu6880
- invensense,mpu9150
- invensense,mpu9250
- invensense,mpu9255
reg:
maxItems: 1
interrupts:
maxItems: 1
spi-max-frequency: true
vdd-supply: true
vddio-supply: true
mount-matrix: true
i2c-gate:
$ref: /schemas/i2c/i2c-controller.yaml
unevaluatedProperties: false
description: |
These devices also support an auxiliary i2c bus via an i2c-gate.
allOf:
- if:
not:
properties:
compatible:
contains:
enum:
- invensense,mpu9150
- invensense,mpu9250
- invensense,mpu9255
then:
properties:
i2c-gate: false
additionalProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
imu@68 {
compatible = "invensense,mpu9250";
reg = <0x68>;
interrupt-parent = <&gpio3>;
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
mount-matrix = "-0.984807753012208", /* x0 */
"0", /* y0 */
"-0.173648177666930", /* z0 */
"0", /* x1 */
"-1", /* y1 */
"0", /* z1 */
"-0.173648177666930", /* x2 */
"0", /* y2 */
"0.984807753012208"; /* z2 */
i2c-gate {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@c {
compatible = "ak,ak8975";
reg = <0x0c>;
};
};
};
};
...

View File

@ -30,6 +30,9 @@ properties:
reg:
maxItems: 1
vdd-supply: true
vddio-supply: true
interrupts:
maxItems: 1

View File

@ -0,0 +1,112 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/magnetometer/yamaha,yas530.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Yamaha YAS530 family of magnetometer sensors
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
description:
The Yamaha YAS530 magnetometers is a line of 3-axis magnetometers
first introduced by Yamaha in 2009 with the YAS530. They are successors
of Yamaha's first magnetometer YAS529. Over the years this magnetometer
has been miniaturized and appeared in a number of different variants.
properties:
$nodename:
pattern: '^magnetometer@[0-9a-f]+$'
compatible:
items:
- enum:
- yamaha,yas530
- yamaha,yas532
- yamaha,yas533
- yamaha,yas535
- yamaha,yas536
- yamaha,yas537
- yamaha,yas539
reg:
maxItems: 1
reset-gpios:
maxItems: 1
description: The YAS530 sensor has a RSTN pin used to reset
the logic inside the sensor. This GPIO line should connect
to that pin and be marked as GPIO_ACTIVE_LOW.
interrupts:
maxItems: 1
description: Interrupt for INT pin for interrupt generation.
The polarity, whether the interrupt is active on the rising
or the falling edge, is software-configurable in the hardware.
vdd-supply:
description: An optional regulator providing core power supply
on the VDD pin, typically 1.8 V or 3.0 V.
iovdd-supply:
description: An optional regulator providing I/O power supply
for the I2C interface on the IOVDD pin, typically 1.8 V.
mount-matrix:
description: An optional 3x3 mounting rotation matrix.
allOf:
- if:
not:
properties:
compatible:
items:
const: yamaha,yas530
then:
properties:
reset-gpios: false
- if:
properties:
compatible:
items:
const: yamaha,yas539
then:
properties:
interrupts: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c-0 {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@2e {
compatible = "yamaha,yas530";
reg = <0x2e>;
vdd-supply = <&ldo1_reg>;
iovdd-supply = <&ldo2_reg>;
reset-gpios = <&gpio6 12 GPIO_ACTIVE_LOW>;
interrupts = <&gpio6 13 IRQ_TYPE_EDGE_RISING>;
};
};
i2c-1 {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@2e {
compatible = "yamaha,yas539";
reg = <0x2e>;
vdd-supply = <&ldo1_reg>;
};
};

View File

@ -148,15 +148,13 @@ properties:
- maxim,max31730
# mCube 3-axis 8-bit digital accelerometer
- mcube,mc3230
# MEMSIC magnetometer
- memsic,mmc35240
# MEMSIC 2-axis 8-bit digital accelerometer
- memsic,mxc6225
# Measurement Specialities I2C temperature and humidity sensor
- meas,htu21
# Measurement Specialities I2C pressure and temperature sensor
- meas,ms5637
# Measurement Specialities I2C pressure and temperature sensor
- meas,ms5803
# Measurement Specialities I2C pressure and temperature sensor
- meas,ms5805
# Measurement Specialities I2C pressure and temperature sensor
- meas,ms5837
@ -166,6 +164,10 @@ properties:
- meas,ms8607-temppressure
# Measurement Specialties temperature sensor
- meas,tsys01
# MEMSIC magnetometer
- memsic,mmc35240
# MEMSIC 2-axis 8-bit digital accelerometer
- memsic,mxc6225
# Microchip differential I2C ADC, 1 Channel, 18 bit
- microchip,mcp3421
# Microchip differential I2C ADC, 2 Channel, 18 bit

View File

@ -1260,6 +1260,8 @@ patternProperties:
description: Shenzhen Xunlong Software CO.,Limited
"^xylon,.*":
description: Xylon
"^yamaha,.*":
description: Yamaha Corporation
"^yes-optoelectronics,.*":
description: Yes Optoelectronics Co.,Ltd.
"^ylm,.*":

View File

@ -15,6 +15,7 @@ Contents:
ethernet/index
fddi/index
hamradio/index
qlogic/index
wan/index
wifi/index

View File

@ -0,0 +1,18 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
QLogic QLGE Device Drivers
===============================================
Contents:
.. toctree::
:maxdepth: 2
qlge
.. only:: subproject and html
Indices
=======
* :ref:`genindex`

View File

@ -0,0 +1,118 @@
.. SPDX-License-Identifier: GPL-2.0
=======================================
QLogic QLGE 10Gb Ethernet device driver
=======================================
This driver use drgn and devlink for debugging.
Dump kernel data structures in drgn
-----------------------------------
To dump kernel data structures, the following Python script can be used
in drgn:
.. code-block:: python
def align(x, a):
"""the alignment a should be a power of 2
"""
mask = a - 1
return (x+ mask) & ~mask
def struct_size(struct_type):
struct_str = "struct {}".format(struct_type)
return sizeof(Object(prog, struct_str, address=0x0))
def netdev_priv(netdevice):
NETDEV_ALIGN = 32
return netdevice.value_() + align(struct_size("net_device"), NETDEV_ALIGN)
name = 'xxx'
qlge_device = None
netdevices = prog['init_net'].dev_base_head.address_of_()
for netdevice in list_for_each_entry("struct net_device", netdevices, "dev_list"):
if netdevice.name.string_().decode('ascii') == name:
print(netdevice.name)
ql_adapter = Object(prog, "struct ql_adapter", address=netdev_priv(qlge_device))
The struct ql_adapter will be printed in drgn as follows,
>>> ql_adapter
(struct ql_adapter){
.ricb = (struct ricb){
.base_cq = (u8)0,
.flags = (u8)120,
.mask = (__le16)26637,
.hash_cq_id = (u8 [1024]){ 172, 142, 255, 255 },
.ipv6_hash_key = (__le32 [10]){},
.ipv4_hash_key = (__le32 [4]){},
},
.flags = (unsigned long)0,
.wol = (u32)0,
.nic_stats = (struct nic_stats){
.tx_pkts = (u64)0,
.tx_bytes = (u64)0,
.tx_mcast_pkts = (u64)0,
.tx_bcast_pkts = (u64)0,
.tx_ucast_pkts = (u64)0,
.tx_ctl_pkts = (u64)0,
.tx_pause_pkts = (u64)0,
...
},
.active_vlans = (unsigned long [64]){
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52780853100545, 18446744073709551615,
18446619461681283072, 0, 42949673024, 2147483647,
},
.rx_ring = (struct rx_ring [17]){
{
.cqicb = (struct cqicb){
.msix_vect = (u8)0,
.reserved1 = (u8)0,
.reserved2 = (u8)0,
.flags = (u8)0,
.len = (__le16)0,
.rid = (__le16)0,
...
},
.cq_base = (void *)0x0,
.cq_base_dma = (dma_addr_t)0,
}
...
}
}
coredump via devlink
--------------------
And the coredump obtained via devlink in json format looks like,
.. code:: shell
$ devlink health dump show DEVICE reporter coredump -p -j
{
"Core Registers": {
"segment": 1,
"values": [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]
},
"Test Logic Regs": {
"segment": 2,
"values": [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]
},
"RMII Registers": {
"segment": 3,
"values": [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]
},
...
"Sem Registers": {
"segment": 50,
"values": [ 0,0,0,0 ]
}
}
When the module parameter qlge_force_coredump is set to be true, the MPI
RISC reset before coredumping. So coredumping will much longer since
devlink tool has to wait for 5 secs for the resetting to be
finished.

View File

@ -14590,6 +14590,12 @@ L: netdev@vger.kernel.org
S: Supported
F: drivers/staging/qlge/
QLOGIC QLGE 10Gb ETHERNET DRIVER
M: Coiby Xu <coiby.xu@gmail.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/networking/device_drivers/qlogic/qlge.rst
QM1D1B0004 MEDIA DRIVER
M: Akihiro Tsukada <tskd08@gmail.com>
L: linux-media@vger.kernel.org

View File

@ -4,6 +4,7 @@
* Copyright (c) 2015, Intel Corporation.
*/
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@ -21,6 +22,7 @@
#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1)
#define HID_CUSTOM_FIFO_SIZE 4096
#define HID_CUSTOM_MAX_FEATURE_BYTES 64
#define HID_SENSOR_USAGE_LENGTH (4 + 1)
struct hid_sensor_custom_field {
int report_id;
@ -50,6 +52,7 @@ struct hid_sensor_custom {
struct kfifo data_fifo;
unsigned long misc_opened;
wait_queue_head_t wait;
struct platform_device *custom_pdev;
};
/* Header for each sample to user space via dev interface */
@ -746,11 +749,130 @@ static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom
}
/* luid defined in FW (e.g. ISH). Maybe used to identify sensor. */
static const char *const known_sensor_luid[] = { "020B000000000000" };
static int get_luid_table_index(unsigned char *usage_str)
{
int i;
for (i = 0; i < ARRAY_SIZE(known_sensor_luid); i++) {
if (!strncmp(usage_str, known_sensor_luid[i],
strlen(known_sensor_luid[i])))
return i;
}
return -ENODEV;
}
static int get_known_custom_sensor_index(struct hid_sensor_hub_device *hsdev)
{
struct hid_sensor_hub_attribute_info sensor_manufacturer = { 0 };
struct hid_sensor_hub_attribute_info sensor_luid_info = { 0 };
int report_size;
int ret;
static u16 w_buf[HID_CUSTOM_MAX_FEATURE_BYTES];
static char buf[HID_CUSTOM_MAX_FEATURE_BYTES];
int i;
memset(w_buf, 0, sizeof(w_buf));
memset(buf, 0, sizeof(buf));
/* get manufacturer info */
ret = sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, hsdev->usage,
HID_USAGE_SENSOR_PROP_MANUFACTURER, &sensor_manufacturer);
if (ret < 0)
return ret;
report_size =
sensor_hub_get_feature(hsdev, sensor_manufacturer.report_id,
sensor_manufacturer.index, sizeof(w_buf),
w_buf);
if (report_size <= 0) {
hid_err(hsdev->hdev,
"Failed to get sensor manufacturer info %d\n",
report_size);
return -ENODEV;
}
/* convert from wide char to char */
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
buf[i] = (char)w_buf[i];
/* ensure it's ISH sensor */
if (strncmp(buf, "INTEL", strlen("INTEL")))
return -ENODEV;
memset(w_buf, 0, sizeof(w_buf));
memset(buf, 0, sizeof(buf));
/* get real usage id */
ret = sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, hsdev->usage,
HID_USAGE_SENSOR_PROP_SERIAL_NUM, &sensor_luid_info);
if (ret < 0)
return ret;
report_size = sensor_hub_get_feature(hsdev, sensor_luid_info.report_id,
sensor_luid_info.index, sizeof(w_buf),
w_buf);
if (report_size <= 0) {
hid_err(hsdev->hdev, "Failed to get real usage info %d\n",
report_size);
return -ENODEV;
}
/* convert from wide char to char */
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
buf[i] = (char)w_buf[i];
if (strlen(buf) != strlen(known_sensor_luid[0]) + 5) {
hid_err(hsdev->hdev,
"%s luid length not match %zu != (%zu + 5)\n", __func__,
strlen(buf), strlen(known_sensor_luid[0]));
return -ENODEV;
}
/* get table index with luid (not matching 'LUID: ' in luid) */
return get_luid_table_index(&buf[5]);
}
static struct platform_device *
hid_sensor_register_platform_device(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
int index)
{
char real_usage[HID_SENSOR_USAGE_LENGTH] = { 0 };
struct platform_device *custom_pdev;
const char *dev_name;
char *c;
/* copy real usage id */
memcpy(real_usage, known_sensor_luid[index], 4);
/* usage id are all lowcase */
for (c = real_usage; *c != '\0'; c++)
*c = tolower(*c);
/* HID-SENSOR-INT-REAL_USAGE_ID */
dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-INT-%s", real_usage);
if (!dev_name)
return ERR_PTR(-ENOMEM);
custom_pdev = platform_device_register_data(pdev->dev.parent, dev_name,
PLATFORM_DEVID_NONE, hsdev,
sizeof(*hsdev));
kfree(dev_name);
return custom_pdev;
}
static int hid_sensor_custom_probe(struct platform_device *pdev)
{
struct hid_sensor_custom *sensor_inst;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
int ret;
int index;
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
GFP_KERNEL);
@ -764,6 +886,22 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
sensor_inst->pdev = pdev;
mutex_init(&sensor_inst->mutex);
platform_set_drvdata(pdev, sensor_inst);
index = get_known_custom_sensor_index(hsdev);
if (index >= 0 && index < ARRAY_SIZE(known_sensor_luid)) {
sensor_inst->custom_pdev =
hid_sensor_register_platform_device(pdev, hsdev, index);
ret = PTR_ERR_OR_ZERO(sensor_inst->custom_pdev);
if (ret) {
dev_err(&pdev->dev,
"register_platform_device failed\n");
return ret;
}
return 0;
}
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
&sensor_inst->callbacks);
if (ret < 0) {
@ -802,6 +940,11 @@ static int hid_sensor_custom_remove(struct platform_device *pdev)
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
if (sensor_inst->custom_pdev) {
platform_device_unregister(sensor_inst->custom_pdev);
return 0;
}
hid_sensor_custom_dev_if_remove(sensor_inst);
hid_sensor_custom_remove_attributes(sensor_inst);
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,

View File

@ -23,6 +23,7 @@ enum accel_3d_channel {
ACCEL_3D_CHANNEL_MAX,
};
#define CHANNEL_SCAN_INDEX_TIMESTAMP ACCEL_3D_CHANNEL_MAX
struct accel_3d_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
@ -75,7 +76,7 @@ static const struct iio_chan_spec accel_3d_channels[] = {
BIT(IIO_CHAN_INFO_HYSTERESIS),
.scan_index = CHANNEL_SCAN_INDEX_Z,
},
IIO_CHAN_SOFT_TIMESTAMP(3)
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
};
/* Channel definitions */
@ -110,7 +111,8 @@ static const struct iio_chan_spec gravity_channels[] = {
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
.scan_index = CHANNEL_SCAN_INDEX_Z,
}
},
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
};
/* Adjust channel real bits based on report descriptor */

View File

@ -14,6 +14,7 @@
#include <linux/acpi.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
@ -133,6 +134,7 @@ enum kx_acpi_type {
};
struct kxcjk1013_data {
struct regulator_bulk_data regulators[2];
struct i2c_client *client;
struct iio_trigger *dready_trig;
struct iio_trigger *motion_trig;
@ -1300,6 +1302,13 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
return dev_name(dev);
}
static void kxcjk1013_disable_regulators(void *d)
{
struct kxcjk1013_data *data = d;
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
}
static int kxcjk1013_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -1330,6 +1339,29 @@ static int kxcjk1013_probe(struct i2c_client *client,
return ret;
}
data->regulators[0].supply = "vdd";
data->regulators[1].supply = "vddio";
ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(data->regulators),
data->regulators);
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to get regulators\n");
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
data->regulators);
if (ret)
return ret;
ret = devm_add_action_or_reset(&client->dev, kxcjk1013_disable_regulators, data);
if (ret)
return ret;
/*
* A typical delay of 10ms is required for powering up
* according to the data sheets of supported chips.
* Hence double that to play safe.
*/
msleep(20);
if (id) {
data->chipset = (enum kx_chipset)(id->driver_data);
name = id->name;

View File

@ -1228,8 +1228,15 @@ config XILINX_XADC
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to have support for the Xilinx XADC. The driver does support
both the ZYNQ interface to the XADC as well as the AXI-XADC interface.
Say yes here to have support for the Xilinx 7 Series XADC or
UltraScale/UltraScale+ System Management Wizard.
For the 7 Series the driver does support both the ZYNQ interface
to the XADC as well as the AXI-XADC interface.
The driver also support the Xilinx System Management Wizard IP core
that can be used to access the System Monitor ADC on the Xilinx
UltraScale and UltraScale+ FPGAs.
The driver can also be build as a module. If so, the module will be called
xilinx-xadc.

View File

@ -1108,10 +1108,14 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
return gpadc->irq_sw;
}
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
if (gpadc->irq_hw < 0) {
dev_err(dev, "failed to get platform hw_conv_end irq\n");
return gpadc->irq_hw;
if (is_ab8500(gpadc->ab8500)) {
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
if (gpadc->irq_hw < 0) {
dev_err(dev, "failed to get platform hw_conv_end irq\n");
return gpadc->irq_hw;
}
} else {
gpadc->irq_hw = 0;
}
/* Initialize completion used to notify completion of conversion */
@ -1128,14 +1132,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
return ret;
}
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
"ab8500-gpadc-hw", gpadc);
if (ret < 0) {
dev_err(dev,
"Failed to request hw conversion irq: %d\n",
gpadc->irq_hw);
return ret;
if (gpadc->irq_hw) {
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
"ab8500-gpadc-hw", gpadc);
if (ret < 0) {
dev_err(dev,
"Failed to request hw conversion irq: %d\n",
gpadc->irq_hw);
return ret;
}
}
/* The VTVout LDO used to power the AB8500 GPADC */

View File

@ -67,6 +67,7 @@ enum ad7476_supported_device_ids {
ID_ADS7866,
ID_ADS7867,
ID_ADS7868,
ID_LTC2314_14,
};
static void ad7091_convst(struct ad7476_state *st)
@ -250,6 +251,10 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
.channel[0] = ADS786X_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_LTC2314_14] = {
.channel[0] = AD7940_CHAN(14),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
};
static const struct iio_info ad7476_info = {
@ -365,6 +370,7 @@ static const struct spi_device_id ad7476_id[] = {
{"ads7866", ID_ADS7866},
{"ads7867", ID_ADS7867},
{"ads7868", ID_ADS7868},
{"ltc2314-14", ID_LTC2314_14},
{}
};
MODULE_DEVICE_TABLE(spi, ad7476_id);

View File

@ -10,6 +10,7 @@
* Author: Linus Walleij <linus.walleij@linaro.org>
*/
#include <linux/iio/adc/qcom-vadc-common.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
@ -21,8 +22,6 @@
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
#include "qcom-vadc-common.h"
/*
* Definitions for the "user processor" registers lifted from the v3.4
* Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC:

View File

@ -7,6 +7,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iio/adc/qcom-vadc-common.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@ -14,12 +15,12 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#include "qcom-vadc-common.h"
#define ADC5_USR_REVISION1 0x0
#define ADC5_USR_STATUS1 0x8
@ -154,18 +155,6 @@ struct adc5_chip {
const struct adc5_data *data;
};
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
{.num = 1, .den = 1},
{.num = 1, .den = 3},
{.num = 1, .den = 4},
{.num = 1, .den = 6},
{.num = 1, .den = 20},
{.num = 1, .den = 8},
{.num = 10, .den = 81},
{.num = 1, .den = 10},
{.num = 1, .den = 16}
};
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
{
return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
@ -181,55 +170,6 @@ static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
}
static int adc5_prescaling_from_dt(u32 num, u32 den)
{
unsigned int pre;
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
if (adc5_prescale_ratios[pre].num == num &&
adc5_prescale_ratios[pre].den == den)
break;
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
return -EINVAL;
return pre;
}
static int adc5_hw_settle_time_from_dt(u32 value,
const unsigned int *hw_settle)
{
unsigned int i;
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
if (value == hw_settle[i])
return i;
}
return -EINVAL;
}
static int adc5_avg_samples_from_dt(u32 value)
{
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
return -EINVAL;
return __ffs(value);
}
static int adc5_decimation_from_dt(u32 value,
const unsigned int *decimation)
{
unsigned int i;
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
if (value == decimation[i])
return i;
}
return -EINVAL;
}
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
{
int ret;
@ -511,7 +451,7 @@ static int adc_read_raw_common(struct iio_dev *indio_dev,
return ret;
ret = qcom_adc5_hw_scale(prop->scale_fn_type,
&adc5_prescale_ratios[prop->prescale],
prop->prescale,
adc->data,
adc_code_volt, val);
if (ret)
@ -717,7 +657,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
ret = of_property_read_u32(node, "qcom,decimation", &value);
if (!ret) {
ret = adc5_decimation_from_dt(value, data->decimation);
ret = qcom_adc5_decimation_from_dt(value, data->decimation);
if (ret < 0) {
dev_err(dev, "%02x invalid decimation %d\n",
chan, value);
@ -730,7 +670,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
if (!ret) {
ret = adc5_prescaling_from_dt(varr[0], varr[1]);
ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
if (ret < 0) {
dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
chan, varr[0], varr[1]);
@ -759,11 +699,9 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
adc->data->info == &adc7_info)
ret = adc5_hw_settle_time_from_dt(value,
data->hw_settle_2);
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2);
else
ret = adc5_hw_settle_time_from_dt(value,
data->hw_settle_1);
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1);
if (ret < 0) {
dev_err(dev, "%02x invalid hw-settle-time %d us\n",
@ -777,7 +715,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
ret = of_property_read_u32(node, "qcom,avg-samples", &value);
if (!ret) {
ret = adc5_avg_samples_from_dt(value);
ret = qcom_adc5_avg_samples_from_dt(value);
if (ret < 0) {
dev_err(dev, "%02x invalid avg-samples %d\n",
chan, value);
@ -870,8 +808,6 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
struct adc5_channel_prop prop, *chan_props;
struct device_node *child;
unsigned int index = 0;
const struct of_device_id *id;
const struct adc5_data *data;
int ret;
adc->nchannels = of_get_available_child_count(node);
@ -890,24 +826,21 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
chan_props = adc->chan_props;
iio_chan = adc->iio_chans;
id = of_match_node(adc5_match_table, node);
if (id)
data = id->data;
else
data = &adc5_data_pmic;
adc->data = data;
adc->data = of_device_get_match_data(adc->dev);
if (!adc->data)
adc->data = &adc5_data_pmic;
for_each_available_child_of_node(node, child) {
ret = adc5_get_dt_channel_data(adc, &prop, child, data);
ret = adc5_get_dt_channel_data(adc, &prop, child, adc->data);
if (ret) {
of_node_put(child);
return ret;
}
prop.scale_fn_type =
data->adc_chans[prop.channel].scale_fn_type;
adc->data->adc_chans[prop.channel].scale_fn_type;
*chan_props = prop;
adc_chan = &data->adc_chans[prop.channel];
adc_chan = &adc->data->adc_chans[prop.channel];
iio_chan->channel = prop.channel;
iio_chan->datasheet_name = prop.datasheet_name;

View File

@ -7,6 +7,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iio/adc/qcom-vadc-common.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@ -20,8 +21,6 @@
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#include "qcom-vadc-common.h"
/* VADC register and bit definitions */
#define VADC_REVISION2 0x1
#define VADC_REVISION2_SUPPORTED_VADC 1

View File

@ -2,50 +2,61 @@
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/fixp-arith.h>
#include <linux/iio/adc/qcom-vadc-common.h>
#include <linux/math64.h>
#include <linux/log2.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/units.h>
#include "qcom-vadc-common.h"
/**
* 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;
};
/* 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}
{1758, -40000 },
{1742, -35000 },
{1719, -30000 },
{1691, -25000 },
{1654, -20000 },
{1608, -15000 },
{1551, -10000 },
{1483, -5000 },
{1404, 0 },
{1315, 5000 },
{1218, 10000 },
{1114, 15000 },
{1007, 20000 },
{900, 25000 },
{795, 30000 },
{696, 35000 },
{605, 40000 },
{522, 45000 },
{448, 50000 },
{383, 55000 },
{327, 60000 },
{278, 65000 },
{237, 70000 },
{202, 75000 },
{172, 80000 },
{146, 85000 },
{125, 90000 },
{107, 95000 },
{92, 100000 },
{79, 105000 },
{68, 110000 },
{59, 115000 },
{51, 120000 },
{44, 125000 }
};
/*
@ -90,18 +101,18 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
};
static const struct vadc_map_pt adcmap7_die_temp[] = {
{ 433700, 1967},
{ 473100, 1964},
{ 512400, 1957},
{ 551500, 1949},
{ 590500, 1940},
{ 629300, 1930},
{ 667900, 1921},
{ 706400, 1910},
{ 744600, 1896},
{ 782500, 1878},
{ 820100, 1859},
{ 857300, 0},
{ 857300, 160000 },
{ 820100, 140000 },
{ 782500, 120000 },
{ 744600, 100000 },
{ 706400, 80000 },
{ 667900, 60000 },
{ 629300, 40000 },
{ 590500, 20000 },
{ 551500, 0 },
{ 512400, -20000 },
{ 473100, -40000 },
{ 433700, -60000 },
};
/*
@ -278,6 +289,18 @@ static const struct vadc_map_pt adcmap7_100k[] = {
{ 2420, 130048 }
};
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
{.num = 1, .den = 1},
{.num = 1, .den = 3},
{.num = 1, .den = 4},
{.num = 1, .den = 6},
{.num = 1, .den = 20},
{.num = 1, .den = 8},
{.num = 10, .den = 81},
{.num = 1, .den = 10},
{.num = 1, .den = 16}
};
static int qcom_vadc_scale_hw_calib_volt(
const struct vadc_prescale_ratio *prescale,
const struct adc5_data *data,
@ -323,43 +346,23 @@ static struct qcom_adc5_scale_type scale_adc5_fn[] = {
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
u32 tablesize, s32 input, int *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;
}
while (i < tablesize && pts[i].x > input)
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);
*output = fixp_linear_interpolate(pts[i - 1].x, pts[i - 1].y,
pts[i].x, pts[i].y,
input);
}
return 0;
@ -415,8 +418,6 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
if (ret)
return ret;
*result_mdec *= 1000;
return 0;
}
@ -563,33 +564,13 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
u16 adc_code, int *result_mdec)
{
int voltage, vtemp0, temp, i;
int voltage;
voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
prescale, data, 1);
if (adcmap7_die_temp[0].x > voltage) {
*result_mdec = DIE_TEMP_ADC7_SCALE_1;
return 0;
}
if (adcmap7_die_temp[ARRAY_SIZE(adcmap7_die_temp) - 1].x <= voltage) {
*result_mdec = DIE_TEMP_ADC7_MAX;
return 0;
}
for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++)
if (adcmap7_die_temp[i].x > voltage)
break;
vtemp0 = adcmap7_die_temp[i - 1].x;
voltage = voltage - vtemp0;
temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
adcmap7_die_temp[i - 1].y);
temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1));
*result_mdec = temp;
return 0;
return qcom_vadc_map_voltage_temp(adcmap7_die_temp, ARRAY_SIZE(adcmap7_die_temp),
voltage, result_mdec);
}
static int qcom_vadc_scale_hw_smb_temp(
@ -647,10 +628,12 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
EXPORT_SYMBOL(qcom_vadc_scale);
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_prescale_ratio *prescale,
unsigned int prescale_ratio,
const struct adc5_data *data,
u16 adc_code, int *result)
{
const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio];
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
scaletype < SCALE_HW_CALIB_INVALID)) {
pr_err("Invalid scale type %d\n", scaletype);
@ -662,6 +645,58 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
}
EXPORT_SYMBOL(qcom_adc5_hw_scale);
int qcom_adc5_prescaling_from_dt(u32 num, u32 den)
{
unsigned int pre;
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
if (adc5_prescale_ratios[pre].num == num &&
adc5_prescale_ratios[pre].den == den)
break;
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
return -EINVAL;
return pre;
}
EXPORT_SYMBOL(qcom_adc5_prescaling_from_dt);
int qcom_adc5_hw_settle_time_from_dt(u32 value,
const unsigned int *hw_settle)
{
unsigned int i;
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
if (value == hw_settle[i])
return i;
}
return -EINVAL;
}
EXPORT_SYMBOL(qcom_adc5_hw_settle_time_from_dt);
int qcom_adc5_avg_samples_from_dt(u32 value)
{
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
return -EINVAL;
return __ffs(value);
}
EXPORT_SYMBOL(qcom_adc5_avg_samples_from_dt);
int qcom_adc5_decimation_from_dt(u32 value, const unsigned int *decimation)
{
unsigned int i;
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
if (value == decimation[i])
return i;
}
return -EINVAL;
}
EXPORT_SYMBOL(qcom_adc5_decimation_from_dt);
int qcom_vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||

View File

@ -307,7 +307,7 @@ static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel,
sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator);
return (volt * denominator + numerator / 2) / numerator;
return DIV_ROUND_CLOSEST(volt * denominator, numerator);
}
static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data,

View File

@ -535,20 +535,16 @@ static int stm32_adc_core_hw_start(struct device *dev)
goto err_switches_dis;
}
if (priv->bclk) {
ret = clk_prepare_enable(priv->bclk);
if (ret < 0) {
dev_err(dev, "bus clk enable failed\n");
goto err_regulator_disable;
}
ret = clk_prepare_enable(priv->bclk);
if (ret < 0) {
dev_err(dev, "bus clk enable failed\n");
goto err_regulator_disable;
}
if (priv->aclk) {
ret = clk_prepare_enable(priv->aclk);
if (ret < 0) {
dev_err(dev, "adc clk enable failed\n");
goto err_bclk_disable;
}
ret = clk_prepare_enable(priv->aclk);
if (ret < 0) {
dev_err(dev, "adc clk enable failed\n");
goto err_bclk_disable;
}
writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
@ -556,8 +552,7 @@ static int stm32_adc_core_hw_start(struct device *dev)
return 0;
err_bclk_disable:
if (priv->bclk)
clk_disable_unprepare(priv->bclk);
clk_disable_unprepare(priv->bclk);
err_regulator_disable:
regulator_disable(priv->vref);
err_switches_dis:
@ -575,10 +570,8 @@ static void stm32_adc_core_hw_stop(struct device *dev)
/* Backup CCR that may be lost (depends on power state to achieve) */
priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
if (priv->aclk)
clk_disable_unprepare(priv->aclk);
if (priv->bclk)
clk_disable_unprepare(priv->bclk);
clk_disable_unprepare(priv->aclk);
clk_disable_unprepare(priv->bclk);
regulator_disable(priv->vref);
stm32_adc_core_switches_supply_dis(priv);
regulator_disable(priv->vdda);

View File

@ -546,8 +546,7 @@ static int stm32_adc_hw_stop(struct device *dev)
if (adc->cfg->unprepare)
adc->cfg->unprepare(indio_dev);
if (adc->clk)
clk_disable_unprepare(adc->clk);
clk_disable_unprepare(adc->clk);
return 0;
}
@ -558,11 +557,9 @@ static int stm32_adc_hw_start(struct device *dev)
struct stm32_adc *adc = iio_priv(indio_dev);
int ret;
if (adc->clk) {
ret = clk_prepare_enable(adc->clk);
if (ret)
return ret;
}
ret = clk_prepare_enable(adc->clk);
if (ret)
return ret;
stm32_adc_set_res(adc);
@ -575,8 +572,7 @@ static int stm32_adc_hw_start(struct device *dev)
return 0;
err_clk_dis:
if (adc->clk)
clk_disable_unprepare(adc->clk);
clk_disable_unprepare(adc->clk);
return ret;
}

View File

@ -117,8 +117,7 @@ static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
{
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
if (priv->aclk)
clk_disable_unprepare(priv->aclk);
clk_disable_unprepare(priv->aclk);
clk_disable_unprepare(priv->clk);
}

View File

@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
@ -92,7 +93,12 @@ static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500;
#define XADC_AXI_REG_GIER 0x5c
#define XADC_AXI_REG_IPISR 0x60
#define XADC_AXI_REG_IPIER 0x68
#define XADC_AXI_ADC_REG_OFFSET 0x200
/* 7 Series */
#define XADC_7S_AXI_ADC_REG_OFFSET 0x200
/* UltraScale */
#define XADC_US_AXI_ADC_REG_OFFSET 0x400
#define XADC_AXI_RESET_MAGIC 0xa
#define XADC_AXI_GIER_ENABLE BIT(31)
@ -447,6 +453,12 @@ static const struct xadc_ops xadc_zynq_ops = {
.get_dclk_rate = xadc_zynq_get_dclk_rate,
.interrupt_handler = xadc_zynq_interrupt_handler,
.update_alarm = xadc_zynq_update_alarm,
.type = XADC_TYPE_S7,
};
static const unsigned int xadc_axi_reg_offsets[] = {
[XADC_TYPE_S7] = XADC_7S_AXI_ADC_REG_OFFSET,
[XADC_TYPE_US] = XADC_US_AXI_ADC_REG_OFFSET,
};
static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
@ -454,7 +466,8 @@ static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
{
uint32_t val32;
xadc_read_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, &val32);
xadc_read_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
&val32);
*val = val32 & 0xffff;
return 0;
@ -463,7 +476,8 @@ static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg,
uint16_t val)
{
xadc_write_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, val);
xadc_write_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
val);
return 0;
}
@ -541,7 +555,7 @@ static unsigned long xadc_axi_get_dclk(struct xadc *xadc)
return clk_get_rate(xadc->clk);
}
static const struct xadc_ops xadc_axi_ops = {
static const struct xadc_ops xadc_7s_axi_ops = {
.read = xadc_axi_read_adc_reg,
.write = xadc_axi_write_adc_reg,
.setup = xadc_axi_setup,
@ -549,6 +563,18 @@ static const struct xadc_ops xadc_axi_ops = {
.update_alarm = xadc_axi_update_alarm,
.interrupt_handler = xadc_axi_interrupt_handler,
.flags = XADC_FLAGS_BUFFERED,
.type = XADC_TYPE_S7,
};
static const struct xadc_ops xadc_us_axi_ops = {
.read = xadc_axi_read_adc_reg,
.write = xadc_axi_write_adc_reg,
.setup = xadc_axi_setup,
.get_dclk_rate = xadc_axi_get_dclk,
.update_alarm = xadc_axi_update_alarm,
.interrupt_handler = xadc_axi_interrupt_handler,
.flags = XADC_FLAGS_BUFFERED,
.type = XADC_TYPE_US,
};
static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg,
@ -585,15 +611,22 @@ static int xadc_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *mask)
{
struct xadc *xadc = iio_priv(indio_dev);
unsigned int n;
size_t new_size, n;
void *data;
n = bitmap_weight(mask, indio_dev->masklength);
kfree(xadc->data);
xadc->data = kcalloc(n, sizeof(*xadc->data), GFP_KERNEL);
if (!xadc->data)
if (check_mul_overflow(n, sizeof(*xadc->data), &new_size))
return -ENOMEM;
data = devm_krealloc(indio_dev->dev.parent, xadc->data,
new_size, GFP_KERNEL);
if (!data)
return -ENOMEM;
memset(data, 0, new_size);
xadc->data = data;
return 0;
}
@ -705,11 +738,12 @@ static const struct iio_trigger_ops xadc_trigger_ops = {
static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
const char *name)
{
struct device *dev = indio_dev->dev.parent;
struct iio_trigger *trig;
int ret;
trig = iio_trigger_alloc("%s%d-%s", indio_dev->name,
indio_dev->id, name);
trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name,
indio_dev->id, name);
if (trig == NULL)
return ERR_PTR(-ENOMEM);
@ -717,21 +751,26 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
trig->ops = &xadc_trigger_ops;
iio_trigger_set_drvdata(trig, iio_priv(indio_dev));
ret = iio_trigger_register(trig);
ret = devm_iio_trigger_register(dev, trig);
if (ret)
goto error_free_trig;
return ERR_PTR(ret);
return trig;
error_free_trig:
iio_trigger_free(trig);
return ERR_PTR(ret);
}
static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode)
{
uint16_t val;
/*
* As per datasheet the power-down bits are don't care in the
* UltraScale, but as per reality setting the power-down bit for the
* non-existing ADC-B powers down the main ADC, so just return and don't
* do anything.
*/
if (xadc->ops->type == XADC_TYPE_US)
return 0;
/* Powerdown the ADC-B when it is not needed. */
switch (seq_mode) {
case XADC_CONF1_SEQ_SIMULTANEOUS:
@ -751,6 +790,10 @@ static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode)
{
unsigned int aux_scan_mode = scan_mode >> 16;
/* UltraScale has only one ADC and supports only continuous mode */
if (xadc->ops->type == XADC_TYPE_US)
return XADC_CONF1_SEQ_CONTINUOUS;
if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL)
return XADC_CONF1_SEQ_SIMULTANEOUS;
@ -863,6 +906,7 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long info)
{
struct xadc *xadc = iio_priv(indio_dev);
unsigned int bits = chan->scan_type.realbits;
uint16_t val16;
int ret;
@ -874,17 +918,17 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
val16 >>= 4;
val16 >>= chan->scan_type.shift;
if (chan->scan_type.sign == 'u')
*val = val16;
else
*val = sign_extend32(val16, 11);
*val = sign_extend32(val16, bits - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE:
/* V = (val * 3.0) / 4096 */
/* V = (val * 3.0) / 2**bits */
switch (chan->address) {
case XADC_REG_VCCINT:
case XADC_REG_VCCAUX:
@ -900,19 +944,19 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
*val = 1000;
break;
}
*val2 = 12;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_TEMP:
/* Temp in C = (val * 503.975) / 4096 - 273.15 */
/* Temp in C = (val * 503.975) / 2**bits - 273.15 */
*val = 503975;
*val2 = 12;
*val2 = bits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
/* Only the temperature channel has an offset */
*val = -((273150 << 12) / 503975);
*val = -((273150 << bits) / 503975);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = xadc_read_samplerate(xadc);
@ -1001,7 +1045,7 @@ static const struct iio_event_spec xadc_voltage_events[] = {
},
};
#define XADC_CHAN_TEMP(_chan, _scan_index, _addr) { \
#define XADC_CHAN_TEMP(_chan, _scan_index, _addr, _bits) { \
.type = IIO_TEMP, \
.indexed = 1, \
.channel = (_chan), \
@ -1015,14 +1059,14 @@ static const struct iio_event_spec xadc_voltage_events[] = {
.scan_index = (_scan_index), \
.scan_type = { \
.sign = 'u', \
.realbits = 12, \
.realbits = (_bits), \
.storagebits = 16, \
.shift = 4, \
.shift = 16 - (_bits), \
.endianness = IIO_CPU, \
}, \
}
#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) { \
#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _bits, _ext, _alarm) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (_chan), \
@ -1035,41 +1079,82 @@ static const struct iio_event_spec xadc_voltage_events[] = {
.scan_index = (_scan_index), \
.scan_type = { \
.sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \
.realbits = 12, \
.realbits = (_bits), \
.storagebits = 16, \
.shift = 4, \
.shift = 16 - (_bits), \
.endianness = IIO_CPU, \
}, \
.extend_name = _ext, \
}
static const struct iio_chan_spec xadc_channels[] = {
XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP),
XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
XADC_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
XADC_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
XADC_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
XADC_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
XADC_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
XADC_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
XADC_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
XADC_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
XADC_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
XADC_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
XADC_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
XADC_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
XADC_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
XADC_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
XADC_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
XADC_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
XADC_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
XADC_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
XADC_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
XADC_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
/* 7 Series */
#define XADC_7S_CHAN_TEMP(_chan, _scan_index, _addr) \
XADC_CHAN_TEMP(_chan, _scan_index, _addr, 12)
#define XADC_7S_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 12, _ext, _alarm)
static const struct iio_chan_spec xadc_7s_channels[] = {
XADC_7S_CHAN_TEMP(0, 8, XADC_REG_TEMP),
XADC_7S_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
XADC_7S_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
XADC_7S_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
XADC_7S_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
XADC_7S_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
XADC_7S_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
XADC_7S_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
XADC_7S_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
XADC_7S_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
XADC_7S_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
XADC_7S_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
XADC_7S_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
XADC_7S_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
XADC_7S_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
XADC_7S_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
XADC_7S_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
XADC_7S_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
XADC_7S_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
XADC_7S_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
XADC_7S_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
XADC_7S_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
XADC_7S_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
XADC_7S_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
XADC_7S_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
XADC_7S_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
};
/* UltraScale */
#define XADC_US_CHAN_TEMP(_chan, _scan_index, _addr) \
XADC_CHAN_TEMP(_chan, _scan_index, _addr, 10)
#define XADC_US_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 10, _ext, _alarm)
static const struct iio_chan_spec xadc_us_channels[] = {
XADC_US_CHAN_TEMP(0, 8, XADC_REG_TEMP),
XADC_US_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
XADC_US_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
XADC_US_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
XADC_US_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpsintlp", true),
XADC_US_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpsintfp", true),
XADC_US_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccpsaux", true),
XADC_US_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
XADC_US_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
XADC_US_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
XADC_US_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
XADC_US_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
XADC_US_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
XADC_US_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
XADC_US_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
XADC_US_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
XADC_US_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
XADC_US_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
XADC_US_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
XADC_US_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
XADC_US_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
XADC_US_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
XADC_US_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
XADC_US_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
XADC_US_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
XADC_US_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
};
static const struct iio_info xadc_info = {
@ -1083,8 +1168,16 @@ static const struct iio_info xadc_info = {
};
static const struct of_device_id xadc_of_match_table[] = {
{ .compatible = "xlnx,zynq-xadc-1.00.a", (void *)&xadc_zynq_ops },
{ .compatible = "xlnx,axi-xadc-1.00.a", (void *)&xadc_axi_ops },
{
.compatible = "xlnx,zynq-xadc-1.00.a",
.data = &xadc_zynq_ops
}, {
.compatible = "xlnx,axi-xadc-1.00.a",
.data = &xadc_7s_axi_ops
}, {
.compatible = "xlnx,system-management-wiz-1.3",
.data = &xadc_us_axi_ops
},
{ },
};
MODULE_DEVICE_TABLE(of, xadc_of_match_table);
@ -1094,8 +1187,10 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
{
struct device *dev = indio_dev->dev.parent;
struct xadc *xadc = iio_priv(indio_dev);
const struct iio_chan_spec *channel_templates;
struct iio_chan_spec *channels, *chan;
struct device_node *chan_node, *child;
unsigned int max_channels;
unsigned int num_channels;
const char *external_mux;
u32 ext_mux_chan;
@ -1136,9 +1231,15 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
*conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan);
}
channels = devm_kmemdup(dev, xadc_channels,
sizeof(xadc_channels), GFP_KERNEL);
if (xadc->ops->type == XADC_TYPE_S7) {
channel_templates = xadc_7s_channels;
max_channels = ARRAY_SIZE(xadc_7s_channels);
} else {
channel_templates = xadc_us_channels;
max_channels = ARRAY_SIZE(xadc_us_channels);
}
channels = devm_kmemdup(dev, channel_templates,
sizeof(channels[0]) * max_channels, GFP_KERNEL);
if (!channels)
return -ENOMEM;
@ -1148,7 +1249,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
chan_node = of_get_child_by_name(np, "xlnx,channels");
if (chan_node) {
for_each_child_of_node(chan_node, child) {
if (num_channels >= ARRAY_SIZE(xadc_channels)) {
if (num_channels >= max_channels) {
of_node_put(child);
break;
}
@ -1184,8 +1285,28 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
return 0;
}
static const char * const xadc_type_names[] = {
[XADC_TYPE_S7] = "xadc",
[XADC_TYPE_US] = "xilinx-system-monitor",
};
static void xadc_clk_disable_unprepare(void *data)
{
struct clk *clk = data;
clk_disable_unprepare(clk);
}
static void xadc_cancel_delayed_work(void *data)
{
struct delayed_work *work = data;
cancel_delayed_work_sync(work);
}
static int xadc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *id;
struct iio_dev *indio_dev;
unsigned int bipolar_mask;
@ -1195,10 +1316,10 @@ static int xadc_probe(struct platform_device *pdev)
int irq;
int i;
if (!pdev->dev.of_node)
if (!dev->of_node)
return -ENODEV;
id = of_match_node(xadc_of_match_table, pdev->dev.of_node);
id = of_match_node(xadc_of_match_table, dev->of_node);
if (!id)
return -EINVAL;
@ -1206,7 +1327,7 @@ static int xadc_probe(struct platform_device *pdev)
if (irq <= 0)
return -ENXIO;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*xadc));
indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc));
if (!indio_dev)
return -ENOMEM;
@ -1222,43 +1343,44 @@ static int xadc_probe(struct platform_device *pdev)
if (IS_ERR(xadc->base))
return PTR_ERR(xadc->base);
indio_dev->name = "xadc";
indio_dev->name = xadc_type_names[xadc->ops->type];
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &xadc_info;
ret = xadc_parse_dt(indio_dev, pdev->dev.of_node, &conf0);
ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0);
if (ret)
return ret;
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time, &xadc_trigger_handler,
&xadc_buffer_ops);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&xadc_trigger_handler,
&xadc_buffer_ops);
if (ret)
return ret;
xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst");
if (IS_ERR(xadc->convst_trigger)) {
ret = PTR_ERR(xadc->convst_trigger);
goto err_triggered_buffer_cleanup;
}
if (IS_ERR(xadc->convst_trigger))
return PTR_ERR(xadc->convst_trigger);
xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev,
"samplerate");
if (IS_ERR(xadc->samplerate_trigger)) {
ret = PTR_ERR(xadc->samplerate_trigger);
goto err_free_convst_trigger;
}
if (IS_ERR(xadc->samplerate_trigger))
return PTR_ERR(xadc->samplerate_trigger);
}
xadc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(xadc->clk)) {
ret = PTR_ERR(xadc->clk);
goto err_free_samplerate_trigger;
}
xadc->clk = devm_clk_get(dev, NULL);
if (IS_ERR(xadc->clk))
return PTR_ERR(xadc->clk);
ret = clk_prepare_enable(xadc->clk);
if (ret)
goto err_free_samplerate_trigger;
return ret;
ret = devm_add_action_or_reset(dev,
xadc_clk_disable_unprepare, xadc->clk);
if (ret)
return ret;
/*
* Make sure not to exceed the maximum samplerate since otherwise the
@ -1267,22 +1389,28 @@ static int xadc_probe(struct platform_device *pdev)
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
ret = xadc_read_samplerate(xadc);
if (ret < 0)
goto err_free_samplerate_trigger;
return ret;
if (ret > XADC_MAX_SAMPLERATE) {
ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE);
if (ret < 0)
goto err_free_samplerate_trigger;
return ret;
}
}
ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
dev_name(&pdev->dev), indio_dev);
ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0,
dev_name(dev), indio_dev);
if (ret)
goto err_clk_disable_unprepare;
return ret;
ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
&xadc->zynq_unmask_work);
if (ret)
return ret;
ret = xadc->ops->setup(pdev, indio_dev, xadc->irq);
if (ret)
goto err_free_irq;
return ret;
for (i = 0; i < 16; i++)
xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
@ -1290,7 +1418,7 @@ static int xadc_probe(struct platform_device *pdev)
ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0);
if (ret)
goto err_free_irq;
return ret;
bipolar_mask = 0;
for (i = 0; i < indio_dev->num_channels; i++) {
@ -1300,17 +1428,18 @@ static int xadc_probe(struct platform_device *pdev)
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask);
if (ret)
goto err_free_irq;
return ret;
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1),
bipolar_mask >> 16);
if (ret)
goto err_free_irq;
return ret;
/* Disable all alarms */
ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
XADC_CONF1_ALARM_MASK);
if (ret)
goto err_free_irq;
return ret;
/* Set thresholds to min/max */
for (i = 0; i < 16; i++) {
@ -1325,60 +1454,17 @@ static int xadc_probe(struct platform_device *pdev)
ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
xadc->threshold[i]);
if (ret)
goto err_free_irq;
return ret;
}
/* Go to non-buffered mode */
xadc_postdisable(indio_dev);
ret = iio_device_register(indio_dev);
if (ret)
goto err_free_irq;
platform_set_drvdata(pdev, indio_dev);
return 0;
err_free_irq:
free_irq(xadc->irq, indio_dev);
cancel_delayed_work_sync(&xadc->zynq_unmask_work);
err_clk_disable_unprepare:
clk_disable_unprepare(xadc->clk);
err_free_samplerate_trigger:
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
iio_trigger_free(xadc->samplerate_trigger);
err_free_convst_trigger:
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
iio_trigger_free(xadc->convst_trigger);
err_triggered_buffer_cleanup:
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
static int xadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct xadc *xadc = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
iio_trigger_free(xadc->samplerate_trigger);
iio_trigger_free(xadc->convst_trigger);
iio_triggered_buffer_cleanup(indio_dev);
}
free_irq(xadc->irq, indio_dev);
cancel_delayed_work_sync(&xadc->zynq_unmask_work);
clk_disable_unprepare(xadc->clk);
kfree(xadc->data);
return 0;
return devm_iio_device_register(dev, indio_dev);
}
static struct platform_driver xadc_driver = {
.probe = xadc_probe,
.remove = xadc_remove,
.driver = {
.name = "xadc",
.of_match_table = xadc_of_match_table,

View File

@ -155,9 +155,6 @@ err_out:
return ret;
}
/* Register value is msb aligned, the lower 4 bits are ignored */
#define XADC_THRESHOLD_VALUE_SHIFT 4
int xadc_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_info info,
@ -177,7 +174,8 @@ int xadc_read_event_value(struct iio_dev *indio_dev,
return -EINVAL;
}
*val >>= XADC_THRESHOLD_VALUE_SHIFT;
/* MSB aligned */
*val >>= 16 - chan->scan_type.realbits;
return IIO_VAL_INT;
}
@ -191,7 +189,8 @@ int xadc_write_event_value(struct iio_dev *indio_dev,
struct xadc *xadc = iio_priv(indio_dev);
int ret = 0;
val <<= XADC_THRESHOLD_VALUE_SHIFT;
/* MSB aligned */
val <<= 16 - chan->scan_type.realbits;
if (val < 0 || val > 0xffff)
return -EINVAL;

View File

@ -70,6 +70,11 @@ struct xadc {
int irq;
};
enum xadc_type {
XADC_TYPE_S7, /* Series 7 */
XADC_TYPE_US, /* UltraScale and UltraScale+ */
};
struct xadc_ops {
int (*read)(struct xadc *xadc, unsigned int reg, uint16_t *val);
int (*write)(struct xadc *xadc, unsigned int reg, uint16_t val);
@ -80,6 +85,7 @@ struct xadc_ops {
irqreturn_t (*interrupt_handler)(int irq, void *devid);
unsigned int flags;
enum xadc_type type;
};
static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,

View File

@ -479,7 +479,7 @@ static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
var4 = (var3 / (calib->res_heat_range + 4));
var5 = 131 * calib->res_heat_val + 65536;
heatr_res_x100 = ((var4 / var5) - 250) * 34;
heatr_res = (heatr_res_x100 + 50) / 100;
heatr_res = DIV_ROUND_CLOSEST(heatr_res_x100, 100);
return heatr_res;
}

View File

@ -282,7 +282,7 @@ static int pms7003_probe(struct serdev_device *serdev)
state->serdev = serdev;
indio_dev->info = &pms7003_info;
indio_dev->name = PMS7003_DRIVER_NAME;
indio_dev->channels = pms7003_channels,
indio_dev->channels = pms7003_channels;
indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->available_scan_masks = pms7003_scan_masks;

View File

@ -71,6 +71,8 @@ static struct {
{HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
{HID_USAGE_SENSOR_HINGE, 0, 0, 17453293},
{HID_USAGE_SENSOR_HINGE, HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},
};
static void simple_div(int dividend, int divisor, int *whole,

View File

@ -488,24 +488,20 @@ int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,
EXPORT_SYMBOL(ms_sensors_ht_read_humidity);
/**
* ms_sensors_tp_crc_valid() - CRC check function for
* ms_sensors_tp_crc4() - Calculate PROM CRC for
* Temperature and pressure devices.
* This function is only used when reading PROM coefficients
*
* @prom: pointer to PROM coefficients array
* @len: length of PROM coefficients array
*
* Return: True if CRC is ok.
* Return: CRC.
*/
static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
static u8 ms_sensors_tp_crc4(u16 *prom)
{
unsigned int cnt, n_bit;
u16 n_rem = 0x0000, crc_read = prom[0], crc = (*prom & 0xF000) >> 12;
u16 n_rem = 0x0000;
prom[len - 1] = 0;
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
for (cnt = 0; cnt < len * 2; cnt++) {
for (cnt = 0; cnt < MS_SENSORS_TP_PROM_WORDS_NB * 2; cnt++) {
if (cnt % 2 == 1)
n_rem ^= prom[cnt >> 1] & 0x00FF;
else
@ -518,10 +514,55 @@ static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
n_rem <<= 1;
}
}
n_rem >>= 12;
prom[0] = crc_read;
return n_rem == crc;
return n_rem >> 12;
}
/**
* ms_sensors_tp_crc_valid_112() - CRC check function for
* Temperature and pressure devices for 112bit PROM.
* This function is only used when reading PROM coefficients
*
* @prom: pointer to PROM coefficients array
*
* Return: True if CRC is ok.
*/
static bool ms_sensors_tp_crc_valid_112(u16 *prom)
{
u16 w0 = prom[0], crc_read = (w0 & 0xF000) >> 12;
u8 crc;
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
prom[MS_SENSORS_TP_PROM_WORDS_NB - 1] = 0;
crc = ms_sensors_tp_crc4(prom);
prom[0] = w0;
return crc == crc_read;
}
/**
* ms_sensors_tp_crc_valid_128() - CRC check function for
* Temperature and pressure devices for 128bit PROM.
* This function is only used when reading PROM coefficients
*
* @prom: pointer to PROM coefficients array
*
* Return: True if CRC is ok.
*/
static bool ms_sensors_tp_crc_valid_128(u16 *prom)
{
u16 w7 = prom[7], crc_read = w7 & 0x000F;
u8 crc;
prom[7] &= 0xFF00; /* Clear the CRC and LSB part */
crc = ms_sensors_tp_crc4(prom);
prom[7] = w7;
return crc == crc_read;
}
/**
@ -536,8 +577,9 @@ static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
{
int i, ret;
bool valid;
for (i = 0; i < MS_SENSORS_TP_PROM_WORDS_NB; i++) {
for (i = 0; i < dev_data->hw->prom_len; i++) {
ret = ms_sensors_read_prom_word(
dev_data->client,
MS_SENSORS_TP_PROM_READ + (i << 1),
@ -547,8 +589,12 @@ int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
return ret;
}
if (!ms_sensors_tp_crc_valid(dev_data->prom,
MS_SENSORS_TP_PROM_WORDS_NB + 1)) {
if (dev_data->hw->prom_len == 8)
valid = ms_sensors_tp_crc_valid_128(dev_data->prom);
else
valid = ms_sensors_tp_crc_valid_112(dev_data->prom);
if (!valid) {
dev_err(&dev_data->client->dev,
"Calibration coefficients crc check error\n");
return -ENODEV;

View File

@ -11,7 +11,7 @@
#include <linux/i2c.h>
#include <linux/mutex.h>
#define MS_SENSORS_TP_PROM_WORDS_NB 7
#define MS_SENSORS_TP_PROM_WORDS_NB 8
/**
* struct ms_ht_dev - Humidity/Temperature sensor device structure
@ -25,6 +25,16 @@ struct ms_ht_dev {
u8 res_index;
};
/**
* struct ms_hw_data - Temperature/Pressure sensor hardware data
* @prom_len: number of words in the PROM
* @max_res_index: maximum sensor resolution index
*/
struct ms_tp_hw_data {
u8 prom_len;
u8 max_res_index;
};
/**
* struct ms_tp_dev - Temperature/Pressure sensor device structure
* @client: i2c client
@ -36,7 +46,8 @@ struct ms_ht_dev {
struct ms_tp_dev {
struct i2c_client *client;
struct mutex lock;
u16 prom[MS_SENSORS_TP_PROM_WORDS_NB + 1];
const struct ms_tp_hw_data *hw;
u16 prom[MS_SENSORS_TP_PROM_WORDS_NB];
u8 res_index;
};

View File

@ -189,6 +189,16 @@ config AD5764
To compile this driver as a module, choose M here: the
module will be called ad5764.
config AD5766
tristate "Analog Devices AD5766/AD5767 DAC driver"
depends on SPI_MASTER
help
Say yes here to build support for Analog Devices AD5766, AD5767
Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5766.
config AD5770R
tristate "Analog Devices AD5770R IDAC driver"
depends on SPI_MASTER

View File

@ -19,6 +19,7 @@ obj-$(CONFIG_AD5755) += ad5755.o
obj-$(CONFIG_AD5755) += ad5758.o
obj-$(CONFIG_AD5761) += ad5761.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5766) += ad5766.o
obj-$(CONFIG_AD5770R) += ad5770r.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o

643
drivers/iio/dac/ad5766.c Normal file
View File

@ -0,0 +1,643 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices AD5766, AD5767
* Digital to Analog Converters driver
* Copyright 2019-2020 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <asm/unaligned.h>
#define AD5766_UPPER_WORD_SPI_MASK GENMASK(31, 16)
#define AD5766_LOWER_WORD_SPI_MASK GENMASK(15, 0)
#define AD5766_DITHER_SOURCE_MASK(ch) GENMASK(((2 * ch) + 1), (2 * ch))
#define AD5766_DITHER_SOURCE(ch, source) BIT((ch * 2) + source)
#define AD5766_DITHER_SCALE_MASK(x) AD5766_DITHER_SOURCE_MASK(x)
#define AD5766_DITHER_SCALE(ch, scale) (scale << (ch * 2))
#define AD5766_DITHER_ENABLE_MASK(ch) BIT(ch)
#define AD5766_DITHER_ENABLE(ch, state) ((!state) << ch)
#define AD5766_DITHER_INVERT_MASK(ch) BIT(ch)
#define AD5766_DITHER_INVERT(ch, state) (state << ch)
#define AD5766_CMD_NOP_MUX_OUT 0x00
#define AD5766_CMD_SDO_CNTRL 0x01
#define AD5766_CMD_WR_IN_REG(x) (0x10 | ((x) & GENMASK(3, 0)))
#define AD5766_CMD_WR_DAC_REG(x) (0x20 | ((x) & GENMASK(3, 0)))
#define AD5766_CMD_SW_LDAC 0x30
#define AD5766_CMD_SPAN_REG 0x40
#define AD5766_CMD_WR_PWR_DITHER 0x51
#define AD5766_CMD_WR_DAC_REG_ALL 0x60
#define AD5766_CMD_SW_FULL_RESET 0x70
#define AD5766_CMD_READBACK_REG(x) (0x80 | ((x) & GENMASK(3, 0)))
#define AD5766_CMD_DITHER_SIG_1 0x90
#define AD5766_CMD_DITHER_SIG_2 0xA0
#define AD5766_CMD_INV_DITHER 0xB0
#define AD5766_CMD_DITHER_SCALE_1 0xC0
#define AD5766_CMD_DITHER_SCALE_2 0xD0
#define AD5766_FULL_RESET_CODE 0x1234
enum ad5766_type {
ID_AD5766,
ID_AD5767,
};
enum ad5766_voltage_range {
AD5766_VOLTAGE_RANGE_M20V_0V,
AD5766_VOLTAGE_RANGE_M16V_to_0V,
AD5766_VOLTAGE_RANGE_M10V_to_0V,
AD5766_VOLTAGE_RANGE_M12V_to_14V,
AD5766_VOLTAGE_RANGE_M16V_to_10V,
AD5766_VOLTAGE_RANGE_M10V_to_6V,
AD5766_VOLTAGE_RANGE_M5V_to_5V,
AD5766_VOLTAGE_RANGE_M10V_to_10V,
};
/**
* struct ad5766_chip_info - chip specific information
* @num_channels: number of channels
* @channels: channel specification
*/
struct ad5766_chip_info {
unsigned int num_channels;
const struct iio_chan_spec *channels;
};
enum {
AD5766_DITHER_ENABLE,
AD5766_DITHER_INVERT,
AD5766_DITHER_SOURCE,
};
/*
* Dither signal can also be scaled.
* Available dither scale strings corresponding to "dither_scale" field in
* "struct ad5766_state".
*/
static const char * const ad5766_dither_scales[] = {
"1",
"0.75",
"0.5",
"0.25",
};
/**
* struct ad5766_state - driver instance specific data
* @spi: SPI device
* @lock: Lock used to restrict concurent access to SPI device
* @chip_info: Chip model specific constants
* @gpio_reset: Reset GPIO, used to reset the device
* @crt_range: Current selected output range
* @dither_enable: Power enable bit for each channel dither block (for
* example, D15 = DAC 15,D8 = DAC 8, and D0 = DAC 0)
* 0 - Normal operation, 1 - Power down
* @dither_invert: Inverts the dither signal applied to the selected DAC
* outputs
* @dither_source: Selects between 2 possible sources:
* 1: N0, 2: N1
* Two bits are used for each channel
* @dither_scale: Two bits are used for each of the 16 channels:
* 0: 1 SCALING, 1: 0.75 SCALING, 2: 0.5 SCALING,
* 3: 0.25 SCALING.
* @data: SPI transfer buffers
*/
struct ad5766_state {
struct spi_device *spi;
struct mutex lock;
const struct ad5766_chip_info *chip_info;
struct gpio_desc *gpio_reset;
enum ad5766_voltage_range crt_range;
u16 dither_enable;
u16 dither_invert;
u32 dither_source;
u32 dither_scale;
union {
u32 d32;
u16 w16[2];
u8 b8[4];
} data[3] ____cacheline_aligned;
};
struct ad5766_span_tbl {
int min;
int max;
};
static const struct ad5766_span_tbl ad5766_span_tbl[] = {
[AD5766_VOLTAGE_RANGE_M20V_0V] = {-20, 0},
[AD5766_VOLTAGE_RANGE_M16V_to_0V] = {-16, 0},
[AD5766_VOLTAGE_RANGE_M10V_to_0V] = {-10, 0},
[AD5766_VOLTAGE_RANGE_M12V_to_14V] = {-12, 14},
[AD5766_VOLTAGE_RANGE_M16V_to_10V] = {-16, 10},
[AD5766_VOLTAGE_RANGE_M10V_to_6V] = {-10, 6},
[AD5766_VOLTAGE_RANGE_M5V_to_5V] = {-5, 5},
[AD5766_VOLTAGE_RANGE_M10V_to_10V] = {-10, 10},
};
static int __ad5766_spi_read(struct ad5766_state *st, u8 dac, int *val)
{
int ret;
struct spi_transfer xfers[] = {
{
.tx_buf = &st->data[0].d32,
.bits_per_word = 8,
.len = 3,
.cs_change = 1,
}, {
.tx_buf = &st->data[1].d32,
.rx_buf = &st->data[2].d32,
.bits_per_word = 8,
.len = 3,
},
};
st->data[0].d32 = AD5766_CMD_READBACK_REG(dac);
st->data[1].d32 = AD5766_CMD_NOP_MUX_OUT;
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
if (ret)
return ret;
*val = st->data[2].w16[1];
return ret;
}
static int __ad5766_spi_write(struct ad5766_state *st, u8 command, u16 data)
{
st->data[0].b8[0] = command;
put_unaligned_be16(data, &st->data[0].b8[1]);
return spi_write(st->spi, &st->data[0].b8[0], 3);
}
static int ad5766_read(struct iio_dev *indio_dev, u8 dac, int *val)
{
struct ad5766_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
ret = __ad5766_spi_read(st, dac, val);
mutex_unlock(&st->lock);
return ret;
}
static int ad5766_write(struct iio_dev *indio_dev, u8 dac, u16 data)
{
struct ad5766_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
ret = __ad5766_spi_write(st, AD5766_CMD_WR_DAC_REG(dac), data);
mutex_unlock(&st->lock);
return ret;
}
static int ad5766_reset(struct ad5766_state *st)
{
int ret;
if (st->gpio_reset) {
gpiod_set_value_cansleep(st->gpio_reset, 1);
ndelay(100); /* t_reset >= 100ns */
gpiod_set_value_cansleep(st->gpio_reset, 0);
} else {
ret = __ad5766_spi_write(st, AD5766_CMD_SW_FULL_RESET,
AD5766_FULL_RESET_CODE);
if (ret < 0)
return ret;
}
/*
* Minimum time between a reset and the subsequent successful write is
* typically 25 ns
*/
ndelay(25);
return 0;
}
static int ad5766_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long m)
{
struct ad5766_state *st = iio_priv(indio_dev);
int ret;
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = ad5766_read(indio_dev, chan->address, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
*val = ad5766_span_tbl[st->crt_range].min;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = ad5766_span_tbl[st->crt_range].max -
ad5766_span_tbl[st->crt_range].min;
*val2 = st->chip_info->channels[0].scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int ad5766_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long info)
{
switch (info) {
case IIO_CHAN_INFO_RAW:
{
const int max_val = GENMASK(chan->scan_type.realbits - 1, 0);
if (val > max_val || val < 0)
return -EINVAL;
val <<= chan->scan_type.shift;
return ad5766_write(indio_dev, chan->address, val);
}
default:
return -EINVAL;
}
}
static const struct iio_info ad5766_info = {
.read_raw = ad5766_read_raw,
.write_raw = ad5766_write_raw,
};
static int ad5766_get_dither_source(struct iio_dev *dev,
const struct iio_chan_spec *chan)
{
struct ad5766_state *st = iio_priv(dev);
u32 source;
source = st->dither_source & AD5766_DITHER_SOURCE_MASK(chan->channel);
source = source >> (chan->channel * 2);
source -= 1;
return source;
}
static int ad5766_set_dither_source(struct iio_dev *dev,
const struct iio_chan_spec *chan,
unsigned int source)
{
struct ad5766_state *st = iio_priv(dev);
uint16_t val;
int ret;
st->dither_source &= ~AD5766_DITHER_SOURCE_MASK(chan->channel);
st->dither_source |= AD5766_DITHER_SOURCE(chan->channel, source);
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_source);
ret = ad5766_write(dev, AD5766_CMD_DITHER_SIG_1, val);
if (ret)
return ret;
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_source);
return ad5766_write(dev, AD5766_CMD_DITHER_SIG_2, val);
}
static int ad5766_get_dither_scale(struct iio_dev *dev,
const struct iio_chan_spec *chan)
{
struct ad5766_state *st = iio_priv(dev);
u32 scale;
scale = st->dither_scale & AD5766_DITHER_SCALE_MASK(chan->channel);
return (scale >> (chan->channel * 2));
}
static int ad5766_set_dither_scale(struct iio_dev *dev,
const struct iio_chan_spec *chan,
unsigned int scale)
{
int ret;
struct ad5766_state *st = iio_priv(dev);
uint16_t val;
st->dither_scale &= ~AD5766_DITHER_SCALE_MASK(chan->channel);
st->dither_scale |= AD5766_DITHER_SCALE(chan->channel, scale);
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_scale);
ret = ad5766_write(dev, AD5766_CMD_DITHER_SCALE_1, val);
if (ret)
return ret;
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_scale);
return ad5766_write(dev, AD5766_CMD_DITHER_SCALE_2, val);
}
static const struct iio_enum ad5766_dither_scale_enum = {
.items = ad5766_dither_scales,
.num_items = ARRAY_SIZE(ad5766_dither_scales),
.set = ad5766_set_dither_scale,
.get = ad5766_get_dither_scale,
};
static ssize_t ad5766_read_ext(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct ad5766_state *st = iio_priv(indio_dev);
switch (private) {
case AD5766_DITHER_ENABLE:
return sprintf(buf, "%u\n",
!(st->dither_enable & BIT(chan->channel)));
break;
case AD5766_DITHER_INVERT:
return sprintf(buf, "%u\n",
!!(st->dither_invert & BIT(chan->channel)));
break;
case AD5766_DITHER_SOURCE:
return sprintf(buf, "%d\n",
ad5766_get_dither_source(indio_dev, chan));
default:
return -EINVAL;
}
}
static ssize_t ad5766_write_ext(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct ad5766_state *st = iio_priv(indio_dev);
bool readin;
int ret;
ret = kstrtobool(buf, &readin);
if (ret)
return ret;
switch (private) {
case AD5766_DITHER_ENABLE:
st->dither_enable &= ~AD5766_DITHER_ENABLE_MASK(chan->channel);
st->dither_enable |= AD5766_DITHER_ENABLE(chan->channel,
readin);
ret = ad5766_write(indio_dev, AD5766_CMD_WR_PWR_DITHER,
st->dither_enable);
break;
case AD5766_DITHER_INVERT:
st->dither_invert &= ~AD5766_DITHER_INVERT_MASK(chan->channel);
st->dither_invert |= AD5766_DITHER_INVERT(chan->channel,
readin);
ret = ad5766_write(indio_dev, AD5766_CMD_INV_DITHER,
st->dither_invert);
break;
case AD5766_DITHER_SOURCE:
ret = ad5766_set_dither_source(indio_dev, chan, readin);
break;
default:
return -EINVAL;
}
return ret ? ret : len;
}
#define _AD5766_CHAN_EXT_INFO(_name, _what, _shared) { \
.name = _name, \
.read = ad5766_read_ext, \
.write = ad5766_write_ext, \
.private = _what, \
.shared = _shared, \
}
#define IIO_ENUM_AVAILABLE_SHARED(_name, _shared, _e) \
{ \
.name = (_name "_available"), \
.shared = _shared, \
.read = iio_enum_available_read, \
.private = (uintptr_t)(_e), \
}
static const struct iio_chan_spec_ext_info ad5766_ext_info[] = {
_AD5766_CHAN_EXT_INFO("dither_enable", AD5766_DITHER_ENABLE,
IIO_SEPARATE),
_AD5766_CHAN_EXT_INFO("dither_invert", AD5766_DITHER_INVERT,
IIO_SEPARATE),
_AD5766_CHAN_EXT_INFO("dither_source", AD5766_DITHER_SOURCE,
IIO_SEPARATE),
IIO_ENUM("dither_scale", IIO_SEPARATE, &ad5766_dither_scale_enum),
IIO_ENUM_AVAILABLE_SHARED("dither_scale",
IIO_SEPARATE,
&ad5766_dither_scale_enum),
{}
};
#define AD576x_CHANNEL(_chan, _bits) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.output = 1, \
.channel = (_chan), \
.address = (_chan), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_type = { \
.sign = 'u', \
.realbits = (_bits), \
.storagebits = 16, \
.shift = 16 - (_bits), \
}, \
.ext_info = ad5766_ext_info, \
}
#define DECLARE_AD576x_CHANNELS(_name, _bits) \
const struct iio_chan_spec _name[] = { \
AD576x_CHANNEL(0, (_bits)), \
AD576x_CHANNEL(1, (_bits)), \
AD576x_CHANNEL(2, (_bits)), \
AD576x_CHANNEL(3, (_bits)), \
AD576x_CHANNEL(4, (_bits)), \
AD576x_CHANNEL(5, (_bits)), \
AD576x_CHANNEL(6, (_bits)), \
AD576x_CHANNEL(7, (_bits)), \
AD576x_CHANNEL(8, (_bits)), \
AD576x_CHANNEL(9, (_bits)), \
AD576x_CHANNEL(10, (_bits)), \
AD576x_CHANNEL(11, (_bits)), \
AD576x_CHANNEL(12, (_bits)), \
AD576x_CHANNEL(13, (_bits)), \
AD576x_CHANNEL(14, (_bits)), \
AD576x_CHANNEL(15, (_bits)), \
}
static DECLARE_AD576x_CHANNELS(ad5766_channels, 16);
static DECLARE_AD576x_CHANNELS(ad5767_channels, 12);
static const struct ad5766_chip_info ad5766_chip_infos[] = {
[ID_AD5766] = {
.num_channels = ARRAY_SIZE(ad5766_channels),
.channels = ad5766_channels,
},
[ID_AD5767] = {
.num_channels = ARRAY_SIZE(ad5767_channels),
.channels = ad5767_channels,
},
};
static int ad5766_get_output_range(struct ad5766_state *st)
{
int i, ret, min, max, tmp[2];
ret = device_property_read_u32_array(&st->spi->dev,
"output-range-voltage",
tmp, 2);
if (ret)
return ret;
min = tmp[0] / 1000;
max = tmp[1] / 1000;
for (i = 0; i < ARRAY_SIZE(ad5766_span_tbl); i++) {
if (ad5766_span_tbl[i].min != min ||
ad5766_span_tbl[i].max != max)
continue;
st->crt_range = i;
return 0;
}
return -EINVAL;
}
static int ad5766_default_setup(struct ad5766_state *st)
{
uint16_t val;
int ret, i;
/* Always issue a reset before writing to the span register. */
ret = ad5766_reset(st);
if (ret)
return ret;
ret = ad5766_get_output_range(st);
if (ret)
return ret;
/* Dither power down */
st->dither_enable = GENMASK(15, 0);
ret = __ad5766_spi_write(st, AD5766_CMD_WR_PWR_DITHER,
st->dither_enable);
if (ret)
return ret;
st->dither_source = 0;
for (i = 0; i < ARRAY_SIZE(ad5766_channels); i++)
st->dither_source |= AD5766_DITHER_SOURCE(i, 0);
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_source);
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SIG_1, val);
if (ret)
return ret;
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_source);
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SIG_2, val);
if (ret)
return ret;
st->dither_scale = 0;
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_scale);
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SCALE_1, val);
if (ret)
return ret;
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_scale);
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SCALE_2, val);
if (ret)
return ret;
st->dither_invert = 0;
ret = __ad5766_spi_write(st, AD5766_CMD_INV_DITHER, st->dither_invert);
if (ret)
return ret;
return __ad5766_spi_write(st, AD5766_CMD_SPAN_REG, st->crt_range);
}
static int ad5766_probe(struct spi_device *spi)
{
enum ad5766_type type;
struct iio_dev *indio_dev;
struct ad5766_state *st;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
mutex_init(&st->lock);
st->spi = spi;
type = spi_get_device_id(spi)->driver_data;
st->chip_info = &ad5766_chip_infos[type];
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
indio_dev->info = &ad5766_info;
indio_dev->dev.parent = &spi->dev;
indio_dev->dev.of_node = spi->dev.of_node;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_reset))
return PTR_ERR(st->gpio_reset);
ret = ad5766_default_setup(st);
if (ret)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct of_device_id ad5766_dt_match[] = {
{ .compatible = "adi,ad5766" },
{ .compatible = "adi,ad5767" },
{}
};
MODULE_DEVICE_TABLE(of, ad5766_dt_match);
static const struct spi_device_id ad5766_spi_ids[] = {
{ "ad5766", ID_AD5766 },
{ "ad5767", ID_AD5767 },
{}
};
MODULE_DEVICE_TABLE(spi, ad5766_spi_ids);
static struct spi_driver ad5766_driver = {
.driver = {
.name = "ad5766",
.of_match_table = ad5766_dt_match,
},
.probe = ad5766_probe,
.id_table = ad5766_spi_ids,
};
module_spi_driver(ad5766_driver);
MODULE_AUTHOR("Denis-Gabriel Gheorghescu <denis.gheorghescu@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5766/AD5767 DACs");
MODULE_LICENSE("GPL v2");

View File

@ -582,8 +582,7 @@ error_disable_reg:
if (!IS_ERR(st->reg))
regulator_disable(st->reg);
error_disable_clk:
if (clk)
clk_disable_unprepare(clk);
clk_disable_unprepare(clk);
return ret;
}
@ -599,8 +598,7 @@ static int adf4350_remove(struct spi_device *spi)
iio_device_unregister(indio_dev);
if (st->clk)
clk_disable_unprepare(st->clk);
clk_disable_unprepare(st->clk);
if (!IS_ERR(reg))
regulator_disable(reg);

View File

@ -19,6 +19,7 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include "bmg160.h"
#define BMG160_IRQ_NAME "bmg160_event"
@ -92,6 +93,7 @@
struct bmg160_data {
struct regmap *regmap;
struct regulator_bulk_data regulators[2];
struct iio_trigger *dready_trig;
struct iio_trigger *motion_trig;
struct iio_mount_matrix orientation;
@ -1061,6 +1063,13 @@ static const char *bmg160_match_acpi_device(struct device *dev)
return dev_name(dev);
}
static void bmg160_disable_regulators(void *d)
{
struct bmg160_data *data = d;
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
}
int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
const char *name)
{
@ -1077,6 +1086,22 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
data->irq = irq;
data->regmap = regmap;
data->regulators[0].supply = "vdd";
data->regulators[1].supply = "vddio";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
data->regulators);
if (ret)
return dev_err_probe(dev, ret, "Failed to get regulators\n");
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
data->regulators);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, bmg160_disable_regulators, data);
if (ret)
return ret;
ret = iio_read_mount_matrix(dev, "mount-matrix",
&data->orientation);
if (ret)

View File

@ -23,15 +23,20 @@ enum gyro_3d_channel {
GYRO_3D_CHANNEL_MAX,
};
#define CHANNEL_SCAN_INDEX_TIMESTAMP GYRO_3D_CHANNEL_MAX
struct gyro_3d_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
struct {
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
u64 timestamp __aligned(8);
} scan;
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
s64 timestamp;
};
static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
@ -72,7 +77,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = {
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
.scan_index = CHANNEL_SCAN_INDEX_Z,
}
},
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
};
/* Adjust channel real bits based on report descriptor */
@ -178,14 +184,6 @@ static const struct iio_info gyro_3d_info = {
.write_raw = &gyro_3d_write_raw,
};
/* Function to push data to buffer */
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
int len)
{
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
iio_push_to_buffers(indio_dev, data);
}
/* Callback handler to send event after all samples are received and captured */
static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned usage_id,
@ -195,10 +193,15 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n");
if (atomic_read(&gyro_state->common_attributes.data_ready))
hid_sensor_push_data(indio_dev,
gyro_state->gyro_val,
sizeof(gyro_state->gyro_val));
if (atomic_read(&gyro_state->common_attributes.data_ready)) {
if (!gyro_state->timestamp)
gyro_state->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev, &gyro_state->scan,
gyro_state->timestamp);
gyro_state->timestamp = 0;
}
return 0;
}
@ -219,10 +222,15 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
*(u32 *)raw_data;
gyro_state->scan.gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
*(u32 *)raw_data;
ret = 0;
break;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
gyro_state->timestamp =
hid_sensor_convert_timestamp(&gyro_state->common_attributes,
*(s64 *)raw_data);
break;
default:
break;
}

View File

@ -16,8 +16,8 @@ config INV_MPU6050_I2C
select REGMAP_I2C
help
This driver supports the Invensense MPU6050/9150,
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
IAM20680 motion tracking devices over I2C.
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
and IAM20680 motion tracking devices over I2C.
This driver can be built as a module. The module will be called
inv-mpu6050-i2c.
@ -28,7 +28,7 @@ config INV_MPU6050_SPI
select REGMAP_SPI
help
This driver supports the Invensense MPU6000,
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
IAM20680 motion tracking devices over SPI.
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
and IAM20680 motion tracking devices over SPI.
This driver can be built as a module. The module will be called
inv-mpu6050-spi.

View File

@ -160,6 +160,14 @@ static const struct inv_mpu6050_hw hw_info[] = {
.fifo_size = 512,
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
},
{
.whoami = INV_MPU6880_WHOAMI_VALUE,
.name = "MPU6880",
.reg = &reg_set_6500,
.config = &chip_config_6500,
.fifo_size = 4096,
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
},
{
.whoami = INV_MPU6000_WHOAMI_VALUE,
.name = "MPU6000",
@ -1323,6 +1331,7 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
case INV_MPU6000:
case INV_MPU6500:
case INV_MPU6515:
case INV_MPU6880:
case INV_MPU9250:
case INV_MPU9255:
/* reset signal path (required for spi connection) */

View File

@ -177,6 +177,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
{"mpu6050", INV_MPU6050},
{"mpu6500", INV_MPU6500},
{"mpu6515", INV_MPU6515},
{"mpu6880", INV_MPU6880},
{"mpu9150", INV_MPU9150},
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
@ -204,6 +205,10 @@ static const struct of_device_id inv_of_match[] = {
.compatible = "invensense,mpu6515",
.data = (void *)INV_MPU6515
},
{
.compatible = "invensense,mpu6880",
.data = (void *)INV_MPU6880
},
{
.compatible = "invensense,mpu9150",
.data = (void *)INV_MPU9150

View File

@ -70,6 +70,7 @@ enum inv_devices {
INV_MPU6050,
INV_MPU6500,
INV_MPU6515,
INV_MPU6880,
INV_MPU6000,
INV_MPU9150,
INV_MPU9250,
@ -373,6 +374,7 @@ struct inv_mpu6050_state {
#define INV_MPU6000_WHOAMI_VALUE 0x68
#define INV_MPU6050_WHOAMI_VALUE 0x68
#define INV_MPU6500_WHOAMI_VALUE 0x70
#define INV_MPU6880_WHOAMI_VALUE 0x78
#define INV_MPU9150_WHOAMI_VALUE 0x68
#define INV_MPU9250_WHOAMI_VALUE 0x71
#define INV_MPU9255_WHOAMI_VALUE 0x73

View File

@ -70,6 +70,7 @@ static const struct spi_device_id inv_mpu_id[] = {
{"mpu6000", INV_MPU6000},
{"mpu6500", INV_MPU6500},
{"mpu6515", INV_MPU6515},
{"mpu6880", INV_MPU6880},
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
{"icm20608", INV_ICM20608},
@ -96,6 +97,10 @@ static const struct of_device_id inv_of_match[] = {
.compatible = "invensense,mpu6515",
.data = (void *)INV_MPU6515
},
{
.compatible = "invensense,mpu6880",
.data = (void *)INV_MPU6880
},
{
.compatible = "invensense,mpu9250",
.data = (void *)INV_MPU9250

View File

@ -169,6 +169,36 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient",
};
/**
* iio_sysfs_match_string_with_gaps - matches given string in an array with gaps
* @array: array of strings
* @n: number of strings in the array
* @str: string to match with
*
* Returns index of @str in the @array or -EINVAL, similar to match_string().
* Uses sysfs_streq instead of strcmp for matching.
*
* This routine will look for a string in an array of strings.
* The search will continue until the element is found or the n-th element
* is reached, regardless of any NULL elements in the array.
*/
static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n,
const char *str)
{
const char *item;
int index;
for (index = 0; index < n; index++) {
item = array[index];
if (!item)
continue;
if (sysfs_streq(item, str))
return index;
}
return -EINVAL;
}
#if defined(CONFIG_DEBUG_FS)
/*
* There's also a CONFIG_DEBUG_FS guard in include/linux/iio/iio.h for
@ -470,8 +500,11 @@ ssize_t iio_enum_available_read(struct iio_dev *indio_dev,
if (!e->num_items)
return 0;
for (i = 0; i < e->num_items; ++i)
for (i = 0; i < e->num_items; ++i) {
if (!e->items[i])
continue;
len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]);
}
/* replace last space with a newline */
buf[len - 1] = '\n';
@ -492,7 +525,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev,
i = e->get(indio_dev, chan);
if (i < 0)
return i;
else if (i >= e->num_items)
else if (i >= e->num_items || !e->items[i])
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%s\n", e->items[i]);
@ -509,7 +542,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
if (!e->set)
return -EINVAL;
ret = __sysfs_match_string(e->items, e->num_items, buf);
ret = iio_sysfs_match_string_with_gaps(e->items, e->num_items, buf);
if (ret < 0)
return ret;
@ -1473,11 +1506,14 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
goto error_clear_attrs;
}
/* Copy across original attributes */
if (indio_dev->info->attrs)
if (indio_dev->info->attrs) {
memcpy(iio_dev_opaque->chan_attr_group.attrs,
indio_dev->info->attrs->attrs,
sizeof(iio_dev_opaque->chan_attr_group.attrs[0])
*attrcount_orig);
iio_dev_opaque->chan_attr_group.is_visible =
indio_dev->info->attrs->is_visible;
}
attrn = attrcount_orig;
/* Add all elements from the list. */
list_for_each_entry(p, &iio_dev_opaque->channel_attr_list, l)

View File

@ -191,8 +191,8 @@ err_free_channel:
return ERR_PTR(err);
}
static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
const char *name)
struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
const char *name)
{
struct iio_channel *chan = NULL;
@ -230,6 +230,7 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
return chan;
}
EXPORT_SYMBOL_GPL(of_iio_channel_get_by_name);
static struct iio_channel *of_iio_channel_get_all(struct device *dev)
{
@ -272,12 +273,6 @@ error_free_chans:
#else /* CONFIG_OF */
static inline struct iio_channel *
of_iio_channel_get_by_name(struct device_node *np, const char *name)
{
return NULL;
}
static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
{
return NULL;
@ -393,6 +388,29 @@ struct iio_channel *devm_iio_channel_get(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_iio_channel_get);
struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev,
struct device_node *np,
const char *channel_name)
{
struct iio_channel **ptr, *channel;
ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
channel = of_iio_channel_get_by_name(np, channel_name);
if (IS_ERR(channel)) {
devres_free(ptr);
return channel;
}
*ptr = channel;
devres_add(dev, ptr);
return channel;
}
EXPORT_SYMBOL_GPL(devm_of_iio_channel_get_by_name);
struct iio_channel *iio_channel_get_all(struct device *dev)
{
const char *name;

View File

@ -8,6 +8,7 @@
* TODO: gesture + proximity calib offsets
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@ -1113,6 +1114,12 @@ static const struct i2c_device_id apds9960_id[] = {
};
MODULE_DEVICE_TABLE(i2c, apds9960_id);
static const struct acpi_device_id apds9960_acpi_match[] = {
{ "MSHW0184" },
{ }
};
MODULE_DEVICE_TABLE(acpi, apds9960_acpi_match);
static const struct of_device_id apds9960_of_match[] = {
{ .compatible = "avago,apds9960" },
{ }
@ -1124,6 +1131,7 @@ static struct i2c_driver apds9960_driver = {
.name = APDS9960_DRV_NAME,
.of_match_table = apds9960_of_match,
.pm = &apds9960_pm_ops,
.acpi_match_table = apds9960_acpi_match,
},
.probe = apds9960_probe,
.remove = apds9960_remove,

View File

@ -22,15 +22,21 @@ enum {
CHANNEL_SCAN_INDEX_MAX
};
#define CHANNEL_SCAN_INDEX_TIMESTAMP 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[CHANNEL_SCAN_INDEX_MAX];
struct {
u32 illum[CHANNEL_SCAN_INDEX_MAX];
u64 timestamp __aligned(8);
} scan;
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
s64 timestamp;
};
/* Channel definitions */
@ -54,7 +60,8 @@ static const struct iio_chan_spec als_channels[] = {
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
}
},
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
};
/* Adjust channel real bits based on report descriptor */
@ -168,14 +175,6 @@ static const struct iio_info als_info = {
.write_raw = &als_write_raw,
};
/* Function to push data to buffer */
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
int len)
{
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
iio_push_to_buffers(indio_dev, data);
}
/* Callback handler to send event after all samples are received and captured */
static int als_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned usage_id,
@ -185,10 +184,14 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev,
struct als_state *als_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "als_proc_event\n");
if (atomic_read(&als_state->common_attributes.data_ready))
hid_sensor_push_data(indio_dev,
&als_state->illum,
sizeof(als_state->illum));
if (atomic_read(&als_state->common_attributes.data_ready)) {
if (!als_state->timestamp)
als_state->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev, &als_state->scan,
als_state->timestamp);
als_state->timestamp = 0;
}
return 0;
}
@ -206,10 +209,14 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
switch (usage_id) {
case HID_USAGE_SENSOR_LIGHT_ILLUM:
als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
als_state->scan.illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
ret = 0;
break;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes,
*(s64 *)raw_data);
break;
default:
break;
}

View File

@ -285,7 +285,7 @@ static int tsl2583_get_lux(struct iio_dev *indio_dev)
lux64 = lux64 * chip->als_settings.als_gain_trim;
lux64 >>= 13;
lux = lux64;
lux = (lux + 500) / 1000;
lux = DIV_ROUND_CLOSEST(lux, 1000);
if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */
return_max:
@ -361,12 +361,12 @@ static int tsl2583_set_als_time(struct tsl2583_chip *chip)
u8 val;
/* determine als integration register */
als_count = (chip->als_settings.als_time * 100 + 135) / 270;
als_count = DIV_ROUND_CLOSEST(chip->als_settings.als_time * 100, 270);
if (!als_count)
als_count = 1; /* ensure at least one cycle */
/* convert back to time (encompasses overrides) */
als_time = (als_count * 27 + 5) / 10;
als_time = DIV_ROUND_CLOSEST(als_count * 27, 10);
val = 256 - als_count;
ret = i2c_smbus_write_byte_data(chip->client,
@ -380,7 +380,7 @@ static int tsl2583_set_als_time(struct tsl2583_chip *chip)
/* set chip struct re scaling and saturation */
chip->als_saturation = als_count * 922; /* 90% of full scale */
chip->als_time_scale = (als_time + 25) / 50;
chip->als_time_scale = DIV_ROUND_CLOSEST(als_time, 50);
return ret;
}

View File

@ -392,7 +392,7 @@ static int vl6180_set_it(struct vl6180_data *data, int val, int val2)
{
int ret, it_ms;
it_ms = (val2 + 500) / 1000; /* round to ms */
it_ms = DIV_ROUND_CLOSEST(val2, 1000); /* round to ms */
if (val != 0 || it_ms < 1 || it_ms > 512)
return -EINVAL;

View File

@ -205,4 +205,19 @@ config SENSORS_RM3100_SPI
To compile this driver as a module, choose M here: the module
will be called rm3100-spi.
config YAMAHA_YAS530
tristate "Yamaha YAS530 family of 3-Axis Magnetometers (I2C)"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here to add support for the Yamaha YAS530 series of
3-Axis Magnetometers. Right now YAS530, YAS532 and YAS533 are
fully supported.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
will be called yamaha-yas.
endmenu

View File

@ -28,3 +28,5 @@ obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o

View File

@ -25,6 +25,7 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include "bmc150_magn.h"
@ -135,6 +136,7 @@ struct bmc150_magn_data {
*/
struct mutex mutex;
struct regmap *regmap;
struct regulator_bulk_data regulators[2];
struct iio_mount_matrix orientation;
/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
s32 buffer[6];
@ -692,12 +694,24 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
int ret, chip_id;
struct bmc150_magn_preset preset;
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
data->regulators);
if (ret < 0) {
dev_err(data->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
/*
* 3ms power-on time according to datasheet, let's better
* be safe than sorry and set this delay to 5ms.
*/
msleep(5);
ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND,
false);
if (ret < 0) {
dev_err(data->dev,
"Failed to bring up device from suspend mode\n");
return ret;
goto err_regulator_disable;
}
ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id);
@ -752,6 +766,8 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
err_poweroff:
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
err_regulator_disable:
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
return ret;
}
@ -867,6 +883,13 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
data->irq = irq;
data->dev = dev;
data->regulators[0].supply = "vdd";
data->regulators[1].supply = "vddio";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
data->regulators);
if (ret)
return dev_err_probe(dev, ret, "failed to get regulators\n");
ret = iio_read_mount_matrix(dev, "mount-matrix",
&data->orientation);
if (ret)
@ -984,6 +1007,7 @@ int bmc150_magn_remove(struct device *dev)
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
mutex_unlock(&data->mutex);
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
return 0;
}
EXPORT_SYMBOL(bmc150_magn_remove);

View File

@ -24,6 +24,7 @@ enum magn_3d_channel {
CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
CHANNEL_SCAN_INDEX_NORTH_MAGN,
CHANNEL_SCAN_INDEX_NORTH_TRUE,
CHANNEL_SCAN_INDEX_TIMESTAMP,
MAGN_3D_CHANNEL_MAX,
};
@ -47,6 +48,7 @@ struct magn_3d_state {
struct common_attributes magn_flux_attr;
struct common_attributes rot_attr;
s64 timestamp;
};
static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
@ -57,6 +59,7 @@ static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,
HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,
HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
HID_USAGE_SENSOR_TIME_TIMESTAMP,
};
/* Channel definitions */
@ -124,7 +127,8 @@ static const struct iio_chan_spec magn_3d_channels[] = {
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
}
},
IIO_CHAN_SOFT_TIMESTAMP(7)
};
/* Adjust channel real bits based on report descriptor */
@ -273,13 +277,6 @@ static const struct iio_info magn_3d_info = {
.write_raw = &magn_3d_write_raw,
};
/* Function to push data to buffer */
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data)
{
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
iio_push_to_buffers(indio_dev, data);
}
/* Callback handler to send event after all samples are received and captured */
static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned usage_id,
@ -289,8 +286,15 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
struct magn_3d_state *magn_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
if (atomic_read(&magn_state->magn_flux_attributes.data_ready))
hid_sensor_push_data(indio_dev, magn_state->iio_vals);
if (atomic_read(&magn_state->magn_flux_attributes.data_ready)) {
if (!magn_state->timestamp)
magn_state->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev,
magn_state->iio_vals,
magn_state->timestamp);
magn_state->timestamp = 0;
}
return 0;
}
@ -321,6 +325,11 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
+ CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
break;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
magn_state->timestamp =
hid_sensor_convert_timestamp(&magn_state->magn_flux_attributes,
*(s64 *)raw_data);
return ret;
default:
return -EINVAL;
}
@ -386,9 +395,10 @@ static int magn_3d_parse_report(struct platform_device *pdev,
return -ENOMEM;
}
st->iio_vals = devm_kcalloc(&pdev->dev, attr_count,
sizeof(u32),
GFP_KERNEL);
/* attr_count include timestamp channel, and the iio_vals should be aligned to 8byte */
st->iio_vals = devm_kcalloc(&pdev->dev,
((attr_count + 1) % 2 + (attr_count + 1) / 2) * 2,
sizeof(u32), GFP_KERNEL);
if (!st->iio_vals) {
dev_err(&pdev->dev,
"failed to allocate space for iio values array\n");
@ -404,11 +414,13 @@ static int magn_3d_parse_report(struct platform_device *pdev,
(_channels[*chan_count]).scan_index = *chan_count;
(_channels[*chan_count]).address = i;
/* Set magn_val_addr to iio value address */
st->magn_val_addr[i] = &(st->iio_vals[*chan_count]);
magn_3d_adjust_channel_bit_mask(_channels,
*chan_count,
st->magn[i].size);
if (i != CHANNEL_SCAN_INDEX_TIMESTAMP) {
/* Set magn_val_addr to iio value address */
st->magn_val_addr[i] = &st->iio_vals[*chan_count];
magn_3d_adjust_channel_bit_mask(_channels,
*chan_count,
st->magn[i].size);
}
(*chan_count)++;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -24,15 +24,21 @@ enum incl_3d_channel {
INCLI_3D_CHANNEL_MAX,
};
#define CHANNEL_SCAN_INDEX_TIMESTAMP INCLI_3D_CHANNEL_MAX
struct incl_3d_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX];
u32 incl_val[INCLI_3D_CHANNEL_MAX];
struct {
u32 incl_val[INCLI_3D_CHANNEL_MAX];
u64 timestamp __aligned(8);
} scan;
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
s64 timestamp;
};
static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = {
@ -73,7 +79,8 @@ static const struct iio_chan_spec incl_3d_channels[] = {
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
.scan_index = CHANNEL_SCAN_INDEX_Z,
}
},
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
};
/* Adjust channel real bits based on report descriptor */
@ -178,13 +185,6 @@ static const struct iio_info incl_3d_info = {
.write_raw = &incl_3d_write_raw,
};
/* Function to push data to buffer */
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
{
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
iio_push_to_buffers(indio_dev, (u8 *)data);
}
/* Callback handler to send event after all samples are received and captured */
static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned usage_id,
@ -194,10 +194,16 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
struct incl_3d_state *incl_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n");
if (atomic_read(&incl_state->common_attributes.data_ready))
hid_sensor_push_data(indio_dev,
(u8 *)incl_state->incl_val,
sizeof(incl_state->incl_val));
if (atomic_read(&incl_state->common_attributes.data_ready)) {
if (!incl_state->timestamp)
incl_state->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev,
&incl_state->scan,
incl_state->timestamp);
incl_state->timestamp = 0;
}
return 0;
}
@ -214,13 +220,18 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
switch (usage_id) {
case HID_USAGE_SENSOR_ORIENT_TILT_X:
incl_state->incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data;
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data;
break;
case HID_USAGE_SENSOR_ORIENT_TILT_Y:
incl_state->incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data;
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data;
break;
case HID_USAGE_SENSOR_ORIENT_TILT_Z:
incl_state->incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data;
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data;
break;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
incl_state->timestamp =
hid_sensor_convert_timestamp(&incl_state->common_attributes,
*(s64 *)raw_data);
break;
default:
ret = -EINVAL;

View File

@ -20,11 +20,15 @@ struct dev_rot_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
struct hid_sensor_hub_attribute_info quaternion;
u32 sampled_vals[4];
struct {
u32 sampled_vals[4] __aligned(16);
u64 timestamp __aligned(8);
} scan;
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
s64 timestamp;
};
/* Channel definitions */
@ -37,8 +41,10 @@ static const struct iio_chan_spec dev_rot_channels[] = {
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_HYSTERESIS)
}
BIT(IIO_CHAN_INFO_HYSTERESIS),
.scan_index = 0
},
IIO_CHAN_SOFT_TIMESTAMP(1)
};
/* Adjust channel real bits based on report descriptor */
@ -70,7 +76,7 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
if (size >= 4) {
for (i = 0; i < 4; ++i)
vals[i] = rot_state->sampled_vals[i];
vals[i] = rot_state->scan.sampled_vals[i];
ret_type = IIO_VAL_INT_MULTIPLE;
*val_len = 4;
} else
@ -132,15 +138,6 @@ static const struct iio_info dev_rot_info = {
.write_raw = &dev_rot_write_raw,
};
/* Function to push data to buffer */
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
{
dev_dbg(&indio_dev->dev, "hid_sensor_push_data >>\n");
iio_push_to_buffers(indio_dev, (u8 *)data);
dev_dbg(&indio_dev->dev, "hid_sensor_push_data <<\n");
}
/* Callback handler to send event after all samples are received and captured */
static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned usage_id,
@ -150,10 +147,15 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
struct dev_rot_state *rot_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n");
if (atomic_read(&rot_state->common_attributes.data_ready))
hid_sensor_push_data(indio_dev,
(u8 *)rot_state->sampled_vals,
sizeof(rot_state->sampled_vals));
if (atomic_read(&rot_state->common_attributes.data_ready)) {
if (!rot_state->timestamp)
rot_state->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan,
rot_state->timestamp);
rot_state->timestamp = 0;
}
return 0;
}
@ -168,10 +170,14 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
struct dev_rot_state *rot_state = iio_priv(indio_dev);
if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
memcpy(rot_state->sampled_vals, raw_data,
sizeof(rot_state->sampled_vals));
memcpy(&rot_state->scan.sampled_vals, raw_data,
sizeof(rot_state->scan.sampled_vals));
dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len,
sizeof(rot_state->sampled_vals));
sizeof(rot_state->scan.sampled_vals));
} else if (usage_id == HID_USAGE_SENSOR_TIME_TIMESTAMP) {
rot_state->timestamp = hid_sensor_convert_timestamp(&rot_state->common_attributes,
*(s64 *)raw_data);
}
return 0;

View File

@ -16,4 +16,20 @@ config IQS624_POS
To compile this driver as a module, choose M here: the module
will be called iqs624-pos.
config HID_SENSOR_CUSTOM_INTEL_HINGE
depends on HID_SENSOR_HUB
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select HID_SENSOR_IIO_COMMON
select HID_SENSOR_IIO_TRIGGER
tristate "HID Hinge"
help
This sensor present three angles, hinge angel, screen angles
and keyboard angle respect to horizon (ground).
Say yes here to build support for the HID custom
intel hinge sensor.
To compile this driver as a module, choose M here: the
module will be called hid-sensor-custom-hinge.
endmenu

View File

@ -4,4 +4,5 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_HID_SENSOR_CUSTOM_INTEL_HINGE) += hid-sensor-custom-intel-hinge.o
obj-$(CONFIG_IQS624_POS) += iqs624-pos.o

View File

@ -0,0 +1,385 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2020, Intel Corporation.
*/
#include <linux/hid-sensor-hub.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/platform_device.h>
#include "../common/hid-sensors/hid-sensor-trigger.h"
enum hinge_channel {
CHANNEL_SCAN_INDEX_HINGE_ANGLE,
CHANNEL_SCAN_INDEX_SCREEN_ANGLE,
CHANNEL_SCAN_INDEX_KEYBOARD_ANGLE,
CHANNEL_SCAN_INDEX_MAX,
};
#define CHANNEL_SCAN_INDEX_TIMESTAMP CHANNEL_SCAN_INDEX_MAX
static const u32 hinge_addresses[CHANNEL_SCAN_INDEX_MAX] = {
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1),
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(2),
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(3)
};
static const char *const hinge_labels[CHANNEL_SCAN_INDEX_MAX] = { "hinge",
"screen",
"keyboard" };
struct hinge_state {
struct iio_dev *indio_dev;
struct hid_sensor_hub_attribute_info hinge[CHANNEL_SCAN_INDEX_MAX];
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
const char *labels[CHANNEL_SCAN_INDEX_MAX];
struct {
u32 hinge_val[3];
u64 timestamp __aligned(8);
} scan;
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
u64 timestamp;
};
/* Channel definitions */
static const struct iio_chan_spec hinge_channels[] = {
{
.type = IIO_ANGL,
.indexed = 1,
.channel = 0,
.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_HINGE_ANGLE,
.scan_type = {
.sign = 's',
.storagebits = 32,
},
}, {
.type = IIO_ANGL,
.indexed = 1,
.channel = 1,
.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_SCREEN_ANGLE,
.scan_type = {
.sign = 's',
.storagebits = 32,
},
}, {
.type = IIO_ANGL,
.indexed = 1,
.channel = 2,
.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_KEYBOARD_ANGLE,
.scan_type = {
.sign = 's',
.storagebits = 32,
},
},
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
};
/* Adjust channel real bits based on report descriptor */
static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels,
int channel, int size)
{
channels[channel].scan_type.realbits = size * 8;
}
/* Channel read_raw handler */
static int hinge_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
{
struct hinge_state *st = iio_priv(indio_dev);
struct hid_sensor_hub_device *hsdev;
int report_id;
s32 min;
hsdev = st->common_attributes.hsdev;
switch (mask) {
case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&st->common_attributes, true);
report_id = st->hinge[chan->scan_index].report_id;
min = st->hinge[chan->scan_index].logical_minimum;
if (report_id < 0) {
hid_sensor_power_state(&st->common_attributes, false);
return -EINVAL;
}
*val = sensor_hub_input_attr_get_raw_value(st->common_attributes.hsdev,
hsdev->usage,
hinge_addresses[chan->scan_index],
report_id,
SENSOR_HUB_SYNC, min < 0);
hid_sensor_power_state(&st->common_attributes, false);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = st->scale_pre_decml;
*val2 = st->scale_post_decml;
return st->scale_precision;
case IIO_CHAN_INFO_OFFSET:
*val = st->value_offset;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
return hid_sensor_read_samp_freq_value(&st->common_attributes,
val, val2);
case IIO_CHAN_INFO_HYSTERESIS:
return hid_sensor_read_raw_hyst_value(&st->common_attributes,
val, val2);
default:
return -EINVAL;
}
}
/* Channel write_raw handler */
static int hinge_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
{
struct hinge_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return hid_sensor_write_samp_freq_value(&st->common_attributes,
val, val2);
case IIO_CHAN_INFO_HYSTERESIS:
return hid_sensor_write_raw_hyst_value(&st->common_attributes,
val, val2);
default:
return -EINVAL;
}
}
static int hinge_read_label(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, char *label)
{
struct hinge_state *st = iio_priv(indio_dev);
return sprintf(label, "%s\n", st->labels[chan->channel]);
}
static const struct iio_info hinge_info = {
.read_raw = hinge_read_raw,
.write_raw = hinge_write_raw,
.read_label = hinge_read_label,
};
/*
* Callback handler to send event after all samples are received
* and captured.
*/
static int hinge_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned int usage_id, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct hinge_state *st = iio_priv(indio_dev);
if (atomic_read(&st->common_attributes.data_ready)) {
if (!st->timestamp)
st->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev, &st->scan,
st->timestamp);
st->timestamp = 0;
}
return 0;
}
/* Capture samples in local storage */
static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev,
unsigned int usage_id, size_t raw_len,
char *raw_data, void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct hinge_state *st = iio_priv(indio_dev);
int offset;
switch (usage_id) {
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1):
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(2):
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(3):
offset = usage_id - HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1);
st->scan.hinge_val[offset] = *(u32 *)raw_data;
return 0;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
st->timestamp = hid_sensor_convert_timestamp(&st->common_attributes,
*(int64_t *)raw_data);
return 0;
default:
return -EINVAL;
}
}
/* Parse report which is specific to an usage id */
static int hinge_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
struct iio_chan_spec *channels,
unsigned int usage_id, struct hinge_state *st)
{
int ret;
int i;
for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; ++i) {
ret = sensor_hub_input_get_attribute_info(hsdev,
HID_INPUT_REPORT,
usage_id,
hinge_addresses[i],
&st->hinge[i]);
if (ret < 0)
return ret;
hinge_adjust_channel_realbits(channels, i, st->hinge[i].size);
}
st->scale_precision = hid_sensor_format_scale(HID_USAGE_SENSOR_HINGE,
&st->hinge[CHANNEL_SCAN_INDEX_HINGE_ANGLE],
&st->scale_pre_decml, &st->scale_post_decml);
/* Set Sensitivity field ids, when there is no individual modifier */
if (st->common_attributes.sensitivity.index < 0) {
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1),
&st->common_attributes.sensitivity);
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
st->common_attributes.sensitivity.index,
st->common_attributes.sensitivity.report_id);
}
return ret;
}
/* Function to initialize the processing for usage id */
static int hid_hinge_probe(struct platform_device *pdev)
{
struct hinge_state *st;
struct iio_dev *indio_dev;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
int ret;
int i;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
st = iio_priv(indio_dev);
st->common_attributes.hsdev = hsdev;
st->common_attributes.pdev = pdev;
st->indio_dev = indio_dev;
for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; i++)
st->labels[i] = hinge_labels[i];
ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
&st->common_attributes);
if (ret) {
dev_err(&pdev->dev, "failed to setup common attributes\n");
return ret;
}
indio_dev->num_channels = ARRAY_SIZE(hinge_channels);
indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels,
sizeof(hinge_channels), GFP_KERNEL);
if (!indio_dev->channels)
return -ENOMEM;
ret = hinge_parse_report(pdev, hsdev,
(struct iio_chan_spec *)indio_dev->channels,
hsdev->usage, st);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
return ret;
}
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &hinge_info;
indio_dev->name = "hinge";
indio_dev->modes = INDIO_DIRECT_MODE;
atomic_set(&st->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, indio_dev->name,
&st->common_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
st->callbacks.send_event = hinge_proc_event;
st->callbacks.capture_sample = hinge_capture_sample;
st->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, hsdev->usage, &st->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
goto error_remove_trigger;
}
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "device register failed\n");
goto error_remove_callback;
}
return ret;
error_remove_callback:
sensor_hub_remove_callback(hsdev, hsdev->usage);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
return ret;
}
/* Function to deinitialize the processing for usage id */
static int hid_hinge_remove(struct platform_device *pdev)
{
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hinge_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
sensor_hub_remove_callback(hsdev, hsdev->usage);
hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
return 0;
}
static const struct platform_device_id hid_hinge_ids[] = {
{
/* Format: HID-SENSOR-INT-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-INT-020b",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_hinge_ids);
static struct platform_driver hid_hinge_platform_driver = {
.id_table = hid_hinge_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_hinge_probe,
.remove = hid_hinge_remove,
};
module_platform_driver(hid_hinge_platform_driver);
MODULE_DESCRIPTION("HID Sensor INTEL Hinge");
MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>");
MODULE_LICENSE("GPL");

View File

@ -30,9 +30,25 @@
#include "../common/ms_sensors/ms_sensors_i2c.h"
struct ms_tp_data {
const char *name;
const struct ms_tp_hw_data *hw;
};
static const int ms5637_samp_freq[6] = { 960, 480, 240, 120, 60, 30 };
/* String copy of the above const for readability purpose */
static const char ms5637_show_samp_freq[] = "960 480 240 120 60 30";
static ssize_t ms5637_show_samp_freq(struct device *dev, struct device_attribute *attr, char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ms_tp_dev *dev_data = iio_priv(indio_dev);
int i, len = 0;
for (i = 0; i <= dev_data->hw->max_res_index; i++)
len += sysfs_emit_at(buf, len, "%u ", ms5637_samp_freq[i]);
sysfs_emit_at(buf, len - 1, "\n");
return len;
}
static int ms5637_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
@ -109,10 +125,10 @@ static const struct iio_chan_spec ms5637_channels[] = {
}
};
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
static struct attribute *ms5637_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
@ -129,6 +145,7 @@ static const struct iio_info ms5637_info = {
static int ms5637_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct ms_tp_data *data;
struct ms_tp_dev *dev_data;
struct iio_dev *indio_dev;
int ret;
@ -142,17 +159,25 @@ static int ms5637_probe(struct i2c_client *client,
return -EOPNOTSUPP;
}
if (id)
data = (const struct ms_tp_data *)id->driver_data;
else
data = device_get_match_data(&client->dev);
if (!data)
return -EINVAL;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
if (!indio_dev)
return -ENOMEM;
dev_data = iio_priv(indio_dev);
dev_data->client = client;
dev_data->res_index = 5;
dev_data->res_index = data->hw->max_res_index;
dev_data->hw = data->hw;
mutex_init(&dev_data->lock);
indio_dev->info = &ms5637_info;
indio_dev->name = id->name;
indio_dev->name = data->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ms5637_channels;
indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
@ -170,20 +195,44 @@ static int ms5637_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct ms_tp_hw_data ms5637_hw_data = {
.prom_len = 7,
.max_res_index = 5
};
static const struct ms_tp_hw_data ms5803_hw_data = {
.prom_len = 8,
.max_res_index = 4
};
static const struct ms_tp_data ms5637_data = { .name = "ms5637", .hw = &ms5637_hw_data };
static const struct ms_tp_data ms5803_data = { .name = "ms5803", .hw = &ms5803_hw_data };
static const struct ms_tp_data ms5805_data = { .name = "ms5805", .hw = &ms5637_hw_data };
static const struct ms_tp_data ms5837_data = { .name = "ms5837", .hw = &ms5637_hw_data };
static const struct ms_tp_data ms8607_data = {
.name = "ms8607-temppressure",
.hw = &ms5637_hw_data,
};
static const struct i2c_device_id ms5637_id[] = {
{"ms5637", 0},
{"ms5805", 0},
{"ms5837", 0},
{"ms8607-temppressure", 0},
{"ms5637", (kernel_ulong_t)&ms5637_data },
{"ms5805", (kernel_ulong_t)&ms5805_data },
{"ms5837", (kernel_ulong_t)&ms5837_data },
{"ms8607-temppressure", (kernel_ulong_t)&ms8607_data },
{}
};
MODULE_DEVICE_TABLE(i2c, ms5637_id);
static const struct of_device_id ms5637_of_match[] = {
{ .compatible = "meas,ms5637", },
{ .compatible = "meas,ms5805", },
{ .compatible = "meas,ms5837", },
{ .compatible = "meas,ms8607-temppressure", },
{ .compatible = "meas,ms5637", .data = &ms5637_data },
{ .compatible = "meas,ms5803", .data = &ms5803_data },
{ .compatible = "meas,ms5805", .data = &ms5805_data },
{ .compatible = "meas,ms5837", .data = &ms5837_data },
{ .compatible = "meas,ms8607-temppressure", .data = &ms8607_data },
{ },
};
MODULE_DEVICE_TABLE(of, ms5637_of_match);

View File

@ -933,7 +933,7 @@ static int __init ashmem_init(void)
ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
sizeof(struct ashmem_range),
0, 0, NULL);
0, SLAB_RECLAIM_ACCOUNT, NULL);
if (!ashmem_range_cachep) {
pr_err("failed to create slab cache\n");
goto out_free1;

View File

@ -3,7 +3,10 @@ config STAGING_BOARD
bool "Staging Board Support"
depends on OF_ADDRESS && OF_IRQ && CLKDEV_LOOKUP
help
Select to enable per-board staging support code.
If in doubt, say N here.
Staging board base is to support continuous upstream
in-tree development and integration of platform devices.
Helps developers integrate devices as platform devices for
device drivers that only provide platform device bindings.
This in turn allows for incremental development of both
hardware feature support and DT binding work in parallel.

View File

@ -2,7 +2,8 @@ TODO:
- support for fractional multiplier
- support for fractional divider (output 0 only)
- support for set_rate() operations (may benefit from Stephen Boyd's
refactoring of the clk primitives: https://lkml.org/lkml/2014/9/5/766)
refactoring of the clk primitives:
https://lore.kernel.org/lkml/1409957256-23729-1-git-send-email-sboyd@codeaurora.org)
- review arithmetic
- overflow after multiplication?
- maximize accuracy before divisions

View File

@ -939,8 +939,8 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
/* fill devinfo structure */
devinfo.version_code = COMEDI_VERSION_CODE;
devinfo.n_subdevs = dev->n_subdevices;
strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
strscpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
strscpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
s = comedi_file_read_subdevice(file);
if (s)

View File

@ -19,14 +19,15 @@
* PCI-7234 (adl_pci7234), PCI-7432 (adl_pci7432), PCI-7433 (adl_pci7433),
* PCI-7434 (adl_pci7434)
* Author: H Hartley Sweeten <hsweeten@visionengravers.com>
* Updated: Thu, 02 Aug 2012 14:27:46 -0700
* Status: untested
* Updated: Fri, 20 Nov 2020 14:49:36 +0000
* Status: works (tested on PCI-7230)
*
* One or two subdevices are setup by this driver depending on
* the number of digital inputs and/or outputs provided by the
* board. Each subdevice has a maximum of 32 channels.
*
* PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
* PCI-7230 - 4 subdevices: 0 - 16 input, 1 - 16 output,
* 2 - IRQ_IDI0, 3 - IRQ_IDI1
* PCI-7233 - 1 subdevice: 0 - 32 input
* PCI-7234 - 1 subdevice: 0 - 32 output
* PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
@ -37,8 +38,9 @@
* interrupt signals on digital input channels 0 and 1. The PCI-7233
* has dual-interrupt sources for change-of-state (COS) on any 16
* digital input channels of LSB and for COS on any 16 digital input
* lines of MSB. Interrupts are not currently supported by this
* driver.
* lines of MSB.
*
* Currently, this driver only supports interrupts for PCI-7230.
*
* Configuration Options: not applicable, uses comedi PCI auto config
*/
@ -47,13 +49,22 @@
#include "../comedi_pci.h"
#include "plx9052.h"
/*
* Register I/O map (32-bit access only)
*/
#define PCI7X3X_DIO_REG 0x00
#define PCI743X_DIO_REG 0x04
#define PCI7X3X_DIO_REG 0x0000 /* in the DigIO Port area */
#define PCI743X_DIO_REG 0x0004
enum apci1516_boardid {
#define ADL_PT_CLRIRQ 0x0040 /* in the DigIO Port area */
#define LINTI1_EN_ACT_IDI0 (PLX9052_INTCSR_LI1ENAB | PLX9052_INTCSR_LI1STAT)
#define LINTI2_EN_ACT_IDI1 (PLX9052_INTCSR_LI2ENAB | PLX9052_INTCSR_LI2STAT)
#define EN_PCI_LINT2H_LINT1H \
(PLX9052_INTCSR_PCIENAB | PLX9052_INTCSR_LI2POL | PLX9052_INTCSR_LI1POL)
enum adl_pci7x3x_boardid {
BOARD_PCI7230,
BOARD_PCI7233,
BOARD_PCI7234,
@ -67,14 +78,16 @@ struct adl_pci7x3x_boardinfo {
int nsubdevs;
int di_nchan;
int do_nchan;
int irq_nchan;
};
static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
[BOARD_PCI7230] = {
.name = "adl_pci7230",
.nsubdevs = 2,
.nsubdevs = 4, /* IDI, IDO, IRQ_IDI0, IRQ_IDI1 */
.di_nchan = 16,
.do_nchan = 16,
.irq_nchan = 2,
},
[BOARD_PCI7233] = {
.name = "adl_pci7233",
@ -104,6 +117,178 @@ static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
}
};
struct adl_pci7x3x_dev_private_data {
unsigned long lcr_io_base;
unsigned int int_ctrl;
};
struct adl_pci7x3x_sd_private_data {
spinlock_t subd_slock; /* spin-lock for cmd_running */
unsigned long port_offset;
short int cmd_running;
};
static void process_irq(struct comedi_device *dev, unsigned int subdev,
unsigned short intcsr)
{
struct comedi_subdevice *s = &dev->subdevices[subdev];
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
unsigned long reg = sd_priv->port_offset;
struct comedi_async *async_p = s->async;
if (async_p) {
unsigned short val = inw(dev->iobase + reg);
spin_lock(&sd_priv->subd_slock);
if (sd_priv->cmd_running)
comedi_buf_write_samples(s, &val, 1);
spin_unlock(&sd_priv->subd_slock);
comedi_handle_events(dev, s);
}
}
static irqreturn_t adl_pci7x3x_interrupt(int irq, void *p_device)
{
struct comedi_device *dev = p_device;
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
unsigned long cpu_flags;
unsigned int intcsr;
bool li1stat, li2stat;
if (!dev->attached) {
/* Ignore interrupt before device fully attached. */
/* Might not even have allocated subdevices yet! */
return IRQ_NONE;
}
/* Check if we are source of interrupt */
spin_lock_irqsave(&dev->spinlock, cpu_flags);
intcsr = inl(dev_private->lcr_io_base + PLX9052_INTCSR);
li1stat = (intcsr & LINTI1_EN_ACT_IDI0) == LINTI1_EN_ACT_IDI0;
li2stat = (intcsr & LINTI2_EN_ACT_IDI1) == LINTI2_EN_ACT_IDI1;
if (li1stat || li2stat) {
/* clear all current interrupt flags */
/* Fixme: Reset all 2 Int Flags */
outb(0x00, dev->iobase + ADL_PT_CLRIRQ);
}
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
/* SubDev 2, 3 = Isolated DigIn , on "SCSI2" jack!*/
if (li1stat) /* 0x0005 LINTi1 is Enabled && IDI0 is 1 */
process_irq(dev, 2, intcsr);
if (li2stat) /* 0x0028 LINTi2 is Enabled && IDI1 is 1 */
process_irq(dev, 3, intcsr);
return IRQ_RETVAL(li1stat || li2stat);
}
static int adl_pci7x3x_asy_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
int err = 0;
/* Step 1 : check if triggers are trivially valid */
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
/* Step 2a : make sure trigger sources are unique */
/* Step 2b : and mutually compatible */
/* Step 3: check if arguments are trivially valid */
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
cmd->chanlist_len);
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
if (err)
return 3;
/* Step 4: fix up any arguments */
/* Step 5: check channel list if it exists */
return 0;
}
static int adl_pci7x3x_asy_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
unsigned long cpu_flags;
unsigned int int_enab;
if (s->index == 2) {
/* enable LINTi1 == IDI sdi[0] Ch 0 IRQ ActHigh */
int_enab = PLX9052_INTCSR_LI1ENAB;
} else {
/* enable LINTi2 == IDI sdi[0] Ch 1 IRQ ActHigh */
int_enab = PLX9052_INTCSR_LI2ENAB;
}
spin_lock_irqsave(&dev->spinlock, cpu_flags);
dev_private->int_ctrl |= int_enab;
outl(dev_private->int_ctrl, dev_private->lcr_io_base + PLX9052_INTCSR);
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
sd_priv->cmd_running = 1;
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
return 0;
}
static int adl_pci7x3x_asy_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
unsigned long cpu_flags;
unsigned int int_enab;
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
sd_priv->cmd_running = 0;
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
/* disable Interrupts */
if (s->index == 2)
int_enab = PLX9052_INTCSR_LI1ENAB;
else
int_enab = PLX9052_INTCSR_LI2ENAB;
spin_lock_irqsave(&dev->spinlock, cpu_flags);
dev_private->int_ctrl &= ~int_enab;
outl(dev_private->int_ctrl, dev_private->lcr_io_base + PLX9052_INTCSR);
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
return 0;
}
/* same as _di_insn_bits because the IRQ-pins are the DI-ports */
static int adl_pci7x3x_dirq_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
unsigned long reg = (unsigned long)sd_priv->port_offset;
data[1] = inl(dev->iobase + reg);
return insn->n;
}
static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
@ -143,15 +328,28 @@ static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
return insn->n;
}
static int adl_pci7x3x_reset(struct comedi_device *dev)
{
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
/* disable Interrupts */
dev_private->int_ctrl = 0x00; /* Disable PCI + LINTi2 + LINTi1 */
outl(dev_private->int_ctrl, dev_private->lcr_io_base + PLX9052_INTCSR);
return 0;
}
static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
unsigned long context)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
const struct adl_pci7x3x_boardinfo *board = NULL;
struct comedi_subdevice *s;
struct adl_pci7x3x_dev_private_data *dev_private;
int subdev;
int nchan;
int ret;
int ic;
if (context < ARRAY_SIZE(adl_pci7x3x_boards))
board = &adl_pci7x3x_boards[context];
@ -160,10 +358,34 @@ static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
dev->board_ptr = board;
dev->board_name = board->name;
dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
if (!dev_private)
return -ENOMEM;
ret = comedi_pci_enable(dev);
if (ret)
return ret;
dev->iobase = pci_resource_start(pcidev, 2);
dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
adl_pci7x3x_reset(dev);
if (board->irq_nchan) {
/* discard all evtl. old IRQs */
outb(0x00, dev->iobase + ADL_PT_CLRIRQ);
if (pcidev->irq) {
ret = request_irq(pcidev->irq, adl_pci7x3x_interrupt,
IRQF_SHARED, dev->board_name, dev);
if (ret == 0) {
dev->irq = pcidev->irq;
/* 0x52 PCI + IDI Ch 1 Ch 0 IRQ Off ActHigh */
dev_private->int_ctrl = EN_PCI_LINT2H_LINT1H;
outl(dev_private->int_ctrl,
dev_private->lcr_io_base + PLX9052_INTCSR);
}
}
}
ret = comedi_alloc_subdevices(dev, board->nsubdevs);
if (ret)
@ -237,14 +459,56 @@ static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
}
}
for (ic = 0; ic < board->irq_nchan; ++ic) {
struct adl_pci7x3x_sd_private_data *sd_priv;
nchan = 1;
s = &dev->subdevices[subdev];
/* Isolated digital inputs 0 or 1 */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = nchan;
s->maxdata = 1;
s->insn_bits = adl_pci7x3x_dirq_insn_bits;
s->range_table = &range_digital;
sd_priv = comedi_alloc_spriv(s, sizeof(*sd_priv));
if (!sd_priv)
return -ENOMEM;
spin_lock_init(&sd_priv->subd_slock);
sd_priv->port_offset = PCI7X3X_DIO_REG;
sd_priv->cmd_running = 0;
if (dev->irq) {
dev->read_subdev = s;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
s->len_chanlist = 1;
s->do_cmdtest = adl_pci7x3x_asy_cmdtest;
s->do_cmd = adl_pci7x3x_asy_cmd;
s->cancel = adl_pci7x3x_asy_cancel;
}
subdev++;
}
return 0;
}
static void adl_pci7x3x_detach(struct comedi_device *dev)
{
if (dev->iobase)
adl_pci7x3x_reset(dev);
comedi_pci_detach(dev);
}
static struct comedi_driver adl_pci7x3x_driver = {
.driver_name = "adl_pci7x3x",
.module = THIS_MODULE,
.auto_attach = adl_pci7x3x_auto_attach,
.detach = comedi_pci_detach,
.detach = adl_pci7x3x_detach,
};
static int adl_pci7x3x_pci_probe(struct pci_dev *dev,

View File

@ -34,9 +34,15 @@
*/
/* PCI-1730, PCI-1733, PCI-1736 interrupt control registers */
#define PCI173X_INT_EN_REG 0x08 /* R/W: enable/disable */
#define PCI173X_INT_RF_REG 0x0c /* R/W: falling/rising edge */
#define PCI173X_INT_CLR_REG 0x10 /* R/W: clear */
#define PCI173X_INT_EN_REG 0x0008 /* R/W: enable/disable */
#define PCI173X_INT_RF_REG 0x000c /* R/W: falling/rising edge */
#define PCI173X_INT_FLAG_REG 0x0010 /* R: status */
#define PCI173X_INT_CLR_REG 0x0010 /* W: clear */
#define PCI173X_INT_IDI0 0x01 /* IDI0 edge occurred */
#define PCI173X_INT_IDI1 0x02 /* IDI1 edge occurred */
#define PCI173X_INT_DI0 0x04 /* DI0 edge occurred */
#define PCI173X_INT_DI1 0x08 /* DI1 edge occurred */
/* PCI-1739U, PCI-1750, PCI1751 interrupt control registers */
#define PCI1750_INT_REG 0x20 /* R/W: status/control */
@ -63,6 +69,7 @@
#define PCI_DIO_MAX_DI_SUBDEVS 2 /* 2 x 8/16/32 input channels max */
#define PCI_DIO_MAX_DO_SUBDEVS 2 /* 2 x 8/16/32 output channels max */
#define PCI_DIO_MAX_DIO_SUBDEVG 2 /* 2 x any number of 8255 devices max */
#define PCI_DIO_MAX_IRQ_SUBDEVS 4 /* 4 x 1 input IRQ channels max */
enum pci_dio_boardid {
TYPE_PCI1730,
@ -84,7 +91,12 @@ enum pci_dio_boardid {
struct diosubd_data {
int chans; /* num of chans or 8255 devices */
unsigned long addr; /* PCI address ofset */
unsigned long addr; /* PCI address offset */
};
struct dio_irq_subd_data {
unsigned short int_en; /* interrupt enable/status bit */
unsigned long addr; /* PCI address offset */
};
struct dio_boardtype {
@ -93,6 +105,7 @@ struct dio_boardtype {
struct diosubd_data sdi[PCI_DIO_MAX_DI_SUBDEVS];
struct diosubd_data sdo[PCI_DIO_MAX_DO_SUBDEVS];
struct diosubd_data sdio[PCI_DIO_MAX_DIO_SUBDEVG];
struct dio_irq_subd_data sdirq[PCI_DIO_MAX_IRQ_SUBDEVS];
unsigned long id_reg;
unsigned long timer_regbase;
unsigned int is_16bit:1;
@ -101,12 +114,17 @@ struct dio_boardtype {
static const struct dio_boardtype boardtypes[] = {
[TYPE_PCI1730] = {
.name = "pci1730",
.nsubdevs = 5,
/* DI, IDI, DO, IDO, ID, IRQ_DI0, IRQ_DI1, IRQ_IDI0, IRQ_IDI1 */
.nsubdevs = 9,
.sdi[0] = { 16, 0x02, }, /* DI 0-15 */
.sdi[1] = { 16, 0x00, }, /* ISO DI 0-15 */
.sdo[0] = { 16, 0x02, }, /* DO 0-15 */
.sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
.id_reg = 0x04,
.sdirq[0] = { PCI173X_INT_DI0, 0x02, }, /* DI 0 */
.sdirq[1] = { PCI173X_INT_DI1, 0x02, }, /* DI 1 */
.sdirq[2] = { PCI173X_INT_IDI0, 0x00, }, /* ISO DI 0 */
.sdirq[3] = { PCI173X_INT_IDI1, 0x00, }, /* ISO DI 1 */
},
[TYPE_PCI1733] = {
.name = "pci1733",
@ -205,6 +223,188 @@ static const struct dio_boardtype boardtypes[] = {
},
};
struct pci_dio_dev_private_data {
int boardtype;
int irq_subd;
unsigned short int_ctrl;
unsigned short int_rf;
};
struct pci_dio_sd_private_data {
spinlock_t subd_slock; /* spin-lock for cmd_running */
unsigned long port_offset;
short int cmd_running;
};
static void process_irq(struct comedi_device *dev, unsigned int subdev,
unsigned char irqflags)
{
struct comedi_subdevice *s = &dev->subdevices[subdev];
struct pci_dio_sd_private_data *sd_priv = s->private;
unsigned long reg = sd_priv->port_offset;
struct comedi_async *async_p = s->async;
if (async_p) {
unsigned short val = inw(dev->iobase + reg);
spin_lock(&sd_priv->subd_slock);
if (sd_priv->cmd_running)
comedi_buf_write_samples(s, &val, 1);
spin_unlock(&sd_priv->subd_slock);
comedi_handle_events(dev, s);
}
}
static irqreturn_t pci_dio_interrupt(int irq, void *p_device)
{
struct comedi_device *dev = p_device;
struct pci_dio_dev_private_data *dev_private = dev->private;
const struct dio_boardtype *board = dev->board_ptr;
unsigned long cpu_flags;
unsigned char irqflags;
int i;
if (!dev->attached) {
/* Ignore interrupt before device fully attached. */
/* Might not even have allocated subdevices yet! */
return IRQ_NONE;
}
/* Check if we are source of interrupt */
spin_lock_irqsave(&dev->spinlock, cpu_flags);
irqflags = inb(dev->iobase + PCI173X_INT_FLAG_REG);
if (!(irqflags & 0x0F)) {
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
return IRQ_NONE;
}
/* clear all current interrupt flags */
outb(irqflags, dev->iobase + PCI173X_INT_CLR_REG);
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
/* check irq subdevice triggers */
for (i = 0; i < PCI_DIO_MAX_IRQ_SUBDEVS; i++) {
if (irqflags & board->sdirq[i].int_en)
process_irq(dev, dev_private->irq_subd + i, irqflags);
}
return IRQ_HANDLED;
}
static int pci_dio_asy_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
int err = 0;
/* Step 1 : check if triggers are trivially valid */
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
/* Step 2a : make sure trigger sources are unique */
/* Step 2b : and mutually compatible */
/* Step 3: check if arguments are trivially valid */
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
/*
* For scan_begin_arg, the trigger number must be 0 and the only
* allowed flags are CR_EDGE and CR_INVERT. CR_EDGE is ignored,
* CR_INVERT sets the trigger to falling edge.
*/
if (cmd->scan_begin_arg & ~(CR_EDGE | CR_INVERT)) {
cmd->scan_begin_arg &= (CR_EDGE | CR_INVERT);
err |= -EINVAL;
}
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
cmd->chanlist_len);
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
if (err)
return 3;
/* Step 4: fix up any arguments */
/* Step 5: check channel list if it exists */
return 0;
}
static int pci_dio_asy_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct pci_dio_dev_private_data *dev_private = dev->private;
struct pci_dio_sd_private_data *sd_priv = s->private;
const struct dio_boardtype *board = dev->board_ptr;
struct comedi_cmd *cmd = &s->async->cmd;
unsigned long cpu_flags;
unsigned short int_en;
int_en = board->sdirq[s->index - dev_private->irq_subd].int_en;
spin_lock_irqsave(&dev->spinlock, cpu_flags);
if (cmd->scan_begin_arg & CR_INVERT)
dev_private->int_rf |= int_en; /* falling edge */
else
dev_private->int_rf &= ~int_en; /* rising edge */
outb(dev_private->int_rf, dev->iobase + PCI173X_INT_RF_REG);
dev_private->int_ctrl |= int_en; /* enable interrupt source */
outb(dev_private->int_ctrl, dev->iobase + PCI173X_INT_EN_REG);
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
sd_priv->cmd_running = 1;
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
return 0;
}
static int pci_dio_asy_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct pci_dio_dev_private_data *dev_private = dev->private;
struct pci_dio_sd_private_data *sd_priv = s->private;
const struct dio_boardtype *board = dev->board_ptr;
unsigned long cpu_flags;
unsigned short int_en;
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
sd_priv->cmd_running = 0;
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
int_en = board->sdirq[s->index - dev_private->irq_subd].int_en;
spin_lock_irqsave(&dev->spinlock, cpu_flags);
dev_private->int_ctrl &= ~int_en;
outb(dev_private->int_ctrl, dev->iobase + PCI173X_INT_EN_REG);
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
return 0;
}
/* same as _insn_bits_di_ because the IRQ-pins are the DI-ports */
static int pci_dio_insn_bits_dirq_b(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct pci_dio_sd_private_data *sd_priv = s->private;
unsigned long reg = (unsigned long)sd_priv->port_offset;
unsigned long iobase = dev->iobase + reg;
data[1] = inb(iobase);
return insn->n;
}
static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
@ -283,6 +483,7 @@ static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
{
struct pci_dio_dev_private_data *dev_private = dev->private;
/* disable channel freeze function on the PCI-1752/1756 boards */
if (cardtype == TYPE_PCI1752 || cardtype == TYPE_PCI1756)
outw(0, dev->iobase + PCI1752_CFC_REG);
@ -292,9 +493,13 @@ static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
case TYPE_PCI1730:
case TYPE_PCI1733:
case TYPE_PCI1736:
outb(0, dev->iobase + PCI173X_INT_EN_REG);
dev_private->int_ctrl = 0x00;
outb(dev_private->int_ctrl, dev->iobase + PCI173X_INT_EN_REG);
/* Reset all 4 Int Flags */
outb(0x0f, dev->iobase + PCI173X_INT_CLR_REG);
outb(0, dev->iobase + PCI173X_INT_RF_REG);
/* Rising Edge => IRQ . On all 4 Pins */
dev_private->int_rf = 0x00;
outb(dev_private->int_rf, dev->iobase + PCI173X_INT_RF_REG);
break;
case TYPE_PCI1739:
case TYPE_PCI1750:
@ -346,8 +551,8 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
const struct dio_boardtype *board = NULL;
const struct diosubd_data *d;
struct comedi_subdevice *s;
struct pci_dio_dev_private_data *dev_private;
int ret, subdev, i, j;
if (context < ARRAY_SIZE(boardtypes))
@ -357,6 +562,10 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
dev->board_ptr = board;
dev->board_name = board->name;
dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
if (!dev_private)
return -ENOMEM;
ret = comedi_pci_enable(dev);
if (ret)
return ret;
@ -365,15 +574,25 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
else
dev->iobase = pci_resource_start(pcidev, 2);
dev_private->boardtype = context;
pci_dio_reset(dev, context);
/* request IRQ if device has irq subdevices */
if (board->sdirq[0].int_en && pcidev->irq) {
ret = request_irq(pcidev->irq, pci_dio_interrupt, IRQF_SHARED,
dev->board_name, dev);
if (ret == 0)
dev->irq = pcidev->irq;
}
ret = comedi_alloc_subdevices(dev, board->nsubdevs);
if (ret)
return ret;
subdev = 0;
for (i = 0; i < PCI_DIO_MAX_DI_SUBDEVS; i++) {
d = &board->sdi[i];
const struct diosubd_data *d = &board->sdi[i];
if (d->chans) {
s = &dev->subdevices[subdev++];
s->type = COMEDI_SUBD_DI;
@ -385,11 +604,13 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
? pci_dio_insn_bits_di_w
: pci_dio_insn_bits_di_b;
s->private = (void *)d->addr;
}
}
for (i = 0; i < PCI_DIO_MAX_DO_SUBDEVS; i++) {
d = &board->sdo[i];
const struct diosubd_data *d = &board->sdo[i];
if (d->chans) {
s = &dev->subdevices[subdev++];
s->type = COMEDI_SUBD_DO;
@ -420,7 +641,8 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
}
for (i = 0; i < PCI_DIO_MAX_DIO_SUBDEVG; i++) {
d = &board->sdio[i];
const struct diosubd_data *d = &board->sdio[i];
for (j = 0; j < d->chans; j++) {
s = &dev->subdevices[subdev++];
ret = subdev_8255_init(dev, s, NULL,
@ -454,14 +676,57 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
comedi_8254_subdevice_init(s, dev->pacer);
}
dev_private->irq_subd = subdev; /* first interrupt subdevice index */
for (i = 0; i < PCI_DIO_MAX_IRQ_SUBDEVS; ++i) {
struct pci_dio_sd_private_data *sd_priv = NULL;
const struct dio_irq_subd_data *d = &board->sdirq[i];
if (d->int_en) {
s = &dev->subdevices[subdev++];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 1;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = pci_dio_insn_bits_dirq_b;
sd_priv = comedi_alloc_spriv(s, sizeof(*sd_priv));
if (!sd_priv)
return -ENOMEM;
spin_lock_init(&sd_priv->subd_slock);
sd_priv->port_offset = d->addr;
sd_priv->cmd_running = 0;
if (dev->irq) {
dev->read_subdev = s;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
s->len_chanlist = 1;
s->do_cmdtest = pci_dio_asy_cmdtest;
s->do_cmd = pci_dio_asy_cmd;
s->cancel = pci_dio_asy_cancel;
}
}
}
return 0;
}
static void pci_dio_detach(struct comedi_device *dev)
{
struct pci_dio_dev_private_data *dev_private = dev->private;
int boardtype = dev_private->boardtype;
if (dev->iobase)
pci_dio_reset(dev, boardtype);
comedi_pci_detach(dev);
}
static struct comedi_driver adv_pci_dio_driver = {
.driver_name = "adv_pci_dio",
.module = THIS_MODULE,
.auto_attach = pci_dio_auto_attach,
.detach = comedi_pci_detach,
.detach = pci_dio_detach,
};
static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,

View File

@ -34,6 +34,9 @@
#define DRIVER_DESC "EMXX UDC driver"
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
static struct gpio_desc *vbus_gpio;
static int vbus_irq;
static const char driver_name[] = "emxx_udc";
static const char driver_desc[] = DRIVER_DESC;

View File

@ -20,8 +20,6 @@
/* below hacked up for staging integration */
#define GPIO_VBUS 0 /* GPIO_P153 on KZM9D */
#define INT_VBUS 0 /* IRQ for GPIO_P153 */
struct gpio_desc *vbus_gpio;
int vbus_irq;
/*------------ Board dependence(Wait) */

View File

@ -7,9 +7,13 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/module.h>
#include <video/mipi_display.h>
#include "fbtft.h"
@ -66,6 +70,62 @@ enum st7789v_command {
#define MADCTL_MX BIT(6) /* bitmask for column address order */
#define MADCTL_MY BIT(7) /* bitmask for page address order */
/* 60Hz for 16.6ms, configured as 2*16.6ms */
#define PANEL_TE_TIMEOUT_MS 33
static struct completion panel_te; /* completion for panel TE line */
static int irq_te; /* Linux IRQ for LCD TE line */
static irqreturn_t panel_te_handler(int irq, void *data)
{
complete(&panel_te);
return IRQ_HANDLED;
}
/*
* init_tearing_effect_line() - init tearing effect line.
* @par: FBTFT parameter object.
*
* Return: 0 on success, or a negative error code otherwise.
*/
static int init_tearing_effect_line(struct fbtft_par *par)
{
struct device *dev = par->info->device;
struct gpio_desc *te;
int rc, irq;
te = gpiod_get_optional(dev, "te", GPIOD_IN);
if (IS_ERR(te))
return dev_err_probe(dev, PTR_ERR(te), "Failed to request te GPIO\n");
/* if te is NULL, indicating no configuration, directly return success */
if (!te) {
irq_te = 0;
return 0;
}
irq = gpiod_to_irq(te);
/* GPIO is locked as an IRQ, we may drop the reference */
gpiod_put(te);
if (irq < 0)
return irq;
irq_te = irq;
init_completion(&panel_te);
/* The effective state is high and lasts no more than 1000 microseconds */
rc = devm_request_irq(dev, irq_te, panel_te_handler,
IRQF_TRIGGER_RISING, "TE_GPIO", par);
if (rc)
return dev_err_probe(dev, rc, "TE IRQ request failed.\n");
disable_irq_nosync(irq_te);
return 0;
}
/**
* init_display() - initialize the display controller
*
@ -82,6 +142,12 @@ enum st7789v_command {
*/
static int init_display(struct fbtft_par *par)
{
int rc;
rc = init_tearing_effect_line(par);
if (rc)
return rc;
/* turn off sleep mode */
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
mdelay(120);
@ -137,6 +203,10 @@ static int init_display(struct fbtft_par *par)
*/
write_reg(par, PWCTRL1, 0xA4, 0xA1);
/* TE line output is off by default when powering on */
if (irq_te)
write_reg(par, MIPI_DCS_SET_TEAR_ON, 0x00);
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
if (HSD20_IPS)
@ -145,6 +215,50 @@ static int init_display(struct fbtft_par *par)
return 0;
}
/*
* write_vmem() - write data to display.
* @par: FBTFT parameter object.
* @offset: offset from screen_buffer.
* @len: the length of data to be writte.
*
* Return: 0 on success, or a negative error code otherwise.
*/
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
struct device *dev = par->info->device;
int ret;
if (irq_te) {
enable_irq(irq_te);
reinit_completion(&panel_te);
ret = wait_for_completion_timeout(&panel_te,
msecs_to_jiffies(PANEL_TE_TIMEOUT_MS));
if (ret == 0)
dev_err(dev, "wait panel TE timeout\n");
disable_irq(irq_te);
}
switch (par->pdata->display.buswidth) {
case 8:
ret = fbtft_write_vmem16_bus8(par, offset, len);
break;
case 9:
ret = fbtft_write_vmem16_bus9(par, offset, len);
break;
case 16:
ret = fbtft_write_vmem16_bus16(par, offset, len);
break;
default:
dev_err(dev, "Unsupported buswidth %d\n",
par->pdata->display.buswidth);
ret = 0;
break;
}
return ret;
}
/**
* set_var() - apply LCD properties like rotation and BGR mode
*
@ -259,6 +373,7 @@ static struct fbtft_display display = {
.gamma = HSD20_IPS_GAMMA,
.fbtftops = {
.init_display = init_display,
.write_vmem = write_vmem,
.set_var = set_var,
.set_gamma = set_gamma,
.blank = blank,

View File

@ -185,7 +185,7 @@ static struct attribute *controller_attributes[] = {
NULL,
};
static struct attribute_group controller_attribute_group = {
static const struct attribute_group controller_attribute_group = {
.attrs = controller_attributes,
};
@ -206,7 +206,7 @@ static int can_power_is_enabled(struct regulator_dev *rdev)
return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
}
static struct regulator_ops can_power_ops = {
static const struct regulator_ops can_power_ops = {
.is_enabled = can_power_is_enabled,
};

View File

@ -38,19 +38,19 @@ static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
u16 version_major, version_minor;
int err;
strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
&version_major,
&version_minor);
if (err)
strlcpy(drvinfo->fw_version, "N/A",
strscpy(drvinfo->fw_version, "N/A",
sizeof(drvinfo->fw_version));
else
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
"%u.%u", version_major, version_minor);
strlcpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
sizeof(drvinfo->bus_info));
}

View File

@ -2189,6 +2189,7 @@ static int fwserial_create(struct fw_unit *unit)
err = fw_core_add_address_handler(&port->rx_handler,
&fw_high_memory_region);
if (err) {
tty_port_destroy(&port->port);
kfree(port);
goto free_ports;
}
@ -2271,6 +2272,7 @@ unregister_ttys:
free_ports:
for (--i; i >= 0; --i) {
fw_core_remove_address_handler(&serial->ports[i]->rx_handler);
tty_port_destroy(&serial->ports[i]->port);
kfree(serial->ports[i]);
}

View File

@ -40,10 +40,11 @@ static int gasket_set_event_fd(struct gasket_dev *gasket_dev,
/* Read the size of the page table. */
static int gasket_read_page_table_size(struct gasket_dev *gasket_dev,
struct gasket_page_table_ioctl __user *argp)
struct gasket_page_table_ioctl __user *argp)
{
int ret = 0;
struct gasket_page_table_ioctl ibuf;
struct gasket_page_table *table;
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
return -EFAULT;
@ -51,8 +52,8 @@ static int gasket_read_page_table_size(struct gasket_dev *gasket_dev,
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
return -EFAULT;
ibuf.size = gasket_page_table_num_entries(
gasket_dev->page_table[ibuf.page_table_index]);
table = gasket_dev->page_table[ibuf.page_table_index];
ibuf.size = gasket_page_table_num_entries(table);
trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
ibuf.host_address,
@ -66,10 +67,11 @@ static int gasket_read_page_table_size(struct gasket_dev *gasket_dev,
/* Read the size of the simple page table. */
static int gasket_read_simple_page_table_size(struct gasket_dev *gasket_dev,
struct gasket_page_table_ioctl __user *argp)
struct gasket_page_table_ioctl __user *argp)
{
int ret = 0;
struct gasket_page_table_ioctl ibuf;
struct gasket_page_table *table;
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
return -EFAULT;
@ -77,8 +79,8 @@ static int gasket_read_simple_page_table_size(struct gasket_dev *gasket_dev,
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
return -EFAULT;
ibuf.size =
gasket_page_table_num_simple_entries(gasket_dev->page_table[ibuf.page_table_index]);
table = gasket_dev->page_table[ibuf.page_table_index];
ibuf.size = gasket_page_table_num_simple_entries(table);
trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
ibuf.host_address,
@ -92,11 +94,12 @@ static int gasket_read_simple_page_table_size(struct gasket_dev *gasket_dev,
/* Set the boundary between the simple and extended page tables. */
static int gasket_partition_page_table(struct gasket_dev *gasket_dev,
struct gasket_page_table_ioctl __user *argp)
struct gasket_page_table_ioctl __user *argp)
{
int ret;
struct gasket_page_table_ioctl ibuf;
uint max_page_table_size;
struct gasket_page_table *table;
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
return -EFAULT;
@ -107,8 +110,8 @@ static int gasket_partition_page_table(struct gasket_dev *gasket_dev,
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
return -EFAULT;
max_page_table_size = gasket_page_table_max_size(
gasket_dev->page_table[ibuf.page_table_index]);
table = gasket_dev->page_table[ibuf.page_table_index];
max_page_table_size = gasket_page_table_max_size(table);
if (ibuf.size > max_page_table_size) {
dev_dbg(gasket_dev->dev,
@ -119,8 +122,7 @@ static int gasket_partition_page_table(struct gasket_dev *gasket_dev,
mutex_lock(&gasket_dev->mutex);
ret = gasket_page_table_partition(
gasket_dev->page_table[ibuf.page_table_index], ibuf.size);
ret = gasket_page_table_partition(table, ibuf.size);
mutex_unlock(&gasket_dev->mutex);
return ret;
@ -131,6 +133,7 @@ static int gasket_map_buffers(struct gasket_dev *gasket_dev,
struct gasket_page_table_ioctl __user *argp)
{
struct gasket_page_table_ioctl ibuf;
struct gasket_page_table *table;
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
return -EFAULT;
@ -142,13 +145,12 @@ static int gasket_map_buffers(struct gasket_dev *gasket_dev,
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
return -EFAULT;
if (gasket_page_table_are_addrs_bad(gasket_dev->page_table[ibuf.page_table_index],
ibuf.host_address,
table = gasket_dev->page_table[ibuf.page_table_index];
if (gasket_page_table_are_addrs_bad(table, ibuf.host_address,
ibuf.device_address, ibuf.size))
return -EINVAL;
return gasket_page_table_map(gasket_dev->page_table[ibuf.page_table_index],
ibuf.host_address, ibuf.device_address,
return gasket_page_table_map(table, ibuf.host_address, ibuf.device_address,
ibuf.size / PAGE_SIZE);
}
@ -157,6 +159,7 @@ static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
struct gasket_page_table_ioctl __user *argp)
{
struct gasket_page_table_ioctl ibuf;
struct gasket_page_table *table;
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
return -EFAULT;
@ -168,12 +171,11 @@ static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
return -EFAULT;
if (gasket_page_table_is_dev_addr_bad(gasket_dev->page_table[ibuf.page_table_index],
ibuf.device_address, ibuf.size))
table = gasket_dev->page_table[ibuf.page_table_index];
if (gasket_page_table_is_dev_addr_bad(table, ibuf.device_address, ibuf.size))
return -EINVAL;
gasket_page_table_unmap(gasket_dev->page_table[ibuf.page_table_index],
ibuf.device_address, ibuf.size / PAGE_SIZE);
gasket_page_table_unmap(table, ibuf.device_address, ibuf.size / PAGE_SIZE);
return 0;
}
@ -183,7 +185,7 @@ static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
* corresponding memory.
*/
static int gasket_config_coherent_allocator(struct gasket_dev *gasket_dev,
struct gasket_coherent_alloc_config_ioctl __user *argp)
struct gasket_coherent_alloc_config_ioctl __user *argp)
{
int ret;
struct gasket_coherent_alloc_config_ioctl ibuf;

View File

@ -56,20 +56,24 @@ static int gdm_usb_recv(void *priv_dev,
static int request_mac_address(struct lte_udev *udev)
{
u8 buf[16] = {0,};
struct hci_packet *hci = (struct hci_packet *)buf;
struct hci_packet *hci;
struct usb_device *usbdev = udev->usbdev;
int actual;
int ret = -1;
hci = kmalloc(struct_size(hci, data, 1), GFP_KERNEL);
if (!hci)
return -ENOMEM;
hci->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_GET_INFORMATION);
hci->len = gdm_cpu_to_dev16(udev->gdm_ed, 1);
hci->data[0] = MAC_ADDRESS;
ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), buf, 5,
ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), hci, 5,
&actual, 1000);
udev->request_mac_addr = 1;
kfree(hci);
return ret;
}

View File

@ -166,7 +166,7 @@ static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
snprintf(id.name, sizeof(id.name), "%s %s", prefix,
control->name);
else
strlcpy(id.name, control->name, sizeof(id.name));
strscpy(id.name, control->name, sizeof(id.name));
id.numid = 0;
id.iface = control->iface;
id.device = control->device;

View File

@ -18,8 +18,8 @@ static ssize_t manager_sysfs_add_store(struct kobject *kobj,
struct gb_audio_manager_module_descriptor desc = { {0} };
int num = sscanf(buf,
"name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s "
"vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X",
"name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF
"s vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X",
desc.name, &desc.vid, &desc.pid, &desc.intf_id,
&desc.ip_devices, &desc.op_devices);

View File

@ -342,7 +342,7 @@ static int gb_audio_probe(struct gb_bundle *bundle,
/* inform above layer for uevent */
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
/* prepare for the audio manager */
strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
strscpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
desc.vid = 2; /* todo */
desc.pid = 3; /* todo */
desc.intf_id = gbmodule->dev_id;

View File

@ -200,7 +200,7 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
return -EINVAL;
name = gbaudio_map_controlid(module, data->ctl_id,
uinfo->value.enumerated.item);
strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE);
strscpy(uinfo->value.enumerated.name, name, NAME_SIZE);
break;
default:
dev_err(comp->dev, "Invalid type: %d for %s:kcontrol\n",
@ -1047,7 +1047,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module,
}
/* Prefix dev_id to widget control_name */
strlcpy(temp_name, w->name, NAME_SIZE);
strscpy(temp_name, w->name, NAME_SIZE);
snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name);
switch (w->type) {
@ -1169,7 +1169,7 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module,
}
control->id = curr->id;
/* Prefix dev_id to widget_name */
strlcpy(temp_name, curr->name, NAME_SIZE);
strscpy(temp_name, curr->name, NAME_SIZE);
snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id,
temp_name);
control->name = curr->name;

View File

@ -221,8 +221,8 @@ static void gb_hid_init_reports(struct gb_hid *ghid)
}
static int __gb_hid_get_raw_report(struct hid_device *hid,
unsigned char report_number, __u8 *buf, size_t count,
unsigned char report_type)
unsigned char report_number, __u8 *buf, size_t count,
unsigned char report_type)
{
struct gb_hid *ghid = hid->driver_data;
int ret;
@ -254,7 +254,7 @@ static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
ret = gb_hid_set_report(ghid, report_type, report_id, buf, len);
if (report_id && ret >= 0)
ret++; /* add report_id to the number of transfered bytes */
ret++; /* add report_id to the number of transferred bytes */
return 0;
}

View File

@ -290,8 +290,7 @@ static int channel_attr_groups_set(struct gb_channel *channel,
channel->attrs = kcalloc(size + 1, sizeof(*channel->attrs), GFP_KERNEL);
if (!channel->attrs)
return -ENOMEM;
channel->attr_group = kcalloc(1, sizeof(*channel->attr_group),
GFP_KERNEL);
channel->attr_group = kzalloc(sizeof(*channel->attr_group), GFP_KERNEL);
if (!channel->attr_group)
return -ENOMEM;
channel->attr_groups = kcalloc(2, sizeof(*channel->attr_groups),

View File

@ -449,7 +449,7 @@ static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
if (!strlen(init_name))
init_name = "gb_power_supply";
strlcpy(name, init_name, len);
strscpy(name, init_name, len);
while ((ret < len) && (psy = power_supply_get_by_name(name))) {
power_supply_put(psy);

View File

@ -455,10 +455,10 @@ static int gb_spi_setup_device(struct gb_spilib *spi, u8 cs)
dev_type = response.device_type;
if (dev_type == GB_SPI_SPI_DEV)
strlcpy(spi_board.modalias, "spidev",
strscpy(spi_board.modalias, "spidev",
sizeof(spi_board.modalias));
else if (dev_type == GB_SPI_SPI_NOR)
strlcpy(spi_board.modalias, "spi-nor",
strscpy(spi_board.modalias, "spi-nor",
sizeof(spi_board.modalias));
else if (dev_type == GB_SPI_SPI_MODALIAS)
memcpy(spi_board.modalias, response.name,

View File

@ -29,6 +29,7 @@ config MFD_HI6421_SPMI
depends on OF
depends on SPMI
select MFD_CORE
select REGMAP_SPMI
help
Add support for HiSilicon Hi6421v600 SPMI PMIC. Hi6421 includes
multi-functions, such as regulators, RTC, codec, Coulomb counter,
@ -44,6 +45,7 @@ config REGULATOR_HI6421V600
tristate "HiSilicon Hi6421v600 PMIC voltage regulator support"
depends on MFD_HI6421_SPMI && OF
depends on REGULATOR
select REGMAP
help
This driver provides support for the voltage regulators on
HiSilicon Hi6421v600 PMU / Codec IC.

View File

@ -4,149 +4,105 @@
*
* Copyright (c) 2013 Linaro Ltd.
* Copyright (c) 2011 Hisilicon.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/mfd/hi6421-spmi-pmic.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spmi.h>
/* 8-bit register offset in PMIC */
#define HISI_MASK_STATE 0xff
enum hi6421_spmi_pmic_irq_list {
OTMP = 0,
VBUS_CONNECT,
VBUS_DISCONNECT,
ALARMON_R,
HOLD_6S,
HOLD_1S,
POWERKEY_UP,
POWERKEY_DOWN,
OCP_SCP_R,
COUL_R,
SIM0_HPD_R,
SIM0_HPD_F,
SIM1_HPD_R,
SIM1_HPD_F,
PMIC_IRQ_LIST_MAX,
};
#define HISI_IRQ_ARRAY 2
#define HISI_IRQ_NUM (HISI_IRQ_ARRAY * 8)
#define HISI_IRQ_KEY_NUM 0
#define HISI_BITS 8
#define HISI_IRQ_KEY_VALUE (BIT(POWERKEY_DOWN) | BIT(POWERKEY_UP))
#define HISI_MASK GENMASK(HISI_BITS - 1, 0)
/*
* The IRQs are mapped as:
*
* ====================== ============= ============ =====
* IRQ MASK REGISTER IRQ REGISTER BIT
* ====================== ============= ============ =====
* OTMP 0x0202 0x212 bit 0
* VBUS_CONNECT 0x0202 0x212 bit 1
* VBUS_DISCONNECT 0x0202 0x212 bit 2
* ALARMON_R 0x0202 0x212 bit 3
* HOLD_6S 0x0202 0x212 bit 4
* HOLD_1S 0x0202 0x212 bit 5
* POWERKEY_UP 0x0202 0x212 bit 6
* POWERKEY_DOWN 0x0202 0x212 bit 7
*
* OCP_SCP_R 0x0203 0x213 bit 0
* COUL_R 0x0203 0x213 bit 1
* SIM0_HPD_R 0x0203 0x213 bit 2
* SIM0_HPD_F 0x0203 0x213 bit 3
* SIM1_HPD_R 0x0203 0x213 bit 4
* SIM1_HPD_F 0x0203 0x213 bit 5
* ====================== ============= ============ =====
*/
#define SOC_PMIC_IRQ_MASK_0_ADDR 0x0202
#define SOC_PMIC_IRQ0_ADDR 0x0212
#define HISI_IRQ_KEY_NUM 0
#define HISI_IRQ_KEY_VALUE 0xc0
#define HISI_IRQ_KEY_DOWN 7
#define HISI_IRQ_KEY_UP 6
#define HISI_MASK_FIELD 0xFF
#define HISI_BITS 8
/*define the first group interrupt register number*/
#define HISI_PMIC_FIRST_GROUP_INT_NUM 2
#define IRQ_MASK_REGISTER(irq_data) (SOC_PMIC_IRQ_MASK_0_ADDR + \
(irqd_to_hwirq(irq_data) >> 3))
#define IRQ_MASK_BIT(irq_data) BIT(irqd_to_hwirq(irq_data) & 0x07)
static const struct mfd_cell hi6421v600_devs[] = {
{ .name = "hi6421v600-regulator", },
};
/*
* The PMIC register is only 8-bit.
* Hisilicon SoC use hardware to map PMIC register into SoC mapping.
* At here, we are accessing SoC register with 32-bit.
*/
int hi6421_spmi_pmic_read(struct hi6421_spmi_pmic *pmic, int reg)
static irqreturn_t hi6421_spmi_irq_handler(int irq, void *priv)
{
struct spmi_device *pdev;
u8 read_value = 0;
u32 ret;
pdev = to_spmi_device(pmic->dev);
if (!pdev) {
pr_err("%s: pdev get failed!\n", __func__);
return -ENODEV;
}
ret = spmi_ext_register_readl(pdev, reg, &read_value, 1);
if (ret) {
pr_err("%s: spmi_ext_register_readl failed!\n", __func__);
return ret;
}
return read_value;
}
EXPORT_SYMBOL(hi6421_spmi_pmic_read);
int hi6421_spmi_pmic_write(struct hi6421_spmi_pmic *pmic, int reg, u32 val)
{
struct spmi_device *pdev;
u32 ret;
pdev = to_spmi_device(pmic->dev);
if (!pdev) {
pr_err("%s: pdev get failed!\n", __func__);
return -ENODEV;
}
ret = spmi_ext_register_writel(pdev, reg, (unsigned char *)&val, 1);
if (ret)
pr_err("%s: spmi_ext_register_writel failed!\n", __func__);
return ret;
}
EXPORT_SYMBOL(hi6421_spmi_pmic_write);
int hi6421_spmi_pmic_rmw(struct hi6421_spmi_pmic *pmic, int reg,
u32 mask, u32 bits)
{
unsigned long flags;
u32 data;
int ret;
spin_lock_irqsave(&pmic->lock, flags);
data = hi6421_spmi_pmic_read(pmic, reg) & ~mask;
data |= mask & bits;
ret = hi6421_spmi_pmic_write(pmic, reg, data);
spin_unlock_irqrestore(&pmic->lock, flags);
return ret;
}
EXPORT_SYMBOL(hi6421_spmi_pmic_rmw);
static irqreturn_t hi6421_spmi_irq_handler(int irq, void *data)
{
struct hi6421_spmi_pmic *pmic = (struct hi6421_spmi_pmic *)data;
struct hi6421_spmi_pmic *ddata = (struct hi6421_spmi_pmic *)priv;
unsigned long pending;
unsigned int in;
int i, offset;
for (i = 0; i < HISI_IRQ_ARRAY; i++) {
pending = hi6421_spmi_pmic_read(pmic, (i + SOC_PMIC_IRQ0_ADDR));
pending &= HISI_MASK_FIELD;
if (pending != 0)
pr_debug("pending[%d]=0x%lx\n\r", i, pending);
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &in);
pending = HISI_MASK & in;
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, pending);
hi6421_spmi_pmic_write(pmic, (i + SOC_PMIC_IRQ0_ADDR), pending);
/* solve powerkey order */
if ((i == HISI_IRQ_KEY_NUM) &&
((pending & HISI_IRQ_KEY_VALUE) == HISI_IRQ_KEY_VALUE)) {
generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_DOWN]);
generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_UP]);
if (i == HISI_IRQ_KEY_NUM &&
(pending & HISI_IRQ_KEY_VALUE) == HISI_IRQ_KEY_VALUE) {
generic_handle_irq(ddata->irqs[POWERKEY_DOWN]);
generic_handle_irq(ddata->irqs[POWERKEY_UP]);
pending &= (~HISI_IRQ_KEY_VALUE);
}
if (pending) {
for_each_set_bit(offset, &pending, HISI_BITS)
generic_handle_irq(pmic->irqs[offset + i * HISI_BITS]);
}
if (!pending)
continue;
for_each_set_bit(offset, &pending, HISI_BITS)
generic_handle_irq(ddata->irqs[offset + i * HISI_BITS]);
}
return IRQ_HANDLED;
@ -154,34 +110,38 @@ static irqreturn_t hi6421_spmi_irq_handler(int irq, void *data)
static void hi6421_spmi_irq_mask(struct irq_data *d)
{
struct hi6421_spmi_pmic *pmic = irq_data_get_irq_chip_data(d);
u32 data, offset;
struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d);
unsigned long flags;
unsigned int data;
u32 offset;
offset = (irqd_to_hwirq(d) >> 3);
offset += SOC_PMIC_IRQ_MASK_0_ADDR;
offset = IRQ_MASK_REGISTER(d);
spin_lock_irqsave(&pmic->lock, flags);
data = hi6421_spmi_pmic_read(pmic, offset);
data |= (1 << (irqd_to_hwirq(d) & 0x07));
hi6421_spmi_pmic_write(pmic, offset, data);
spin_unlock_irqrestore(&pmic->lock, flags);
spin_lock_irqsave(&ddata->lock, flags);
regmap_read(ddata->regmap, offset, &data);
data |= IRQ_MASK_BIT(d);
regmap_write(ddata->regmap, offset, data);
spin_unlock_irqrestore(&ddata->lock, flags);
}
static void hi6421_spmi_irq_unmask(struct irq_data *d)
{
struct hi6421_spmi_pmic *pmic = irq_data_get_irq_chip_data(d);
struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d);
u32 data, offset;
unsigned long flags;
offset = (irqd_to_hwirq(d) >> 3);
offset += SOC_PMIC_IRQ_MASK_0_ADDR;
spin_lock_irqsave(&pmic->lock, flags);
data = hi6421_spmi_pmic_read(pmic, offset);
spin_lock_irqsave(&ddata->lock, flags);
regmap_read(ddata->regmap, offset, &data);
data &= ~(1 << (irqd_to_hwirq(d) & 0x07));
hi6421_spmi_pmic_write(pmic, offset, data);
spin_unlock_irqrestore(&pmic->lock, flags);
regmap_write(ddata->regmap, offset, data);
spin_unlock_irqrestore(&ddata->lock, flags);
}
static struct irq_chip hi6421_spmi_pmu_irqchip = {
@ -195,11 +155,11 @@ static struct irq_chip hi6421_spmi_pmu_irqchip = {
static int hi6421_spmi_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
struct hi6421_spmi_pmic *pmic = d->host_data;
struct hi6421_spmi_pmic *ddata = d->host_data;
irq_set_chip_and_handler_name(virq, &hi6421_spmi_pmu_irqchip,
handle_simple_irq, "hisi");
irq_set_chip_data(virq, pmic);
irq_set_chip_data(virq, ddata);
irq_set_irq_type(virq, IRQ_TYPE_NONE);
return 0;
@ -210,118 +170,111 @@ static const struct irq_domain_ops hi6421_spmi_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
static void hi6421_spmi_pmic_irq_prc(struct hi6421_spmi_pmic *pmic)
static void hi6421_spmi_pmic_irq_init(struct hi6421_spmi_pmic *ddata)
{
int i, pending;
int i;
unsigned int pending;
for (i = 0 ; i < HISI_IRQ_ARRAY; i++)
hi6421_spmi_pmic_write(pmic, SOC_PMIC_IRQ_MASK_0_ADDR + i,
HISI_MASK_STATE);
for (i = 0; i < HISI_IRQ_ARRAY; i++)
regmap_write(ddata->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i,
HISI_MASK);
for (i = 0 ; i < HISI_IRQ_ARRAY; i++) {
pending = hi6421_spmi_pmic_read(pmic, SOC_PMIC_IRQ0_ADDR + i);
pr_debug("PMU IRQ address value:irq[0x%x] = 0x%x\n",
SOC_PMIC_IRQ0_ADDR + i, pending);
hi6421_spmi_pmic_write(pmic, SOC_PMIC_IRQ0_ADDR + i,
HISI_MASK_STATE);
for (i = 0; i < HISI_IRQ_ARRAY; i++) {
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending);
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i,
HISI_MASK);
}
}
static const struct regmap_config regmap_config = {
.reg_bits = 16,
.val_bits = HISI_BITS,
.max_register = 0xffff,
.fast_io = true
};
static int hi6421_spmi_pmic_probe(struct spmi_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct hi6421_spmi_pmic *pmic;
struct hi6421_spmi_pmic *ddata;
unsigned int virq;
int ret, i;
pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
if (!pmic)
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
spin_lock_init(&pmic->lock);
ddata->regmap = devm_regmap_init_spmi_ext(pdev, &regmap_config);
if (IS_ERR(ddata->regmap))
return PTR_ERR(ddata->regmap);
pmic->dev = dev;
spin_lock_init(&ddata->lock);
pmic->gpio = of_get_gpio(np, 0);
if (pmic->gpio < 0)
return pmic->gpio;
ddata->dev = dev;
if (!gpio_is_valid(pmic->gpio))
ddata->gpio = of_get_gpio(np, 0);
if (ddata->gpio < 0)
return ddata->gpio;
if (!gpio_is_valid(ddata->gpio))
return -EINVAL;
ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN, "pmic");
ret = devm_gpio_request_one(dev, ddata->gpio, GPIOF_IN, "pmic");
if (ret < 0) {
dev_err(dev, "failed to request gpio%d\n", pmic->gpio);
dev_err(dev, "Failed to request gpio%d\n", ddata->gpio);
return ret;
}
pmic->irq = gpio_to_irq(pmic->gpio);
ddata->irq = gpio_to_irq(ddata->gpio);
hi6421_spmi_pmic_irq_prc(pmic);
hi6421_spmi_pmic_irq_init(ddata);
pmic->irqs = devm_kzalloc(dev, HISI_IRQ_NUM * sizeof(int), GFP_KERNEL);
if (!pmic->irqs) {
ret = -ENOMEM;
goto irq_malloc;
}
ddata->irqs = devm_kzalloc(dev, HISI_IRQ_NUM * sizeof(int), GFP_KERNEL);
if (!ddata->irqs)
return -ENOMEM;
pmic->domain = irq_domain_add_simple(np, HISI_IRQ_NUM, 0,
&hi6421_spmi_domain_ops, pmic);
if (!pmic->domain) {
dev_err(dev, "failed irq domain add simple!\n");
ret = -ENODEV;
goto irq_malloc;
ddata->domain = irq_domain_add_simple(np, HISI_IRQ_NUM, 0,
&hi6421_spmi_domain_ops, ddata);
if (!ddata->domain) {
dev_err(dev, "Failed to create IRQ domain\n");
return -ENODEV;
}
for (i = 0; i < HISI_IRQ_NUM; i++) {
virq = irq_create_mapping(pmic->domain, i);
virq = irq_create_mapping(ddata->domain, i);
if (!virq) {
dev_err(dev, "Failed mapping hwirq\n");
ret = -ENOSPC;
goto irq_malloc;
dev_err(dev, "Failed to map H/W IRQ\n");
return -ENOSPC;
}
pmic->irqs[i] = virq;
dev_dbg(dev, "%s: pmic->irqs[%d] = %d\n",
__func__, i, pmic->irqs[i]);
ddata->irqs[i] = virq;
}
ret = request_threaded_irq(pmic->irq, hi6421_spmi_irq_handler, NULL,
ret = request_threaded_irq(ddata->irq, hi6421_spmi_irq_handler, NULL,
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
"pmic", pmic);
"pmic", ddata);
if (ret < 0) {
dev_err(dev, "could not claim pmic IRQ: error %d\n", ret);
goto irq_malloc;
dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
ret);
return ret;
}
dev_set_drvdata(&pdev->dev, pmic);
dev_set_drvdata(&pdev->dev, ddata);
/*
* The logic below will rely that the pmic is already stored at
* drvdata.
*/
dev_dbg(&pdev->dev, "SPMI-PMIC: adding children for %pOF\n",
pdev->dev.of_node);
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
hi6421v600_devs, ARRAY_SIZE(hi6421v600_devs),
NULL, 0, NULL);
if (!ret)
return 0;
dev_err(dev, "Failed to add child devices: %d\n", ret);
irq_malloc:
free_irq(pmic->irq, pmic);
if (ret < 0)
dev_err(dev, "Failed to add child devices: %d\n", ret);
return ret;
}
static void hi6421_spmi_pmic_remove(struct spmi_device *pdev)
{
struct hi6421_spmi_pmic *pmic = dev_get_drvdata(&pdev->dev);
struct hi6421_spmi_pmic *ddata = dev_get_drvdata(&pdev->dev);
free_irq(pmic->irq, pmic);
free_irq(ddata->irq, ddata);
}
static const struct of_device_id pmic_spmi_id_table[] = {

View File

@ -1,183 +1,148 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Device driver for regulators in Hisi IC
*
* Copyright (c) 2013 Linaro Ltd.
* Copyright (c) 2011 Hisilicon.
*
* Guodong Xu <guodong.xu@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
//
// Device driver for regulators in Hisi IC
//
// Copyright (c) 2013 Linaro Ltd.
// Copyright (c) 2011 Hisilicon.
// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
//
// Guodong Xu <guodong.xu@linaro.org>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mfd/hi6421-spmi-pmic.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spmi.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#define rdev_dbg(rdev, fmt, arg...) \
pr_debug("%s: %s: " fmt, (rdev)->desc->name, __func__, ##arg)
struct hi6421v600_regulator {
struct regulator_desc rdesc;
struct hi6421_spmi_reg_info {
struct regulator_desc desc;
struct hi6421_spmi_pmic *pmic;
u32 eco_mode_mask, eco_uA;
u8 eco_mode_mask;
u32 eco_uA;
/* Serialize regulator enable logic */
struct mutex enable_mutex;
};
static DEFINE_MUTEX(enable_mutex);
static const unsigned int ldo3_voltages[] = {
1500000, 1550000, 1600000, 1650000,
1700000, 1725000, 1750000, 1775000,
1800000, 1825000, 1850000, 1875000,
1900000, 1925000, 1950000, 2000000
};
/*
* helper function to ensure when it returns it is at least 'delay_us'
* microseconds after 'since'.
static const unsigned int ldo4_voltages[] = {
1725000, 1750000, 1775000, 1800000,
1825000, 1850000, 1875000, 1900000
};
static const unsigned int ldo9_voltages[] = {
1750000, 1800000, 1825000, 2800000,
2850000, 2950000, 3000000, 3300000
};
static const unsigned int ldo15_voltages[] = {
1800000, 1850000, 2400000, 2600000,
2700000, 2850000, 2950000, 3000000
};
static const unsigned int ldo17_voltages[] = {
2500000, 2600000, 2700000, 2800000,
3000000, 3100000, 3200000, 3300000
};
static const unsigned int ldo34_voltages[] = {
2600000, 2700000, 2800000, 2900000,
3000000, 3100000, 3200000, 3300000
};
/**
* HI6421V600_LDO() - specify a LDO power line
* @_id: LDO id name string
* @vtable: voltage table
* @ereg: enable register
* @emask: enable mask
* @vreg: voltage select register
* @odelay: off/on delay time in uS
* @etime: enable time in uS
* @ecomask: eco mode mask
* @ecoamp: eco mode load uppler limit in uA
*/
static int hi6421_spmi_regulator_is_enabled(struct regulator_dev *rdev)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_pmic *pmic = sreg->pmic;
u32 reg_val;
reg_val = hi6421_spmi_pmic_read(pmic, rdev->desc->enable_reg);
rdev_dbg(rdev,
"enable_reg=0x%x, val= 0x%x, enable_state=%d\n",
rdev->desc->enable_reg,
reg_val, (reg_val & rdev->desc->enable_mask));
return ((reg_val & rdev->desc->enable_mask) != 0);
}
#define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \
odelay, etime, ecomask, ecoamp) \
[HI6421V600_##_id] = { \
.desc = { \
.name = #_id, \
.of_match = of_match_ptr(#_id), \
.regulators_node = of_match_ptr("regulators"), \
.ops = &hi6421_spmi_ldo_rops, \
.type = REGULATOR_VOLTAGE, \
.id = HI6421V600_##_id, \
.owner = THIS_MODULE, \
.volt_table = vtable, \
.n_voltages = ARRAY_SIZE(vtable), \
.vsel_mask = (1 << (ARRAY_SIZE(vtable) - 1)) - 1, \
.vsel_reg = vreg, \
.enable_reg = ereg, \
.enable_mask = emask, \
.enable_time = etime, \
.ramp_delay = etime, \
.off_on_delay = odelay, \
}, \
.eco_mode_mask = ecomask, \
.eco_uA = ecoamp, \
}
static int hi6421_spmi_regulator_enable(struct regulator_dev *rdev)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_pmic *pmic = sreg->pmic;
int ret;
/* cannot enable more than one regulator at one time */
mutex_lock(&enable_mutex);
usleep_range(HISI_REGS_ENA_PROTECT_TIME,
HISI_REGS_ENA_PROTECT_TIME + 1000);
mutex_lock(&sreg->enable_mutex);
/* set enable register */
rdev_dbg(rdev,
"off_on_delay=%d us, enable_reg=0x%x, enable_mask=0x%x\n",
rdev->desc->off_on_delay, rdev->desc->enable_reg,
rdev->desc->enable_mask);
ret = regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
rdev->desc->enable_mask,
rdev->desc->enable_mask);
hi6421_spmi_pmic_rmw(pmic, rdev->desc->enable_reg,
rdev->desc->enable_mask,
rdev->desc->enable_mask);
/* Avoid powering up multiple devices at the same time */
usleep_range(rdev->desc->off_on_delay, rdev->desc->off_on_delay + 60);
mutex_unlock(&enable_mutex);
mutex_unlock(&sreg->enable_mutex);
return 0;
return ret;
}
static int hi6421_spmi_regulator_disable(struct regulator_dev *rdev)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_pmic *pmic = sreg->pmic;
/* set enable register to 0 */
rdev_dbg(rdev, "enable_reg=0x%x, enable_mask=0x%x\n",
rdev->desc->enable_reg, rdev->desc->enable_mask);
hi6421_spmi_pmic_rmw(pmic, rdev->desc->enable_reg,
rdev->desc->enable_mask, 0);
return 0;
}
static int hi6421_spmi_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_pmic *pmic = sreg->pmic;
u32 reg_val, selector;
/* get voltage selector */
reg_val = hi6421_spmi_pmic_read(pmic, rdev->desc->vsel_reg);
selector = (reg_val & rdev->desc->vsel_mask) >> (ffs(rdev->desc->vsel_mask) - 1);
rdev_dbg(rdev,
"vsel_reg=0x%x, value=0x%x, entry=0x%x, voltage=%d mV\n",
rdev->desc->vsel_reg, reg_val, selector,
rdev->desc->ops->list_voltage(rdev, selector) / 1000);
return selector;
}
static int hi6421_spmi_regulator_set_voltage_sel(struct regulator_dev *rdev,
unsigned int selector)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_pmic *pmic = sreg->pmic;
u32 reg_val;
if (unlikely(selector >= rdev->desc->n_voltages))
return -EINVAL;
reg_val = selector << (ffs(rdev->desc->vsel_mask) - 1);
/* set voltage selector */
rdev_dbg(rdev,
"vsel_reg=0x%x, mask=0x%x, value=0x%x, voltage=%d mV\n",
rdev->desc->vsel_reg, rdev->desc->vsel_mask, reg_val,
rdev->desc->ops->list_voltage(rdev, selector) / 1000);
hi6421_spmi_pmic_rmw(pmic, rdev->desc->vsel_reg,
rdev->desc->vsel_mask, reg_val);
return 0;
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
rdev->desc->enable_mask, 0);
}
static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_pmic *pmic = sreg->pmic;
unsigned int mode;
u32 reg_val;
reg_val = hi6421_spmi_pmic_read(pmic, rdev->desc->enable_reg);
regmap_read(pmic->regmap, rdev->desc->enable_reg, &reg_val);
if (reg_val & sreg->eco_mode_mask)
mode = REGULATOR_MODE_IDLE;
else
mode = REGULATOR_MODE_NORMAL;
return REGULATOR_MODE_IDLE;
rdev_dbg(rdev,
"enable_reg=0x%x, eco_mode_mask=0x%x, reg_val=0x%x, %s mode\n",
rdev->desc->enable_reg, sreg->eco_mode_mask, reg_val,
mode == REGULATOR_MODE_IDLE ? "idle" : "normal");
return mode;
return REGULATOR_MODE_NORMAL;
}
static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_pmic *pmic = sreg->pmic;
u32 val;
@ -192,14 +157,8 @@ static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
return -EINVAL;
}
/* set mode */
rdev_dbg(rdev, "enable_reg=0x%x, eco_mode_mask=0x%x, value=0x%x\n",
rdev->desc->enable_reg, sreg->eco_mode_mask, val);
hi6421_spmi_pmic_rmw(pmic, rdev->desc->enable_reg,
sreg->eco_mode_mask, val);
return 0;
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
sreg->eco_mode_mask, val);
}
static unsigned int
@ -207,199 +166,84 @@ hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev,
int input_uV, int output_uV,
int load_uA)
{
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
if (load_uA || ((unsigned int)load_uA > sreg->eco_uA))
if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA))
return REGULATOR_MODE_NORMAL;
return REGULATOR_MODE_IDLE;
}
static int hi6421_spmi_dt_parse(struct platform_device *pdev,
struct hi6421v600_regulator *sreg,
struct regulator_desc *rdesc)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
unsigned int *v_table;
int ret;
ret = of_property_read_u32(np, "reg", &rdesc->enable_reg);
if (ret) {
dev_err(dev, "missing reg property\n");
return ret;
}
ret = of_property_read_u32(np, "vsel-reg", &rdesc->vsel_reg);
if (ret) {
dev_err(dev, "missing vsel-reg property\n");
return ret;
}
ret = of_property_read_u32(np, "enable-mask", &rdesc->enable_mask);
if (ret) {
dev_err(dev, "missing enable-mask property\n");
return ret;
}
/*
* Not all regulators work on idle mode
*/
ret = of_property_read_u32(np, "idle-mode-mask", &sreg->eco_mode_mask);
if (ret) {
dev_dbg(dev, "LDO doesn't support economy mode.\n");
sreg->eco_mode_mask = 0;
sreg->eco_uA = 0;
} else {
ret = of_property_read_u32(np, "eco-microamp", &sreg->eco_uA);
if (ret) {
dev_err(dev, "missing eco-microamp property\n");
return ret;
}
}
/* parse .off-on-delay */
ret = of_property_read_u32(np, "off-on-delay-us",
&rdesc->off_on_delay);
if (ret) {
dev_err(dev, "missing off-on-delay-us property\n");
return ret;
}
/* parse .enable_time */
ret = of_property_read_u32(np, "startup-delay-us",
&rdesc->enable_time);
if (ret) {
dev_err(dev, "missing startup-delay-us property\n");
return ret;
}
/* FIXME: are there a better value for this? */
rdesc->ramp_delay = rdesc->enable_time;
/* parse volt_table */
rdesc->n_voltages = of_property_count_u32_elems(np, "voltage-table");
v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages,
GFP_KERNEL);
if (unlikely(!v_table))
return -ENOMEM;
rdesc->volt_table = v_table;
ret = of_property_read_u32_array(np, "voltage-table",
v_table, rdesc->n_voltages);
if (ret) {
dev_err(dev, "missing voltage-table property\n");
return ret;
}
/*
* Instead of explicitly requiring a mask for the voltage selector,
* as they all start from bit zero (at least on the known LDOs),
* just use the number of voltages at the voltage table, getting the
* minimal mask that would pick everything.
*/
rdesc->vsel_mask = (1 << (fls(rdesc->n_voltages) - 1)) - 1;
dev_dbg(dev, "voltage selector settings: reg: 0x%x, mask: 0x%x\n",
rdesc->vsel_reg, rdesc->vsel_mask);
return 0;
}
static const struct regulator_ops hi6421_spmi_ldo_rops = {
.is_enabled = hi6421_spmi_regulator_is_enabled,
.is_enabled = regulator_is_enabled_regmap,
.enable = hi6421_spmi_regulator_enable,
.disable = hi6421_spmi_regulator_disable,
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_iterate,
.get_voltage_sel = hi6421_spmi_regulator_get_voltage_sel,
.set_voltage_sel = hi6421_spmi_regulator_set_voltage_sel,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_mode = hi6421_spmi_regulator_get_mode,
.set_mode = hi6421_spmi_regulator_set_mode,
.get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode,
};
static int hi6421_spmi_regulator_probe_ldo(struct platform_device *pdev,
struct device_node *np,
struct hi6421_spmi_pmic *pmic)
{
struct regulation_constraints *constraint;
struct regulator_init_data *initdata;
struct regulator_config config = { };
struct hi6421v600_regulator *sreg;
struct device *dev = &pdev->dev;
struct regulator_desc *rdesc;
struct regulator_dev *rdev;
const char *supplyname;
int ret;
/* HI6421v600 regulators with known registers */
enum hi6421_spmi_regulator_id {
HI6421V600_LDO3,
HI6421V600_LDO4,
HI6421V600_LDO9,
HI6421V600_LDO15,
HI6421V600_LDO16,
HI6421V600_LDO17,
HI6421V600_LDO33,
HI6421V600_LDO34,
};
initdata = of_get_regulator_init_data(dev, np, NULL);
if (!initdata) {
dev_err(dev, "failed to get regulator data\n");
return -EINVAL;
}
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
if (!sreg)
return -ENOMEM;
sreg->pmic = pmic;
rdesc = &sreg->rdesc;
rdesc->name = initdata->constraints.name;
rdesc->ops = &hi6421_spmi_ldo_rops;
rdesc->type = REGULATOR_VOLTAGE;
rdesc->min_uV = initdata->constraints.min_uV;
supplyname = of_get_property(np, "supply_name", NULL);
if (supplyname)
initdata->supply_regulator = supplyname;
/* parse device tree data for regulator specific */
ret = hi6421_spmi_dt_parse(pdev, sreg, rdesc);
if (ret)
return ret;
/* hisi regulator supports two modes */
constraint = &initdata->constraints;
constraint->valid_modes_mask = REGULATOR_MODE_NORMAL;
if (sreg->eco_mode_mask) {
constraint->valid_modes_mask |= REGULATOR_MODE_IDLE;
constraint->valid_ops_mask |= REGULATOR_CHANGE_MODE;
}
config.dev = &pdev->dev;
config.init_data = initdata;
config.driver_data = sreg;
config.of_node = pdev->dev.of_node;
/* register regulator */
rdev = regulator_register(rdesc, &config);
if (IS_ERR(rdev)) {
dev_err(dev, "failed to register %s\n",
rdesc->name);
return PTR_ERR(rdev);
}
rdev_dbg(rdev, "valid_modes_mask: 0x%x, valid_ops_mask: 0x%x\n",
constraint->valid_modes_mask, constraint->valid_ops_mask);
dev_set_drvdata(dev, rdev);
return 0;
}
static struct hi6421_spmi_reg_info regulator_info[] = {
HI6421V600_LDO(LDO3, ldo3_voltages,
0x16, 0x01, 0x51,
20000, 120,
0, 0),
HI6421V600_LDO(LDO4, ldo4_voltages,
0x17, 0x01, 0x52,
20000, 120,
0x10, 10000),
HI6421V600_LDO(LDO9, ldo9_voltages,
0x1c, 0x01, 0x57,
20000, 360,
0x10, 10000),
HI6421V600_LDO(LDO15, ldo15_voltages,
0x21, 0x01, 0x5c,
20000, 360,
0x10, 10000),
HI6421V600_LDO(LDO16, ldo15_voltages,
0x22, 0x01, 0x5d,
20000, 360,
0x10, 10000),
HI6421V600_LDO(LDO17, ldo17_voltages,
0x23, 0x01, 0x5e,
20000, 120,
0x10, 10000),
HI6421V600_LDO(LDO33, ldo17_voltages,
0x32, 0x01, 0x6d,
20000, 120,
0, 0),
HI6421V600_LDO(LDO34, ldo34_voltages,
0x33, 0x01, 0x6e,
20000, 120,
0, 0),
};
static int hi6421_spmi_regulator_probe(struct platform_device *pdev)
{
struct device *pmic_dev = pdev->dev.parent;
struct device_node *np = pmic_dev->of_node;
struct device_node *regulators, *child;
struct platform_device *new_pdev;
struct regulator_config config = { };
struct hi6421_spmi_reg_info *sreg;
struct hi6421_spmi_reg_info *info;
struct device *dev = &pdev->dev;
struct hi6421_spmi_pmic *pmic;
int ret;
struct regulator_dev *rdev;
int i;
/*
* This driver is meant to be called by hi6421-spmi-core,
@ -410,69 +254,46 @@ static int hi6421_spmi_regulator_probe(struct platform_device *pdev)
if (WARN_ON(!pmic))
return -ENODEV;
regulators = of_get_child_by_name(np, "regulators");
if (!regulators) {
dev_err(&pdev->dev, "regulator node not found\n");
return -ENODEV;
}
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
if (!sreg)
return -ENOMEM;
/*
* Parse all LDO regulator nodes
*/
for_each_child_of_node(regulators, child) {
dev_dbg(&pdev->dev, "adding child %pOF\n", child);
sreg->pmic = pmic;
mutex_init(&sreg->enable_mutex);
new_pdev = platform_device_alloc(child->name, -1);
new_pdev->dev.parent = pmic_dev;
new_pdev->dev.of_node = of_node_get(child);
for (i = 0; i < ARRAY_SIZE(regulator_info); i++) {
info = &regulator_info[i];
ret = platform_device_add(new_pdev);
if (ret < 0) {
platform_device_put(new_pdev);
continue;
config.dev = pdev->dev.parent;
config.driver_data = sreg;
config.regmap = pmic->regmap;
rdev = devm_regulator_register(dev, &info->desc, &config);
if (IS_ERR(rdev)) {
dev_err(dev, "failed to register %s\n",
info->desc.name);
return PTR_ERR(rdev);
}
ret = hi6421_spmi_regulator_probe_ldo(new_pdev, child, pmic);
if (ret < 0)
platform_device_put(new_pdev);
}
of_node_put(regulators);
return 0;
}
static int hi6421_spmi_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = dev_get_drvdata(&pdev->dev);
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
regulator_unregister(rdev);
if (rdev->desc->volt_table)
devm_kfree(&pdev->dev, (unsigned int *)rdev->desc->volt_table);
kfree(sreg);
return 0;
}
static const struct platform_device_id hi6421v600_regulator_table[] = {
static const struct platform_device_id hi6421_spmi_regulator_table[] = {
{ .name = "hi6421v600-regulator" },
{},
};
MODULE_DEVICE_TABLE(platform, hi6421v600_regulator_table);
MODULE_DEVICE_TABLE(platform, hi6421_spmi_regulator_table);
static struct platform_driver hi6421v600_regulator_driver = {
.id_table = hi6421v600_regulator_table,
static struct platform_driver hi6421_spmi_regulator_driver = {
.id_table = hi6421_spmi_regulator_table,
.driver = {
.name = "hi6421v600-regulator",
.name = "hi6421v600-regulator",
},
.probe = hi6421_spmi_regulator_probe,
.remove = hi6421_spmi_regulator_remove,
};
module_platform_driver(hi6421v600_regulator_driver);
module_platform_driver(hi6421_spmi_regulator_driver);
MODULE_DESCRIPTION("Hi6421v600 regulator driver");
MODULE_DESCRIPTION("Hi6421v600 SPMI regulator driver");
MODULE_LICENSE("GPL v2");

View File

@ -55,52 +55,13 @@ properties:
$ref: "/schemas/regulator/regulator.yaml#"
properties:
reg:
description: Enable register.
'#address-cells':
const: 1
'#size-cells':
const: 0
vsel-reg:
description: Voltage selector register.
enable-mask:
description: Bitmask used to enable the regulator.
voltage-table:
description: Table with the selector items for the voltage regulator.
minItems: 2
maxItems: 16
off-on-delay-us:
description: Time required for changing state to enabled in microseconds.
startup-delay-us:
description: Startup time in microseconds.
idle-mode-mask:
description: Bitmask used to put the regulator on idle mode.
eco-microamp:
description: Maximum current while on idle mode.
required:
- reg
- vsel-reg
- enable-mask
- voltage-table
- off-on-delay-us
- startup-delay-us
required:
- compatible
- reg
- regulators
additionalProperties: false
examples:
- |
/* pmic properties */
@ -117,43 +78,58 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
ldo3: ldo3@16 {
reg = <0x16>;
vsel-reg = <0x51>;
ldo3: LDO3 {
regulator-name = "ldo3";
regulator-min-microvolt = <1500000>;
regulator-max-microvolt = <2000000>;
regulator-boot-on;
enable-mask = <0x01>;
voltage-table = <1500000>, <1550000>, <1600000>, <1650000>,
<1700000>, <1725000>, <1750000>, <1775000>,
<1800000>, <1825000>, <1850000>, <1875000>,
<1900000>, <1925000>, <1950000>, <2000000>;
off-on-delay-us = <20000>;
startup-delay-us = <120>;
};
ldo4: ldo4@17 { /* 40 PIN */
reg = <0x17>;
vsel-reg = <0x52>;
ldo4: LDO4 {
regulator-name = "ldo4";
regulator-min-microvolt = <1725000>;
regulator-max-microvolt = <1900000>;
regulator-boot-on;
};
enable-mask = <0x01>;
idle-mode-mask = <0x10>;
eco-microamp = <10000>;
ldo9: LDO9 {
regulator-name = "ldo9";
regulator-min-microvolt = <1750000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
};
hi6421-vsel = <0x52 0x07>;
voltage-table = <1725000>, <1750000>, <1775000>, <1800000>,
<1825000>, <1850000>, <1875000>, <1900000>;
off-on-delay-us = <20000>;
startup-delay-us = <120>;
ldo15: LDO15 {
regulator-name = "ldo15";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3000000>;
regulator-always-on;
};
ldo16: LDO16 {
regulator-name = "ldo16";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3000000>;
regulator-boot-on;
};
ldo17: LDO17 {
regulator-name = "ldo17";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <3300000>;
};
ldo33: LDO33 {
regulator-name = "ldo33";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
};
ldo34: LDO34 {
regulator-name = "ldo34";
regulator-min-microvolt = <2600000>;
regulator-max-microvolt = <3300000>;
};
};
};

View File

@ -26,14 +26,22 @@ properties:
reg:
maxItems: 1
"#address-cells":
const: 2
"#size-cells":
const: 0
spmi-channel:
description: |
number of the Kirin 970 SPMI channel where the SPMI devices are connected.
required:
- compatible
- reg
- spmi-channel
- compatible
- reg
- spmi-channel
- "#address-cells"
- "#size-cells"
patternProperties:
"^pmic@[0-9a-f]$":
@ -43,6 +51,8 @@ patternProperties:
are documented at
drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml.
additionalProperties: false
examples:
- |
bus {
@ -51,11 +61,14 @@ examples:
spmi: spmi@fff24000 {
compatible = "hisilicon,kirin970-spmi-controller";
#address-cells = <2>;
#size-cells = <0>;
status = "ok";
reg = <0x0 0xfff24000 0x0 0x1000>;
spmi-channel = <2>;
pmic@0 {
reg = <0 0>;
/* pmic properties */
};
};

View File

@ -8,6 +8,7 @@
* Authors: Yu Chen <chenyu56@huawei.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
@ -41,15 +42,15 @@
#define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
#define USB3OTG_CTRL0 (0x00)
#define USB3OTG_CTRL3 (0x0C)
#define USB3OTG_CTRL3 (0x0c)
#define USB3OTG_CTRL4 (0x10)
#define USB3OTG_CTRL5 (0x14)
#define USB3OTG_CTRL7 (0x1C)
#define USB3OTG_CTRL7 (0x1c)
#define USB_MISC_CFG50 (0x50)
#define USB_MISC_CFG54 (0x54)
#define USB_MISC_CFG58 (0x58)
#define USB_MISC_CFG5C (0x5C)
#define USB_MISC_CFGA0 (0xA0)
#define USB_MISC_CFG5C (0x5c)
#define USB_MISC_CFGA0 (0xa0)
#define TCA_CLK_RST (0x200)
#define TCA_INTR_EN (0x204)
#define TCA_INTR_STS (0x208)
@ -66,14 +67,14 @@
#define CTRL5_USB2_SIDDQ BIT(0)
#define CTRL7_USB2_REFCLKSEL_MASK (3 << 3)
#define CTRL7_USB2_REFCLKSEL_ABB (3 << 3)
#define CTRL7_USB2_REFCLKSEL_PAD (2 << 3)
#define CTRL7_USB2_REFCLKSEL_MASK GENMASK(4, 3)
#define CTRL7_USB2_REFCLKSEL_ABB (BIT(4) | BIT(3))
#define CTRL7_USB2_REFCLKSEL_PAD BIT(4)
#define CFG50_USB3_PHY_TEST_POWERDOWN BIT(23)
#define CFG54_USB31PHY_CR_ADDR_MASK (0xFFFF)
#define CFG54_USB31PHY_CR_ADDR_SHIFT (16)
#define CFG54_USB31PHY_CR_ADDR_MASK GENMASK(31, 16)
#define CFG54_USB3PHY_REF_USE_PAD BIT(12)
#define CFG54_PHY0_PMA_PWR_STABLE BIT(11)
#define CFG54_PHY0_PCS_PWR_STABLE BIT(9)
@ -84,8 +85,7 @@
#define CFG54_USB31PHY_CR_CLK BIT(2)
#define CFG54_USB3_PHY0_ANA_PWR_EN BIT(1)
#define CFG58_USB31PHY_CR_DATA_MASK (0xFFFF)
#define CFG58_USB31PHY_CR_DATA_RD_START (16)
#define CFG58_USB31PHY_CR_DATA_MASK GENMASK(31, 16)
#define CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN BIT(1)
@ -102,20 +102,20 @@
#define CLK_RST_SUSPEND_CLK_EN BIT(0)
#define GCFG_ROLE_HSTDEV BIT(4)
#define GCFG_OP_MODE (3 << 0)
#define GCFG_OP_MODE GENMASK(1, 0)
#define GCFG_OP_MODE_CTRL_SYNC_MODE BIT(0)
#define TCPC_VALID BIT(4)
#define TCPC_LOW_POWER_EN BIT(3)
#define TCPC_MUX_CONTROL_MASK (3 << 0)
#define TCPC_MUX_CONTROL_MASK GENMASK(1, 0)
#define TCPC_MUX_CONTROL_USB31 BIT(0)
#define SYSMODE_CFG_TYPEC_DISABLE BIT(3)
#define VBUS_CTRL_POWERPRESENT_OVERRD (3 << 2)
#define VBUS_CTRL_VBUSVALID_OVERRD (3 << 0)
#define VBUS_CTRL_POWERPRESENT_OVERRD GENMASK(3, 2)
#define VBUS_CTRL_VBUSVALID_OVERRD GENMASK(1, 0)
#define KIRIN970_USB_DEFAULT_PHY_PARAM (0xFDFEE4)
#define KIRIN970_USB_DEFAULT_PHY_PARAM (0xfdfee4)
#define KIRIN970_USB_DEFAULT_PHY_VBOOST (0x5)
#define TX_VBOOST_LVL_REG (0xf)
@ -162,16 +162,14 @@ static int hi3670_phy_cr_set_sel(struct regmap *usb31misc)
static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction)
{
int ret;
int ret, reg;
if (direction)
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
CFG54_USB31PHY_CR_WR_EN,
CFG54_USB31PHY_CR_WR_EN);
reg = CFG54_USB31PHY_CR_WR_EN;
else
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
CFG54_USB31PHY_CR_RD_EN,
CFG54_USB31PHY_CR_RD_EN);
reg = CFG54_USB31PHY_CR_RD_EN;
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54, reg, reg);
if (ret)
return ret;
@ -180,16 +178,14 @@ static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction)
if (ret)
return ret;
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0);
return ret;
return regmap_update_bits(usb31misc, USB_MISC_CFG54,
CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0);
}
static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
{
u32 reg;
int retry = 100000;
int retry = 10;
int ret;
while (retry-- > 0) {
@ -202,6 +198,8 @@ static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
ret = hi3670_phy_cr_clk(usb31misc);
if (ret)
return ret;
usleep_range(10, 20);
}
return -ETIMEDOUT;
@ -216,9 +214,9 @@ static int hi3670_phy_cr_set_addr(struct regmap *usb31misc, u32 addr)
if (ret)
return ret;
reg &= ~(CFG54_USB31PHY_CR_ADDR_MASK << CFG54_USB31PHY_CR_ADDR_SHIFT);
reg |= ((addr & CFG54_USB31PHY_CR_ADDR_MASK) << CFG54_USB31PHY_CR_ADDR_SHIFT);
ret = regmap_write(usb31misc, USB_MISC_CFG54, reg);
reg = FIELD_PREP(CFG54_USB31PHY_CR_ADDR_MASK, addr);
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
CFG54_USB31PHY_CR_ADDR_MASK, reg);
return ret;
}
@ -255,8 +253,7 @@ static int hi3670_phy_cr_read(struct regmap *usb31misc, u32 addr, u32 *val)
if (ret)
return ret;
*val = (reg >> CFG58_USB31PHY_CR_DATA_RD_START) &
CFG58_USB31PHY_CR_DATA_MASK;
*val = FIELD_GET(CFG58_USB31PHY_CR_DATA_MASK, reg);
return 0;
}
@ -281,7 +278,7 @@ static int hi3670_phy_cr_write(struct regmap *usb31misc, u32 addr, u32 val)
return ret;
ret = regmap_write(usb31misc, USB_MISC_CFG58,
val & CFG58_USB31PHY_CR_DATA_MASK);
FIELD_PREP(CFG58_USB31PHY_CR_DATA_MASK, val));
if (ret)
return ret;
@ -329,24 +326,24 @@ static int hi3670_phy_set_params(struct hi3670_priv *priv)
return ret;
}
static int hi3670_is_abbclk_seleted(struct hi3670_priv *priv)
static bool hi3670_is_abbclk_selected(struct hi3670_priv *priv)
{
u32 reg;
if (!priv->sctrl) {
dev_err(priv->dev, "priv->sctrl is null!\n");
return 1;
return false;
}
if (regmap_read(priv->sctrl, SCTRL_SCDEEPSLEEPED, &reg)) {
dev_err(priv->dev, "SCTRL_SCDEEPSLEEPED read failed!\n");
return 1;
return false;
}
if ((reg & USB_CLK_SELECTED) == 0)
return 1;
return false;
return 0;
return true;
}
static int hi3670_config_phy_clock(struct hi3670_priv *priv)
@ -354,7 +351,7 @@ static int hi3670_config_phy_clock(struct hi3670_priv *priv)
u32 val, mask;
int ret;
if (hi3670_is_abbclk_seleted(priv)) {
if (!hi3670_is_abbclk_selected(priv)) {
/* usb refclk iso disable */
ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS,
USB_REFCLK_ISO_EN);
@ -571,7 +568,7 @@ static int hi3670_phy_exit(struct phy *phy)
if (ret)
goto out;
if (hi3670_is_abbclk_seleted(priv)) {
if (!hi3670_is_abbclk_selected(priv)) {
/* disable usb_tcxo_en */
ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3,
USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START);
@ -588,7 +585,7 @@ out:
return ret;
}
static struct phy_ops hi3670_phy_ops = {
static const struct phy_ops hi3670_phy_ops = {
.init = hi3670_phy_init,
.exit = hi3670_phy_exit,
.owner = THIS_MODULE,

View File

@ -8,6 +8,7 @@ title: Hisilicon Kirin970 USB PHY
maintainers:
- Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
description: |+
Bindings for USB3 PHY on HiSilicon Kirin 970.

View File

@ -269,7 +269,7 @@ ia_css_ptr hmm_alloc(size_t bytes, enum hmm_bo_type type,
hmm_set(bo->start, 0, bytes);
dev_dbg(atomisp_dev,
"%s: pages: 0x%08x (%ld bytes), type: %d from highmem %d, user ptr %p, cached %d\n",
"%s: pages: 0x%08x (%zu bytes), type: %d from highmem %d, user ptr %p, cached %d\n",
__func__, bo->start, bytes, type, from_highmem, userptr, cached);
return bo->start;

View File

@ -68,7 +68,7 @@ struct net_dev_context {
};
static struct list_head net_devices = LIST_HEAD_INIT(net_devices);
static struct mutex probe_disc_mt; /* ch->linked = true, most_nd_open */
static DEFINE_MUTEX(probe_disc_mt); /* ch->linked = true, most_nd_open */
static DEFINE_SPINLOCK(list_lock); /* list_head, ch->linked = false, dev_hold */
static struct most_component comp;
@ -520,7 +520,6 @@ static int __init most_net_init(void)
{
int err;
mutex_init(&probe_disc_mt);
err = most_register_component(&comp);
if (err)
return err;

Some files were not shown because too many files have changed in this diff Show More