Commit 59142f80 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'iio-for-4.17a' of...

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

Jonathan writes:

First round of new devices, features and cleanups for IIO in the 4.17 cycle.

Outside of IIO
* Strongly typed 64bit int_sqrt function needed by the mlx90632

New device support
* adc081s
  - New driver supporting adc081s, adc101s and adc121s TI ADCs.
* ad5272
  - New driver supproting the ad5272 and ad5274 ADI digital potentiometers
    with DT bindings.
* axp20x_adc
  - support the AXP813 ADC - includes rework patches to prepare for this.
* mlx90632
  - New driver with dt bindings for this IR temperature sensor.

Features
* axp20x_adc
  - Add DT bindings and probing.
* dht11
  - The sensor has a wider range than advertised in the datasheet - support it.
* st_lsm6dsx
  - Add hardware timestamp su9pport.

Cleanups
* ABI docs
  - Update email contact for Matt Ranostay
* SPDX changes
  - Matt Ranostay has moved his drivers over to SPDX.  Currently we are making
    this an author choice in IIO.
* ad7192
  - Disable burnout current on misconfiguration.  No actually effect as
    they simply won't work otherwise.
* ad7476
  - Drop a license definition that was replicating information in SPDX tag.
* ade7758
  - Expand buf_lock to cover both buffer and state protection allowing
    unintented uses of mlock in the core to be removed.
* ade7759
  - Align parameters to opening parenthesis.
* at91_adc
  - Depend on sysfs instead of selecting it - for try wide consistency.
* ccs811
  - trivial naming type for a define.
* ep93xx
  - Drop a redundant return as a result checking platform_get_resource.
* hts221
  - Regmap conversion which simplifies the driver somewhat.
  - Clean up some restricted endian cast warnings.
  - Drop a trailing whitespace from a comment
  - Drop an unnecessary get_unaligned by changing to the right 16bit data type.
* ms5611
  - Fix coding style in the probe function (whitespace)
* st_accel
  - Use strlcpy instead of strncpy to avoid potentially truncating a string.
parents 54ef5b9d d6ad8058
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_VOC_short_raw
Date: September 2015
KernelVersion: 4.3
Contact: Matt Ranostay <mranostay@gmail.com>
Contact: Matt Ranostay <matt.ranostay@konsulko.com>
Description:
Get the raw calibration VOC value from the sensor.
This value has little application outside of calibration.
What /sys/bus/iio/devices/iio:deviceX/in_proximity_input
Date: March 2014
KernelVersion: 3.15
Contact: Matt Ranostay <mranostay@gmail.com>
Contact: Matt Ranostay <matt.ranostay@konsulko.com>
Description:
Get the current distance in meters of storm (1km steps)
1000-40000 = distance in meters
......@@ -9,7 +9,7 @@ Description:
What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
Date: March 2014
KernelVersion: 3.15
Contact: Matt Ranostay <mranostay@gmail.com>
Contact: Matt Ranostay <matt.ranostay@konsulko.com>
Description:
Show or set the gain boost of the amp, from 0-31 range.
18 = indoors (default)
......
* X-Powers AXP ADC bindings
Required properties:
- compatible: should be one of:
- "x-powers,axp209-adc",
- "x-powers,axp221-adc",
- "x-powers,axp813-adc",
- #io-channel-cells: should be 1,
Example:
&axp22x {
adc {
compatible = "x-powers,axp221-adc";
#io-channel-cells = <1>;
};
};
ADC channels and their indexes per variant:
AXP209
------
0 | acin_v
1 | acin_i
2 | vbus_v
3 | vbus_i
4 | pmic_temp
5 | gpio0_v
6 | gpio1_v
7 | ipsout_v
8 | batt_v
9 | batt_chrg_i
10 | batt_dischrg_i
AXP22x
------
0 | pmic_temp
1 | batt_v
2 | batt_chrg_i
3 | batt_dischrg_i
AXP813
------
0 | pmic_temp
1 | gpio0_v
2 | batt_v
3 | batt_chrg_i
4 | batt_dischrg_i
* Analog Devices AD5272 digital potentiometer
The node for this device must be a child node of a I2C controller, hence
all mandatory properties for your controller must be specified. See directory:
Documentation/devicetree/bindings/i2c
for more details.
Required properties:
- compatible: Must be one of the following, depending on the model:
adi,ad5272-020
adi,ad5272-050
adi,ad5272-100
adi,ad5274-020
adi,ad5274-100
Optional properties:
- reset-gpios: GPIO specification for the RESET input. This is an
active low signal to the AD5272.
Example:
ad5272: potentiometer@2f {
reg = <0x2F>;
compatible = "adi,ad5272-020";
reset-gpios = <&gpio3 6 GPIO_ACTIVE_HIGH>;
};
* Melexis MLX90632 contactless Infra Red temperature sensor
Link to datasheet: https://www.melexis.com/en/documents/documentation/datasheets/datasheet-mlx90632
There are various applications for the Infra Red contactless temperature sensor
and MLX90632 is most suitable for consumer applications where measured object
temperature is in range between -20 to 200 degrees Celsius with relative error
of measurement below 1 degree Celsius in object temperature range for
industrial applications. Since it can operate and measure ambient temperature
in range of -20 to 85 degrees Celsius it is suitable also for outdoor use.
Be aware that electronics surrounding the sensor can increase ambient
temperature. MLX90632 can be calibrated to reduce the housing effect via
already existing EEPROM parameters.
Since measured object emissivity effects Infra Red energy emitted, emissivity
should be set before requesting the object temperature.
Required properties:
- compatible: should be "melexis,mlx90632"
- reg: the I2C address of the sensor (default 0x3a)
Example:
mlx90632@3a {
compatible = "melexis,mlx90632";
reg = <0x3a>;
};
......@@ -8878,6 +8878,13 @@ W: http://www.melexis.com
S: Supported
F: drivers/iio/temperature/mlx90614.c
MELEXIS MLX90632 DRIVER
M: Crt Mori <cmo@melexis.com>
L: linux-iio@vger.kernel.org
W: http://www.melexis.com
S: Supported
F: drivers/iio/temperature/mlx90632.c
MELFAS MIP4 TOUCHSCREEN DRIVER
M: Sangwon Jee <jeesw@melfas.com>
W: http://www.melfas.com
......
......@@ -159,9 +159,8 @@ static int st_accel_i2c_probe(struct i2c_client *client,
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
return -ENODEV;
strncpy(client->name, st_accel_id_table[ret].name,
strlcpy(client->name, st_accel_id_table[ret].name,
sizeof(client->name));
client->name[sizeof(client->name) - 1] = '\0';
} else if (!id)
return -ENODEV;
......
......@@ -144,10 +144,9 @@ config ASPEED_ADC
config AT91_ADC
tristate "Atmel AT91 ADC"
depends on ARCH_AT91
depends on INPUT
depends on INPUT && SYSFS
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select SYSFS
help
Say yes here to build support for Atmel AT91 ADC.
......
// SPDX-License-Identifier: GPL-2.0
/*
* AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
* Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
* TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver
*
* Copyright 2010 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
......@@ -56,6 +56,9 @@ enum ad7476_supported_device_ids {
ID_AD7468,
ID_AD7495,
ID_AD7940,
ID_ADC081S,
ID_ADC101S,
ID_ADC121S,
};
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
......@@ -147,6 +150,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
}, \
}
#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
......@@ -192,6 +197,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
.channel[0] = AD7940_CHAN(14),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADC081S] = {
.channel[0] = ADC081S_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADC101S] = {
.channel[0] = ADC081S_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADC121S] = {
.channel[0] = ADC081S_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
};
static const struct iio_info ad7476_info = {
......@@ -294,6 +311,9 @@ static const struct spi_device_id ad7476_id[] = {
{"ad7910", ID_AD7467},
{"ad7920", ID_AD7466},
{"ad7940", ID_AD7940},
{"adc081s", ID_ADC081S},
{"adc101s", ID_ADC101S},
{"adc121s", ID_ADC121S},
{}
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
......
......@@ -35,8 +35,13 @@
#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
#define AXP20X_ADC_RATE_MASK GENMASK(7, 6)
#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK)
#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x))
#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
{ \
......@@ -95,6 +100,12 @@ enum axp22x_adc_channel_i {
AXP22X_BATT_DISCHRG_I,
};
enum axp813_adc_channel_v {
AXP813_TS_IN = 0,
AXP813_GPIO0_V,
AXP813_BATT_V,
};
static struct iio_map axp20x_maps[] = {
{
.consumer_dev_name = "axp20x-usb-power-supply",
......@@ -197,6 +208,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
AXP20X_BATT_DISCHRG_I_H),
};
static const struct iio_chan_spec axp813_adc_channels[] = {
{
.type = IIO_TEMP,
.address = AXP22X_PMIC_TEMP_H,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.datasheet_name = "pmic_temp",
},
AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
AXP288_GP_ADC_H),
AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE,
AXP20X_BATT_V_H),
AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
AXP20X_BATT_CHRG_I_H),
AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
AXP20X_BATT_DISCHRG_I_H),
};
static int axp20x_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
......@@ -243,6 +273,18 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
}
static int axp813_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
struct axp20x_adc_iio *info = iio_priv(indio_dev);
*val = axp20x_read_variable_width(info->regmap, chan->address, 12);
if (*val < 0)
return *val;
return IIO_VAL_INT;
}
static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
{
switch (channel) {
......@@ -273,6 +315,24 @@ static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
}
}
static int axp813_adc_scale_voltage(int channel, int *val, int *val2)
{
switch (channel) {
case AXP813_GPIO0_V:
*val = 0;
*val2 = 800000;
return IIO_VAL_INT_PLUS_MICRO;
case AXP813_BATT_V:
*val = 1;
*val2 = 100000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int axp20x_adc_scale_current(int channel, int *val, int *val2)
{
switch (channel) {
......@@ -342,6 +402,26 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
}
}
static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
int *val2)
{
switch (chan->type) {
case IIO_VOLTAGE:
return axp813_adc_scale_voltage(chan->channel, val, val2);
case IIO_CURRENT:
*val = 1;
return IIO_VAL_INT;
case IIO_TEMP:
*val = 100;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
int *val)
{
......@@ -425,6 +505,26 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
}
}
static int axp813_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_OFFSET:
*val = -2667;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
return axp813_adc_scale(chan, val, val2);
case IIO_CHAN_INFO_RAW:
return axp813_adc_raw(indio_dev, chan, val);
default:
return -EINVAL;
}
}
static int axp20x_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
......@@ -470,14 +570,29 @@ static const struct iio_info axp22x_adc_iio_info = {
.read_raw = axp22x_read_raw,
};
static int axp20x_adc_rate(int rate)
static const struct iio_info axp813_adc_iio_info = {
.read_raw = axp813_read_raw,
};
static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate)
{
return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
AXP20X_ADC_RATE_MASK,
AXP20X_ADC_RATE_HZ(rate));
}
static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate)
{
return AXP20X_ADC_RATE_HZ(rate);
return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
AXP20X_ADC_RATE_MASK,
AXP22X_ADC_RATE_HZ(rate));
}
static int axp22x_adc_rate(int rate)
static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate)
{
return AXP22X_ADC_RATE_HZ(rate);
return regmap_update_bits(info->regmap, AXP813_ADC_RATE,
AXP813_ADC_RATE_MASK,
AXP813_ADC_RATE_HZ(rate));
}
struct axp_data {
......@@ -485,7 +600,8 @@ struct axp_data {
int num_channels;
struct iio_chan_spec const *channels;
unsigned long adc_en1_mask;
int (*adc_rate)(int rate);
int (*adc_rate)(struct axp20x_adc_iio *info,
int rate);
bool adc_en2;
struct iio_map *maps;
};
......@@ -510,9 +626,28 @@ static const struct axp_data axp22x_data = {
.maps = axp22x_maps,
};
static const struct axp_data axp813_data = {
.iio_info = &axp813_adc_iio_info,
.num_channels = ARRAY_SIZE(axp813_adc_channels),
.channels = axp813_adc_channels,
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
.adc_rate = axp813_adc_rate,
.adc_en2 = false,
.maps = axp22x_maps,
};
static const struct of_device_id axp20x_adc_of_match[] = {
{ .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
{ .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
{ .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
static const struct platform_device_id axp20x_adc_id_match[] = {
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);
......@@ -538,7 +673,16 @@ static int axp20x_probe(struct platform_device *pdev)
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->modes = INDIO_DIRECT_MODE;
info->data = (struct axp_data *)platform_get_device_id(pdev)->driver_data;
if (!pdev->dev.of_node) {
const struct platform_device_id *id;
id = platform_get_device_id(pdev);
info->data = (struct axp_data *)id->driver_data;
} else {
struct device *dev = &pdev->dev;
info->data = (struct axp_data *)of_device_get_match_data(dev);
}
indio_dev->name = platform_get_device_id(pdev)->name;
indio_dev->info = info->data->iio_info;
......@@ -554,8 +698,7 @@ static int axp20x_probe(struct platform_device *pdev)
AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
/* Configure ADCs rate */
regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK,
info->data->adc_rate(100));
info->data->adc_rate(info, 100);
ret = iio_map_array_register(indio_dev, info->data->maps);
if (ret < 0) {
......@@ -602,6 +745,7 @@ static int axp20x_remove(struct platform_device *pdev)
static struct platform_driver axp20x_adc_driver = {
.driver = {
.name = "axp20x-adc",
.of_match_table = of_match_ptr(axp20x_adc_of_match),
},
.id_table = axp20x_adc_id_match,
.probe = axp20x_probe,
......
......@@ -167,10 +167,6 @@ static int ep93xx_adc_probe(struct platform_device *pdev)
priv = iio_priv(iiodev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Cannot obtain memory resource\n");
return -ENXIO;
}
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base)) {
dev_err(&pdev->dev, "Cannot map memory resource\n");
......
// SPDX-License-Identifier: GPL-2.0+
/*
* ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
*
......@@ -5,17 +6,8 @@
* adc141s626 - 14-bit ADC
* adc161s626 - 16-bit ADC
*
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2016-2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
......@@ -275,6 +267,6 @@ static struct spi_driver ti_adc_driver = {
};
module_spi_driver(ti_adc_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0+
/*
* ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (C) 2015, 2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
......@@ -194,6 +185,6 @@ static struct i2c_driver ams_iaqcore_driver = {
};
module_i2c_driver(ams_iaqcore_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0+
/*
* atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2015-2018 Matt Ranostay
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
......@@ -689,6 +681,6 @@ static struct i2c_driver atlas_driver = {
};
module_i2c_driver(atlas_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
MODULE_LICENSE("GPL");
......@@ -32,7 +32,7 @@
#define CCS811_ALG_RESULT_DATA 0x02
#define CCS811_RAW_DATA 0x03
#define CCS811_HW_ID 0x20
#define CCS881_HW_ID_VALUE 0x81
#define CCS811_HW_ID_VALUE 0x81
#define CCS811_HW_VERSION 0x21
#define CCS811_HW_VERSION_VALUE 0x10
#define CCS811_HW_VERSION_MASK 0xF0
......@@ -353,7 +353,7 @@ static int ccs811_probe(struct i2c_client *client,
if (ret < 0)
return ret;
if (ret != CCS881_HW_ID_VALUE) {
if (ret != CCS811_HW_ID_VALUE) {
dev_err(&client->dev, "hardware id doesn't match CCS81x\n");
return -ENODEV;
}
......
// SPDX-License-Identifier: GPL-2.0+
/*
* vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (C) 2015-2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
......@@ -419,6 +410,6 @@ static struct i2c_driver vz89x_driver = {
};
module_i2c_driver(vz89x_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0+
/*
* max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2015, 2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: enable pulse length controls via device tree properties
*/
......@@ -518,6 +510,6 @@ static struct i2c_driver max30100_driver = {
};
module_i2c_driver(max30100_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor");
MODULE_LICENSE("GPL");
......@@ -68,10 +68,12 @@ config HTS221
config HTS221_I2C
tristate
depends on HTS221
select REGMAP_I2C
config HTS221_SPI
tristate
depends on HTS221
select REGMAP_SPI
config HTU21
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
......
......@@ -159,7 +159,7 @@ static int dht11_decode(struct dht11 *dht11, int offset)
}
dht11->timestamp = ktime_get_boot_ns();
if (hum_int < 20) { /* DHT22 */
if (hum_int < 4) { /* DHT22: 100000 = (3*256+232)*100 */
dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
((temp_int & 0x80) ? -100 : 100);
dht11->humidity = ((hum_int << 8) + hum_dec) * 100;
......
// SPDX-License-Identifier: GPL-2.0+
/*
* hdc100x.c - Support for the TI HDC100x temperature + humidity sensors
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2015, 2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* Datasheets:
* http://www.ti.com/product/HDC1000/datasheet
......@@ -449,6 +441,6 @@ static struct i2c_driver hdc100x_driver = {
};
module_i2c_driver(hdc100x_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver");
MODULE_LICENSE("GPL");
......@@ -15,21 +15,8 @@
#include <linux/iio/iio.h>
#define HTS221_RX_MAX_LENGTH 8
#define HTS221_TX_MAX_LENGTH 8
#define HTS221_DATA_SIZE 2
struct hts221_transfer_buffer {
u8 rx_buf[HTS221_RX_MAX_LENGTH];
u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
};
struct hts221_transfer_function {
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
};
enum hts221_sensor_type {
HTS221_SENSOR_H,
HTS221_SENSOR_T,
......@@ -44,8 +31,8 @@ struct hts221_sensor {
struct hts221_hw {
const char *name;
struct device *dev;
struct regmap *regmap;
struct mutex lock;
struct iio_trigger *trig;
int irq;
......@@ -53,16 +40,12 @@ struct hts221_hw {
bool enabled;
u8 odr;
const struct hts221_transfer_function *tf;
struct hts221_transfer_buffer tb;
};
extern const struct dev_pm_ops hts221_pm_ops;
int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val);
int hts221_probe(struct device *dev, int irq, const char *name,
const struct hts221_transfer_function *tf_ops);
struct regmap *regmap);
int hts221_set_enable(struct hts221_hw *hw, bool enable);
int hts221_allocate_buffers(struct hts221_hw *hw);
int hts221_allocate_trigger(struct hts221_hw *hw);
......
......@@ -12,6 +12,8 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/regmap.h>
#include <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
......@@ -38,12 +40,10 @@ static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
{
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
struct hts221_hw *hw = iio_priv(iio_dev);
int err;
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR,
HTS221_REG_DRDY_EN_MASK, state);
return err < 0 ? err : 0;
return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR,
HTS221_REG_DRDY_EN_MASK,
FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state));
}
static const struct iio_trigger_ops hts221_trigger_ops = {
......@@ -53,15 +53,13 @@ static const struct iio_trigger_ops hts221_trigger_ops = {
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
{
struct hts221_hw *hw = private;
u8 status;
int err;
int err, status;
err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
&status);
err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status);
if (err < 0)
return IRQ_HANDLED;
/*
/*
* H_DA bit (humidity data available) is routed to DRDY line.
* Humidity sample is computed after temperature one.
* Here we can assume data channels are both available if H_DA bit
......@@ -102,8 +100,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
break;
}
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR,
HTS221_REG_DRDY_HL_MASK, irq_active_low);
err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_HL_ADDR,
HTS221_REG_DRDY_HL_MASK,
FIELD_PREP(HTS221_REG_DRDY_HL_MASK,
irq_active_low));
if (err < 0)
return err;
......@@ -114,9 +114,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
open_drain = true;
}
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR,
HTS221_REG_DRDY_PP_OD_MASK,
open_drain);
err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR,
HTS221_REG_DRDY_PP_OD_MASK,
FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK,
open_drain));
if (err < 0)
return err;
......@@ -171,15 +172,15 @@ static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
/* humidity data */
ch = &iio_dev->channels[HTS221_SENSOR_H];
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
buffer);
err = regmap_bulk_read(hw->regmap, ch->address,
buffer, HTS221_DATA_SIZE);
if (err < 0)
goto out;
/* temperature data */
ch = &iio_dev->channels[HTS221_SENSOR_T];
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
buffer + HTS221_DATA_SIZE);
err = regmap_bulk_read(hw->regmap, ch->address,
buffer + HTS221_DATA_SIZE, HTS221_DATA_SIZE);
if (err < 0)
goto out;
......
......@@ -14,7 +14,8 @@
#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <asm/unaligned.h>
#include <linux/regmap.h>
#include <linux/bitfield.h>
#include "hts221.h"
......@@ -131,38 +132,11 @@ static const struct iio_chan_spec hts221_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(2),
};
int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val)
{
u8 data;
int err;
mutex_lock(&hw->lock);
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
if (err < 0) {
dev_err(hw->dev, "failed to read %02x register\n", addr);
goto unlock;
}
data = (data & ~mask) | ((val << __ffs(mask)) & mask);
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
if (err < 0)
dev_err(hw->dev, "failed to write %02x register\n", addr);
unlock:
mutex_unlock(&hw->lock);
return err;
}
static int hts221_check_whoami(struct hts221_hw *hw)
{
u8 data;
int err;
int err, data;
err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
&data);
err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data);
if (err < 0) {
dev_err(hw->dev, "failed to read whoami register\n");
return err;
......@@ -188,8 +162,10 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
if (i == ARRAY_SIZE(hts221_odr_table))
return -EINVAL;
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
HTS221_ODR_MASK, hts221_odr_table[i].val);
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
HTS221_ODR_MASK,
FIELD_PREP(HTS221_ODR_MASK,
hts221_odr_table[i].val));
if (err < 0)
return err;
......@@ -202,8 +178,8 @@ static int hts221_update_avg(struct hts221_hw *hw,
enum hts221_sensor_type type,
u16 val)
{
int i, err;
const struct hts221_avg *avg = &hts221_avg_list[type];
int i, err, data;
for (i = 0; i < HTS221_AVG_DEPTH; i++)
if (avg->avg_avl[i] == val)
......@@ -212,7 +188,9 @@ static int hts221_update_avg(struct hts221_hw *hw,
if (i == HTS221_AVG_DEPTH)
return -EINVAL;
err = hts221_write_with_mask(hw, avg->addr, avg->mask, i);
data = ((i << __ffs(avg->mask)) & avg->mask);
err = regmap_update_bits(hw->regmap, avg->addr,
avg->mask, data);
if (err < 0)
return err;
......@@ -274,8 +252,9 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
{
int err;
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
HTS221_ENABLE_MASK, enable);
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
HTS221_ENABLE_MASK,
FIELD_PREP(HTS221_ENABLE_MASK, enable));
if (err < 0)
return err;
......@@ -286,38 +265,35 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
static int hts221_parse_temp_caldata(struct hts221_hw *hw)
{
int err, *slope, *b_gen;
int err, *slope, *b_gen, cal0, cal1;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
u8 cal0, cal1;
__le16 val;
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
sizeof(cal0), &cal0);
err = regmap_read(hw->regmap, HTS221_REG_0T_CAL_Y_H, &cal0);
if (err < 0)
return err;
err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
sizeof(cal1), &cal1);
err = regmap_read(hw->regmap, HTS221_REG_T1_T0_CAL_Y_H, &cal1);
if (err < 0)
return err;
cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
cal_y0 = ((cal1 & 0x3) << 8) | cal0;
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
sizeof(cal0), &cal0);
err = regmap_read(hw->regmap, HTS221_REG_1T_CAL_Y_H, &cal0);
if (err < 0)
return err;
cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
(u8 *)&cal_x0);
err = regmap_bulk_read(hw->regmap, HTS221_REG_0T_CAL_X_L,
&val, sizeof(val));
if (err < 0)
return err;
cal_x0 = le16_to_cpu(cal_x0);
cal_x0 = le16_to_cpu(val);
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
(u8 *)&cal_x1);
err = regmap_bulk_read(hw->regmap, HTS221_REG_1T_CAL_X_L,
&val, sizeof(val));
if (err < 0)
return err;
cal_x1 = le16_to_cpu(cal_x1);
cal_x1 = le16_to_cpu(val);
slope = &hw->sensors[HTS221_SENSOR_T].slope;
b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
......@@ -332,33 +308,31 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw)
static int hts221_parse_rh_caldata(struct hts221_hw *hw)
{
int err, *slope, *b_gen;
int err, *slope, *b_gen, data;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
u8 data;
__le16 val;
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
&data);
err = regmap_read(hw->regmap, HTS221_REG_0RH_CAL_Y_H, &data);
if (err < 0)
return err;
cal_y0 = data;
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
&data);
err = regmap_read(hw->regmap, HTS221_REG_1RH_CAL_Y_H, &data);
if (err < 0)
return err;
cal_y1 = data;
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
(u8 *)&cal_x0);
err = regmap_bulk_read(hw->regmap, HTS221_REG_0RH_CAL_X_H,
&val, sizeof(val));
if (err < 0)
return err;
cal_x0 = le16_to_cpu(cal_x0);
cal_x0 = le16_to_cpu(val);
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
(u8 *)&cal_x1);
err = regmap_bulk_read(hw->regmap, HTS221_REG_1RH_CAL_X_H,
&val, sizeof(val));
if (err < 0)
return err;
cal_x1 = le16_to_cpu(cal_x1);
cal_x1 = le16_to_cpu(val);
slope = &hw->sensors[HTS221_SENSOR_H].slope;
b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
......@@ -431,7 +405,7 @@ static int hts221_get_sensor_offset(struct hts221_hw *hw,
static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
{
u8 data[HTS221_DATA_SIZE];
__le16 data;
int err;
err = hts221_set_enable(hw, true);
......@@ -440,13 +414,13 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
msleep(50);
err = hw->tf->read(hw->dev, addr, sizeof(data), data);
err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
if (err < 0)
return err;
hts221_set_enable(hw, false);
*val = (s16)get_unaligned_le16(data);
*val = (s16)le16_to_cpu(data);
return IIO_VAL_INT;
}
......@@ -582,7 +556,7 @@ static const struct iio_info hts221_info = {
static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
int hts221_probe(struct device *dev, int irq, const char *name,
const struct hts221_transfer_function *tf_ops)
struct regmap *regmap)
{
struct iio_dev *iio_dev;
struct hts221_hw *hw;
......@@ -599,9 +573,7 @@ int hts221_probe(struct device *dev, int irq, const char *name,
hw->name = name;
hw->dev = dev;
hw->irq = irq;
hw->tf = tf_ops;
mutex_init(&hw->lock);
hw->regmap = regmap;
err = hts221_check_whoami(hw);
if (err < 0)
......@@ -616,8 +588,9 @@ int hts221_probe(struct device *dev, int irq, const char *name,
iio_dev->info = &hts221_info;
/* enable Block Data Update */
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
HTS221_BDU_MASK, 1);
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
HTS221_BDU_MASK,
FIELD_PREP(HTS221_BDU_MASK, 1));
if (err < 0)
return err;
......@@ -673,12 +646,10 @@ static int __maybe_unused hts221_suspend(struct device *dev)
{
struct iio_dev *iio_dev = dev_get_drvdata(dev);
struct hts221_hw *hw = iio_priv(iio_dev);
int err;
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
HTS221_ENABLE_MASK, false);
return err < 0 ? err : 0;
return regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
HTS221_ENABLE_MASK,
FIELD_PREP(HTS221_ENABLE_MASK, false));
}
static int __maybe_unused hts221_resume(struct device *dev)
......@@ -688,9 +659,10 @@ static int __maybe_unused hts221_resume(struct device *dev)
int err = 0;
if (hw->enabled)
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
HTS221_ENABLE_MASK, true);
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
HTS221_ENABLE_MASK,
FIELD_PREP(HTS221_ENABLE_MASK,
true));
return err;
}
......
......@@ -13,61 +13,33 @@
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include "hts221.h"
#define I2C_AUTO_INCREMENT 0x80
static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
{
struct i2c_msg msg[2];
struct i2c_client *client = to_i2c_client(dev);
if (len > 1)
addr |= I2C_AUTO_INCREMENT;
msg[0].addr = client->addr;
msg[0].flags = client->flags;
msg[0].len = 1;
msg[0].buf = &addr;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = len;
msg[1].buf = data;
return i2c_transfer(client->adapter, msg, 2);
}
#include <linux/regmap.h>
static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
{
u8 send[len + 1];
struct i2c_msg msg;
struct i2c_client *client = to_i2c_client(dev);
if (len > 1)
addr |= I2C_AUTO_INCREMENT;
send[0] = addr;
memcpy(&send[1], data, len * sizeof(u8));
msg.addr = client->addr;
msg.flags = client->flags;
msg.len = len + 1;
msg.buf = send;
#include "hts221.h"
return i2c_transfer(client->adapter, &msg, 1);
}
#define HTS221_I2C_AUTO_INCREMENT BIT(7)
static const struct hts221_transfer_function hts221_transfer_fn = {
.read = hts221_i2c_read,
.write = hts221_i2c_write,
static const struct regmap_config hts221_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.write_flag_mask = HTS221_I2C_AUTO_INCREMENT,
.read_flag_mask = HTS221_I2C_AUTO_INCREMENT,
};
static int hts221_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &hts221_i2c_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return hts221_probe(&client->dev, client->irq,
client->name, &hts221_transfer_fn);
client->name, regmap);
}
static const struct acpi_device_id hts221_acpi_match[] = {
......
......@@ -12,76 +12,33 @@
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include "hts221.h"
#define SENSORS_SPI_READ 0x80
#define SPI_AUTO_INCREMENT 0x40
static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
{
int err;
struct spi_device *spi = to_spi_device(dev);
struct iio_dev *iio_dev = spi_get_drvdata(spi);
struct hts221_hw *hw = iio_priv(iio_dev);
struct spi_transfer xfers[] = {
{
.tx_buf = hw->tb.tx_buf,
.bits_per_word = 8,
.len = 1,
},
{
.rx_buf = hw->tb.rx_buf,
.bits_per_word = 8,
.len = len,
}
};
if (len > 1)
addr |= SPI_AUTO_INCREMENT;
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
if (err < 0)
return err;
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
return len;
}
static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
{
struct spi_device *spi = to_spi_device(dev);
struct iio_dev *iio_dev = spi_get_drvdata(spi);
struct hts221_hw *hw = iio_priv(iio_dev);
struct spi_transfer xfers = {
.tx_buf = hw->tb.tx_buf,
.bits_per_word = 8,
.len = len + 1,
};
if (len >= HTS221_TX_MAX_LENGTH)
return -ENOMEM;
#include <linux/regmap.h>
if (len > 1)
addr |= SPI_AUTO_INCREMENT;
hw->tb.tx_buf[0] = addr;
memcpy(&hw->tb.tx_buf[1], data, len);
#include "hts221.h"
return spi_sync_transfer(spi, &xfers, 1);
}
#define HTS221_SPI_READ BIT(7)
#define HTS221_SPI_AUTO_INCREMENT BIT(6)
static const struct hts221_transfer_function hts221_transfer_fn = {
.read = hts221_spi_read,
.write = hts221_spi_write,
static const struct regmap_config hts221_spi_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.write_flag_mask = HTS221_SPI_AUTO_INCREMENT,
.read_flag_mask = HTS221_SPI_READ | HTS221_SPI_AUTO_INCREMENT,
};
static int hts221_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &hts221_spi_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return hts221_probe(&spi->dev, spi->irq,
spi->modalias, &hts221_transfer_fn);
spi->modalias, regmap);
}
static const struct of_device_id hts221_spi_of_match[] = {
......
......@@ -27,7 +27,7 @@ enum st_lsm6dsx_hw_id {
ST_LSM6DSX_MAX_ID,
};
#define ST_LSM6DSX_BUFF_SIZE 256
#define ST_LSM6DSX_BUFF_SIZE 400
#define ST_LSM6DSX_CHAN_SIZE 2
#define ST_LSM6DSX_SAMPLE_SIZE 6
#define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \
......@@ -57,6 +57,20 @@ struct st_lsm6dsx_fifo_ops {
u8 th_wl;
};
/**
* struct st_lsm6dsx_hw_ts_settings - ST IMU hw timer settings
* @timer_en: Hw timer enable register info (addr + mask).
* @hr_timer: Hw timer resolution register info (addr + mask).
* @fifo_en: Hw timer FIFO enable register info (addr + mask).
* @decimator: Hw timer FIFO decimator register info (addr + mask).
*/
struct st_lsm6dsx_hw_ts_settings {
struct st_lsm6dsx_reg timer_en;
struct st_lsm6dsx_reg hr_timer;
struct st_lsm6dsx_reg fifo_en;
struct st_lsm6dsx_reg decimator;
};
/**
* struct st_lsm6dsx_settings - ST IMU sensor settings
* @wai: Sensor WhoAmI default value.
......@@ -64,6 +78,7 @@ struct st_lsm6dsx_fifo_ops {
* @id: List of hw id supported by the driver configuration.
* @decimator: List of decimator register info (addr + mask).
* @fifo_ops: Sensor hw FIFO parameters.
* @ts_settings: Hw timer related settings.
*/
struct st_lsm6dsx_settings {
u8 wai;
......@@ -71,6 +86,7 @@ struct st_lsm6dsx_settings {
enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
struct st_lsm6dsx_fifo_ops fifo_ops;
struct st_lsm6dsx_hw_ts_settings ts_settings;
};
enum st_lsm6dsx_sensor_id {
......@@ -94,8 +110,7 @@ enum st_lsm6dsx_fifo_mode {
* @watermark: Sensor watermark level.
* @sip: Number of samples in a given pattern.
* @decimator: FIFO decimation factor.
* @delta_ts: Delta time between two consecutive interrupts.
* @ts: Latest timestamp from the interrupt handler.
* @ts_ref: Sensor timestamp reference for hw one.
*/
struct st_lsm6dsx_sensor {
char name[32];
......@@ -108,9 +123,7 @@ struct st_lsm6dsx_sensor {
u16 watermark;
u8 sip;
u8 decimator;
s64 delta_ts;
s64 ts;
s64 ts_ref;
};
/**
......@@ -122,7 +135,8 @@ struct st_lsm6dsx_sensor {
* @conf_lock: Mutex to prevent concurrent FIFO configuration update.
* @fifo_mode: FIFO operating mode supported by the device.
* @enable_mask: Enabled sensor bitmask.
* @sip: Total number of samples (acc/gyro) in a given pattern.
* @ts_sip: Total number of timestamp samples in a given pattern.
* @sip: Total number of samples (acc/gyro/ts) in a given pattern.
* @buff: Device read buffer.
* @iio_devs: Pointers to acc/gyro iio_dev instances.
* @settings: Pointer to the specific sensor settings in use.
......@@ -137,6 +151,7 @@ struct st_lsm6dsx_hw {
enum st_lsm6dsx_fifo_mode fifo_mode;
u8 enable_mask;
u8 ts_sip;
u8 sip;
u8 *buff;
......
......@@ -46,9 +46,13 @@
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */
#define ST_LSM6DSX_TS_RESET_VAL 0xaa
struct st_lsm6dsx_decimator_entry {
u8 decimator;
u8 val;
......@@ -98,9 +102,10 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
{
u16 max_odr, min_odr, sip = 0, ts_sip = 0;
const struct st_lsm6dsx_reg *ts_dec_reg;
struct st_lsm6dsx_sensor *sensor;
u16 max_odr, min_odr, sip = 0;
int err, i;
int err = 0, i;
u8 data;
st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
......@@ -119,6 +124,7 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
sensor->decimator = 0;
data = 0;
}
ts_sip = max_t(u16, ts_sip, sensor->sip);
dec_reg = &hw->settings->decimator[sensor->id];
if (dec_reg->addr) {
......@@ -131,9 +137,23 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
}
sip += sensor->sip;
}
hw->sip = sip;
hw->sip = sip + ts_sip;
hw->ts_sip = ts_sip;
return 0;
/*
* update hw ts decimator if necessary. Decimator for hw timestamp
* is always 1 or 0 in order to have a ts sample for each data
* sample in FIFO
*/
ts_dec_reg = &hw->settings->ts_settings.decimator;
if (ts_dec_reg->addr) {
int val, ts_dec = !!hw->ts_sip;
val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask);
err = regmap_update_bits(hw->regmap, ts_dec_reg->addr,
ts_dec_reg->mask, val);
}
return err;
}
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
......@@ -208,6 +228,28 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
&wdata, sizeof(wdata));
}
static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
{
struct st_lsm6dsx_sensor *sensor;
int i, err;
/* reset hw ts counter */
err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR,
ST_LSM6DSX_TS_RESET_VAL);
if (err < 0)
return err;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
sensor = iio_priv(hw->iio_devs[i]);
/*
* store enable buffer timestamp as reference for
* hw timestamp
*/
sensor->ts_ref = iio_get_time_ns(hw->iio_devs[i]);
}
return 0;
}
/*
* Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid
* a kmalloc for each bus access
......@@ -231,6 +273,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
return 0;
}
#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \
sizeof(s64)) + sizeof(s64))
/**
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO routine
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
......@@ -243,11 +287,13 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
{
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
int err, acc_sip, gyro_sip, read_len, samples, offset;
int err, acc_sip, gyro_sip, ts_sip, read_len, offset;
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts;
u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
u8 gyro_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
u8 acc_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
bool reset_ts = false;
__le16 fifo_status;
s64 ts = 0;
err = regmap_bulk_read(hw->regmap,
hw->settings->fifo_ops.fifo_diff.addr,
......@@ -260,23 +306,10 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) *
ST_LSM6DSX_CHAN_SIZE;
samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE;
fifo_len = (fifo_len / pattern_len) * pattern_len;
/*
* compute delta timestamp between two consecutive samples
* in order to estimate queueing time of data generated
* by the sensor
*/
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
acc_ts = acc_sensor->ts - acc_sensor->delta_ts;
acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator,
samples);
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts;
gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator,
samples);
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
......@@ -287,7 +320,7 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
* Data are written to the FIFO with a specific pattern
* depending on the configured ODRs. The first sequence of data
* stored in FIFO contains the data of all enabled sensors
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated
* (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated
* depending on the value of the decimation factor set for each
* sensor.
*
......@@ -296,35 +329,65 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
* - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz
* Since the gyroscope ODR is twice the accelerometer one, the
* following pattern is repeated every 9 samples:
* - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz
* - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, ..
*/
gyro_sip = gyro_sensor->sip;
acc_sip = acc_sensor->sip;
ts_sip = hw->ts_sip;
offset = 0;
while (acc_sip > 0 || gyro_sip > 0) {
if (gyro_sip-- > 0) {
memcpy(iio_buff, &hw->buff[offset],
if (gyro_sip > 0) {
memcpy(gyro_buff, &hw->buff[offset],
ST_LSM6DSX_SAMPLE_SIZE);
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
iio_buff, gyro_ts);
offset += ST_LSM6DSX_SAMPLE_SIZE;
gyro_ts += gyro_delta_ts;
}
if (acc_sip-- > 0) {
memcpy(iio_buff, &hw->buff[offset],
if (acc_sip > 0) {
memcpy(acc_buff, &hw->buff[offset],
ST_LSM6DSX_SAMPLE_SIZE);
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LSM6DSX_ID_ACC],
iio_buff, acc_ts);
offset += ST_LSM6DSX_SAMPLE_SIZE;
acc_ts += acc_delta_ts;
}
if (ts_sip-- > 0) {
u8 data[ST_LSM6DSX_SAMPLE_SIZE];
memcpy(data, &hw->buff[offset], sizeof(data));
/*
* hw timestamp is 3B long and it is stored
* in FIFO using 6B as 4th FIFO data set
* according to this schema:
* B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0]
*/
ts = data[1] << 16 | data[0] << 8 | data[3];
/*
* check if hw timestamp engine is going to
* reset (the sensor generates an interrupt
* to signal the hw timestamp will reset in
* 1.638s)
*/
if (!reset_ts && ts >= 0xff0000)
reset_ts = true;
ts *= ST_LSM6DSX_TS_SENSITIVITY;
offset += ST_LSM6DSX_SAMPLE_SIZE;
}
if (gyro_sip-- > 0)
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
gyro_buff, gyro_sensor->ts_ref + ts);
if (acc_sip-- > 0)
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LSM6DSX_ID_ACC],
acc_buff, acc_sensor->ts_ref + ts);
}
}
if (unlikely(reset_ts)) {
err = st_lsm6dsx_reset_hw_ts(hw);
if (err < 0)
return err;
}
return read_len;
}
......@@ -379,15 +442,12 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
goto out;
if (hw->enable_mask) {
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
/* reset hw ts counter */
err = st_lsm6dsx_reset_hw_ts(hw);
if (err < 0)
goto out;
/*
* store enable buffer timestamp as reference to compute
* first delta timestamp
*/
sensor->ts = iio_get_time_ns(iio_dev);
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
}
out:
......@@ -399,25 +459,8 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
{
struct st_lsm6dsx_hw *hw = private;
struct st_lsm6dsx_sensor *sensor;
int i;
if (!hw->sip)
return IRQ_NONE;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
sensor = iio_priv(hw->iio_devs[i]);
if (sensor->sip > 0) {
s64 timestamp;
timestamp = iio_get_time_ns(hw->iio_devs[i]);
sensor->delta_ts = timestamp - sensor->ts;
sensor->ts = timestamp;
}
}
return IRQ_WAKE_THREAD;
return hw->sip > 0 ? IRQ_WAKE_THREAD : IRQ_NONE;
}
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
......
......@@ -181,6 +181,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.th_wl = 3, /* 1LSB = 2B */
},
.ts_settings = {
.timer_en = {
.addr = 0x58,
.mask = BIT(7),
},
.hr_timer = {
.addr = 0x5c,
.mask = BIT(4),
},
.fifo_en = {
.addr = 0x07,
.mask = BIT(7),
},
.decimator = {
.addr = 0x09,
.mask = GENMASK(5, 3),
},
},
},
{
.wai = 0x69,
......@@ -209,6 +227,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.th_wl = 3, /* 1LSB = 2B */
},
.ts_settings = {
.timer_en = {
.addr = 0x58,
.mask = BIT(7),
},
.hr_timer = {
.addr = 0x5c,
.mask = BIT(4),
},
.fifo_en = {
.addr = 0x07,
.mask = BIT(7),
},
.decimator = {
.addr = 0x09,
.mask = GENMASK(5, 3),
},
},
},
{
.wai = 0x6a,
......@@ -238,6 +274,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.th_wl = 3, /* 1LSB = 2B */
},
.ts_settings = {
.timer_en = {
.addr = 0x19,
.mask = BIT(5),
},
.hr_timer = {
.addr = 0x5c,
.mask = BIT(4),
},
.fifo_en = {
.addr = 0x07,
.mask = BIT(7),
},
.decimator = {
.addr = 0x09,
.mask = GENMASK(5, 3),
},
},
},
};
......@@ -630,6 +684,44 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
return err;
}
static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
{
const struct st_lsm6dsx_hw_ts_settings *ts_settings;
int err, val;
ts_settings = &hw->settings->ts_settings;
/* enable hw timestamp generation if necessary */
if (ts_settings->timer_en.addr) {
val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->timer_en.mask);
err = regmap_update_bits(hw->regmap,
ts_settings->timer_en.addr,
ts_settings->timer_en.mask, val);
if (err < 0)
return err;
}
/* enable high resolution for hw ts timer if necessary */
if (ts_settings->hr_timer.addr) {
val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->hr_timer.mask);
err = regmap_update_bits(hw->regmap,
ts_settings->hr_timer.addr,
ts_settings->hr_timer.mask, val);
if (err < 0)
return err;
}
/* enable ts queueing in FIFO if necessary */
if (ts_settings->fifo_en.addr) {
val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->fifo_en.mask);
err = regmap_update_bits(hw->regmap,
ts_settings->fifo_en.addr,
ts_settings->fifo_en.mask, val);
if (err < 0)
return err;
}
return 0;
}
static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
{
u8 drdy_int_reg;
......@@ -654,10 +746,14 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
if (err < 0)
return err;
return regmap_update_bits(hw->regmap, drdy_int_reg,
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
1));
err = regmap_update_bits(hw->regmap, drdy_int_reg,
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
1));
if (err < 0)
return err;
return st_lsm6dsx_init_hw_timer(hw);
}
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
......
// SPDX-License-Identifier: GPL-2.0+
/*
* apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2015, 2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: gesture + proximity calib offsets
*/
......@@ -1141,6 +1133,6 @@ static struct i2c_driver apds9960_driver = {
};
module_i2c_driver(apds9960_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor");
MODULE_LICENSE("GPL");
......@@ -5,6 +5,16 @@
menu "Digital potentiometers"
config AD5272
tristate "Analog Devices AD5272 and similar Digital Potentiometer driver"
depends on I2C
help
Say yes here to build support for the Analog Devices AD5272 and AD5274
digital potentiometer chip.
To compile this driver as a module, choose M here: the
module will be called ad5272.
config DS1803
tristate "Maxim Integrated DS1803 Digital Potentiometer driver"
depends on I2C
......
......@@ -4,6 +4,7 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD5272) += ad5272.o
obj-$(CONFIG_DS1803) += ds1803.o
obj-$(CONFIG_MAX5481) += max5481.o
obj-$(CONFIG_MAX5487) += max5487.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Analog Devices AD5272 digital potentiometer driver
* Copyright (C) 2018 Phil Reid <preid@electromag.com.au>
*
* Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5272_5274.pdf
*
* DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address
* ad5272 1 1024 20, 50, 100 01011xx
* ad5274 1 256 20, 100 01011xx
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#define AD5272_RDAC_WR 1
#define AD5272_RDAC_RD 2
#define AD5272_RESET 4
#define AD5272_CTL 7
#define AD5272_RDAC_WR_EN BIT(1)
struct ad5272_cfg {
int max_pos;
int kohms;
int shift;
};
enum ad5272_type {
AD5272_020,
AD5272_050,
AD5272_100,
AD5274_020,
AD5274_100,
};
static const struct ad5272_cfg ad5272_cfg[] = {
[AD5272_020] = { .max_pos = 1024, .kohms = 20 },
[AD5272_050] = { .max_pos = 1024, .kohms = 50 },
[AD5272_100] = { .max_pos = 1024, .kohms = 100 },
[AD5274_020] = { .max_pos = 256, .kohms = 20, .shift = 2 },
[AD5274_100] = { .max_pos = 256, .kohms = 100, .shift = 2 },
};
struct ad5272_data {
struct i2c_client *client;
struct mutex lock;
const struct ad5272_cfg *cfg;
u8 buf[2] ____cacheline_aligned;
};
static const struct iio_chan_spec ad5272_channel = {
.type = IIO_RESISTANCE,
.output = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
};
static int ad5272_write(struct ad5272_data *data, int reg, int val)
{
int ret;
data->buf[0] = (reg << 2) | ((val >> 8) & 0x3);
data->buf[1] = (u8)val;
mutex_lock(&data->lock);
ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
mutex_unlock(&data->lock);
return ret < 0 ? ret : 0;
}
static int ad5272_read(struct ad5272_data *data, int reg, int *val)
{
int ret;
data->buf[0] = reg << 2;
data->buf[1] = 0;
mutex_lock(&data->lock);
ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
if (ret < 0)
goto error;
ret = i2c_master_recv(data->client, data->buf, sizeof(data->buf));
if (ret < 0)
goto error;
*val = ((data->buf[0] & 0x3) << 8) | data->buf[1];
ret = 0;
error:
mutex_unlock(&data->lock);
return ret;
}
static int ad5272_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ad5272_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW: {
ret = ad5272_read(data, AD5272_RDAC_RD, val);
*val = *val >> data->cfg->shift;
return ret ? ret : IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
*val = 1000 * data->cfg->kohms;
*val2 = data->cfg->max_pos;
return IIO_VAL_FRACTIONAL;
}
return -EINVAL;
}
static int ad5272_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct ad5272_data *data = iio_priv(indio_dev);
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
if (val >= data->cfg->max_pos || val < 0 || val2)
return -EINVAL;
return ad5272_write(data, AD5272_RDAC_WR, val << data->cfg->shift);
}
static const struct iio_info ad5272_info = {
.read_raw = ad5272_read_raw,
.write_raw = ad5272_write_raw,
};
static int ad5272_reset(struct ad5272_data *data)
{
struct gpio_desc *reset_gpio;
reset_gpio = devm_gpiod_get_optional(&data->client->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(reset_gpio))
return PTR_ERR(reset_gpio);
if (reset_gpio) {
udelay(1);
gpiod_set_value(reset_gpio, 1);
} else {
ad5272_write(data, AD5272_RESET, 0);
}
usleep_range(1000, 2000);
return 0;
}
static int ad5272_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct iio_dev *indio_dev;
struct ad5272_data *data;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
i2c_set_clientdata(client, indio_dev);
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
data->cfg = &ad5272_cfg[id->driver_data];
ret = ad5272_reset(data);
if (ret)
return ret;
ret = ad5272_write(data, AD5272_CTL, AD5272_RDAC_WR_EN);
if (ret < 0)
return -ENODEV;
indio_dev->dev.parent = dev;
indio_dev->info = &ad5272_info;
indio_dev->channels = &ad5272_channel;
indio_dev->num_channels = 1;
indio_dev->name = client->name;
return devm_iio_device_register(dev, indio_dev);
}
#if defined(CONFIG_OF)
static const struct of_device_id ad5272_dt_ids[] = {
{ .compatible = "adi,ad5272-020", .data = (void *)AD5272_020 },
{ .compatible = "adi,ad5272-050", .data = (void *)AD5272_050 },
{ .compatible = "adi,ad5272-100", .data = (void *)AD5272_100 },
{ .compatible = "adi,ad5274-020", .data = (void *)AD5274_020 },
{ .compatible = "adi,ad5274-100", .data = (void *)AD5274_100 },
{}
};
MODULE_DEVICE_TABLE(of, ad5272_dt_ids);
#endif /* CONFIG_OF */
static const struct i2c_device_id ad5272_id[] = {
{ "ad5272-020", AD5272_020 },
{ "ad5272-050", AD5272_050 },
{ "ad5272-100", AD5272_100 },
{ "ad5274-020", AD5274_020 },
{ "ad5274-100", AD5274_100 },
{}
};
MODULE_DEVICE_TABLE(i2c, ad5272_id);
static struct i2c_driver ad5272_driver = {
.driver = {
.name = "ad5272",
.of_match_table = of_match_ptr(ad5272_dt_ids),
},
.probe = ad5272_probe,
.id_table = ad5272_id,
};
module_i2c_driver(ad5272_driver);
MODULE_AUTHOR("Phil Reid <preid@eletromag.com.au>");
MODULE_DESCRIPTION("AD5272 digital potentiometer");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0+
/*
* tpl0102.c - Support for Texas Instruments digital potentiometers
*
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2016, 2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: enable/disable hi-z output control
*/
......@@ -156,6 +148,6 @@ static struct i2c_driver tpl0102_driver = {
module_i2c_driver(tpl0102_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("TPL0102 digital potentiometer");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0+
/*
* lmp91000.c - Support for Texas Instruments digital potentiostats
*
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2016, 2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: bias voltage + polarity control, and multiple chip support
*/
......@@ -440,6 +432,6 @@ static struct i2c_driver lmp91000_driver = {
};
module_i2c_driver(lmp91000_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("LMP91000 digital potentiostat");
MODULE_LICENSE("GPL");
......@@ -63,7 +63,7 @@ struct ms5611_state {
};
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
const char* name, int type);
const char *name, int type);
int ms5611_remove(struct iio_dev *indio_dev);
#endif /* _MS5611_H */
// SPDX-License-Identifier: GPL-2.0+
/*
* as3935.c - Support for AS3935 Franklin lightning sensor
*
* Copyright (C) 2014 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (C) 2014, 2017-2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
......@@ -502,6 +493,6 @@ static struct spi_driver as3935_driver = {
};
module_spi_driver(as3935_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("AS3935 lightning sensor");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0+
/*
* pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2015, 2017-2018
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: interrupt mode, and signal strength reporting
*/
......@@ -377,6 +369,6 @@ static struct i2c_driver lidar_driver = {
};
module_i2c_driver(lidar_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
MODULE_LICENSE("GPL");
......@@ -43,6 +43,18 @@ config MLX90614
This driver can also be built as a module. If so, the module will
be called mlx90614.
config MLX90632
tristate "MLX90632 contact-less infrared sensor with medical accuracy"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for the Melexis
MLX90632 contact-less infrared sensor with medical accuracy
connected with I2C.
This driver can also be built as a module. If so, the module will
be called mlx90632.
config TMP006
tristate "TMP006 infrared thermopile sensor"
depends on I2C
......
......@@ -6,6 +6,7 @@
obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
obj-$(CONFIG_MLX90614) += mlx90614.o
obj-$(CONFIG_MLX90632) += mlx90632.o
obj-$(CONFIG_TMP006) += tmp006.o
obj-$(CONFIG_TMP007) += tmp007.o
obj-$(CONFIG_TSYS01) += tsys01.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* maxim_thermocouple.c - Support for Maxim thermocouple chips
*
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Copyright (C) 2016-2018 Matt Ranostay
* Author: <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
......@@ -281,6 +273,6 @@ static struct spi_driver maxim_thermocouple_driver = {
};
module_spi_driver(maxim_thermocouple_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("Maxim thermocouple sensors");
MODULE_LICENSE("GPL");
This diff is collapsed.
......@@ -290,8 +290,12 @@ static int ad7192_setup(struct ad7192_state *st,
if (pdata->unipolar_en)
st->conf |= AD7192_CONF_UNIPOLAR;
if (pdata->burnout_curr_en)
if (pdata->burnout_curr_en && pdata->buf_en && !pdata->chop_en) {
st->conf |= AD7192_CONF_BURN;
} else if (pdata->burnout_curr_en) {
dev_warn(&st->sd.spi->dev,
"Can't enable burnout currents: see CHOP or buffer\n");
}
ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
if (ret)
......
......@@ -111,7 +111,7 @@
* @trig: data ready trigger registered with iio
* @tx: transmit buffer
* @rx: receive buffer
* @buf_lock: mutex to protect tx and rx
* @buf_lock: mutex to protect tx, rx, read and write frequency
**/
struct ade7758_state {
struct spi_device *us;
......
......@@ -24,17 +24,25 @@
#include "meter.h"
#include "ade7758.h"
int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
static int __ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7758_state *st = iio_priv(indio_dev);
mutex_lock(&st->buf_lock);
st->tx[0] = ADE7758_WRITE_REG(reg_address);
st->tx[1] = val;
ret = spi_write(st->us, st->tx, 2);
return spi_write(st->us, st->tx, 2);
}
int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7758_state *st = iio_priv(indio_dev);
mutex_lock(&st->buf_lock);
ret = __ade7758_spi_write_reg_8(dev, reg_address, val);
mutex_unlock(&st->buf_lock);
return ret;
......@@ -91,7 +99,7 @@ static int ade7758_spi_write_reg_24(struct device *dev, u8 reg_address,
return ret;
}
int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
static int __ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7758_state *st = iio_priv(indio_dev);
......@@ -111,7 +119,6 @@ int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
},
};
mutex_lock(&st->buf_lock);
st->tx[0] = ADE7758_READ_REG(reg_address);
st->tx[1] = 0;
......@@ -124,7 +131,19 @@ int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
*val = st->rx[0];
error_ret:
return ret;
}
int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7758_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->buf_lock);
ret = __ade7758_spi_read_reg_8(dev, reg_address, val);
mutex_unlock(&st->buf_lock);
return ret;
}
......@@ -484,6 +503,8 @@ static int ade7758_write_samp_freq(struct device *dev, int val)
{
int ret;
u8 reg, t;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7758_state *st = iio_priv(indio_dev);
switch (val) {
case 26040:
......@@ -499,20 +520,23 @@ static int ade7758_write_samp_freq(struct device *dev, int val)
t = 3;
break;
default:
ret = -EINVAL;
goto out;
return -EINVAL;
}
ret = ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, &reg);
mutex_lock(&st->buf_lock);
ret = __ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, &reg);
if (ret)
goto out;
reg &= ~(5 << 3);
reg |= t << 5;
ret = ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg);
ret = __ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg);
out:
mutex_unlock(&st->buf_lock);
return ret;
}
......@@ -526,9 +550,9 @@ static int ade7758_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&indio_dev->mlock);
ret = ade7758_read_samp_freq(&indio_dev->dev, val);
mutex_unlock(&indio_dev->mlock);
return ret;
default:
return -EINVAL;
......@@ -547,9 +571,9 @@ static int ade7758_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2)
return -EINVAL;
mutex_lock(&indio_dev->mlock);
ret = ade7758_write_samp_freq(&indio_dev->dev, val);
mutex_unlock(&indio_dev->mlock);
return ret;
default:
return -EINVAL;
......
......@@ -72,8 +72,8 @@ struct ade7759_state {
};
static int ade7759_spi_write_reg_8(struct device *dev,
u8 reg_address,
u8 val)
u8 reg_address,
u8 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
......@@ -91,8 +91,8 @@ static int ade7759_spi_write_reg_8(struct device *dev,
/*Unlocked version of ade7759_spi_write_reg_16 function */
static int __ade7759_spi_write_reg_16(struct device *dev,
u8 reg_address,
u16 value)
u8 reg_address,
u16 value)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
......@@ -104,8 +104,8 @@ static int __ade7759_spi_write_reg_16(struct device *dev,
}
static int ade7759_spi_write_reg_16(struct device *dev,
u8 reg_address,
u16 value)
u8 reg_address,
u16 value)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
......@@ -119,8 +119,8 @@ static int ade7759_spi_write_reg_16(struct device *dev,
}
static int ade7759_spi_read_reg_8(struct device *dev,
u8 reg_address,
u8 *val)
u8 reg_address,
u8 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
......@@ -128,8 +128,9 @@ static int ade7759_spi_read_reg_8(struct device *dev,
ret = spi_w8r8(st->us, ADE7759_READ_REG(reg_address));
if (ret < 0) {
dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
reg_address);
dev_err(&st->us->dev,
"problem when reading 8 bit register 0x%02X",
reg_address);
return ret;
}
*val = ret;
......@@ -138,8 +139,8 @@ static int ade7759_spi_read_reg_8(struct device *dev,
}
static int ade7759_spi_read_reg_16(struct device *dev,
u8 reg_address,
u16 *val)
u8 reg_address,
u16 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
......@@ -158,8 +159,8 @@ static int ade7759_spi_read_reg_16(struct device *dev,
}
static int ade7759_spi_read_reg_40(struct device *dev,
u8 reg_address,
u64 *val)
u8 reg_address,
u64 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
......@@ -179,8 +180,9 @@ static int ade7759_spi_read_reg_40(struct device *dev,
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
if (ret) {
dev_err(&st->us->dev, "problem when reading 40 bit register 0x%02X",
reg_address);
dev_err(&st->us->dev,
"problem when reading 40 bit register 0x%02X",
reg_address);
goto error_ret;
}
*val = ((u64)st->rx[1] << 32) | ((u64)st->rx[2] << 24) |
......@@ -192,8 +194,8 @@ static int ade7759_spi_read_reg_40(struct device *dev,
}
static ssize_t ade7759_read_8bit(struct device *dev,
struct device_attribute *attr,
char *buf)
struct device_attribute *attr,
char *buf)
{
int ret;
u8 val = 0;
......@@ -207,8 +209,8 @@ static ssize_t ade7759_read_8bit(struct device *dev,
}
static ssize_t ade7759_read_16bit(struct device *dev,
struct device_attribute *attr,
char *buf)
struct device_attribute *attr,
char *buf)
{
int ret;
u16 val = 0;
......@@ -222,8 +224,8 @@ static ssize_t ade7759_read_16bit(struct device *dev,
}
static ssize_t ade7759_read_40bit(struct device *dev,
struct device_attribute *attr,
char *buf)
struct device_attribute *attr,
char *buf)
{
int ret;
u64 val = 0;
......@@ -237,9 +239,9 @@ static ssize_t ade7759_read_40bit(struct device *dev,
}
static ssize_t ade7759_write_8bit(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
int ret;
......@@ -255,9 +257,9 @@ static ssize_t ade7759_write_8bit(struct device *dev,
}
static ssize_t ade7759_write_16bit(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
int ret;
......@@ -277,9 +279,7 @@ static int ade7759_reset(struct device *dev)
int ret;
u16 val;
ret = ade7759_spi_read_reg_16(dev,
ADE7759_MODE,
&val);
ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val);
if (ret < 0)
return ret;
......@@ -365,9 +365,7 @@ static int ade7759_stop_device(struct device *dev)
int ret;
u16 val;
ret = ade7759_spi_read_reg_16(dev,
ADE7759_MODE,
&val);
ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val);
if (ret < 0) {
dev_err(dev, "unable to power down the device, error: %d\n",
ret);
......@@ -404,16 +402,14 @@ static int ade7759_initial_setup(struct iio_dev *indio_dev)
}
static ssize_t ade7759_read_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
struct device_attribute *attr,
char *buf)
{
int ret;
u16 t;
int sps;
ret = ade7759_spi_read_reg_16(dev,
ADE7759_MODE,
&t);
ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &t);
if (ret)
return ret;
......@@ -424,9 +420,9 @@ static ssize_t ade7759_read_frequency(struct device *dev,
}
static ssize_t ade7759_write_frequency(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
......
......@@ -479,6 +479,15 @@ extern int func_ptr_is_kernel_text(void *ptr);
unsigned long int_sqrt(unsigned long);
#if BITS_PER_LONG < 64
u32 int_sqrt64(u64 x);
#else
static inline u32 int_sqrt64(u64 x)
{
return (u32)int_sqrt(x);
}
#endif
extern void bust_spinlocks(int yes);
extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in progress */
extern int panic_timeout;
......
......@@ -266,6 +266,8 @@ enum axp20x_variants {
#define AXP288_RT_BATT_V_H 0xa0
#define AXP288_RT_BATT_V_L 0xa1
#define AXP813_ADC_RATE 0x85
/* Fuel Gauge */
#define AXP288_FG_RDC1_REG 0xba
#define AXP288_FG_RDC0_REG 0xbb
......
......@@ -38,3 +38,33 @@ unsigned long int_sqrt(unsigned long x)
return y;
}
EXPORT_SYMBOL(int_sqrt);
#if BITS_PER_LONG < 64
/**
* int_sqrt64 - strongly typed int_sqrt function when minimum 64 bit input
* is expected.
* @x: 64bit integer of which to calculate the sqrt
*/
u32 int_sqrt64(u64 x)
{
u64 b, m, y = 0;
if (x <= ULONG_MAX)
return int_sqrt((unsigned long) x);
m = 1ULL << (fls64(x) & ~1ULL);
while (m != 0) {
b = y + m;
y >>= 1;
if (x >= b) {
x -= b;
y += m;
}
m >>= 2;
}
return y;
}
EXPORT_SYMBOL(int_sqrt64);
#endif
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