Commit 888a87b5 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'iio-for-4.9c' of...

Merge tag 'iio-for-4.9c' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next

Jonathan writes:

Third set of new device support, functionality and cleanups for IIO in the 4.9 cycle.

Given Linus is hinting (strongly!) at an rc8 this last set is hopefully in
time for the 4.9 merge window.  The zpa2326 and si1145 drivers provide
fine illustrations that devices aren't getting any simpler!

I'm also particularly pleased Linus Walliej did such a thorough job of cleaning
up one of my old drivers.

New device support
* mCube MC3230 accelerometer
  - new fairly minimal driver.
* Murata zpa2326
  - extensive new driver supporting the rather 'novel' buffering of data this
    device provides and handling both it's own data ready trigger and other
    triggers rather elegantly.
* si1141, si1142, si1143, si1145, si1146 and si1147 proximity, UV, visible and
  IR sensors.
  - another extensive new driver supporting all the key bits of what this
    set of devices supplies including dataready triggers, buffers and all the
    various data channels.

Functionality
* kxsd9 - Linus brought this scratch driver I wrote in one afternoon years ago
  up to date adding lots of good stuff along the way.
  - SPI support after extensive rework of the driver.
  - Triggered buffer capture support.
  - Runtime PM.
  - Regulator handling.
  - Mounting matrix support.
* mma7660
  - Add MODULE_DEVICE_TABLE to support autoprobing.

Cleanups
* ad5933
  - Align some function arguements nicely.
* med_z188
  - Constify iio_info structure.
* sca3000
  - Implement IIO_CHAN_INFO_SAMP_FREQ rather than a hand rolled attr.
    There are still quite a few drivers that would benefit from similar updates.
* ssp_sensors
  - Constify iio_info structures in accel and gyro drivers.
parents 530a7061 ac45e57f
...@@ -57,6 +57,7 @@ maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator ...@@ -57,6 +57,7 @@ maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
mc,rv3029c2 Real Time Clock Module with I2C-Bus mc,rv3029c2 Real Time Clock Module with I2C-Bus
mcube,mc3230 mCube 3-axis 8-bit digital accelerometer
microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k) microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k)
microchip,mcp4531-103 Microchip 7-bit Single I2C Digital Potentiometer (10k) microchip,mcp4531-103 Microchip 7-bit Single I2C Digital Potentiometer (10k)
microchip,mcp4531-503 Microchip 7-bit Single I2C Digital Potentiometer (50k) microchip,mcp4531-503 Microchip 7-bit Single I2C Digital Potentiometer (50k)
......
Murata ZPA2326 pressure sensor
Pressure sensor from Murata with SPI and I2C bus interfaces.
Required properties:
- compatible: "murata,zpa2326"
- reg: the I2C address or SPI chip select the device will respond to
Recommended properties for SPI bus usage:
- spi-max-frequency: maximum SPI bus frequency as documented in
Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
- vref-supply: an optional regulator that needs to be on to provide VREF
power to the sensor
- vdd-supply: an optional regulator that needs to be on to provide VDD
power to the sensor
- interrupt-parent: phandle to the parent interrupt controller as documented in
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- interrupts: interrupt mapping for IRQ as documented in
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
Example:
zpa2326@5c {
compatible = "murata,zpa2326";
reg = <0x5c>;
interrupt-parent = <&gpio>;
interrupts = <12>;
vdd-supply = <&ldo_1v8_gnss>;
};
...@@ -119,14 +119,35 @@ config IIO_ST_ACCEL_SPI_3AXIS ...@@ -119,14 +119,35 @@ config IIO_ST_ACCEL_SPI_3AXIS
config KXSD9 config KXSD9
tristate "Kionix KXSD9 Accelerometer Driver" tristate "Kionix KXSD9 Accelerometer Driver"
depends on SPI select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help help
Say yes here to build support for the Kionix KXSD9 accelerometer. Say yes here to build support for the Kionix KXSD9 accelerometer.
Currently this only supports the device via an SPI interface. It can be accessed using an (optional) SPI or I2C interface.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called kxsd9. will be called kxsd9.
config KXSD9_SPI
tristate "Kionix KXSD9 SPI transport"
depends on KXSD9
depends on SPI
default KXSD9
select REGMAP_SPI
help
Say yes here to enable the Kionix KXSD9 accelerometer
SPI transport channel.
config KXSD9_I2C
tristate "Kionix KXSD9 I2C transport"
depends on KXSD9
depends on I2C
default KXSD9
select REGMAP_I2C
help
Say yes here to enable the Kionix KXSD9 accelerometer
I2C transport channel.
config KXCJK1013 config KXCJK1013
tristate "Kionix 3-Axis Accelerometer Driver" tristate "Kionix 3-Axis Accelerometer Driver"
depends on I2C depends on I2C
...@@ -140,6 +161,16 @@ config KXCJK1013 ...@@ -140,6 +161,16 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called kxcjk-1013. be called kxcjk-1013.
config MC3230
tristate "mCube MC3230 Digital Accelerometer Driver"
depends on I2C
help
Say yes here to build support for the mCube MC3230 low-g tri-axial
digital accelerometer.
To compile this driver as a module, choose M here: the
module will be called mc3230.
config MMA7455 config MMA7455
tristate tristate
select IIO_BUFFER select IIO_BUFFER
......
...@@ -13,6 +13,9 @@ obj-$(CONFIG_DMARD09) += dmard09.o ...@@ -13,6 +13,9 @@ obj-$(CONFIG_DMARD09) += dmard09.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o obj-$(CONFIG_KXSD9) += kxsd9.o
obj-$(CONFIG_KXSD9_SPI) += kxsd9-spi.o
obj-$(CONFIG_KXSD9_I2C) += kxsd9-i2c.o
obj-$(CONFIG_MC3230) += mc3230.o
obj-$(CONFIG_MMA7455) += mma7455_core.o obj-$(CONFIG_MMA7455) += mma7455_core.o
obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o
......
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include "kxsd9.h"
static int kxsd9_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
static const struct regmap_config config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x0e,
};
struct regmap *regmap;
regmap = devm_regmap_init_i2c(i2c, &config);
if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "Failed to register i2c regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return kxsd9_common_probe(&i2c->dev,
regmap,
i2c->name);
}
static int kxsd9_i2c_remove(struct i2c_client *client)
{
return kxsd9_common_remove(&client->dev);
}
#ifdef CONFIG_OF
static const struct of_device_id kxsd9_of_match[] = {
{ .compatible = "kionix,kxsd9", },
{ },
};
MODULE_DEVICE_TABLE(of, kxsd9_of_match);
#else
#define kxsd9_of_match NULL
#endif
static const struct i2c_device_id kxsd9_i2c_id[] = {
{"kxsd9", 0},
{ },
};
MODULE_DEVICE_TABLE(i2c, kxsd9_i2c_id);
static struct i2c_driver kxsd9_i2c_driver = {
.driver = {
.name = "kxsd9",
.of_match_table = of_match_ptr(kxsd9_of_match),
.pm = &kxsd9_dev_pm_ops,
},
.probe = kxsd9_i2c_probe,
.remove = kxsd9_i2c_remove,
.id_table = kxsd9_i2c_id,
};
module_i2c_driver(kxsd9_i2c_driver);
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include "kxsd9.h"
static int kxsd9_spi_probe(struct spi_device *spi)
{
static const struct regmap_config config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x0e,
};
struct regmap *regmap;
spi->mode = SPI_MODE_0;
regmap = devm_regmap_init_spi(spi, &config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n",
__func__, PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return kxsd9_common_probe(&spi->dev,
regmap,
spi_get_device_id(spi)->name);
}
static int kxsd9_spi_remove(struct spi_device *spi)
{
return kxsd9_common_remove(&spi->dev);
}
static const struct spi_device_id kxsd9_spi_id[] = {
{"kxsd9", 0},
{ },
};
MODULE_DEVICE_TABLE(spi, kxsd9_spi_id);
static struct spi_driver kxsd9_spi_driver = {
.driver = {
.name = "kxsd9",
.pm = &kxsd9_dev_pm_ops,
},
.probe = kxsd9_spi_probe,
.remove = kxsd9_spi_remove,
.id_table = kxsd9_spi_id,
};
module_spi_driver(kxsd9_spi_driver);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
#include <linux/device.h>
#include <linux/kernel.h>
#define KXSD9_STATE_RX_SIZE 2
#define KXSD9_STATE_TX_SIZE 2
int kxsd9_common_probe(struct device *dev,
struct regmap *map,
const char *name);
int kxsd9_common_remove(struct device *dev);
extern const struct dev_pm_ops kxsd9_dev_pm_ops;
/**
* mCube MC3230 3-Axis Accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define MC3230_REG_XOUT 0x00
#define MC3230_REG_YOUT 0x01
#define MC3230_REG_ZOUT 0x02
#define MC3230_REG_MODE 0x07
#define MC3230_MODE_OPCON_MASK 0x03
#define MC3230_MODE_OPCON_WAKE 0x01
#define MC3230_MODE_OPCON_STANDBY 0x03
#define MC3230_REG_CHIP_ID 0x18
#define MC3230_CHIP_ID 0x01
#define MC3230_REG_PRODUCT_CODE 0x3b
#define MC3230_PRODUCT_CODE 0x19
/*
* The accelerometer has one measurement range:
*
* -1.5g - +1.5g (8-bit, signed)
*
* scale = (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765
*/
static const int mc3230_nscale = 115411765;
#define MC3230_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec mc3230_channels[] = {
MC3230_CHANNEL(MC3230_REG_XOUT, X),
MC3230_CHANNEL(MC3230_REG_YOUT, Y),
MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
};
struct mc3230_data {
struct i2c_client *client;
};
static int mc3230_set_opcon(struct mc3230_data *data, int opcon)
{
int ret;
struct i2c_client *client = data->client;
ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE);
if (ret < 0) {
dev_err(&client->dev, "failed to read mode reg: %d\n", ret);
return ret;
}
ret &= ~MC3230_MODE_OPCON_MASK;
ret |= opcon;
ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret);
if (ret < 0) {
dev_err(&client->dev, "failed to write mode reg: %d\n", ret);
return ret;
}
return 0;
}
static int mc3230_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mc3230_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = i2c_smbus_read_byte_data(data->client, chan->address);
if (ret < 0)
return ret;
*val = sign_extend32(ret, 7);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = mc3230_nscale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info mc3230_info = {
.driver_module = THIS_MODULE,
.read_raw = mc3230_read_raw,
};
static int mc3230_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct mc3230_data *data;
/* First check chip-id and product-id */
ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
if (ret != MC3230_CHIP_ID)
return (ret < 0) ? ret : -ENODEV;
ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
if (ret != MC3230_PRODUCT_CODE)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &mc3230_info;
indio_dev->name = "mc3230";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mc3230_channels;
indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
}
return ret;
}
static int mc3230_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
}
#ifdef CONFIG_PM_SLEEP
static int mc3230_suspend(struct device *dev)
{
struct mc3230_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
}
static int mc3230_resume(struct device *dev)
{
struct mc3230_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
}
#endif
static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
static const struct i2c_device_id mc3230_i2c_id[] = {
{"mc3230", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
static struct i2c_driver mc3230_driver = {
.driver = {
.name = "mc3230",
.pm = &mc3230_pm_ops,
},
.probe = mc3230_probe,
.remove = mc3230_remove,
.id_table = mc3230_i2c_id,
};
module_i2c_driver(mc3230_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");
...@@ -251,6 +251,7 @@ static const struct i2c_device_id mma7660_i2c_id[] = { ...@@ -251,6 +251,7 @@ static const struct i2c_device_id mma7660_i2c_id[] = {
{"mma7660", 0}, {"mma7660", 0},
{} {}
}; };
MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id);
static const struct acpi_device_id mma7660_acpi_id[] = { static const struct acpi_device_id mma7660_acpi_id[] = {
{"MMA7660", 0}, {"MMA7660", 0},
......
...@@ -74,7 +74,7 @@ static int ssp_accel_write_raw(struct iio_dev *indio_dev, ...@@ -74,7 +74,7 @@ static int ssp_accel_write_raw(struct iio_dev *indio_dev,
return -EINVAL; return -EINVAL;
} }
static struct iio_info ssp_accel_iio_info = { static const struct iio_info ssp_accel_iio_info = {
.read_raw = &ssp_accel_read_raw, .read_raw = &ssp_accel_read_raw,
.write_raw = &ssp_accel_write_raw, .write_raw = &ssp_accel_write_raw,
}; };
......
...@@ -78,7 +78,7 @@ static int z188_iio_read_raw(struct iio_dev *iio_dev, ...@@ -78,7 +78,7 @@ static int z188_iio_read_raw(struct iio_dev *iio_dev,
return ret; return ret;
} }
static struct iio_info z188_adc_info = { static const struct iio_info z188_adc_info = {
.read_raw = &z188_iio_read_raw, .read_raw = &z188_iio_read_raw,
.driver_module = THIS_MODULE, .driver_module = THIS_MODULE,
}; };
......
...@@ -74,7 +74,7 @@ static int ssp_gyro_write_raw(struct iio_dev *indio_dev, ...@@ -74,7 +74,7 @@ static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
return -EINVAL; return -EINVAL;
} }
static struct iio_info ssp_gyro_iio_info = { static const struct iio_info ssp_gyro_iio_info = {
.read_raw = &ssp_gyro_read_raw, .read_raw = &ssp_gyro_read_raw,
.write_raw = &ssp_gyro_write_raw, .write_raw = &ssp_gyro_write_raw,
}; };
......
...@@ -267,6 +267,19 @@ config PA12203001 ...@@ -267,6 +267,19 @@ config PA12203001
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called pa12203001. will be called pa12203001.
config SI1145
tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here if you want to build a driver for the Silicon Labs SI1132 or
SI1141/2/3/5/6/7 combined ambient light, UV index and proximity sensor
chips.
To compile this driver as a module, choose M here: the module will be
called si1145.
config STK3310 config STK3310
tristate "STK3310 ALS and proximity sensor" tristate "STK3310 ALS and proximity sensor"
depends on I2C depends on I2C
......
...@@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o ...@@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_PA12203001) += pa12203001.o
obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_RPR0521) += rpr0521.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
obj-$(CONFIG_SI1145) += si1145.o
obj-$(CONFIG_STK3310) += stk3310.o obj-$(CONFIG_STK3310) += stk3310.o
obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3414) += tcs3414.o
obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TCS3472) += tcs3472.o
......
This diff is collapsed.
...@@ -187,4 +187,26 @@ config HP206C ...@@ -187,4 +187,26 @@ config HP206C
This driver can also be built as a module. If so, the module will This driver can also be built as a module. If so, the module will
be called hp206c. be called hp206c.
config ZPA2326
tristate "Murata ZPA2326 pressure sensor driver"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP
select ZPA2326_I2C if I2C
select ZPA2326_SPI if SPI_MASTER
help
Say Y here to build support for the Murata ZPA2326 pressure and
temperature sensor.
To compile this driver as a module, choose M here: the module will
be called zpa2326.
config ZPA2326_I2C
tristate
select REGMAP_I2C
config ZPA2326_SPI
tristate
select REGMAP_SPI
endmenu endmenu
...@@ -22,6 +22,9 @@ st_pressure-y := st_pressure_core.o ...@@ -22,6 +22,9 @@ st_pressure-y := st_pressure_core.o
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
obj-$(CONFIG_T5403) += t5403.o obj-$(CONFIG_T5403) += t5403.o
obj-$(CONFIG_HP206C) += hp206c.o obj-$(CONFIG_HP206C) += hp206c.o
obj-$(CONFIG_ZPA2326) += zpa2326.o
obj-$(CONFIG_ZPA2326_I2C) += zpa2326_i2c.o
obj-$(CONFIG_ZPA2326_SPI) += zpa2326_spi.o
obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o
obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o
This diff is collapsed.
/*
* Murata ZPA2326 pressure and temperature sensor IIO driver
*
* Copyright (c) 2016 Parrot S.A.
*
* Author: Gregor Boirie <gregor.boirie@parrot.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef _ZPA2326_H
#define _ZPA2326_H
/* Register map. */
#define ZPA2326_REF_P_XL_REG (0x8)
#define ZPA2326_REF_P_L_REG (0x9)
#define ZPA2326_REF_P_H_REG (0xa)
#define ZPA2326_DEVICE_ID_REG (0xf)
#define ZPA2326_DEVICE_ID (0xb9)
#define ZPA2326_RES_CONF_REG (0x10)
#define ZPA2326_CTRL_REG0_REG (0x20)
#define ZPA2326_CTRL_REG0_ONE_SHOT BIT(0)
#define ZPA2326_CTRL_REG0_ENABLE BIT(1)
#define ZPA2326_CTRL_REG1_REG (0x21)
#define ZPA2326_CTRL_REG1_MASK_DATA_READY BIT(2)
#define ZPA2326_CTRL_REG2_REG (0x22)
#define ZPA2326_CTRL_REG2_SWRESET BIT(2)
#define ZPA2326_CTRL_REG3_REG (0x23)
#define ZPA2326_CTRL_REG3_ODR_SHIFT (4)
#define ZPA2326_CTRL_REG3_ENABLE_MEAS BIT(7)
#define ZPA2326_INT_SOURCE_REG (0x24)
#define ZPA2326_INT_SOURCE_DATA_READY BIT(2)
#define ZPA2326_THS_P_LOW_REG (0x25)
#define ZPA2326_THS_P_HIGH_REG (0x26)
#define ZPA2326_STATUS_REG (0x27)
#define ZPA2326_STATUS_P_DA BIT(1)
#define ZPA2326_STATUS_FIFO_E BIT(2)
#define ZPA2326_STATUS_P_OR BIT(5)
#define ZPA2326_PRESS_OUT_XL_REG (0x28)
#define ZPA2326_PRESS_OUT_L_REG (0x29)
#define ZPA2326_PRESS_OUT_H_REG (0x2a)
#define ZPA2326_TEMP_OUT_L_REG (0x2b)
#define ZPA2326_TEMP_OUT_H_REG (0x2c)
struct device;
struct regmap;
bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg);
bool zpa2326_isreg_readable(struct device *dev, unsigned int reg);
bool zpa2326_isreg_precious(struct device *dev, unsigned int reg);
/**
* zpa2326_probe() - Instantiate and register core ZPA2326 IIO device
* @parent: Hardware sampling device the created IIO device will be a child of.
* @name: Arbitrary name to identify the device.
* @irq: Interrupt line, negative if none.
* @hwid: Expected device hardware id.
* @regmap: Registers map used to abstract underlying bus accesses.
*
* Return: Zero when successful, a negative error code otherwise.
*/
int zpa2326_probe(struct device *parent,
const char *name,
int irq,
unsigned int hwid,
struct regmap *regmap);
/**
* zpa2326_remove() - Unregister and destroy core ZPA2326 IIO device.
* @parent: Hardware sampling device the IIO device to remove is a child of.
*/
void zpa2326_remove(const struct device *parent);
#ifdef CONFIG_PM
#include <linux/pm.h>
extern const struct dev_pm_ops zpa2326_pm_ops;
#define ZPA2326_PM_OPS (&zpa2326_pm_ops)
#else
#define ZPA2326_PM_OPS (NULL)
#endif
#endif
/*
* Murata ZPA2326 I2C pressure and temperature sensor driver
*
* Copyright (c) 2016 Parrot S.A.
*
* Author: Gregor Boirie <gregor.boirie@parrot.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/i2c.h>
#include <linux/of_device.h>
#include "zpa2326.h"
/*
* read_flag_mask:
* - address bit 7 must be set to request a register read operation
*/
static const struct regmap_config zpa2326_regmap_i2c_config = {
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = zpa2326_isreg_writeable,
.readable_reg = zpa2326_isreg_readable,
.precious_reg = zpa2326_isreg_precious,
.max_register = ZPA2326_TEMP_OUT_H_REG,
.read_flag_mask = BIT(7),
.cache_type = REGCACHE_NONE,
};
static unsigned int zpa2326_i2c_hwid(const struct i2c_client *client)
{
#define ZPA2326_SA0(_addr) (_addr & BIT(0))
#define ZPA2326_DEVICE_ID_SA0_SHIFT (1)
/* Identification register bit 1 mirrors device address bit 0. */
return (ZPA2326_DEVICE_ID |
(ZPA2326_SA0(client->addr) << ZPA2326_DEVICE_ID_SA0_SHIFT));
}
static int zpa2326_probe_i2c(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &zpa2326_regmap_i2c_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "failed to init registers map");
return PTR_ERR(regmap);
}
return zpa2326_probe(&client->dev, i2c_id->name, client->irq,
zpa2326_i2c_hwid(client), regmap);
}
static int zpa2326_remove_i2c(struct i2c_client *client)
{
zpa2326_remove(&client->dev);
return 0;
}
static const struct i2c_device_id zpa2326_i2c_ids[] = {
{ "zpa2326", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, zpa2326_i2c_ids);
#if defined(CONFIG_OF)
static const struct of_device_id zpa2326_i2c_matches[] = {
{ .compatible = "murata,zpa2326" },
{ }
};
MODULE_DEVICE_TABLE(of, zpa2326_i2c_matches);
#endif
static struct i2c_driver zpa2326_i2c_driver = {
.driver = {
.name = "zpa2326-i2c",
.of_match_table = of_match_ptr(zpa2326_i2c_matches),
.pm = ZPA2326_PM_OPS,
},
.probe = zpa2326_probe_i2c,
.remove = zpa2326_remove_i2c,
.id_table = zpa2326_i2c_ids,
};
module_i2c_driver(zpa2326_i2c_driver);
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
MODULE_DESCRIPTION("I2C driver for Murata ZPA2326 pressure sensor");
MODULE_LICENSE("GPL v2");
/*
* Murata ZPA2326 SPI pressure and temperature sensor driver
*
* Copyright (c) 2016 Parrot S.A.
*
* Author: Gregor Boirie <gregor.boirie@parrot.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/of_device.h>
#include "zpa2326.h"
/*
* read_flag_mask:
* - address bit 7 must be set to request a register read operation
* - address bit 6 must be set to request register address auto increment
*/
static const struct regmap_config zpa2326_regmap_spi_config = {
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = zpa2326_isreg_writeable,
.readable_reg = zpa2326_isreg_readable,
.precious_reg = zpa2326_isreg_precious,
.max_register = ZPA2326_TEMP_OUT_H_REG,
.read_flag_mask = BIT(7) | BIT(6),
.cache_type = REGCACHE_NONE,
};
static int zpa2326_probe_spi(struct spi_device *spi)
{
struct regmap *regmap;
int err;
regmap = devm_regmap_init_spi(spi, &zpa2326_regmap_spi_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "failed to init registers map");
return PTR_ERR(regmap);
}
/*
* Enforce SPI slave settings to prevent from DT misconfiguration.
*
* Clock is idle high. Sampling happens on trailing edge, i.e., rising
* edge. Maximum bus frequency is 1 MHz. Registers are 8 bits wide.
*/
spi->mode = SPI_MODE_3;
spi->max_speed_hz = min(spi->max_speed_hz, 1000000U);
spi->bits_per_word = 8;
err = spi_setup(spi);
if (err < 0)
return err;
return zpa2326_probe(&spi->dev, spi_get_device_id(spi)->name,
spi->irq, ZPA2326_DEVICE_ID, regmap);
}
static int zpa2326_remove_spi(struct spi_device *spi)
{
zpa2326_remove(&spi->dev);
return 0;
}
static const struct spi_device_id zpa2326_spi_ids[] = {
{ "zpa2326", 0 },
{ },
};
MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids);
#if defined(CONFIG_OF)
static const struct of_device_id zpa2326_spi_matches[] = {
{ .compatible = "murata,zpa2326" },
{ }
};
MODULE_DEVICE_TABLE(of, zpa2326_spi_matches);
#endif
static struct spi_driver zpa2326_spi_driver = {
.driver = {
.name = "zpa2326-spi",
.of_match_table = of_match_ptr(zpa2326_spi_matches),
.pm = ZPA2326_PM_OPS,
},
.probe = zpa2326_probe_spi,
.remove = zpa2326_remove_spi,
.id_table = zpa2326_spi_ids,
};
module_spi_driver(zpa2326_spi_driver);
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
MODULE_DESCRIPTION("SPI driver for Murata ZPA2326 pressure sensor");
MODULE_LICENSE("GPL v2");
...@@ -113,6 +113,7 @@ ...@@ -113,6 +113,7 @@
#define SCA3000_OUT_CTRL_BUF_X_EN 0x10 #define SCA3000_OUT_CTRL_BUF_X_EN 0x10
#define SCA3000_OUT_CTRL_BUF_Y_EN 0x08 #define SCA3000_OUT_CTRL_BUF_Y_EN 0x08
#define SCA3000_OUT_CTRL_BUF_Z_EN 0x04 #define SCA3000_OUT_CTRL_BUF_Z_EN 0x04
#define SCA3000_OUT_CTRL_BUF_DIV_MASK 0x03
#define SCA3000_OUT_CTRL_BUF_DIV_4 0x02 #define SCA3000_OUT_CTRL_BUF_DIV_4 0x02
#define SCA3000_OUT_CTRL_BUF_DIV_2 0x01 #define SCA3000_OUT_CTRL_BUF_DIV_2 0x01
......
...@@ -402,6 +402,7 @@ static const struct iio_event_spec sca3000_event = { ...@@ -402,6 +402,7 @@ static const struct iio_event_spec sca3000_event = {
.channel2 = mod, \ .channel2 = mod, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
.address = index, \ .address = index, \
.scan_index = index, \ .scan_index = index, \
.scan_type = { \ .scan_type = { \
...@@ -443,6 +444,97 @@ static u8 sca3000_addresses[3][3] = { ...@@ -443,6 +444,97 @@ static u8 sca3000_addresses[3][3] = {
SCA3000_MD_CTRL_OR_Z}, SCA3000_MD_CTRL_OR_Z},
}; };
/**
* __sca3000_get_base_freq() obtain mode specific base frequency
*
* lock must be held
**/
static inline int __sca3000_get_base_freq(struct sca3000_state *st,
const struct sca3000_chip_info *info,
int *base_freq)
{
int ret;
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
if (ret)
goto error_ret;
switch (0x03 & st->rx[0]) {
case SCA3000_MEAS_MODE_NORMAL:
*base_freq = info->measurement_mode_freq;
break;
case SCA3000_MEAS_MODE_OP_1:
*base_freq = info->option_mode_1_freq;
break;
case SCA3000_MEAS_MODE_OP_2:
*base_freq = info->option_mode_2_freq;
break;
}
error_ret:
return ret;
}
/**
* read_raw handler for IIO_CHAN_INFO_SAMP_FREQ
*
* lock must be held
**/
static int read_raw_samp_freq(struct sca3000_state *st, int *val)
{
int ret;
ret = __sca3000_get_base_freq(st, st->info, val);
if (ret)
return ret;
ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
if (ret < 0)
return ret;
if (*val > 0) {
ret &= SCA3000_OUT_CTRL_BUF_DIV_MASK;
switch (ret) {
case SCA3000_OUT_CTRL_BUF_DIV_2:
*val /= 2;
break;
case SCA3000_OUT_CTRL_BUF_DIV_4:
*val /= 4;
break;
}
}
return 0;
}
/**
* write_raw handler for IIO_CHAN_INFO_SAMP_FREQ
*
* lock must be held
**/
static int write_raw_samp_freq(struct sca3000_state *st, int val)
{
int ret, base_freq, ctrlval;
ret = __sca3000_get_base_freq(st, st->info, &base_freq);
if (ret)
return ret;
ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
if (ret < 0)
return ret;
ctrlval = ret & ~SCA3000_OUT_CTRL_BUF_DIV_MASK;
if (val == base_freq / 2)
ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2;
if (val == base_freq / 4)
ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4;
else if (val != base_freq)
return -EINVAL;
return sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
ctrlval);
}
static int sca3000_read_raw(struct iio_dev *indio_dev, static int sca3000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, struct iio_chan_spec const *chan,
int *val, int *val,
...@@ -495,11 +587,38 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, ...@@ -495,11 +587,38 @@ static int sca3000_read_raw(struct iio_dev *indio_dev,
*val = -214; *val = -214;
*val2 = 600000; *val2 = 600000;
return IIO_VAL_INT_PLUS_MICRO; return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&st->lock);
ret = read_raw_samp_freq(st, val);
mutex_unlock(&st->lock);
return ret ? ret : IIO_VAL_INT;
default: default:
return -EINVAL; return -EINVAL;
} }
} }
static int sca3000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct sca3000_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2)
return -EINVAL;
mutex_lock(&st->lock);
ret = write_raw_samp_freq(st, val);
mutex_unlock(&st->lock);
return ret;
default:
return -EINVAL;
}
return ret;
}
/** /**
* sca3000_read_av_freq() sysfs function to get available frequencies * sca3000_read_av_freq() sysfs function to get available frequencies
* *
...@@ -548,133 +667,12 @@ static ssize_t sca3000_read_av_freq(struct device *dev, ...@@ -548,133 +667,12 @@ static ssize_t sca3000_read_av_freq(struct device *dev,
return ret; return ret;
} }
/**
* __sca3000_get_base_freq() obtain mode specific base frequency
*
* lock must be held
**/
static inline int __sca3000_get_base_freq(struct sca3000_state *st,
const struct sca3000_chip_info *info,
int *base_freq)
{
int ret;
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
if (ret)
goto error_ret;
switch (0x03 & st->rx[0]) {
case SCA3000_MEAS_MODE_NORMAL:
*base_freq = info->measurement_mode_freq;
break;
case SCA3000_MEAS_MODE_OP_1:
*base_freq = info->option_mode_1_freq;
break;
case SCA3000_MEAS_MODE_OP_2:
*base_freq = info->option_mode_2_freq;
break;
}
error_ret:
return ret;
}
/**
* sca3000_read_frequency() sysfs interface to get the current frequency
**/
static ssize_t sca3000_read_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct sca3000_state *st = iio_priv(indio_dev);
int ret, len = 0, base_freq = 0, val;
mutex_lock(&st->lock);
ret = __sca3000_get_base_freq(st, st->info, &base_freq);
if (ret)
goto error_ret_mut;
ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
mutex_unlock(&st->lock);
if (ret < 0)
goto error_ret;
val = ret;
if (base_freq > 0)
switch (val & 0x03) {
case 0x00:
case 0x03:
len = sprintf(buf, "%d\n", base_freq);
break;
case 0x01:
len = sprintf(buf, "%d\n", base_freq / 2);
break;
case 0x02:
len = sprintf(buf, "%d\n", base_freq / 4);
break;
}
return len;
error_ret_mut:
mutex_unlock(&st->lock);
error_ret:
return ret;
}
/**
* sca3000_set_frequency() sysfs interface to set the current frequency
**/
static ssize_t sca3000_set_frequency(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct sca3000_state *st = iio_priv(indio_dev);
int ret, base_freq = 0;
int ctrlval;
int val;
ret = kstrtoint(buf, 10, &val);
if (ret)
return ret;
mutex_lock(&st->lock);
/* What mode are we in? */
ret = __sca3000_get_base_freq(st, st->info, &base_freq);
if (ret)
goto error_free_lock;
ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
if (ret < 0)
goto error_free_lock;
ctrlval = ret;
/* clear the bits */
ctrlval &= ~0x03;
if (val == base_freq / 2) {
ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2;
} else if (val == base_freq / 4) {
ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4;
} else if (val != base_freq) {
ret = -EINVAL;
goto error_free_lock;
}
ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
ctrlval);
error_free_lock:
mutex_unlock(&st->lock);
return ret ? ret : len;
}
/* /*
* Should only really be registered if ring buffer support is compiled in. * Should only really be registered if ring buffer support is compiled in.
* Does no harm however and doing it right would add a fair bit of complexity * Does no harm however and doing it right would add a fair bit of complexity
*/ */
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq); static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq);
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
sca3000_read_frequency,
sca3000_set_frequency);
/** /**
* sca3000_read_thresh() - query of a threshold * sca3000_read_thresh() - query of a threshold
**/ **/
...@@ -751,7 +749,6 @@ static struct attribute *sca3000_attributes[] = { ...@@ -751,7 +749,6 @@ static struct attribute *sca3000_attributes[] = {
&iio_dev_attr_measurement_mode_available.dev_attr.attr, &iio_dev_attr_measurement_mode_available.dev_attr.attr,
&iio_dev_attr_measurement_mode.dev_attr.attr, &iio_dev_attr_measurement_mode.dev_attr.attr,
&iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
NULL, NULL,
}; };
...@@ -1086,6 +1083,7 @@ static int sca3000_clean_setup(struct sca3000_state *st) ...@@ -1086,6 +1083,7 @@ static int sca3000_clean_setup(struct sca3000_state *st)
static const struct iio_info sca3000_info = { static const struct iio_info sca3000_info = {
.attrs = &sca3000_attribute_group, .attrs = &sca3000_attribute_group,
.read_raw = &sca3000_read_raw, .read_raw = &sca3000_read_raw,
.write_raw = &sca3000_write_raw,
.event_attrs = &sca3000_event_attribute_group, .event_attrs = &sca3000_event_attribute_group,
.read_event_value = &sca3000_read_thresh, .read_event_value = &sca3000_read_thresh,
.write_event_value = &sca3000_write_thresh, .write_event_value = &sca3000_write_thresh,
......
...@@ -156,8 +156,7 @@ static const struct iio_chan_spec ad5933_channels[] = { ...@@ -156,8 +156,7 @@ static const struct iio_chan_spec ad5933_channels[] = {
}, },
}; };
static int ad5933_i2c_write(struct i2c_client *client, static int ad5933_i2c_write(struct i2c_client *client, u8 reg, u8 len, u8 *data)
u8 reg, u8 len, u8 *data)
{ {
int ret; int ret;
...@@ -171,8 +170,7 @@ static int ad5933_i2c_write(struct i2c_client *client, ...@@ -171,8 +170,7 @@ static int ad5933_i2c_write(struct i2c_client *client,
return 0; return 0;
} }
static int ad5933_i2c_read(struct i2c_client *client, static int ad5933_i2c_read(struct i2c_client *client, u8 reg, u8 len, u8 *data)
u8 reg, u8 len, u8 *data)
{ {
int ret; int ret;
...@@ -269,7 +267,8 @@ static int ad5933_setup(struct ad5933_state *st) ...@@ -269,7 +267,8 @@ static int ad5933_setup(struct ad5933_state *st)
dat = cpu_to_be16(st->settling_cycles); dat = cpu_to_be16(st->settling_cycles);
ret = ad5933_i2c_write(st->client, ret = ad5933_i2c_write(st->client,
AD5933_REG_SETTLING_CYCLES, 2, (u8 *)&dat); AD5933_REG_SETTLING_CYCLES,
2, (u8 *)&dat);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -451,7 +450,8 @@ static ssize_t ad5933_store(struct device *dev, ...@@ -451,7 +450,8 @@ static ssize_t ad5933_store(struct device *dev,
dat = cpu_to_be16(val); dat = cpu_to_be16(val);
ret = ad5933_i2c_write(st->client, ret = ad5933_i2c_write(st->client,
AD5933_REG_SETTLING_CYCLES, 2, (u8 *)&dat); AD5933_REG_SETTLING_CYCLES,
2, (u8 *)&dat);
break; break;
case AD5933_FREQ_POINTS: case AD5933_FREQ_POINTS:
val = clamp(val, (u16)0, (u16)511); val = clamp(val, (u16)0, (u16)511);
...@@ -545,8 +545,8 @@ static int ad5933_read_raw(struct iio_dev *indio_dev, ...@@ -545,8 +545,8 @@ static int ad5933_read_raw(struct iio_dev *indio_dev,
goto out; goto out;
ret = ad5933_i2c_read(st->client, ret = ad5933_i2c_read(st->client,
AD5933_REG_TEMP_DATA, 2, AD5933_REG_TEMP_DATA,
(u8 *)&dat); 2, (u8 *)&dat);
if (ret < 0) if (ret < 0)
goto out; goto out;
mutex_unlock(&indio_dev->mlock); mutex_unlock(&indio_dev->mlock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment