mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
83
drivers/iio/pressure/Kconfig
Normal file
83
drivers/iio/pressure/Kconfig
Normal file
|
@ -0,0 +1,83 @@
|
|||
#
|
||||
# Pressure drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Pressure sensors"
|
||||
|
||||
config HID_SENSOR_PRESS
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID PRESS"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Pressure driver
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hid-sensor-press.
|
||||
|
||||
config MPL115
|
||||
tristate "Freescale MPL115A2 pressure sensor driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Freescale MPL115A2
|
||||
pressure sensor connected via I2C.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl115.
|
||||
|
||||
config MPL3115
|
||||
tristate "Freescale MPL3115A2 pressure sensor driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Freescale MPL3115A2
|
||||
pressure sensor / altimeter.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl3115.
|
||||
|
||||
config IIO_ST_PRESS
|
||||
tristate "STMicroelectronics pressure sensor Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
select IIO_ST_SENSORS_CORE
|
||||
select IIO_ST_PRESS_I2C if (I2C)
|
||||
select IIO_ST_PRESS_SPI if (SPI_MASTER)
|
||||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics pressure
|
||||
sensors: LPS001WP, LPS25H, LPS331AP.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
- st_pressure (core functions for the driver [it is mandatory]);
|
||||
- st_pressure_i2c (necessary for the I2C devices [optional*]);
|
||||
- st_pressure_spi (necessary for the SPI devices [optional*]);
|
||||
|
||||
(*) one of these is necessary to do something.
|
||||
|
||||
config IIO_ST_PRESS_I2C
|
||||
tristate
|
||||
depends on IIO_ST_PRESS
|
||||
depends on IIO_ST_SENSORS_I2C
|
||||
|
||||
config IIO_ST_PRESS_SPI
|
||||
tristate
|
||||
depends on IIO_ST_PRESS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
config T5403
|
||||
tristate "EPCOS T5403 digital barometric pressure sensor driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the EPCOS T5403 pressure sensor
|
||||
connected via I2C.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called t5403.
|
||||
|
||||
endmenu
|
15
drivers/iio/pressure/Makefile
Normal file
15
drivers/iio/pressure/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# Makefile for industrial I/O pressure drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
obj-$(CONFIG_MPL3115) += mpl3115.o
|
||||
obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
|
||||
st_pressure-y := st_pressure_core.o
|
||||
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
|
||||
obj-$(CONFIG_T5403) += t5403.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o
|
393
drivers/iio/pressure/hid-sensor-press.c
Normal file
393
drivers/iio/pressure/hid-sensor-press.c
Normal file
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_PRESSURE 0
|
||||
|
||||
struct press_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info press_attr;
|
||||
u32 press_data;
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec press_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.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_PRESSURE,
|
||||
}
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void press_adjust_channel_bit_mask(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.sign = 's';
|
||||
/* Real storage bits will change based on the report desc. */
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
/* Maximum size of a sample to capture is u32 */
|
||||
channels[channel].scan_type.storagebits = sizeof(u32) * 8;
|
||||
}
|
||||
|
||||
/* Channel read_raw handler */
|
||||
static int press_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->scan_index) {
|
||||
case CHANNEL_SCAN_INDEX_PRESSURE:
|
||||
report_id = press_state->press_attr.report_id;
|
||||
address =
|
||||
HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE;
|
||||
break;
|
||||
default:
|
||||
report_id = -1;
|
||||
break;
|
||||
}
|
||||
if (report_id >= 0) {
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&press_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
hid_sensor_power_state(&press_state->common_attributes,
|
||||
true);
|
||||
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
press_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_PRESSURE, address,
|
||||
report_id);
|
||||
hid_sensor_power_state(&press_state->common_attributes,
|
||||
false);
|
||||
} else {
|
||||
*val = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = press_state->scale_pre_decml;
|
||||
*val2 = press_state->scale_post_decml;
|
||||
ret_type = press_state->scale_precision;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = press_state->value_offset;
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret_type = hid_sensor_read_samp_freq_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret_type = hid_sensor_read_raw_hyst_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret_type = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_type;
|
||||
}
|
||||
|
||||
/* Channel write_raw handler */
|
||||
static int press_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_write_samp_freq_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_write_raw_hyst_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info press_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &press_read_raw,
|
||||
.write_raw = &press_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 press_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "press_proc_event\n");
|
||||
if (atomic_read(&press_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
&press_state->press_data,
|
||||
sizeof(press_state->press_data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int press_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
size_t raw_len, char *raw_data,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE:
|
||||
press_state->press_data = *(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id*/
|
||||
static int press_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned usage_id,
|
||||
struct press_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE,
|
||||
&st->press_attr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
press_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESSURE,
|
||||
st->press_attr.size);
|
||||
|
||||
dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index,
|
||||
st->press_attr.report_id);
|
||||
|
||||
st->scale_precision = hid_sensor_format_scale(
|
||||
HID_USAGE_SENSOR_PRESSURE,
|
||||
&st->press_attr,
|
||||
&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_ATMOSPHERIC_PRESSURE,
|
||||
&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_press_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "press";
|
||||
struct iio_dev *indio_dev;
|
||||
struct press_state *press_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct press_state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
press_state = iio_priv(indio_dev);
|
||||
press_state->common_attributes.hsdev = hsdev;
|
||||
press_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_PRESSURE,
|
||||
&press_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(press_channels, sizeof(press_channels), GFP_KERNEL);
|
||||
if (!channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = press_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_PRESSURE, press_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(press_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &press_info;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
NULL, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
atomic_set(&press_state->common_attributes.data_ready, 0);
|
||||
ret = hid_sensor_setup_trigger(indio_dev, name,
|
||||
&press_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||
goto error_unreg_buffer_funcs;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "device register failed\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
press_state->callbacks.send_event = press_proc_event;
|
||||
press_state->callbacks.capture_sample = press_capture_sample;
|
||||
press_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE,
|
||||
&press_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
goto error_iio_unreg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_iio_unreg:
|
||||
iio_device_unregister(indio_dev);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(&press_state->common_attributes);
|
||||
error_unreg_buffer_funcs:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_dev_mem:
|
||||
kfree(indio_dev->channels);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int hid_press_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 press_state *press_state = iio_priv(indio_dev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(&press_state->common_attributes);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_press_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200031",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_press_ids);
|
||||
|
||||
static struct platform_driver hid_press_platform_driver = {
|
||||
.id_table = hid_press_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = hid_press_probe,
|
||||
.remove = hid_press_remove,
|
||||
};
|
||||
module_platform_driver(hid_press_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Pressure");
|
||||
MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
211
drivers/iio/pressure/mpl115.c
Normal file
211
drivers/iio/pressure/mpl115.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* mpl115.c - Support for Freescale MPL115A2 pressure/temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* (7-bit I2C slave address 0x60)
|
||||
*
|
||||
* TODO: shutdown pin
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */
|
||||
#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
|
||||
#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
|
||||
#define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */
|
||||
#define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */
|
||||
#define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */
|
||||
#define MPL115_CONVERT 0x12 /* convert temperature and pressure */
|
||||
|
||||
struct mpl115_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
s16 a0;
|
||||
s16 b1, b2;
|
||||
s16 c12;
|
||||
};
|
||||
|
||||
static int mpl115_request(struct mpl115_data *data)
|
||||
{
|
||||
int ret = i2c_smbus_write_byte_data(data->client, MPL115_CONVERT, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(3000, 4000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
u16 padc, tadc;
|
||||
int a1, y1, pcomp;
|
||||
unsigned kpa;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = mpl115_request(data);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
padc = ret >> 6;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
tadc = ret >> 6;
|
||||
|
||||
/* see Freescale AN3785 */
|
||||
a1 = data->b1 + ((data->c12 * tadc) >> 11);
|
||||
y1 = (data->a0 << 10) + a1 * padc;
|
||||
|
||||
/* compensated pressure with 4 fractional bits */
|
||||
pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9;
|
||||
|
||||
kpa = pcomp * (115 - 50) / 1023 + (50 << 4);
|
||||
*val = kpa >> 4;
|
||||
*val2 = (kpa & 15) * (1000000 >> 4);
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mpl115_read_temp(struct mpl115_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = mpl115_request(data);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mpl115_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mpl115_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = mpl115_comp_pressure(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/* temperature -5.35 C / LSB, 472 LSB is 25 C */
|
||||
ret = mpl115_read_temp(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret >> 6;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 605;
|
||||
*val2 = 750000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = -186;
|
||||
*val2 = 915888;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mpl115_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info mpl115_info = {
|
||||
.read_raw = &mpl115_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mpl115_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mpl115_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->info = &mpl115_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mpl115_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_A0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->a0 = ret;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_B1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->b1 = ret;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_B2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->b2 = ret;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_C12);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->c12 = ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mpl115_id[] = {
|
||||
{ "mpl115", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mpl115_id);
|
||||
|
||||
static struct i2c_driver mpl115_driver = {
|
||||
.driver = {
|
||||
.name = "mpl115",
|
||||
},
|
||||
.probe = mpl115_probe,
|
||||
.id_table = mpl115_id,
|
||||
};
|
||||
module_i2c_driver(mpl115_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");
|
||||
MODULE_LICENSE("GPL");
|
329
drivers/iio/pressure/mpl3115.c
Normal file
329
drivers/iio/pressure/mpl3115.c
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* mpl3115.c - Support for Freescale MPL3115A2 pressure/temperature sensor
|
||||
*
|
||||
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* (7-bit I2C slave address 0x60)
|
||||
*
|
||||
* TODO: FIFO buffer, altimeter mode, oversampling, continuous mode,
|
||||
* interrupts, user offset correction, raw mode
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define MPL3115_STATUS 0x00
|
||||
#define MPL3115_OUT_PRESS 0x01 /* MSB first, 20 bit */
|
||||
#define MPL3115_OUT_TEMP 0x04 /* MSB first, 12 bit */
|
||||
#define MPL3115_WHO_AM_I 0x0c
|
||||
#define MPL3115_CTRL_REG1 0x26
|
||||
|
||||
#define MPL3115_DEVICE_ID 0xc4
|
||||
|
||||
#define MPL3115_STATUS_PRESS_RDY BIT(2)
|
||||
#define MPL3115_STATUS_TEMP_RDY BIT(1)
|
||||
|
||||
#define MPL3115_CTRL_RESET BIT(2) /* software reset */
|
||||
#define MPL3115_CTRL_OST BIT(1) /* initiate measurement */
|
||||
#define MPL3115_CTRL_ACTIVE BIT(0) /* continuous measurement */
|
||||
#define MPL3115_CTRL_OS_258MS (BIT(5) | BIT(4)) /* 64x oversampling */
|
||||
|
||||
struct mpl3115_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 ctrl_reg1;
|
||||
};
|
||||
|
||||
static int mpl3115_request(struct mpl3115_data *data)
|
||||
{
|
||||
int ret, tries = 15;
|
||||
|
||||
/* trigger measurement */
|
||||
ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
|
||||
data->ctrl_reg1 | MPL3115_CTRL_OST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (tries-- > 0) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, MPL3115_CTRL_REG1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* wait for data ready, i.e. OST cleared */
|
||||
if (!(ret & MPL3115_CTRL_OST))
|
||||
break;
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev, "data not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpl3115_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mpl3115_data *data = iio_priv(indio_dev);
|
||||
__be32 tmp = 0;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE: /* in 0.25 pascal / LSB */
|
||||
mutex_lock(&data->lock);
|
||||
ret = mpl3115_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MPL3115_OUT_PRESS, 3, (u8 *) &tmp);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = be32_to_cpu(tmp) >> 12;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP: /* in 0.0625 celsius / LSB */
|
||||
mutex_lock(&data->lock);
|
||||
ret = mpl3115_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MPL3115_OUT_TEMP, 2, (u8 *) &tmp);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(be32_to_cpu(tmp) >> 20, 11);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
*val = 0;
|
||||
*val2 = 250; /* want kilopascal */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = 0;
|
||||
*val2 = 62500;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static irqreturn_t mpl3115_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct mpl3115_data *data = iio_priv(indio_dev);
|
||||
u8 buffer[16]; /* 32-bit channel + 16-bit channel + padding + ts */
|
||||
int ret, pos = 0;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = mpl3115_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
goto done;
|
||||
}
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
if (test_bit(0, indio_dev->active_scan_mask)) {
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MPL3115_OUT_PRESS, 3, &buffer[pos]);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
goto done;
|
||||
}
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
if (test_bit(1, indio_dev->active_scan_mask)) {
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MPL3115_OUT_TEMP, 2, &buffer[pos]);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mpl3115_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 20,
|
||||
.storagebits = 32,
|
||||
.shift = 12,
|
||||
.endianness = IIO_BE,
|
||||
}
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 4,
|
||||
.endianness = IIO_BE,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static const struct iio_info mpl3115_info = {
|
||||
.read_raw = &mpl3115_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mpl3115_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mpl3115_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MPL3115_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != MPL3115_DEVICE_ID)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->info = &mpl3115_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mpl3115_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mpl3115_channels);
|
||||
|
||||
/* software reset, I2C transfer is aborted (fails) */
|
||||
i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1,
|
||||
MPL3115_CTRL_RESET);
|
||||
msleep(50);
|
||||
|
||||
data->ctrl_reg1 = MPL3115_CTRL_OS_258MS;
|
||||
ret = i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
mpl3115_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
return 0;
|
||||
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mpl3115_standby(struct mpl3115_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MPL3115_CTRL_ACTIVE);
|
||||
}
|
||||
|
||||
static int mpl3115_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
mpl3115_standby(iio_priv(indio_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mpl3115_suspend(struct device *dev)
|
||||
{
|
||||
return mpl3115_standby(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
}
|
||||
|
||||
static int mpl3115_resume(struct device *dev)
|
||||
{
|
||||
struct mpl3115_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, mpl3115_resume);
|
||||
#define MPL3115_PM_OPS (&mpl3115_pm_ops)
|
||||
#else
|
||||
#define MPL3115_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id mpl3115_id[] = {
|
||||
{ "mpl3115", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mpl3115_id);
|
||||
|
||||
static struct i2c_driver mpl3115_driver = {
|
||||
.driver = {
|
||||
.name = "mpl3115",
|
||||
.pm = MPL3115_PM_OPS,
|
||||
},
|
||||
.probe = mpl3115_probe,
|
||||
.remove = mpl3115_remove,
|
||||
.id_table = mpl3115_id,
|
||||
};
|
||||
module_i2c_driver(mpl3115_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Freescale MPL3115 pressure/temperature driver");
|
||||
MODULE_LICENSE("GPL");
|
50
drivers/iio/pressure/st_pressure.h
Normal file
50
drivers/iio/pressure/st_pressure.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* STMicroelectronics pressures driver
|
||||
*
|
||||
* Copyright 2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
* v. 1.0.0
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_PRESS_H
|
||||
#define ST_PRESS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LPS001WP_PRESS_DEV_NAME "lps001wp"
|
||||
#define LPS25H_PRESS_DEV_NAME "lps25h"
|
||||
#define LPS331AP_PRESS_DEV_NAME "lps331ap"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - default press platform data
|
||||
* @drdy_int_pin: default press DRDY is available on INT1 pin.
|
||||
*/
|
||||
static const struct st_sensors_platform_data default_press_pdata = {
|
||||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
int st_press_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata);
|
||||
void st_press_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
int st_press_allocate_ring(struct iio_dev *indio_dev);
|
||||
void st_press_deallocate_ring(struct iio_dev *indio_dev);
|
||||
int st_press_trig_set_state(struct iio_trigger *trig, bool state);
|
||||
#define ST_PRESS_TRIGGER_SET_STATE (&st_press_trig_set_state)
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
static inline int st_press_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void st_press_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
#define ST_PRESS_TRIGGER_SET_STATE NULL
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#endif /* ST_PRESS_H */
|
96
drivers/iio/pressure/st_pressure_buffer.c
Normal file
96
drivers/iio/pressure/st_pressure_buffer.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* STMicroelectronics pressures driver
|
||||
*
|
||||
* Copyright 2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_pressure.h"
|
||||
|
||||
int st_press_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_press_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_press_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
pdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (pdata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_press_buffer_postenable_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_press_buffer_postenable_error:
|
||||
kfree(pdata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_press_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_press_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
|
||||
st_press_buffer_predisable_error:
|
||||
kfree(pdata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_press_buffer_setup_ops = {
|
||||
.preenable = &st_press_buffer_preenable,
|
||||
.postenable = &st_press_buffer_postenable,
|
||||
.predisable = &st_press_buffer_predisable,
|
||||
};
|
||||
|
||||
int st_press_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&st_sensors_trigger_handler, &st_press_buffer_setup_ops);
|
||||
}
|
||||
|
||||
void st_press_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics pressures buffer");
|
||||
MODULE_LICENSE("GPL v2");
|
496
drivers/iio/pressure/st_pressure_core.c
Normal file
496
drivers/iio/pressure/st_pressure_core.c
Normal file
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* STMicroelectronics pressures driver
|
||||
*
|
||||
* Copyright 2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_pressure.h"
|
||||
|
||||
#define ST_PRESS_LSB_PER_MBAR 4096UL
|
||||
#define ST_PRESS_KPASCAL_NANO_SCALE (100000000UL / \
|
||||
ST_PRESS_LSB_PER_MBAR)
|
||||
#define ST_PRESS_LSB_PER_CELSIUS 480UL
|
||||
#define ST_PRESS_CELSIUS_NANO_SCALE (1000000000UL / \
|
||||
ST_PRESS_LSB_PER_CELSIUS)
|
||||
#define ST_PRESS_NUMBER_DATA_CHANNELS 1
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_PRESS_FS_AVL_1260MB 1260
|
||||
|
||||
#define ST_PRESS_1_OUT_XL_ADDR 0x28
|
||||
#define ST_TEMP_1_OUT_L_ADDR 0x2b
|
||||
|
||||
/* CUSTOM VALUES FOR LPS331AP SENSOR */
|
||||
#define ST_PRESS_LPS331AP_WAI_EXP 0xbb
|
||||
#define ST_PRESS_LPS331AP_ODR_ADDR 0x20
|
||||
#define ST_PRESS_LPS331AP_ODR_MASK 0x70
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL 0x05
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL 0x06
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL 0x07
|
||||
#define ST_PRESS_LPS331AP_PW_ADDR 0x20
|
||||
#define ST_PRESS_LPS331AP_PW_MASK 0x80
|
||||
#define ST_PRESS_LPS331AP_FS_ADDR 0x23
|
||||
#define ST_PRESS_LPS331AP_FS_MASK 0x30
|
||||
#define ST_PRESS_LPS331AP_FS_AVL_1260_VAL 0x00
|
||||
#define ST_PRESS_LPS331AP_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE
|
||||
#define ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE
|
||||
#define ST_PRESS_LPS331AP_BDU_ADDR 0x20
|
||||
#define ST_PRESS_LPS331AP_BDU_MASK 0x04
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_PRESS_LPS331AP_MULTIREAD_BIT true
|
||||
#define ST_PRESS_LPS331AP_TEMP_OFFSET 42500
|
||||
|
||||
/* CUSTOM VALUES FOR LPS001WP SENSOR */
|
||||
#define ST_PRESS_LPS001WP_WAI_EXP 0xba
|
||||
#define ST_PRESS_LPS001WP_ODR_ADDR 0x20
|
||||
#define ST_PRESS_LPS001WP_ODR_MASK 0x30
|
||||
#define ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL 0x02
|
||||
#define ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL 0x03
|
||||
#define ST_PRESS_LPS001WP_PW_ADDR 0x20
|
||||
#define ST_PRESS_LPS001WP_PW_MASK 0x40
|
||||
#define ST_PRESS_LPS001WP_BDU_ADDR 0x20
|
||||
#define ST_PRESS_LPS001WP_BDU_MASK 0x04
|
||||
#define ST_PRESS_LPS001WP_MULTIREAD_BIT true
|
||||
#define ST_PRESS_LPS001WP_OUT_L_ADDR 0x28
|
||||
#define ST_TEMP_LPS001WP_OUT_L_ADDR 0x2a
|
||||
|
||||
/* CUSTOM VALUES FOR LPS25H SENSOR */
|
||||
#define ST_PRESS_LPS25H_WAI_EXP 0xbd
|
||||
#define ST_PRESS_LPS25H_ODR_ADDR 0x20
|
||||
#define ST_PRESS_LPS25H_ODR_MASK 0x70
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL 0x02
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL 0x03
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL 0x04
|
||||
#define ST_PRESS_LPS25H_PW_ADDR 0x20
|
||||
#define ST_PRESS_LPS25H_PW_MASK 0x80
|
||||
#define ST_PRESS_LPS25H_FS_ADDR 0x00
|
||||
#define ST_PRESS_LPS25H_FS_MASK 0x00
|
||||
#define ST_PRESS_LPS25H_FS_AVL_1260_VAL 0x00
|
||||
#define ST_PRESS_LPS25H_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE
|
||||
#define ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE
|
||||
#define ST_PRESS_LPS25H_BDU_ADDR 0x20
|
||||
#define ST_PRESS_LPS25H_BDU_MASK 0x04
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_ADDR 0x23
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10
|
||||
#define ST_PRESS_LPS25H_MULTIREAD_BIT true
|
||||
#define ST_PRESS_LPS25H_TEMP_OFFSET 42500
|
||||
#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28
|
||||
#define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b
|
||||
|
||||
static const struct iio_chan_spec st_press_1_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_PRESS_1_OUT_XL_ADDR,
|
||||
.scan_index = ST_SENSORS_SCAN_X,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 24,
|
||||
.storagebits = 24,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.modified = 0,
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_TEMP_1_OUT_L_ADDR,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.modified = 0,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_PRESS_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = ST_SENSORS_SCAN_X,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.modified = 0,
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_TEMP_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.modified = 0,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_press_sensors[] = {
|
||||
{
|
||||
.wai = ST_PRESS_LPS331AP_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LPS331AP_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_1_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_1_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_LPS331AP_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL, },
|
||||
{ 7, ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL, },
|
||||
{ 13, ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL, },
|
||||
{ 25, ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_PRESS_LPS331AP_PW_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_PRESS_LPS331AP_FS_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_PRESS_FS_AVL_1260MB,
|
||||
.value = ST_PRESS_LPS331AP_FS_AVL_1260_VAL,
|
||||
.gain = ST_PRESS_LPS331AP_FS_AVL_1260_GAIN,
|
||||
.gain2 = ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_PRESS_LPS331AP_BDU_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_PRESS_LPS001WP_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LPS001WP_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_lps001wp_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_lps001wp_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_LPS001WP_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS001WP_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL, },
|
||||
{ 7, ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL, },
|
||||
{ 13, ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_PRESS_LPS001WP_PW_ADDR,
|
||||
.mask = ST_PRESS_LPS001WP_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.addr = 0,
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_PRESS_LPS001WP_BDU_ADDR,
|
||||
.mask = ST_PRESS_LPS001WP_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = 0,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS001WP_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_PRESS_LPS25H_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LPS25H_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_1_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_1_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_LPS25H_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL, },
|
||||
{ 7, ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL, },
|
||||
{ 13, ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL, },
|
||||
{ 25, ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_PRESS_LPS25H_PW_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_PRESS_LPS25H_FS_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_PRESS_FS_AVL_1260MB,
|
||||
.value = ST_PRESS_LPS25H_FS_AVL_1260_VAL,
|
||||
.gain = ST_PRESS_LPS25H_FS_AVL_1260_GAIN,
|
||||
.gain2 = ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_PRESS_LPS25H_BDU_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_press_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
err = st_sensors_set_odr(indio_dev, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return err;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int st_press_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = st_sensors_read_info_raw(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
|
||||
switch (ch->type) {
|
||||
case IIO_PRESSURE:
|
||||
*val2 = pdata->current_fullscale->gain;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
*val2 = pdata->current_fullscale->gain2;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto read_error;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (ch->type) {
|
||||
case IIO_TEMP:
|
||||
*val = 425;
|
||||
*val2 = 10;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto read_error;
|
||||
}
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = pdata->odr;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
read_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
|
||||
|
||||
static struct attribute *st_press_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_press_attribute_group = {
|
||||
.attrs = st_press_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info press_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_press_attribute_group,
|
||||
.read_raw = &st_press_read_raw,
|
||||
.write_raw = &st_press_write_raw,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
static const struct iio_trigger_ops st_press_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = ST_PRESS_TRIGGER_SET_STATE,
|
||||
};
|
||||
#define ST_PRESS_TRIGGER_OPS (&st_press_trigger_ops)
|
||||
#else
|
||||
#define ST_PRESS_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
int st_press_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *plat_data)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
int irq = pdata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &press_info;
|
||||
|
||||
st_sensors_power_enable(indio_dev);
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_press_sensors),
|
||||
st_press_sensors);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pdata->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS;
|
||||
pdata->multiread_bit = pdata->sensor->multi_read_bit;
|
||||
indio_dev->channels = pdata->sensor->ch;
|
||||
indio_dev->num_channels = pdata->sensor->num_ch;
|
||||
|
||||
if (pdata->sensor->fs.addr != 0)
|
||||
pdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&pdata->sensor->fs.fs_avl[0];
|
||||
|
||||
pdata->odr = pdata->sensor->odr.odr_avl[0].hz;
|
||||
|
||||
/* Some devices don't support a data ready pin. */
|
||||
if (!plat_data && pdata->sensor->drdy_irq.addr)
|
||||
plat_data =
|
||||
(struct st_sensors_platform_data *)&default_press_pdata;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, plat_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_press_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_PRESS_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
goto st_press_probe_trigger_error;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto st_press_device_register_error;
|
||||
|
||||
dev_info(&indio_dev->dev, "registered pressure sensor %s\n",
|
||||
indio_dev->name);
|
||||
|
||||
return err;
|
||||
|
||||
st_press_device_register_error:
|
||||
if (irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_press_probe_trigger_error:
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_press_common_probe);
|
||||
|
||||
void st_press_common_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (pdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_press_common_remove);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics pressures driver");
|
||||
MODULE_LICENSE("GPL v2");
|
95
drivers/iio/pressure/st_pressure_i2c.c
Normal file
95
drivers/iio/pressure/st_pressure_i2c.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* STMicroelectronics pressures driver
|
||||
*
|
||||
* Copyright 2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_pressure.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_press_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lps001wp-press",
|
||||
.data = LPS001WP_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps25h-press",
|
||||
.data = LPS25H_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps331ap-press",
|
||||
.data = LPS331AP_PRESS_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_press_of_match);
|
||||
#else
|
||||
#define st_press_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_press_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *pdata;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*pdata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = iio_priv(indio_dev);
|
||||
pdata->dev = &client->dev;
|
||||
st_sensors_of_i2c_probe(client, st_press_of_match);
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, pdata);
|
||||
|
||||
err = st_press_common_probe(indio_dev, client->dev.platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_press_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
st_press_common_remove(i2c_get_clientdata(client));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_press_id_table[] = {
|
||||
{ LPS001WP_PRESS_DEV_NAME },
|
||||
{ LPS25H_PRESS_DEV_NAME },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_press_id_table);
|
||||
|
||||
static struct i2c_driver st_press_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-press-i2c",
|
||||
.of_match_table = of_match_ptr(st_press_of_match),
|
||||
},
|
||||
.probe = st_press_i2c_probe,
|
||||
.remove = st_press_i2c_remove,
|
||||
.id_table = st_press_id_table,
|
||||
};
|
||||
module_i2c_driver(st_press_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics pressures i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
71
drivers/iio/pressure/st_pressure_spi.c
Normal file
71
drivers/iio/pressure/st_pressure_spi.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* STMicroelectronics pressures driver
|
||||
*
|
||||
* Copyright 2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_pressure.h"
|
||||
|
||||
static int st_press_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *pdata;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*pdata));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = iio_priv(indio_dev);
|
||||
pdata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, pdata);
|
||||
|
||||
err = st_press_common_probe(indio_dev, spi->dev.platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_press_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
st_press_common_remove(spi_get_drvdata(spi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id st_press_id_table[] = {
|
||||
{ LPS001WP_PRESS_DEV_NAME },
|
||||
{ LPS25H_PRESS_DEV_NAME },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_press_id_table);
|
||||
|
||||
static struct spi_driver st_press_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-press-spi",
|
||||
},
|
||||
.probe = st_press_spi_probe,
|
||||
.remove = st_press_spi_remove,
|
||||
.id_table = st_press_id_table,
|
||||
};
|
||||
module_spi_driver(st_press_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics pressures spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
275
drivers/iio/pressure/t5403.c
Normal file
275
drivers/iio/pressure/t5403.c
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* t5403.c - Support for EPCOS T5403 pressure/temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* (7-bit I2C slave address 0x77)
|
||||
*
|
||||
* TODO: end-of-conversion irq
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define T5403_DATA 0xf5 /* data, LSB first, 16 bit */
|
||||
#define T5403_CALIB_DATA 0x8e /* 10 calibration coeff., LSB first, 16 bit */
|
||||
#define T5403_SLAVE_ADDR 0x88 /* I2C slave address, 0x77 */
|
||||
#define T5403_COMMAND 0xf1
|
||||
|
||||
/* command bits */
|
||||
#define T5403_MODE_SHIFT 3 /* conversion time: 2, 8, 16, 66 ms */
|
||||
#define T5403_PT BIT(1) /* 0 .. pressure, 1 .. temperature measurement */
|
||||
#define T5403_SCO BIT(0) /* start conversion */
|
||||
|
||||
#define T5403_MODE_LOW 0
|
||||
#define T5403_MODE_STANDARD 1
|
||||
#define T5403_MODE_HIGH 2
|
||||
#define T5403_MODE_ULTRA_HIGH 3
|
||||
|
||||
#define T5403_I2C_MASK (~BIT(7))
|
||||
#define T5403_I2C_ADDR 0x77
|
||||
|
||||
static const int t5403_pressure_conv_ms[] = {2, 8, 16, 66};
|
||||
|
||||
struct t5403_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
int mode;
|
||||
__le16 c[10];
|
||||
};
|
||||
|
||||
#define T5403_C_U16(i) le16_to_cpu(data->c[(i) - 1])
|
||||
#define T5403_C(i) sign_extend32(T5403_C_U16(i), 15)
|
||||
|
||||
static int t5403_read(struct t5403_data *data, bool pressure)
|
||||
{
|
||||
int wait_time = 3; /* wakeup time in ms */
|
||||
|
||||
int ret = i2c_smbus_write_byte_data(data->client, T5403_COMMAND,
|
||||
(pressure ? (data->mode << T5403_MODE_SHIFT) : T5403_PT) |
|
||||
T5403_SCO);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wait_time += pressure ? t5403_pressure_conv_ms[data->mode] : 2;
|
||||
|
||||
msleep(wait_time);
|
||||
|
||||
return i2c_smbus_read_word_data(data->client, T5403_DATA);
|
||||
}
|
||||
|
||||
static int t5403_comp_pressure(struct t5403_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
s16 t_r;
|
||||
u16 p_r;
|
||||
s32 S, O, X;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = t5403_read(data, false);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
t_r = ret;
|
||||
|
||||
ret = t5403_read(data, true);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
p_r = ret;
|
||||
|
||||
/* see EPCOS application note */
|
||||
S = T5403_C_U16(3) + (s32) T5403_C_U16(4) * t_r / 0x20000 +
|
||||
T5403_C(5) * t_r / 0x8000 * t_r / 0x80000 +
|
||||
T5403_C(9) * t_r / 0x8000 * t_r / 0x8000 * t_r / 0x10000;
|
||||
|
||||
O = T5403_C(6) * 0x4000 + T5403_C(7) * t_r / 8 +
|
||||
T5403_C(8) * t_r / 0x8000 * t_r / 16 +
|
||||
T5403_C(9) * t_r / 0x8000 * t_r / 0x10000 * t_r;
|
||||
|
||||
X = (S * p_r + O) / 0x4000;
|
||||
|
||||
X += ((X - 75000) * (X - 75000) / 0x10000 - 9537) *
|
||||
T5403_C(10) / 0x10000;
|
||||
|
||||
*val = X / 1000;
|
||||
*val2 = (X % 1000) * 1000;
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int t5403_comp_temp(struct t5403_data *data, int *val)
|
||||
{
|
||||
int ret;
|
||||
s16 t_r;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = t5403_read(data, false);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
t_r = ret;
|
||||
|
||||
/* see EPCOS application note */
|
||||
*val = ((s32) T5403_C_U16(1) * t_r / 0x100 +
|
||||
(s32) T5403_C_U16(2) * 0x40) * 1000 / 0x10000;
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int t5403_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct t5403_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
ret = t5403_comp_pressure(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
ret = t5403_comp_temp(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = 0;
|
||||
*val2 = t5403_pressure_conv_ms[data->mode] * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int t5403_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct t5403_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(t5403_pressure_conv_ms); i++)
|
||||
if (val2 == t5403_pressure_conv_ms[i] * 1000) {
|
||||
mutex_lock(&data->lock);
|
||||
data->mode = i;
|
||||
mutex_unlock(&data->lock);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec t5403_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_INT_TIME_AVAIL("0.002 0.008 0.016 0.066");
|
||||
|
||||
static struct attribute *t5403_attributes[] = {
|
||||
&iio_const_attr_integration_time_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group t5403_attribute_group = {
|
||||
.attrs = t5403_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info t5403_info = {
|
||||
.read_raw = &t5403_read_raw,
|
||||
.write_raw = &t5403_write_raw,
|
||||
.attrs = &t5403_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int t5403_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct t5403_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, T5403_SLAVE_ADDR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if ((ret & T5403_I2C_MASK) != T5403_I2C_ADDR)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->info = &t5403_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = t5403_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(t5403_channels);
|
||||
|
||||
data->mode = T5403_MODE_STANDARD;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client, T5403_CALIB_DATA,
|
||||
sizeof(data->c), (u8 *) data->c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id t5403_id[] = {
|
||||
{ "t5403", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, t5403_id);
|
||||
|
||||
static struct i2c_driver t5403_driver = {
|
||||
.driver = {
|
||||
.name = "t5403",
|
||||
},
|
||||
.probe = t5403_probe,
|
||||
.id_table = t5403_id,
|
||||
};
|
||||
module_i2c_driver(t5403_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("EPCOS T5403 pressure/temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Add table
Add a link
Reference in a new issue