mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 09:50:04 +00:00
Second round of new device support, cleanups and fixes for IIO in the 4.10 cycle
This includes two branch merges for elements that may also go via MFD. New device support * cros_ec - new driver to support these Chrome OS contiguous sensors which are behind the Chrome OS embedded controller. Requires a few minor MFD and chrome platform changes. One follow up fix deals with some dependency issues in Kconfig. * mpu-3050 - new driver and device tree bindings for this venerable device. * st_accel - support for the lng2dm an Driver features * ad7192 - Add DVdd regulator handling * ad9832 - Add DVDD regulator handling * at91 - Suspend and resume support * si7020 - Device tree bindings * ti-am335x - DMA support - uses dma to accelerate short bursts of read back rather than full blown DMA buffer support. Greatly improved performance. Includes an MFD addition to give access to the address needed for DMA. * tsl2583 - Device tree bindings Cleanups and minor fixes * ad7192 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) - Rename reg variable to reflect which regulator it is * ad5933 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * ad7746 - Fix a missing return value (fallout from previous patch set) * ad7780 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * ad9832 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) - Rename reg regulator to reflect which one it is * ad9834 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * hts221 - Remove a duplicated include * maxim thermocouple - Handle a wrong storage side in read function. Prevent any problems that might be introduced by additions to this driver in future. * tsl2583 - big set from Brian Masney to drive this towards a staging graduation. - Convert to iio_chan_spec and read_raw / write_raw (in a couple of steps) - Improved error handling in various functions - Drop redundant power_state custom sysfs attribute. - Use IIO_*_ATTR* macros for remaining attributes. - Return an error code to userspace on invalid parameters being writen to sysfs files. - Add locking to various attribute accesses to remove possible races. - Add defines for various magic numbers. - Use smbus_read_byte_data instead of a write_byte followed by read_byte. - Query only relevant registers in probe. - Tidy up ordering of code comments. - Remove a pointless power off sequence in taos_chip_on. - Don't bother shutting down the chip when updating the lux table. The table is held entirely in the driver and doesn't effect the chip at all. - Drop a redundant i2c call in taos_als_calibrate where the same register is read twice in a row. -----BEGIN PGP SIGNATURE----- iQIuBAABCAAYBQJYH22HERxqaWMyM0BrZXJuZWwub3JnAAoJEFSFNJnE9BaIUg4P /1GRFGpTbqyzDOX6KZKAdBRsRMn3XPL0XslvbKpupY4T9WFEJbZi01z4P/x+uRIk 6TtRlhdWBLdzVQ6sLpVAZVXR2/FxGj+nsc2ONgHJXm02/fDBmjI89Ed9WVS1HR73 D2yLAFhflvE6mDMExvAnUVBh9ClR2SXoGL0N1k+VQM08Rs+F6IzBAg2albiPkct/ 7UQB9apn4VCCaRY02gllvWrUiJV3w8jsSikwDACZsprQGIxKjLiH4evtRfSOI8bg Z7UoJYaYNMiQtn6+rFaNUjFQtzlnWcjB92RUy/MqnpXmkuAvw3+CbXYteYnailO1 eTsIjKpwssKaQh5HHALrxIwP/a/9a+Wyrd8iheygUQvzSNx3gcxXrtNrUk8eI/9W N8pBRBbu+2pzSzVLQb+7SR5wJG1zQ5NEXI1gpZuyed94g30Be0vQpeiZRNCUL+J0 ta28Xa25cREzHrXGXrhfyxNJuriav/8A5PTtnGJSZZ8RhuHZq24TcC8h6KVOWR+3 BBiehupFcIErbrGcMKuhq01q8A9+Vj/1z6hQaZzfIpXFjEEUvkBo3OMiNtgpkQYM XYdaF2bH25t8wx020z4FrmN48nxg2HDTLtKissP/6bInh4kYyGneKhf6QQVnG2IX 41XLE/fw4QoAnLuTBXD3boydC08uJmVkXeOH2sfaCYwG =ptGw -----END PGP SIGNATURE----- Merge tag 'iio-for-4.10b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-testing Jonathan writes: Second round of new device support, cleanups and fixes for IIO in the 4.10 cycle This includes two branch merges for elements that may also go via MFD. New device support * cros_ec - new driver to support these Chrome OS contiguous sensors which are behind the Chrome OS embedded controller. Requires a few minor MFD and chrome platform changes. One follow up fix deals with some dependency issues in Kconfig. * mpu-3050 - new driver and device tree bindings for this venerable device. * st_accel - support for the lng2dm an Driver features * ad7192 - Add DVdd regulator handling * ad9832 - Add DVDD regulator handling * at91 - Suspend and resume support * si7020 - Device tree bindings * ti-am335x - DMA support - uses dma to accelerate short bursts of read back rather than full blown DMA buffer support. Greatly improved performance. Includes an MFD addition to give access to the address needed for DMA. * tsl2583 - Device tree bindings Cleanups and minor fixes * ad7192 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) - Rename reg variable to reflect which regulator it is * ad5933 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * ad7746 - Fix a missing return value (fallout from previous patch set) * ad7780 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * ad9832 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) - Rename reg regulator to reflect which one it is * ad9834 - Fix regulator naming to match datasheet - Handle regulator errors correctly (so as to not break deferred probing) * hts221 - Remove a duplicated include * maxim thermocouple - Handle a wrong storage side in read function. Prevent any problems that might be introduced by additions to this driver in future. * tsl2583 - big set from Brian Masney to drive this towards a staging graduation. - Convert to iio_chan_spec and read_raw / write_raw (in a couple of steps) - Improved error handling in various functions - Drop redundant power_state custom sysfs attribute. - Use IIO_*_ATTR* macros for remaining attributes. - Return an error code to userspace on invalid parameters being writen to sysfs files. - Add locking to various attribute accesses to remove possible races. - Add defines for various magic numbers. - Use smbus_read_byte_data instead of a write_byte followed by read_byte. - Query only relevant registers in probe. - Tidy up ordering of code comments. - Remove a pointless power off sequence in taos_chip_on. - Don't bother shutting down the chip when updating the lux table. The table is held entirely in the driver and doesn't effect the chip at all. - Drop a redundant i2c call in taos_als_calibrate where the same register is read twice in a row.
This commit is contained in:
commit
7970b9e6a5
42 changed files with 3656 additions and 445 deletions
18
Documentation/ABI/testing/sysfs-bus-iio-cros-ec
Normal file
18
Documentation/ABI/testing/sysfs-bus-iio-cros-ec
Normal file
|
@ -0,0 +1,18 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/calibrate
|
||||
Date: July 2015
|
||||
KernelVersion: 4.7
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Writing '1' will perform a FOC (Fast Online Calibration). The
|
||||
corresponding calibration offsets can be read from *_calibbias
|
||||
entries.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/location
|
||||
Date: July 2015
|
||||
KernelVersion: 4.7
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute returns a string with the physical location where
|
||||
the motion sensor is placed. For example, in a laptop a motion
|
||||
sensor can be located on the base or on the lid. Current valid
|
||||
values are 'base' and 'lid'.
|
|
@ -152,6 +152,7 @@ ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
|||
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
|
||||
sgx,vz89x SGX Sensortech VZ89X Sensors
|
||||
sii,s35390a 2-wire CMOS real-time clock
|
||||
silabs,si7020 Relative Humidity and Temperature Sensors
|
||||
skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
|
||||
st,24c256 i2c serial eeprom (24cxx)
|
||||
st,m41t00 Serial real-time clock (RTC)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
Invensense MPU-3050 Gyroscope device tree bindings
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "invensense,mpu3050"
|
||||
- reg : the I2C address of the sensor
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent : should be the phandle for the interrupt controller
|
||||
- interrupts : interrupt mapping for the trigger interrupt from the
|
||||
internal oscillator. The following IRQ modes are supported:
|
||||
IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
|
||||
IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
|
||||
for the desired interrupt type.
|
||||
- vdd-supply : supply regulator for the main power voltage.
|
||||
- vlogic-supply : supply regulator for the signal voltage.
|
||||
- mount-matrix : see iio/mount-matrix.txt
|
||||
|
||||
Optional subnodes:
|
||||
- The MPU-3050 will pass through and forward the I2C signals from the
|
||||
incoming I2C bus, alternatively drive traffic to a slave device (usually
|
||||
an accelerometer) on its own initiative. Therefore is supports a subnode
|
||||
i2c gate node. For details see: i2c/i2c-gate.txt
|
||||
|
||||
Example:
|
||||
|
||||
mpu3050@68 {
|
||||
compatible = "invensense,mpu3050";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&foo>;
|
||||
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&bar>;
|
||||
vlogic-supply = <&baz>;
|
||||
|
||||
/* External I2C interface */
|
||||
i2c-gate {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
fnord@18 {
|
||||
compatible = "fnord";
|
||||
reg = <0x18>;
|
||||
interrupt-parent = <&foo>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
};
|
26
Documentation/devicetree/bindings/iio/light/tsl2583.txt
Normal file
26
Documentation/devicetree/bindings/iio/light/tsl2583.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
* TAOS TSL 2580/2581/2583 ALS sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of
|
||||
"amstaos,tsl2580"
|
||||
"amstaos,tsl2581"
|
||||
"amstaos,tsl2583"
|
||||
- reg: the I2C address of the device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor.
|
||||
|
||||
Example:
|
||||
|
||||
tsl2581@29 {
|
||||
compatible = "amstaos,tsl2581";
|
||||
reg = <0x29>;
|
||||
};
|
|
@ -42,6 +42,7 @@ Accelerometers:
|
|||
- st,lsm303agr-accel
|
||||
- st,lis2dh12-accel
|
||||
- st,h3lis331dl-accel
|
||||
- st,lng2dm-accel
|
||||
|
||||
Gyroscopes:
|
||||
- st,l3g4200d-gyro
|
||||
|
|
|
@ -6498,6 +6498,13 @@ S: Maintained
|
|||
F: arch/x86/include/asm/pmc_core.h
|
||||
F: drivers/platform/x86/intel_pmc_core*
|
||||
|
||||
INVENSENSE MPU-3050 GYROSCOPE DRIVER
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/gyro/mpu3050*
|
||||
F: Documentation/devicetree/bindings/iio/gyroscope/inv,mpu3050.txt
|
||||
|
||||
IOC3 ETHERNET DRIVER
|
||||
M: Ralf Baechle <ralf@linux-mips.org>
|
||||
L: linux-mips@linux-mips.org
|
||||
|
|
|
@ -127,7 +127,8 @@ config IIO_ST_ACCEL_3AXIS
|
|||
help
|
||||
Say yes here to build support for STMicroelectronics accelerometers:
|
||||
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL.
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
|
||||
LNG2DM
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
|
||||
#define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel"
|
||||
#define LIS3L02DQ_ACCEL_DEV_NAME "lis3l02dq"
|
||||
#define LNG2DM_ACCEL_DEV_NAME "lng2dm"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - default accel platform data
|
||||
|
|
|
@ -231,6 +231,12 @@
|
|||
#define ST_ACCEL_7_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_ACCEL_7_MULTIREAD_BIT false
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 8 */
|
||||
#define ST_ACCEL_8_FS_AVL_2_GAIN IIO_G_TO_M_S_2(15600)
|
||||
#define ST_ACCEL_8_FS_AVL_4_GAIN IIO_G_TO_M_S_2(31200)
|
||||
#define ST_ACCEL_8_FS_AVL_8_GAIN IIO_G_TO_M_S_2(62500)
|
||||
#define ST_ACCEL_8_FS_AVL_16_GAIN IIO_G_TO_M_S_2(187500)
|
||||
|
||||
static const struct iio_chan_spec st_accel_8bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
@ -726,6 +732,73 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.multi_read_bit = ST_ACCEL_7_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_1_WAI_EXP,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = LNG2DM_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_1_ODR_ADDR,
|
||||
.mask = ST_ACCEL_1_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
|
||||
{ 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
|
||||
{ 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
|
||||
{ 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
|
||||
{ 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
|
||||
{ 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
|
||||
{ 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
|
||||
{ 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_1_ODR_ADDR,
|
||||
.mask = ST_ACCEL_1_ODR_MASK,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_1_FS_ADDR,
|
||||
.mask = ST_ACCEL_1_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_1_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_8_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = ST_ACCEL_1_FS_AVL_4_VAL,
|
||||
.gain = ST_ACCEL_8_FS_AVL_4_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_1_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_8_FS_AVL_8_GAIN,
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_ACCEL_FS_AVL_16G,
|
||||
.value = ST_ACCEL_1_FS_AVL_16_VAL,
|
||||
.gain = ST_ACCEL_8_FS_AVL_16_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
@ -84,6 +84,10 @@ static const struct of_device_id st_accel_of_match[] = {
|
|||
.compatible = "st,lis3l02dq",
|
||||
.data = LIS3L02DQ_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lng2dm-accel",
|
||||
.data = LNG2DM_ACCEL_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
|
@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
|
|||
{ LSM303AGR_ACCEL_DEV_NAME },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME },
|
||||
{ LNG2DM_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
|
|
@ -60,6 +60,7 @@ static const struct spi_device_id st_accel_id_table[] = {
|
|||
{ LSM303AGR_ACCEL_DEV_NAME },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME },
|
||||
{ LNG2DM_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/* Registers */
|
||||
#define AT91_ADC_CR 0x00 /* Control Register */
|
||||
|
@ -1347,6 +1348,32 @@ static int at91_adc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int at91_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
clk_disable_unprepare(st->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
|
||||
clk_prepare_enable(st->clk);
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
|
||||
|
||||
static struct at91_adc_caps at91sam9260_caps = {
|
||||
.calc_startup_ticks = calc_startup_ticks_9260,
|
||||
.num_channels = 4,
|
||||
|
@ -1441,6 +1468,7 @@ static struct platform_driver at91_adc_driver = {
|
|||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(at91_adc_dt_ids),
|
||||
.pm = &at91_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -30,10 +30,28 @@
|
|||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#define DMA_BUFFER_SIZE SZ_2K
|
||||
|
||||
struct tiadc_dma {
|
||||
struct dma_slave_config conf;
|
||||
struct dma_chan *chan;
|
||||
dma_addr_t addr;
|
||||
dma_cookie_t cookie;
|
||||
u8 *buf;
|
||||
int current_period;
|
||||
int period_size;
|
||||
u8 fifo_thresh;
|
||||
};
|
||||
|
||||
struct tiadc_device {
|
||||
struct ti_tscadc_dev *mfd_tscadc;
|
||||
struct tiadc_dma dma;
|
||||
struct mutex fifo1_lock; /* to protect fifo access */
|
||||
int channels;
|
||||
int total_ch_enabled;
|
||||
u8 channel_line[8];
|
||||
u8 channel_step[8];
|
||||
int buffer_en_ch_steps;
|
||||
|
@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void tiadc_dma_rx_complete(void *param)
|
||||
{
|
||||
struct iio_dev *indio_dev = param;
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct tiadc_dma *dma = &adc_dev->dma;
|
||||
u8 *data;
|
||||
int i;
|
||||
|
||||
data = dma->buf + dma->current_period * dma->period_size;
|
||||
dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
|
||||
|
||||
for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
data += indio_dev->scan_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static int tiadc_start_dma(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct tiadc_dma *dma = &adc_dev->dma;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
||||
dma->current_period = 0; /* We start to fill period 0 */
|
||||
/*
|
||||
* Make the fifo thresh as the multiple of total number of
|
||||
* channels enabled, so make sure that cyclic DMA period
|
||||
* length is also a multiple of total number of channels
|
||||
* enabled. This ensures that no invalid data is reported
|
||||
* to the stack via iio_push_to_buffers().
|
||||
*/
|
||||
dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
|
||||
adc_dev->total_ch_enabled) - 1;
|
||||
/* Make sure that period length is multiple of fifo thresh level */
|
||||
dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
|
||||
(dma->fifo_thresh + 1) * sizeof(u16));
|
||||
|
||||
dma->conf.src_maxburst = dma->fifo_thresh + 1;
|
||||
dmaengine_slave_config(dma->chan, &dma->conf);
|
||||
|
||||
desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
|
||||
dma->period_size * 2,
|
||||
dma->period_size, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!desc)
|
||||
return -EBUSY;
|
||||
|
||||
desc->callback = tiadc_dma_rx_complete;
|
||||
desc->callback_param = indio_dev;
|
||||
|
||||
dma->cookie = dmaengine_submit(desc);
|
||||
|
||||
dma_async_issue_pending(dma->chan);
|
||||
|
||||
tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
|
||||
tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
|
||||
tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
|
|||
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct tiadc_dma *dma = &adc_dev->dma;
|
||||
unsigned int irq_enable;
|
||||
unsigned int enb = 0;
|
||||
u8 bit;
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
|
||||
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
|
||||
adc_dev->total_ch_enabled++;
|
||||
}
|
||||
adc_dev->buffer_en_ch_steps = enb;
|
||||
|
||||
if (dma->chan)
|
||||
tiadc_start_dma(indio_dev);
|
||||
|
||||
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN);
|
||||
|
||||
irq_enable = IRQENB_FIFO1OVRRUN;
|
||||
if (!dma->chan)
|
||||
irq_enable |= IRQENB_FIFO1THRES;
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
|
|||
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct tiadc_dma *dma = &adc_dev->dma;
|
||||
int fifo1count, i, read;
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
|
||||
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
|
||||
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
|
||||
adc_dev->buffer_en_ch_steps = 0;
|
||||
adc_dev->total_ch_enabled = 0;
|
||||
if (dma->chan) {
|
||||
tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
|
||||
dmaengine_terminate_async(dma->chan);
|
||||
}
|
||||
|
||||
/* Flush FIFO of leftover data in the time it takes to disable adc */
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
|
@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tiadc_request_dma(struct platform_device *pdev,
|
||||
struct tiadc_device *adc_dev)
|
||||
{
|
||||
struct tiadc_dma *dma = &adc_dev->dma;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
/* Default slave configuration parameters */
|
||||
dma->conf.direction = DMA_DEV_TO_MEM;
|
||||
dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_CYCLIC, mask);
|
||||
|
||||
/* Get a channel for RX */
|
||||
dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
|
||||
if (IS_ERR(dma->chan)) {
|
||||
int ret = PTR_ERR(dma->chan);
|
||||
|
||||
dma->chan = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* RX buffer */
|
||||
dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
|
||||
&dma->addr, GFP_KERNEL);
|
||||
if (!dma->buf)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dma_release_channel(dma->chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int tiadc_parse_dt(struct platform_device *pdev,
|
||||
struct tiadc_device *adc_dev)
|
||||
{
|
||||
|
@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
err = tiadc_request_dma(pdev, adc_dev);
|
||||
if (err && err == -EPROBE_DEFER)
|
||||
goto err_dma;
|
||||
|
||||
return 0;
|
||||
|
||||
err_dma:
|
||||
iio_device_unregister(indio_dev);
|
||||
err_buffer_unregister:
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
err_free_channels:
|
||||
|
@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct tiadc_dma *dma = &adc_dev->dma;
|
||||
u32 step_en;
|
||||
|
||||
if (dma->chan) {
|
||||
dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
|
||||
dma->buf, dma->addr);
|
||||
dma_release_channel(dma->chan);
|
||||
}
|
||||
iio_device_unregister(indio_dev);
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
tiadc_channels_remove(indio_dev);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# IIO common modules
|
||||
#
|
||||
|
||||
source "drivers/iio/common/cros_ec_sensors/Kconfig"
|
||||
source "drivers/iio/common/hid-sensors/Kconfig"
|
||||
source "drivers/iio/common/ms_sensors/Kconfig"
|
||||
source "drivers/iio/common/ssp_sensors/Kconfig"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-y += cros_ec_sensors/
|
||||
obj-y += hid-sensors/
|
||||
obj-y += ms_sensors/
|
||||
obj-y += ssp_sensors/
|
||||
|
|
22
drivers/iio/common/cros_ec_sensors/Kconfig
Normal file
22
drivers/iio/common/cros_ec_sensors/Kconfig
Normal file
|
@ -0,0 +1,22 @@
|
|||
#
|
||||
# Chrome OS Embedded Controller managed sensors library
|
||||
#
|
||||
config IIO_CROS_EC_SENSORS_CORE
|
||||
tristate "ChromeOS EC Sensors Core"
|
||||
depends on SYSFS && MFD_CROS_EC
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Base module for the ChromeOS EC Sensors module.
|
||||
Contains core functions used by other IIO CrosEC sensor
|
||||
drivers.
|
||||
Define common attributes and sysfs interrupt handler.
|
||||
|
||||
config IIO_CROS_EC_SENSORS
|
||||
tristate "ChromeOS EC Contiguous Sensors"
|
||||
depends on IIO_CROS_EC_SENSORS_CORE
|
||||
help
|
||||
Module to handle 3d contiguous sensors like
|
||||
Accelerometers, Gyroscope and Magnetometer that are
|
||||
presented by the ChromeOS EC Sensor hub.
|
||||
Creates an IIO device for each functions.
|
6
drivers/iio/common/cros_ec_sensors/Makefile
Normal file
6
drivers/iio/common/cros_ec_sensors/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for sensors seen through the ChromeOS EC sensor hub.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
|
||||
obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
|
322
drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
Normal file
322
drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
|
||||
*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This driver uses the cros-ec interface to communicate with the Chrome OS
|
||||
* EC about sensors data. Data access is presented through iio sysfs.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "cros_ec_sensors_core.h"
|
||||
|
||||
#define CROS_EC_SENSORS_MAX_CHANNELS 4
|
||||
|
||||
/* State data for ec_sensors iio driver. */
|
||||
struct cros_ec_sensors_state {
|
||||
/* Shared by all sensors */
|
||||
struct cros_ec_sensors_core_state core;
|
||||
|
||||
struct iio_chan_spec channels[CROS_EC_SENSORS_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
static int cros_ec_sensors_read(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
|
||||
s16 data = 0;
|
||||
s64 val64;
|
||||
int i;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = st->core.read_ec_sensors_data(indio_dev, 1 << idx, &data);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
*val = data;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
st->core.param.sensor_offset.flags = 0;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.calib[i] =
|
||||
st->core.resp->sensor_offset.offset[i];
|
||||
|
||||
*val = st->core.calib[idx];
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
val64 = st->core.resp->sensor_range.ret;
|
||||
switch (st->core.type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
/*
|
||||
* EC returns data in g, iio exepects m/s^2.
|
||||
* Do not use IIO_G_TO_M_S_2 to avoid precision loss.
|
||||
*/
|
||||
*val = div_s64(val64 * 980665, 10);
|
||||
*val2 = 10000 << (CROS_EC_SENSOR_BITS - 1);
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
/*
|
||||
* EC returns data in dps, iio expects rad/s.
|
||||
* Do not use IIO_DEGREE_TO_RAD to avoid precision
|
||||
* loss. Round to the nearest integer.
|
||||
*/
|
||||
*val = div_s64(val64 * 314159 + 9000000ULL, 1000);
|
||||
*val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
/*
|
||||
* EC returns data in 16LSB / uT,
|
||||
* iio expects Gauss
|
||||
*/
|
||||
*val = val64;
|
||||
*val2 = 100 << (CROS_EC_SENSOR_BITS - 1);
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
|
||||
mask);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->core.cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
|
||||
int i;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
st->core.calib[idx] = val;
|
||||
|
||||
/* Send to EC for each axis, even if not complete */
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
st->core.param.sensor_offset.flags =
|
||||
MOTION_SENSE_SET_OFFSET;
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.param.sensor_offset.offset[i] =
|
||||
st->core.calib[i];
|
||||
st->core.param.sensor_offset.temp =
|
||||
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (st->core.type == MOTIONSENSE_TYPE_MAG) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = val;
|
||||
|
||||
/* Always roundup, so caller gets at least what it asks for. */
|
||||
st->core.param.sensor_range.roundup = 1;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
default:
|
||||
ret = cros_ec_sensors_core_write(
|
||||
&st->core, chan, val, val2, mask);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&st->core.cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ec_sensors_info = {
|
||||
.read_raw = &cros_ec_sensors_read,
|
||||
.write_raw = &cros_ec_sensors_write,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_device *ec_device;
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_sensors_state *state;
|
||||
struct iio_chan_spec *channel;
|
||||
int ret, i;
|
||||
|
||||
if (!ec_dev || !ec_dev->ec_dev) {
|
||||
dev_warn(&pdev->dev, "No CROS EC device found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ec_device = ec_dev->ec_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->info = &ec_sensors_info;
|
||||
state = iio_priv(indio_dev);
|
||||
for (channel = state->channels, i = CROS_EC_SENSOR_X;
|
||||
i < CROS_EC_SENSOR_MAX_AXIS; i++, channel++) {
|
||||
/* Common part */
|
||||
channel->info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS);
|
||||
channel->info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_index = i;
|
||||
channel->ext_info = cros_ec_sensors_ext_info;
|
||||
channel->modified = 1;
|
||||
channel->channel2 = IIO_MOD_X + i;
|
||||
channel->scan_type.sign = 's';
|
||||
|
||||
/* Sensor specific */
|
||||
switch (state->core.type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
channel->type = IIO_ACCEL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
channel->type = IIO_ANGL_VEL;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
channel->type = IIO_MAGN;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unknown motion sensor\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Timestamp */
|
||||
channel->type = IIO_TIMESTAMP;
|
||||
channel->channel = -1;
|
||||
channel->scan_index = CROS_EC_SENSOR_MAX_AXIS;
|
||||
channel->scan_type.sign = 's';
|
||||
channel->scan_type.realbits = 64;
|
||||
channel->scan_type.storagebits = 64;
|
||||
|
||||
indio_dev->channels = state->channels;
|
||||
indio_dev->num_channels = CROS_EC_SENSORS_MAX_CHANNELS;
|
||||
|
||||
/* There is only enough room for accel and gyro in the io space */
|
||||
if ((state->core.ec->cmd_readmem != NULL) &&
|
||||
(state->core.type != MOTIONSENSE_TYPE_MAG))
|
||||
state->core.read_ec_sensors_data = cros_ec_sensors_read_lpc;
|
||||
else
|
||||
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
cros_ec_sensors_capture, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninit_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_uninit_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_sensors_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_sensors_ids[] = {
|
||||
{
|
||||
.name = "cros-ec-accel",
|
||||
},
|
||||
{
|
||||
.name = "cros-ec-gyro",
|
||||
},
|
||||
{
|
||||
.name = "cros-ec-mag",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
|
||||
|
||||
static struct platform_driver cros_ec_sensors_platform_driver = {
|
||||
.driver = {
|
||||
.name = "cros-ec-sensors",
|
||||
},
|
||||
.probe = cros_ec_sensors_probe,
|
||||
.remove = cros_ec_sensors_remove,
|
||||
.id_table = cros_ec_sensors_ids,
|
||||
};
|
||||
module_platform_driver(cros_ec_sensors_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ChromeOS EC 3-axis sensors driver");
|
||||
MODULE_LICENSE("GPL v2");
|
450
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
Normal file
450
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
Normal file
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
|
||||
*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "cros_ec_sensors_core.h"
|
||||
|
||||
static char *cros_ec_loc[] = {
|
||||
[MOTIONSENSE_LOC_BASE] = "base",
|
||||
[MOTIONSENSE_LOC_LID] = "lid",
|
||||
[MOTIONSENSE_LOC_MAX] = "unknown",
|
||||
};
|
||||
|
||||
int cros_ec_sensors_core_init(struct platform_device *pdev,
|
||||
struct iio_dev *indio_dev,
|
||||
bool physical_device)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
|
||||
struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
state->ec = ec->ec_dev;
|
||||
state->msg = devm_kzalloc(&pdev->dev,
|
||||
max((u16)sizeof(struct ec_params_motion_sense),
|
||||
state->ec->max_response), GFP_KERNEL);
|
||||
if (!state->msg)
|
||||
return -ENOMEM;
|
||||
|
||||
state->resp = (struct ec_response_motion_sense *)state->msg->data;
|
||||
|
||||
mutex_init(&state->cmd_lock);
|
||||
|
||||
/* Set up the host command structure. */
|
||||
state->msg->version = 2;
|
||||
state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
|
||||
state->msg->outsize = sizeof(struct ec_params_motion_sense);
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = pdev->name;
|
||||
|
||||
if (physical_device) {
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
state->param.cmd = MOTIONSENSE_CMD_INFO;
|
||||
state->param.info.sensor_num = sensor_platform->sensor_num;
|
||||
if (cros_ec_motion_send_host_cmd(state, 0)) {
|
||||
dev_warn(dev, "Can not access sensor info\n");
|
||||
return -EIO;
|
||||
}
|
||||
state->type = state->resp->info.type;
|
||||
state->loc = state->resp->info.location;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
|
||||
|
||||
int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
|
||||
u16 opt_length)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (opt_length)
|
||||
state->msg->insize = min(opt_length, state->ec->max_response);
|
||||
else
|
||||
state->msg->insize = state->ec->max_response;
|
||||
|
||||
memcpy(state->msg->data, &state->param, sizeof(state->param));
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
|
||||
if (ret &&
|
||||
state->resp != (struct ec_response_motion_sense *)state->msg->data)
|
||||
memcpy(state->resp, state->msg->data, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
|
||||
|
||||
static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
bool calibrate;
|
||||
|
||||
ret = strtobool(buf, &calibrate);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!calibrate)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&st->cmd_lock);
|
||||
st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
if (ret != 0) {
|
||||
dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
|
||||
} else {
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->calib[i] = st->resp->perform_calib.offset[i];
|
||||
}
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", cros_ec_loc[st->loc]);
|
||||
}
|
||||
|
||||
const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
|
||||
{
|
||||
.name = "calibrate",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.write = cros_ec_sensors_calibrate
|
||||
},
|
||||
{
|
||||
.name = "location",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = cros_ec_sensors_loc
|
||||
},
|
||||
{ },
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_idx_to_reg - convert index into offset in shared memory
|
||||
* @st: pointer to state information for device
|
||||
* @idx: sensor index (should be element of enum sensor_index)
|
||||
*
|
||||
* Return: address to read at
|
||||
*/
|
||||
static unsigned int cros_ec_sensors_idx_to_reg(
|
||||
struct cros_ec_sensors_core_state *st,
|
||||
unsigned int idx)
|
||||
{
|
||||
/*
|
||||
* When using LPC interface, only space for 2 Accel and one Gyro.
|
||||
* First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
|
||||
*/
|
||||
if (st->type == MOTIONSENSE_TYPE_ACCEL)
|
||||
return EC_MEMMAP_ACC_DATA + sizeof(u16) *
|
||||
(1 + idx + st->param.info.sensor_num *
|
||||
CROS_EC_SENSOR_MAX_AXIS);
|
||||
|
||||
return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
|
||||
}
|
||||
|
||||
static int cros_ec_sensors_cmd_read_u8(struct cros_ec_device *ec,
|
||||
unsigned int offset, u8 *dest)
|
||||
{
|
||||
return ec->cmd_readmem(ec, offset, 1, dest);
|
||||
}
|
||||
|
||||
static int cros_ec_sensors_cmd_read_u16(struct cros_ec_device *ec,
|
||||
unsigned int offset, u16 *dest)
|
||||
{
|
||||
__le16 tmp;
|
||||
int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
|
||||
|
||||
if (ret >= 0)
|
||||
*dest = le16_to_cpu(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_read_until_not_busy() - read until is not busy
|
||||
*
|
||||
* @st: pointer to state information for device
|
||||
*
|
||||
* Read from EC status byte until it reads not busy.
|
||||
* Return: 8-bit status if ok, -errno on failure.
|
||||
*/
|
||||
static int cros_ec_sensors_read_until_not_busy(
|
||||
struct cros_ec_sensors_core_state *st)
|
||||
{
|
||||
struct cros_ec_device *ec = st->ec;
|
||||
u8 status;
|
||||
int ret, attempts = 0;
|
||||
|
||||
ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
|
||||
/* Give up after enough attempts, return error. */
|
||||
if (attempts++ >= 50)
|
||||
return -EIO;
|
||||
|
||||
/* Small delay every so often. */
|
||||
if (attempts % 5 == 0)
|
||||
msleep(25);
|
||||
|
||||
ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
|
||||
&status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory
|
||||
* @indio_dev: pointer to IIO device
|
||||
* @scan_mask: bitmap of the sensor indices to scan
|
||||
* @data: location to store data
|
||||
*
|
||||
* This is the unsafe function for reading the EC data. It does not guarantee
|
||||
* that the EC will not modify the data as it is being read in.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure.
|
||||
*/
|
||||
static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
struct cros_ec_device *ec = st->ec;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
|
||||
for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
|
||||
ret = cros_ec_sensors_cmd_read_u16(ec,
|
||||
cros_ec_sensors_idx_to_reg(st, i),
|
||||
data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
struct cros_ec_device *ec = st->ec;
|
||||
u8 samp_id = 0xff, status = 0;
|
||||
int ret, attempts = 0;
|
||||
|
||||
/*
|
||||
* Continually read all data from EC until the status byte after
|
||||
* all reads reflects that the EC is not busy and the sample id
|
||||
* matches the sample id from before all reads. This guarantees
|
||||
* that data read in was not modified by the EC while reading.
|
||||
*/
|
||||
while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
|
||||
EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
|
||||
/* If we have tried to read too many times, return error. */
|
||||
if (attempts++ >= 5)
|
||||
return -EIO;
|
||||
|
||||
/* Read status byte until EC is not busy. */
|
||||
status = cros_ec_sensors_read_until_not_busy(st);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* Store the current sample id so that we can compare to the
|
||||
* sample id after reading the data.
|
||||
*/
|
||||
samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
|
||||
|
||||
/* Read all EC data, format it, and store it into data. */
|
||||
ret = cros_ec_sensors_read_data_unsafe(indio_dev, scan_mask,
|
||||
data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Read status byte. */
|
||||
ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
|
||||
&status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
|
||||
|
||||
int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned int i;
|
||||
|
||||
/* Read all sensor data through a command. */
|
||||
st->param.cmd = MOTIONSENSE_CMD_DATA;
|
||||
ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
|
||||
if (ret != 0) {
|
||||
dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
|
||||
*data = st->resp->data.data[i];
|
||||
data++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
|
||||
|
||||
irqreturn_t cros_ec_sensors_capture(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->cmd_lock);
|
||||
|
||||
/* Clear capture data. */
|
||||
memset(st->samples, 0, indio_dev->scan_bytes);
|
||||
|
||||
/* Read data based on which channels are enabled in scan mask. */
|
||||
ret = st->read_ec_sensors_data(indio_dev,
|
||||
*(indio_dev->active_scan_mask),
|
||||
(s16 *)st->samples);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
done:
|
||||
/*
|
||||
* Tell the core we are done with this trigger and ready for the
|
||||
* next one.
|
||||
*/
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
|
||||
|
||||
int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = IIO_VAL_INT;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
|
||||
st->param.ec_rate.data =
|
||||
EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
*val = st->resp->ec_rate.ret;
|
||||
break;
|
||||
case IIO_CHAN_INFO_FREQUENCY:
|
||||
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
|
||||
st->param.sensor_odr.data =
|
||||
EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
*val = st->resp->sensor_odr.ret;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
|
||||
|
||||
int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_FREQUENCY:
|
||||
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
|
||||
st->param.sensor_odr.data = val;
|
||||
|
||||
/* Always roundup, so caller gets at least what it asks for. */
|
||||
st->param.sensor_odr.roundup = 1;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
|
||||
st->param.ec_rate.data = val;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
st->curr_sampl_freq = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
|
||||
|
||||
MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
|
||||
MODULE_LICENSE("GPL v2");
|
175
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
Normal file
175
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* ChromeOS EC sensor hub
|
||||
*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __CROS_EC_SENSORS_CORE_H
|
||||
#define __CROS_EC_SENSORS_CORE_H
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
enum {
|
||||
CROS_EC_SENSOR_X,
|
||||
CROS_EC_SENSOR_Y,
|
||||
CROS_EC_SENSOR_Z,
|
||||
CROS_EC_SENSOR_MAX_AXIS,
|
||||
};
|
||||
|
||||
/* EC returns sensor values using signed 16 bit registers */
|
||||
#define CROS_EC_SENSOR_BITS 16
|
||||
|
||||
/*
|
||||
* 4 16 bit channels are allowed.
|
||||
* Good enough for current sensors, they use up to 3 16 bit vectors.
|
||||
*/
|
||||
#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2)
|
||||
|
||||
/* Minimum sampling period to use when device is suspending */
|
||||
#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */
|
||||
|
||||
/**
|
||||
* struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
|
||||
* @ec: cros EC device structure
|
||||
* @cmd_lock: lock used to prevent simultaneous access to the
|
||||
* commands.
|
||||
* @msg: cros EC command structure
|
||||
* @param: motion sensor parameters structure
|
||||
* @resp: motion sensor response structure
|
||||
* @type: type of motion sensor
|
||||
* @loc: location where the motion sensor is placed
|
||||
* @calib: calibration parameters. Note that trigger
|
||||
* captured data will always provide the calibrated
|
||||
* data
|
||||
* @samples: static array to hold data from a single capture.
|
||||
* For each channel we need 2 bytes, except for
|
||||
* the timestamp. The timestamp is always last and
|
||||
* is always 8-byte aligned.
|
||||
* @read_ec_sensors_data: function used for accessing sensors values
|
||||
* @cuur_sampl_freq: current sampling period
|
||||
*/
|
||||
struct cros_ec_sensors_core_state {
|
||||
struct cros_ec_device *ec;
|
||||
struct mutex cmd_lock;
|
||||
|
||||
struct cros_ec_command *msg;
|
||||
struct ec_params_motion_sense param;
|
||||
struct ec_response_motion_sense *resp;
|
||||
|
||||
enum motionsensor_type type;
|
||||
enum motionsensor_location loc;
|
||||
|
||||
s16 calib[CROS_EC_SENSOR_MAX_AXIS];
|
||||
|
||||
u8 samples[CROS_EC_SAMPLE_SIZE];
|
||||
|
||||
int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
|
||||
unsigned long scan_mask, s16 *data);
|
||||
|
||||
int curr_sampl_freq;
|
||||
};
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_read_lpc() - retrieve data from EC shared memory
|
||||
* @indio_dev: pointer to IIO device
|
||||
* @scan_mask: bitmap of the sensor indices to scan
|
||||
* @data: location to store data
|
||||
*
|
||||
* This is the safe function for reading the EC data. It guarantees that the
|
||||
* data sampled was not modified by the EC while being read.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure.
|
||||
*/
|
||||
int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
|
||||
s16 *data);
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
|
||||
* @indio_dev: pointer to IIO device
|
||||
* @scan_mask: bitmap of the sensor indices to scan
|
||||
* @data: location to store data
|
||||
*
|
||||
* Return: 0 on success, -errno on failure.
|
||||
*/
|
||||
int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
|
||||
s16 *data);
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_core_init() - basic initialization of the core structure
|
||||
* @pdev: platform device created for the sensors
|
||||
* @indio_dev: iio device structure of the device
|
||||
* @physical_device: true if the device refers to a physical device
|
||||
*
|
||||
* Return: 0 on success, -errno on failure.
|
||||
*/
|
||||
int cros_ec_sensors_core_init(struct platform_device *pdev,
|
||||
struct iio_dev *indio_dev, bool physical_device);
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_capture() - the trigger handler function
|
||||
* @irq: the interrupt number.
|
||||
* @p: a pointer to the poll function.
|
||||
*
|
||||
* On a trigger event occurring, if the pollfunc is attached then this
|
||||
* handler is called as a threaded interrupt (and hence may sleep). It
|
||||
* is responsible for grabbing data from the device and pushing it into
|
||||
* the associated buffer.
|
||||
*
|
||||
* Return: IRQ_HANDLED
|
||||
*/
|
||||
irqreturn_t cros_ec_sensors_capture(int irq, void *p);
|
||||
|
||||
/**
|
||||
* cros_ec_motion_send_host_cmd() - send motion sense host command
|
||||
* @st: pointer to state information for device
|
||||
* @opt_length: optional length to reduce the response size, useful on the data
|
||||
* path. Otherwise, the maximal allowed response size is used
|
||||
*
|
||||
* When called, the sub-command is assumed to be set in param->cmd.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure.
|
||||
*/
|
||||
int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
|
||||
u16 opt_length);
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_core_read() - function to request a value from the sensor
|
||||
* @st: pointer to state information for device
|
||||
* @chan: channel specification structure table
|
||||
* @val: will contain one element making up the returned value
|
||||
* @val2: will contain another element making up the returned value
|
||||
* @mask: specifies which values to be requested
|
||||
*
|
||||
* Return: the type of value returned by the device
|
||||
*/
|
||||
int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask);
|
||||
|
||||
/**
|
||||
* cros_ec_sensors_core_write() - function to write a value to the sensor
|
||||
* @st: pointer to state information for device
|
||||
* @chan: channel specification structure table
|
||||
* @val: first part of value to write
|
||||
* @val2: second part of value to write
|
||||
* @mask: specifies which values to write
|
||||
*
|
||||
* Return: the type of value returned by the device
|
||||
*/
|
||||
int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask);
|
||||
|
||||
/* List of extended channel specification for all sensors */
|
||||
extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
|
||||
|
||||
#endif /* __CROS_EC_SENSORS_CORE_H */
|
|
@ -201,7 +201,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
|
|||
int ret;
|
||||
|
||||
if (val1 < 0 || val2 < 0)
|
||||
ret = -EINVAL;
|
||||
return -EINVAL;
|
||||
|
||||
value = val1 * pow_10(6) + val2;
|
||||
if (value) {
|
||||
|
@ -250,6 +250,9 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
|
|||
s32 value;
|
||||
int ret;
|
||||
|
||||
if (val1 < 0 || val2 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
value = convert_to_vtf_format(st->sensitivity.size,
|
||||
st->sensitivity.unit_expo,
|
||||
val1, val2);
|
||||
|
|
|
@ -84,6 +84,23 @@ config HID_SENSOR_GYRO_3D
|
|||
Say yes here to build support for the HID SENSOR
|
||||
Gyroscope 3D.
|
||||
|
||||
config MPU3050
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select REGMAP
|
||||
|
||||
config MPU3050_I2C
|
||||
tristate "Invensense MPU3050 devices on I2C"
|
||||
depends on !(INPUT_MPU3050=y || INPUT_MPU3050=m)
|
||||
select MPU3050
|
||||
select REGMAP_I2C
|
||||
select I2C_MUX
|
||||
help
|
||||
This driver supports the Invensense MPU3050 gyroscope over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu3050-i2c.
|
||||
|
||||
config IIO_ST_GYRO_3AXIS
|
||||
tristate "STMicroelectronics gyroscopes 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
|
|
|
@ -14,6 +14,11 @@ obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
|
|||
|
||||
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
|
||||
|
||||
# Currently this is rolled into one module, split it if
|
||||
# we ever create a separate SPI interface for MPU-3050
|
||||
obj-$(CONFIG_MPU3050) += mpu3050.o
|
||||
mpu3050-objs := mpu3050-core.o mpu3050-i2c.o
|
||||
|
||||
itg3200-y := itg3200_core.o
|
||||
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
|
||||
obj-$(CONFIG_ITG3200) += itg3200.o
|
||||
|
|
1307
drivers/iio/gyro/mpu3050-core.c
Normal file
1307
drivers/iio/gyro/mpu3050-core.c
Normal file
File diff suppressed because it is too large
Load diff
124
drivers/iio/gyro/mpu3050-i2c.c
Normal file
124
drivers/iio/gyro/mpu3050-i2c.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "mpu3050.h"
|
||||
|
||||
static const struct regmap_config mpu3050_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int mpu3050_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
|
||||
{
|
||||
struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
|
||||
|
||||
/* Just power up the device, that is all that is needed */
|
||||
pm_runtime_get_sync(mpu3050->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpu3050_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
|
||||
{
|
||||
struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
|
||||
|
||||
pm_runtime_mark_last_busy(mpu3050->dev);
|
||||
pm_runtime_put_autosuspend(mpu3050->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpu3050_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const char *name;
|
||||
struct mpu3050 *mpu3050;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &mpu3050_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);
|
||||
}
|
||||
|
||||
ret = mpu3050_common_probe(&client->dev, regmap, client->irq, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* The main driver is up, now register the I2C mux */
|
||||
mpu3050 = iio_priv(dev_get_drvdata(&client->dev));
|
||||
mpu3050->i2cmux = i2c_mux_alloc(client->adapter, &client->dev,
|
||||
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
|
||||
mpu3050_i2c_bypass_select,
|
||||
mpu3050_i2c_bypass_deselect);
|
||||
/* Just fail the mux, there is no point in killing the driver */
|
||||
if (!mpu3050->i2cmux)
|
||||
dev_err(&client->dev, "failed to allocate I2C mux\n");
|
||||
else {
|
||||
mpu3050->i2cmux->priv = mpu3050;
|
||||
ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "failed to add I2C mux\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpu3050_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
|
||||
struct mpu3050 *mpu3050 = iio_priv(indio_dev);
|
||||
|
||||
if (mpu3050->i2cmux)
|
||||
i2c_mux_del_adapters(mpu3050->i2cmux);
|
||||
|
||||
return mpu3050_common_remove(&client->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* device id table is used to identify what device can be
|
||||
* supported by this driver
|
||||
*/
|
||||
static const struct i2c_device_id mpu3050_i2c_id[] = {
|
||||
{ "mpu3050" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mpu3050_i2c_id);
|
||||
|
||||
static const struct of_device_id mpu3050_i2c_of_match[] = {
|
||||
{ .compatible = "invensense,mpu3050", .data = "mpu3050" },
|
||||
/* Deprecated vendor ID from the Input driver */
|
||||
{ .compatible = "invn,mpu3050", .data = "mpu3050" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mpu3050_i2c_of_match);
|
||||
|
||||
static struct i2c_driver mpu3050_i2c_driver = {
|
||||
.probe = mpu3050_i2c_probe,
|
||||
.remove = mpu3050_i2c_remove,
|
||||
.id_table = mpu3050_i2c_id,
|
||||
.driver = {
|
||||
.of_match_table = mpu3050_i2c_of_match,
|
||||
.name = "mpu3050-i2c",
|
||||
.pm = &mpu3050_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(mpu3050_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij");
|
||||
MODULE_DESCRIPTION("Invensense MPU3050 gyroscope driver");
|
||||
MODULE_LICENSE("GPL");
|
96
drivers/iio/gyro/mpu3050.h
Normal file
96
drivers/iio/gyro/mpu3050.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
/**
|
||||
* enum mpu3050_fullscale - indicates the full range of the sensor in deg/sec
|
||||
*/
|
||||
enum mpu3050_fullscale {
|
||||
FS_250_DPS = 0,
|
||||
FS_500_DPS,
|
||||
FS_1000_DPS,
|
||||
FS_2000_DPS,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mpu3050_lpf - indicates the low pass filter width
|
||||
*/
|
||||
enum mpu3050_lpf {
|
||||
/* This implicity sets sample frequency to 8 kHz */
|
||||
LPF_256_HZ_NOLPF = 0,
|
||||
/* All others sets the sample frequency to 1 kHz */
|
||||
LPF_188_HZ,
|
||||
LPF_98_HZ,
|
||||
LPF_42_HZ,
|
||||
LPF_20_HZ,
|
||||
LPF_10_HZ,
|
||||
LPF_5_HZ,
|
||||
LPF_2100_HZ_NOLPF,
|
||||
};
|
||||
|
||||
enum mpu3050_axis {
|
||||
AXIS_X = 0,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
AXIS_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mpu3050 - instance state container for the device
|
||||
* @dev: parent device for this instance
|
||||
* @orientation: mounting matrix, flipped axis etc
|
||||
* @map: regmap to reach the registers
|
||||
* @lock: serialization lock to marshal all requests
|
||||
* @irq: the IRQ used for this device
|
||||
* @regs: the regulators to power this device
|
||||
* @fullscale: the current fullscale setting for the device
|
||||
* @lpf: digital low pass filter setting for the device
|
||||
* @divisor: base frequency divider: divides 8 or 1 kHz
|
||||
* @calibration: the three signed 16-bit calibration settings that
|
||||
* get written into the offset registers for each axis to compensate
|
||||
* for DC offsets
|
||||
* @trig: trigger for the MPU-3050 interrupt, if present
|
||||
* @hw_irq_trigger: hardware interrupt trigger is in use
|
||||
* @irq_actl: interrupt is active low
|
||||
* @irq_latch: latched IRQ, this means that it is a level IRQ
|
||||
* @irq_opendrain: the interrupt line shall be configured open drain
|
||||
* @pending_fifo_footer: tells us if there is a pending footer in the FIFO
|
||||
* that we have to read out first when handling the FIFO
|
||||
* @hw_timestamp: latest hardware timestamp from the trigger IRQ, when in
|
||||
* use
|
||||
* @i2cmux: an I2C mux reflecting the fact that this sensor is a hub with
|
||||
* a pass-through I2C interface coming out of it: this device needs to be
|
||||
* powered up in order to reach devices on the other side of this mux
|
||||
*/
|
||||
struct mpu3050 {
|
||||
struct device *dev;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct regmap *map;
|
||||
struct mutex lock;
|
||||
int irq;
|
||||
struct regulator_bulk_data regs[2];
|
||||
enum mpu3050_fullscale fullscale;
|
||||
enum mpu3050_lpf lpf;
|
||||
u8 divisor;
|
||||
s16 calibration[3];
|
||||
struct iio_trigger *trig;
|
||||
bool hw_irq_trigger;
|
||||
bool irq_actl;
|
||||
bool irq_latch;
|
||||
bool irq_opendrain;
|
||||
bool pending_fifo_footer;
|
||||
s64 hw_timestamp;
|
||||
struct i2c_mux_core *i2cmux;
|
||||
};
|
||||
|
||||
/* Probe called from different transports */
|
||||
int mpu3050_common_probe(struct device *dev,
|
||||
struct regmap *map,
|
||||
int irq,
|
||||
const char *name);
|
||||
int mpu3050_common_remove(struct device *dev);
|
||||
|
||||
/* PM ops */
|
||||
extern const struct dev_pm_ops mpu3050_dev_pm_ops;
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
|
|
@ -154,8 +154,17 @@ static const struct i2c_device_id si7020_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si7020_id);
|
||||
|
||||
static const struct of_device_id si7020_dt_ids[] = {
|
||||
{ .compatible = "silabs,si7020" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, si7020_dt_ids);
|
||||
|
||||
static struct i2c_driver si7020_driver = {
|
||||
.driver.name = "si7020",
|
||||
.driver = {
|
||||
.name = "si7020",
|
||||
.of_match_table = of_match_ptr(si7020_dt_ids),
|
||||
},
|
||||
.probe = si7020_probe,
|
||||
.id_table = si7020_id,
|
||||
};
|
||||
|
|
|
@ -136,6 +136,8 @@ static int maxim_thermocouple_read(struct maxim_thermocouple_data *data,
|
|||
ret = spi_read(data->spi, (void *)&buf32, storage_bytes);
|
||||
*val = be32_to_cpu(buf32);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
|
|
|
@ -183,6 +183,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
|
|||
tscadc->irq = err;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tscadc->tscadc_phys_base = res->start;
|
||||
tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(tscadc->tscadc_base))
|
||||
return PTR_ERR(tscadc->tscadc_base);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -87,6 +88,41 @@ static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
|
||||
{
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
|
||||
if (ec->features[0] == -1U && ec->features[1] == -1U) {
|
||||
/* features bitmap not read yet */
|
||||
|
||||
msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->version = 0;
|
||||
msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
|
||||
msg->insize = sizeof(ec->features);
|
||||
msg->outsize = 0;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
|
||||
dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
|
||||
ret, msg->result);
|
||||
memset(ec->features, 0, sizeof(ec->features));
|
||||
}
|
||||
|
||||
memcpy(ec->features, msg->data, sizeof(ec->features));
|
||||
|
||||
dev_dbg(ec->dev, "EC features %08x %08x\n",
|
||||
ec->features[0], ec->features[1]);
|
||||
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
|
||||
}
|
||||
|
||||
/* Device file ops */
|
||||
static int ec_device_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
|
@ -230,6 +266,123 @@ static void __remove(struct device *dev)
|
|||
kfree(ec);
|
||||
}
|
||||
|
||||
static void cros_ec_sensors_register(struct cros_ec_dev *ec)
|
||||
{
|
||||
/*
|
||||
* Issue a command to get the number of sensor reported.
|
||||
* Build an array of sensors driver and register them all.
|
||||
*/
|
||||
int ret, i, id, sensor_num;
|
||||
struct mfd_cell *sensor_cells;
|
||||
struct cros_ec_sensor_platform *sensor_platforms;
|
||||
int sensor_type[MOTIONSENSE_TYPE_MAX];
|
||||
struct ec_params_motion_sense *params;
|
||||
struct ec_response_motion_sense *resp;
|
||||
struct cros_ec_command *msg;
|
||||
|
||||
msg = kzalloc(sizeof(struct cros_ec_command) +
|
||||
max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
|
||||
if (msg == NULL)
|
||||
return;
|
||||
|
||||
msg->version = 2;
|
||||
msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
|
||||
msg->outsize = sizeof(*params);
|
||||
msg->insize = sizeof(*resp);
|
||||
|
||||
params = (struct ec_params_motion_sense *)msg->data;
|
||||
params->cmd = MOTIONSENSE_CMD_DUMP;
|
||||
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
|
||||
dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
|
||||
ret, msg->result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
resp = (struct ec_response_motion_sense *)msg->data;
|
||||
sensor_num = resp->dump.sensor_count;
|
||||
/* Allocate 2 extra sensors in case lid angle or FIFO are needed */
|
||||
sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
|
||||
GFP_KERNEL);
|
||||
if (sensor_cells == NULL)
|
||||
goto error;
|
||||
|
||||
sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
|
||||
(sensor_num + 1), GFP_KERNEL);
|
||||
if (sensor_platforms == NULL)
|
||||
goto error_platforms;
|
||||
|
||||
memset(sensor_type, 0, sizeof(sensor_type));
|
||||
id = 0;
|
||||
for (i = 0; i < sensor_num; i++) {
|
||||
params->cmd = MOTIONSENSE_CMD_INFO;
|
||||
params->info.sensor_num = i;
|
||||
ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
|
||||
if (ret < 0 || msg->result != EC_RES_SUCCESS) {
|
||||
dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
|
||||
i, ret, msg->result);
|
||||
continue;
|
||||
}
|
||||
switch (resp->info.type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
sensor_cells[id].name = "cros-ec-accel";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
sensor_cells[id].name = "cros-ec-gyro";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
sensor_cells[id].name = "cros-ec-mag";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_PROX:
|
||||
sensor_cells[id].name = "cros-ec-prox";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_LIGHT:
|
||||
sensor_cells[id].name = "cros-ec-light";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_ACTIVITY:
|
||||
sensor_cells[id].name = "cros-ec-activity";
|
||||
break;
|
||||
default:
|
||||
dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
|
||||
continue;
|
||||
}
|
||||
sensor_platforms[id].sensor_num = i;
|
||||
sensor_cells[id].id = sensor_type[resp->info.type];
|
||||
sensor_cells[id].platform_data = &sensor_platforms[id];
|
||||
sensor_cells[id].pdata_size =
|
||||
sizeof(struct cros_ec_sensor_platform);
|
||||
|
||||
sensor_type[resp->info.type]++;
|
||||
id++;
|
||||
}
|
||||
if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
|
||||
sensor_platforms[id].sensor_num = sensor_num;
|
||||
|
||||
sensor_cells[id].name = "cros-ec-angle";
|
||||
sensor_cells[id].id = 0;
|
||||
sensor_cells[id].platform_data = &sensor_platforms[id];
|
||||
sensor_cells[id].pdata_size =
|
||||
sizeof(struct cros_ec_sensor_platform);
|
||||
id++;
|
||||
}
|
||||
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
|
||||
sensor_cells[id].name = "cros-ec-ring";
|
||||
id++;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
dev_err(ec->dev, "failed to add EC sensors\n");
|
||||
|
||||
kfree(sensor_platforms);
|
||||
error_platforms:
|
||||
kfree(sensor_cells);
|
||||
error:
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
static int ec_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
int retval = -ENOMEM;
|
||||
|
@ -245,6 +398,8 @@ static int ec_device_probe(struct platform_device *pdev)
|
|||
ec->ec_dev = dev_get_drvdata(dev->parent);
|
||||
ec->dev = dev;
|
||||
ec->cmd_offset = ec_platform->cmd_offset;
|
||||
ec->features[0] = -1U; /* Not cached yet */
|
||||
ec->features[1] = -1U; /* Not cached yet */
|
||||
device_initialize(&ec->class_dev);
|
||||
cdev_init(&ec->cdev, &fops);
|
||||
|
||||
|
@ -282,6 +437,10 @@ static int ec_device_probe(struct platform_device *pdev)
|
|||
goto dev_reg_failed;
|
||||
}
|
||||
|
||||
/* check whether this EC is a sensor hub. */
|
||||
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
|
||||
cros_ec_sensors_register(ec);
|
||||
|
||||
return 0;
|
||||
|
||||
dev_reg_failed:
|
||||
|
|
|
@ -152,7 +152,8 @@
|
|||
*/
|
||||
|
||||
struct ad7192_state {
|
||||
struct regulator *reg;
|
||||
struct regulator *avdd;
|
||||
struct regulator *dvdd;
|
||||
u16 int_vref_mv;
|
||||
u32 mclk;
|
||||
u32 f_order;
|
||||
|
@ -633,15 +634,30 @@ static int ad7192_probe(struct spi_device *spi)
|
|||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->avdd = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(st->avdd))
|
||||
return PTR_ERR(st->avdd);
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
ret = regulator_enable(st->avdd);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->dvdd = devm_regulator_get(&spi->dev, "dvdd");
|
||||
if (IS_ERR(st->dvdd)) {
|
||||
ret = PTR_ERR(st->dvdd);
|
||||
goto error_disable_avdd;
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->dvdd);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified DVdd supply\n");
|
||||
goto error_disable_avdd;
|
||||
}
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->avdd);
|
||||
|
||||
if (pdata->vref_mv)
|
||||
st->int_vref_mv = pdata->vref_mv;
|
||||
else if (voltage_uv)
|
||||
|
@ -675,7 +691,7 @@ static int ad7192_probe(struct spi_device *spi)
|
|||
|
||||
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
ret = ad7192_setup(st, pdata);
|
||||
if (ret)
|
||||
|
@ -688,9 +704,10 @@ static int ad7192_probe(struct spi_device *spi)
|
|||
|
||||
error_remove_trigger:
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
error_disable_dvdd:
|
||||
regulator_disable(st->dvdd);
|
||||
error_disable_avdd:
|
||||
regulator_disable(st->avdd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -703,8 +720,8 @@ static int ad7192_remove(struct spi_device *spi)
|
|||
iio_device_unregister(indio_dev);
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->dvdd);
|
||||
regulator_disable(st->avdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -173,14 +173,16 @@ static int ad7780_probe(struct spi_device *spi)
|
|||
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->reg = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
|
||||
return ret;
|
||||
}
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
|
||||
st->chip_info =
|
||||
&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
@ -222,8 +224,7 @@ static int ad7780_probe(struct spi_device *spi)
|
|||
error_cleanup_buffer_and_trigger:
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -236,8 +237,7 @@ static int ad7780_remove(struct spi_device *spi)
|
|||
iio_device_unregister(indio_dev);
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -70,8 +70,10 @@
|
|||
#define AD7746_EXCSETUP_EXCLVL(x) (((x) & 0x3) << 0)
|
||||
|
||||
/* Config Register Bit Designations (AD7746_REG_CFG) */
|
||||
#define AD7746_CONF_VTFS(x) ((x) << 6)
|
||||
#define AD7746_CONF_CAPFS(x) ((x) << 3)
|
||||
#define AD7746_CONF_VTFS_SHIFT 6
|
||||
#define AD7746_CONF_CAPFS_SHIFT 3
|
||||
#define AD7746_CONF_VTFS_MASK GENMASK(7, 6)
|
||||
#define AD7746_CONF_CAPFS_MASK GENMASK(5, 3)
|
||||
#define AD7746_CONF_MODE_IDLE (0 << 0)
|
||||
#define AD7746_CONF_MODE_CONT_CONV (1 << 0)
|
||||
#define AD7746_CONF_MODE_SINGLE_CONV (2 << 0)
|
||||
|
@ -217,15 +219,16 @@ static int ad7746_select_channel(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
struct ad7746_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret, delay;
|
||||
int ret, delay, idx;
|
||||
u8 vt_setup, cap_setup;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CAPACITANCE:
|
||||
cap_setup = (chan->address & 0xFF) | AD7746_CAPSETUP_CAPEN;
|
||||
vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN;
|
||||
delay = ad7746_cap_filter_rate_table[(chip->config >> 3) &
|
||||
0x7][1];
|
||||
idx = (chip->config & AD7746_CONF_CAPFS_MASK) >>
|
||||
AD7746_CONF_CAPFS_SHIFT;
|
||||
delay = ad7746_cap_filter_rate_table[idx][1];
|
||||
|
||||
if (chip->capdac_set != chan->channel) {
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
|
@ -246,8 +249,9 @@ static int ad7746_select_channel(struct iio_dev *indio_dev,
|
|||
case IIO_TEMP:
|
||||
vt_setup = (chan->address & 0xFF) | AD7746_VTSETUP_VTEN;
|
||||
cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN;
|
||||
delay = ad7746_cap_filter_rate_table[(chip->config >> 6) &
|
||||
0x3][1];
|
||||
idx = (chip->config & AD7746_CONF_VTFS_MASK) >>
|
||||
AD7746_CONF_VTFS_SHIFT;
|
||||
delay = ad7746_cap_filter_rate_table[idx][1];
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -369,8 +373,8 @@ static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip,
|
|||
if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table))
|
||||
i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
|
||||
|
||||
chip->config &= ~AD7746_CONF_CAPFS(0x7);
|
||||
chip->config |= AD7746_CONF_CAPFS(i);
|
||||
chip->config &= ~AD7746_CONF_CAPFS_MASK;
|
||||
chip->config |= i << AD7746_CONF_CAPFS_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -387,8 +391,8 @@ static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
|
|||
if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table))
|
||||
i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
|
||||
|
||||
chip->config &= ~AD7746_CONF_VTFS(0x3);
|
||||
chip->config |= AD7746_CONF_VTFS(i);
|
||||
chip->config &= ~AD7746_CONF_VTFS_MASK;
|
||||
chip->config |= i << AD7746_CONF_VTFS_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -527,7 +531,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
|
|||
long mask)
|
||||
{
|
||||
struct ad7746_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret, delay;
|
||||
int ret, delay, idx;
|
||||
u8 regval, reg;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
@ -635,13 +639,16 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (chan->type) {
|
||||
case IIO_CAPACITANCE:
|
||||
*val = ad7746_cap_filter_rate_table[
|
||||
(chip->config >> 3) & 0x7][0];
|
||||
idx = (chip->config & AD7746_CONF_CAPFS_MASK) >>
|
||||
AD7746_CONF_CAPFS_SHIFT;
|
||||
*val = ad7746_cap_filter_rate_table[idx][0];
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_VOLTAGE:
|
||||
*val = ad7746_vt_filter_rate_table[
|
||||
(chip->config >> 6) & 0x3][0];
|
||||
idx = (chip->config & AD7746_CONF_VTFS_MASK) >>
|
||||
AD7746_CONF_VTFS_SHIFT;
|
||||
*val = ad7746_vt_filter_rate_table[idx][0];
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
@ -204,7 +204,6 @@ static int ad9832_probe(struct spi_device *spi)
|
|||
struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad9832_state *st;
|
||||
struct regulator *reg;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
|
@ -212,21 +211,35 @@ static int ad9832_probe(struct spi_device *spi)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(reg)) {
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->avdd = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(st->avdd))
|
||||
return PTR_ERR(st->avdd);
|
||||
|
||||
ret = regulator_enable(st->avdd);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified AVDD supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->dvdd = devm_regulator_get(&spi->dev, "dvdd");
|
||||
if (IS_ERR(st->dvdd)) {
|
||||
ret = PTR_ERR(st->dvdd);
|
||||
goto error_disable_avdd;
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->dvdd);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified DVDD supply\n");
|
||||
goto error_disable_avdd;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
}
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st = iio_priv(indio_dev);
|
||||
st->reg = reg;
|
||||
st->mclk = pdata->mclk;
|
||||
st->spi = spi;
|
||||
|
||||
|
@ -277,42 +290,43 @@ static int ad9832_probe(struct spi_device *spi)
|
|||
ret = spi_sync(st->spi, &st->msg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "device init failed\n");
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
}
|
||||
|
||||
ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_dvdd;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
error_disable_dvdd:
|
||||
regulator_disable(st->dvdd);
|
||||
error_disable_avdd:
|
||||
regulator_disable(st->avdd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -323,8 +337,8 @@ static int ad9832_remove(struct spi_device *spi)
|
|||
struct ad9832_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->dvdd);
|
||||
regulator_disable(st->avdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@
|
|||
/**
|
||||
* struct ad9832_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @reg: supply regulator
|
||||
* @avdd: supply regulator for the analog section
|
||||
* @dvdd: supply regulator for the digital section
|
||||
* @mclk: external master clock
|
||||
* @ctrl_fp: cached frequency/phase control word
|
||||
* @ctrl_ss: cached sync/selsrc control word
|
||||
|
@ -76,7 +77,8 @@
|
|||
|
||||
struct ad9832_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
struct regulator *avdd;
|
||||
struct regulator *dvdd;
|
||||
unsigned long mclk;
|
||||
unsigned short ctrl_fp;
|
||||
unsigned short ctrl_ss;
|
||||
|
|
|
@ -329,11 +329,14 @@ static int ad9834_probe(struct spi_device *spi)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(reg)) {
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
reg = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(reg))
|
||||
return PTR_ERR(reg);
|
||||
|
||||
ret = regulator_enable(reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified AVDD supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
|
@ -416,8 +419,7 @@ static int ad9834_probe(struct spi_device *spi)
|
|||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
regulator_disable(reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -428,8 +430,7 @@ static int ad9834_remove(struct spi_device *spi)
|
|||
struct ad9834_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -726,13 +726,16 @@ static int ad5933_probe(struct i2c_client *client,
|
|||
if (!pdata)
|
||||
pdata = &ad5933_default_pdata;
|
||||
|
||||
st->reg = devm_regulator_get(&client->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
st->reg = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to enable specified VDD supply\n");
|
||||
return ret;
|
||||
}
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
|
@ -775,8 +778,7 @@ static int ad5933_probe(struct i2c_client *client,
|
|||
error_unreg_ring:
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -788,8 +790,7 @@ static int ad5933_remove(struct i2c_client *client)
|
|||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -29,8 +29,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define TSL258X_MAX_DEVICE_REGS 32
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Triton register offsets */
|
||||
#define TSL258X_REG_MAX 8
|
||||
|
@ -65,6 +64,9 @@
|
|||
/* Lux calculation constants */
|
||||
#define TSL258X_LUX_CALC_OVER_FLOW 65535
|
||||
|
||||
#define TSL2583_CHIP_ID 0x90
|
||||
#define TSL2583_CHIP_ID_MASK 0xf0
|
||||
|
||||
enum {
|
||||
TSL258X_CHIP_UNKNOWN = 0,
|
||||
TSL258X_CHIP_WORKING = 1,
|
||||
|
@ -124,14 +126,15 @@ static struct taos_lux taos_device_lux[11] = {
|
|||
struct gainadj {
|
||||
s16 ch0;
|
||||
s16 ch1;
|
||||
s16 mean;
|
||||
};
|
||||
|
||||
/* Index = (0 - 3) Used to validate the gain selection index */
|
||||
static const struct gainadj gainadj[] = {
|
||||
{ 1, 1 },
|
||||
{ 8, 8 },
|
||||
{ 16, 16 },
|
||||
{ 107, 115 }
|
||||
{ 1, 1, 1 },
|
||||
{ 8, 8, 8 },
|
||||
{ 16, 16, 16 },
|
||||
{ 107, 115, 111 }
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -140,42 +143,23 @@ static const struct gainadj gainadj[] = {
|
|||
*/
|
||||
static void taos_defaults(struct tsl2583_chip *chip)
|
||||
{
|
||||
/* Operational parameters */
|
||||
/*
|
||||
* The integration time must be a multiple of 50ms and within the
|
||||
* range [50, 600] ms.
|
||||
*/
|
||||
chip->taos_settings.als_time = 100;
|
||||
/* must be a multiple of 50mS */
|
||||
|
||||
/*
|
||||
* This is an index into the gainadj table. Assume clear glass as the
|
||||
* default.
|
||||
*/
|
||||
chip->taos_settings.als_gain = 0;
|
||||
/* this is actually an index into the gain table */
|
||||
/* assume clear glass as default */
|
||||
|
||||
/* Default gain trim to account for aperture effects */
|
||||
chip->taos_settings.als_gain_trim = 1000;
|
||||
/* default gain trim to account for aperture effects */
|
||||
chip->taos_settings.als_cal_target = 130;
|
||||
|
||||
/* Known external ALS reading used for calibration */
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a number of bytes starting at register (reg) location.
|
||||
* Return 0, or i2c_smbus_write_byte ERROR code.
|
||||
*/
|
||||
static int
|
||||
taos_i2c_read(struct i2c_client *client, u8 reg, u8 *val, unsigned int len)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* select register to write */
|
||||
ret = i2c_smbus_write_byte(client, (TSL258X_CMD_REG | reg));
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"taos_i2c_read failed to write register %x\n",
|
||||
reg);
|
||||
return ret;
|
||||
}
|
||||
/* read the data */
|
||||
*val = i2c_smbus_read_byte(client);
|
||||
val++;
|
||||
reg++;
|
||||
}
|
||||
return 0;
|
||||
chip->taos_settings.als_cal_target = 130;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -204,40 +188,37 @@ static int taos_get_lux(struct iio_dev *indio_dev)
|
|||
u32 ch0lux = 0;
|
||||
u32 ch1lux = 0;
|
||||
|
||||
if (mutex_trylock(&chip->als_mutex) == 0) {
|
||||
dev_info(&chip->client->dev, "taos_get_lux device is busy\n");
|
||||
return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
|
||||
}
|
||||
|
||||
if (chip->taos_chip_status != TSL258X_CHIP_WORKING) {
|
||||
/* device is not enabled */
|
||||
dev_err(&chip->client->dev, "taos_get_lux device is not enabled\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = taos_i2c_read(chip->client, (TSL258X_CMD_REG), &buf[0], 1);
|
||||
ret = i2c_smbus_read_byte_data(chip->client, TSL258X_CMD_REG);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "taos_get_lux failed to read CMD_REG\n");
|
||||
goto out_unlock;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* is data new & valid */
|
||||
if (!(buf[0] & TSL258X_STA_ADC_INTR)) {
|
||||
if (!(ret & TSL258X_STA_ADC_INTR)) {
|
||||
dev_err(&chip->client->dev, "taos_get_lux data not valid\n");
|
||||
ret = chip->als_cur_info.lux; /* return LAST VALUE */
|
||||
goto out_unlock;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
int reg = TSL258X_CMD_REG | (TSL258X_ALS_CHAN0LO + i);
|
||||
|
||||
ret = taos_i2c_read(chip->client, reg, &buf[i], 1);
|
||||
ret = i2c_smbus_read_byte_data(chip->client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"taos_get_lux failed to read register %x\n",
|
||||
reg);
|
||||
goto out_unlock;
|
||||
goto done;
|
||||
}
|
||||
buf[i] = ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -252,7 +233,7 @@ static int taos_get_lux(struct iio_dev *indio_dev)
|
|||
dev_err(&chip->client->dev,
|
||||
"taos_i2c_write_command failed in taos_get_lux, err = %d\n",
|
||||
ret);
|
||||
goto out_unlock; /* have no data, so return failure */
|
||||
goto done; /* have no data, so return failure */
|
||||
}
|
||||
|
||||
/* extract ALS/lux data */
|
||||
|
@ -269,7 +250,7 @@ static int taos_get_lux(struct iio_dev *indio_dev)
|
|||
/* have no data, so return LAST VALUE */
|
||||
ret = 0;
|
||||
chip->als_cur_info.lux = 0;
|
||||
goto out_unlock;
|
||||
goto done;
|
||||
}
|
||||
/* calculate ratio */
|
||||
ratio = (ch1 << 15) / ch0;
|
||||
|
@ -295,7 +276,7 @@ static int taos_get_lux(struct iio_dev *indio_dev)
|
|||
dev_dbg(&chip->client->dev, "No Data - Return last value\n");
|
||||
ret = 0;
|
||||
chip->als_cur_info.lux = 0;
|
||||
goto out_unlock;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* adjust for active time scale */
|
||||
|
@ -327,8 +308,7 @@ static int taos_get_lux(struct iio_dev *indio_dev)
|
|||
chip->als_cur_info.lux = lux;
|
||||
ret = lux;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -340,39 +320,25 @@ static int taos_get_lux(struct iio_dev *indio_dev)
|
|||
static int taos_als_calibrate(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
u8 reg_val;
|
||||
unsigned int gain_trim_val;
|
||||
int ret;
|
||||
int lux_val;
|
||||
|
||||
ret = i2c_smbus_write_byte(chip->client,
|
||||
(TSL258X_CMD_REG | TSL258X_CNTRL));
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
TSL258X_CMD_REG | TSL258X_CNTRL);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"taos_als_calibrate failed to reach the CNTRL register, ret=%d\n",
|
||||
ret);
|
||||
"%s failed to read from the CNTRL register\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_val = i2c_smbus_read_byte(chip->client);
|
||||
if ((reg_val & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON))
|
||||
if ((ret & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON))
|
||||
!= (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) {
|
||||
dev_err(&chip->client->dev,
|
||||
"taos_als_calibrate failed: device not powered on with ADC enabled\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte(chip->client,
|
||||
(TSL258X_CMD_REG | TSL258X_CNTRL));
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"taos_als_calibrate failed to reach the STATUS register, ret=%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
reg_val = i2c_smbus_read_byte(chip->client);
|
||||
|
||||
if ((reg_val & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) {
|
||||
return -EINVAL;
|
||||
} else if ((ret & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) {
|
||||
dev_err(&chip->client->dev,
|
||||
"taos_als_calibrate failed: STATUS - ADC not valid.\n");
|
||||
return -ENODATA;
|
||||
|
@ -433,17 +399,8 @@ static int taos_chip_on(struct iio_dev *indio_dev)
|
|||
chip->als_saturation = als_count * 922; /* 90% of full scale */
|
||||
chip->als_time_scale = (als_time + 25) / 50;
|
||||
|
||||
/*
|
||||
* TSL258x Specific power-on / adc enable sequence
|
||||
* Power on the device 1st.
|
||||
*/
|
||||
utmp = TSL258X_CNTL_PWR_ON;
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
TSL258X_CMD_REG | TSL258X_CNTRL, utmp);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "taos_chip_on failed on CNTRL reg.\n");
|
||||
return ret;
|
||||
}
|
||||
/* Power on the device; ADC off. */
|
||||
chip->taos_config[TSL258X_CNTRL] = TSL258X_CNTL_PWR_ON;
|
||||
|
||||
/*
|
||||
* Use the following shadow copy for our delay before enabling ADC.
|
||||
|
@ -491,219 +448,60 @@ static int taos_chip_off(struct iio_dev *indio_dev)
|
|||
|
||||
/* Sysfs Interface Functions */
|
||||
|
||||
static ssize_t taos_power_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t in_illuminance_input_target_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", chip->taos_chip_status);
|
||||
}
|
||||
|
||||
static ssize_t taos_power_state_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
int value;
|
||||
|
||||
if (kstrtoint(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
if (!value)
|
||||
taos_chip_off(indio_dev);
|
||||
else
|
||||
taos_chip_on(indio_dev);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t taos_gain_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
char gain[4] = {0};
|
||||
|
||||
switch (chip->taos_settings.als_gain) {
|
||||
case 0:
|
||||
strcpy(gain, "001");
|
||||
break;
|
||||
case 1:
|
||||
strcpy(gain, "008");
|
||||
break;
|
||||
case 2:
|
||||
strcpy(gain, "016");
|
||||
break;
|
||||
case 3:
|
||||
strcpy(gain, "111");
|
||||
break;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", gain);
|
||||
}
|
||||
|
||||
static ssize_t taos_gain_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int value;
|
||||
|
||||
if (kstrtoint(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
switch (value) {
|
||||
case 1:
|
||||
chip->taos_settings.als_gain = 0;
|
||||
break;
|
||||
case 8:
|
||||
chip->taos_settings.als_gain = 1;
|
||||
break;
|
||||
case 16:
|
||||
chip->taos_settings.als_gain = 2;
|
||||
break;
|
||||
case 111:
|
||||
chip->taos_settings.als_gain = 3;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid Gain Index (must be 1,8,16,111)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t taos_gain_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", "1 8 16 111");
|
||||
}
|
||||
|
||||
static ssize_t taos_als_time_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", chip->taos_settings.als_time);
|
||||
}
|
||||
|
||||
static ssize_t taos_als_time_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int value;
|
||||
|
||||
if (kstrtoint(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
if ((value < 50) || (value > 650))
|
||||
return -EINVAL;
|
||||
|
||||
if (value % 50)
|
||||
return -EINVAL;
|
||||
|
||||
chip->taos_settings.als_time = value;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t taos_als_time_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n",
|
||||
"50 100 150 200 250 300 350 400 450 500 550 600 650");
|
||||
}
|
||||
|
||||
static ssize_t taos_als_trim_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", chip->taos_settings.als_gain_trim);
|
||||
}
|
||||
|
||||
static ssize_t taos_als_trim_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int value;
|
||||
|
||||
if (kstrtoint(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
if (value)
|
||||
chip->taos_settings.als_gain_trim = value;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t taos_als_cal_target_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", chip->taos_settings.als_cal_target);
|
||||
}
|
||||
|
||||
static ssize_t taos_als_cal_target_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int value;
|
||||
|
||||
if (kstrtoint(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
if (value)
|
||||
chip->taos_settings.als_cal_target = value;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t taos_lux_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = taos_get_lux(dev_to_iio_dev(dev));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mutex_lock(&chip->als_mutex);
|
||||
ret = sprintf(buf, "%d\n", chip->taos_settings.als_cal_target);
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
|
||||
return sprintf(buf, "%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t taos_do_calibrate(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
static ssize_t in_illuminance_input_target_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int value;
|
||||
|
||||
if (kstrtoint(buf, 0, &value))
|
||||
if (kstrtoint(buf, 0, &value) || !value)
|
||||
return -EINVAL;
|
||||
|
||||
if (value == 1)
|
||||
taos_als_calibrate(indio_dev);
|
||||
mutex_lock(&chip->als_mutex);
|
||||
chip->taos_settings.als_cal_target = value;
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t taos_luxtable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t in_illuminance_calibrate_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int value;
|
||||
|
||||
if (kstrtoint(buf, 0, &value) || value != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->als_mutex);
|
||||
taos_als_calibrate(indio_dev);
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t in_illuminance_lux_table_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int i;
|
||||
int offset = 0;
|
||||
|
@ -727,14 +525,16 @@ static ssize_t taos_luxtable_show(struct device *dev,
|
|||
return offset;
|
||||
}
|
||||
|
||||
static ssize_t taos_luxtable_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
static ssize_t in_illuminance_lux_table_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int value[ARRAY_SIZE(taos_device_lux) * 3 + 1];
|
||||
int n;
|
||||
int n, ret = -EINVAL;
|
||||
|
||||
mutex_lock(&chip->als_mutex);
|
||||
|
||||
get_options(buf, ARRAY_SIZE(value), value);
|
||||
|
||||
|
@ -746,60 +546,38 @@ static ssize_t taos_luxtable_store(struct device *dev,
|
|||
n = value[0];
|
||||
if ((n % 3) || n < 6 || n > ((ARRAY_SIZE(taos_device_lux) - 1) * 3)) {
|
||||
dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
|
||||
return -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
|
||||
dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
|
||||
return -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (chip->taos_chip_status == TSL258X_CHIP_WORKING)
|
||||
taos_chip_off(indio_dev);
|
||||
|
||||
/* Zero out the table */
|
||||
memset(taos_device_lux, 0, sizeof(taos_device_lux));
|
||||
memcpy(taos_device_lux, &value[1], (value[0] * 4));
|
||||
|
||||
taos_chip_on(indio_dev);
|
||||
ret = len;
|
||||
|
||||
return len;
|
||||
done:
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
|
||||
taos_power_state_show, taos_power_state_store);
|
||||
|
||||
static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
|
||||
taos_gain_show, taos_gain_store);
|
||||
static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
|
||||
taos_gain_available_show, NULL);
|
||||
|
||||
static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
|
||||
taos_als_time_show, taos_als_time_store);
|
||||
static DEVICE_ATTR(illuminance0_integration_time_available, S_IRUGO,
|
||||
taos_als_time_available_show, NULL);
|
||||
|
||||
static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR,
|
||||
taos_als_trim_show, taos_als_trim_store);
|
||||
|
||||
static DEVICE_ATTR(illuminance0_input_target, S_IRUGO | S_IWUSR,
|
||||
taos_als_cal_target_show, taos_als_cal_target_store);
|
||||
|
||||
static DEVICE_ATTR(illuminance0_input, S_IRUGO, taos_lux_show, NULL);
|
||||
static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL, taos_do_calibrate);
|
||||
static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
|
||||
taos_luxtable_show, taos_luxtable_store);
|
||||
static IIO_CONST_ATTR(in_illuminance_calibscale_available, "1 8 16 111");
|
||||
static IIO_CONST_ATTR(in_illuminance_integration_time_available,
|
||||
"0.000050 0.000100 0.000150 0.000200 0.000250 0.000300 0.000350 0.000400 0.000450 0.000500 0.000550 0.000600 0.000650");
|
||||
static IIO_DEVICE_ATTR_RW(in_illuminance_input_target, 0);
|
||||
static IIO_DEVICE_ATTR_WO(in_illuminance_calibrate, 0);
|
||||
static IIO_DEVICE_ATTR_RW(in_illuminance_lux_table, 0);
|
||||
|
||||
static struct attribute *sysfs_attrs_ctrl[] = {
|
||||
&dev_attr_power_state.attr,
|
||||
&dev_attr_illuminance0_calibscale.attr, /* Gain */
|
||||
&dev_attr_illuminance0_calibscale_available.attr,
|
||||
&dev_attr_illuminance0_integration_time.attr, /* I time*/
|
||||
&dev_attr_illuminance0_integration_time_available.attr,
|
||||
&dev_attr_illuminance0_calibbias.attr, /* trim */
|
||||
&dev_attr_illuminance0_input_target.attr,
|
||||
&dev_attr_illuminance0_input.attr,
|
||||
&dev_attr_illuminance0_calibrate.attr,
|
||||
&dev_attr_illuminance0_lux_table.attr,
|
||||
&iio_const_attr_in_illuminance_calibscale_available.dev_attr.attr,
|
||||
&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_illuminance_input_target.dev_attr.attr,
|
||||
&iio_dev_attr_in_illuminance_calibrate.dev_attr.attr,
|
||||
&iio_dev_attr_in_illuminance_lux_table.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -807,15 +585,161 @@ static const struct attribute_group tsl2583_attribute_group = {
|
|||
.attrs = sysfs_attrs_ctrl,
|
||||
};
|
||||
|
||||
/* Use the default register values to identify the Taos device */
|
||||
static int taos_tsl258x_device(unsigned char *bufp)
|
||||
static const struct iio_chan_spec tsl2583_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_LIGHT_IR,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
},
|
||||
};
|
||||
|
||||
static int tsl2583_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
return ((bufp[TSL258X_CHIPID] & 0xf0) == 0x90);
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&chip->als_mutex);
|
||||
|
||||
if (chip->taos_chip_status != TSL258X_CHIP_WORKING) {
|
||||
ret = -EBUSY;
|
||||
goto read_done;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
ret = taos_get_lux(indio_dev);
|
||||
if (ret < 0)
|
||||
goto read_done;
|
||||
|
||||
/*
|
||||
* From page 20 of the TSL2581, TSL2583 data
|
||||
* sheet (TAOS134 − MARCH 2011):
|
||||
*
|
||||
* One of the photodiodes (channel 0) is
|
||||
* sensitive to both visible and infrared light,
|
||||
* while the second photodiode (channel 1) is
|
||||
* sensitive primarily to infrared light.
|
||||
*/
|
||||
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
|
||||
*val = chip->als_cur_info.als_ch0;
|
||||
else
|
||||
*val = chip->als_cur_info.als_ch1;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
ret = taos_get_lux(indio_dev);
|
||||
if (ret < 0)
|
||||
goto read_done;
|
||||
|
||||
*val = ret;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
*val = chip->taos_settings.als_gain_trim;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
*val = gainadj[chip->taos_settings.als_gain].mean;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
*val = 0;
|
||||
*val2 = chip->taos_settings.als_time;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
read_done:
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tsl2583_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct tsl2583_chip *chip = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&chip->als_mutex);
|
||||
|
||||
if (chip->taos_chip_status != TSL258X_CHIP_WORKING) {
|
||||
ret = -EBUSY;
|
||||
goto write_done;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
chip->taos_settings.als_gain_trim = val;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gainadj); i++) {
|
||||
if (gainadj[i].mean == val) {
|
||||
chip->taos_settings.als_gain = i;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (chan->type == IIO_LIGHT && !val && val2 >= 50 &&
|
||||
val2 <= 650 && !(val2 % 50)) {
|
||||
chip->taos_settings.als_time = val2;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
write_done:
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info tsl2583_info = {
|
||||
.attrs = &tsl2583_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = tsl2583_read_raw,
|
||||
.write_raw = tsl2583_write_raw,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -825,8 +749,7 @@ static const struct iio_info tsl2583_info = {
|
|||
static int taos_probe(struct i2c_client *clientp,
|
||||
const struct i2c_device_id *idp)
|
||||
{
|
||||
int i, ret;
|
||||
unsigned char buf[TSL258X_MAX_DEVICE_REGS];
|
||||
int ret;
|
||||
struct tsl2583_chip *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
|
@ -847,28 +770,17 @@ static int taos_probe(struct i2c_client *clientp,
|
|||
chip->taos_chip_status = TSL258X_CHIP_UNKNOWN;
|
||||
memcpy(chip->taos_config, taos_config, sizeof(chip->taos_config));
|
||||
|
||||
for (i = 0; i < TSL258X_MAX_DEVICE_REGS; i++) {
|
||||
ret = i2c_smbus_write_byte(clientp,
|
||||
(TSL258X_CMD_REG | (TSL258X_CNTRL + i)));
|
||||
if (ret < 0) {
|
||||
dev_err(&clientp->dev,
|
||||
"i2c_smbus_write_byte to cmd reg failed in taos_probe(), err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
ret = i2c_smbus_read_byte(clientp);
|
||||
if (ret < 0) {
|
||||
dev_err(&clientp->dev,
|
||||
"i2c_smbus_read_byte from reg failed in taos_probe(), err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
buf[i] = ret;
|
||||
ret = i2c_smbus_read_byte_data(clientp,
|
||||
TSL258X_CMD_REG | TSL258X_CHIPID);
|
||||
if (ret < 0) {
|
||||
dev_err(&clientp->dev,
|
||||
"%s failed to read the chip ID register\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!taos_tsl258x_device(buf)) {
|
||||
dev_info(&clientp->dev,
|
||||
"i2c device found but does not match expected id in taos_probe()\n");
|
||||
if ((ret & TSL2583_CHIP_ID_MASK) != TSL2583_CHIP_ID) {
|
||||
dev_info(&clientp->dev, "%s received an unknown chip ID %x\n",
|
||||
__func__, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -881,6 +793,8 @@ static int taos_probe(struct i2c_client *clientp,
|
|||
}
|
||||
|
||||
indio_dev->info = &tsl2583_info;
|
||||
indio_dev->channels = tsl2583_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(tsl2583_channels);
|
||||
indio_dev->dev.parent = &clientp->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->name = chip->client->name;
|
||||
|
@ -894,7 +808,9 @@ static int taos_probe(struct i2c_client *clientp,
|
|||
taos_defaults(chip);
|
||||
|
||||
/* Make sure the chip is on */
|
||||
taos_chip_on(indio_dev);
|
||||
ret = taos_chip_on(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_info(&clientp->dev, "Light sensor found.\n");
|
||||
return 0;
|
||||
|
@ -947,11 +863,24 @@ static struct i2c_device_id taos_idtable[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, taos_idtable);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id taos2583_of_match[] = {
|
||||
{ .compatible = "amstaos,tsl2580", },
|
||||
{ .compatible = "amstaos,tsl2581", },
|
||||
{ .compatible = "amstaos,tsl2583", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, taos2583_of_match);
|
||||
#else
|
||||
#define taos2583_of_match NULL
|
||||
#endif
|
||||
|
||||
/* Driver definition */
|
||||
static struct i2c_driver taos_driver = {
|
||||
.driver = {
|
||||
.name = "tsl2583",
|
||||
.pm = TAOS_PM_OPS,
|
||||
.of_match_table = taos2583_of_match,
|
||||
},
|
||||
.id_table = taos_idtable,
|
||||
.probe = taos_probe,
|
||||
|
|
|
@ -148,6 +148,15 @@ struct cros_ec_device {
|
|||
int event_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cros_ec_sensor_platform - ChromeOS EC sensor platform information
|
||||
*
|
||||
* @sensor_num: Id of the sensor, as reported by the EC.
|
||||
*/
|
||||
struct cros_ec_sensor_platform {
|
||||
u8 sensor_num;
|
||||
};
|
||||
|
||||
/* struct cros_ec_platform - ChromeOS EC platform information
|
||||
*
|
||||
* @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...)
|
||||
|
@ -175,6 +184,7 @@ struct cros_ec_dev {
|
|||
struct cros_ec_device *ec_dev;
|
||||
struct device *dev;
|
||||
u16 cmd_offset;
|
||||
u32 features[2];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -713,6 +713,90 @@ struct ec_response_get_set_value {
|
|||
/* More than one command can use these structs to get/set paramters. */
|
||||
#define EC_CMD_GSV_PAUSE_IN_S5 0x0c
|
||||
|
||||
/*****************************************************************************/
|
||||
/* List the features supported by the firmware */
|
||||
#define EC_CMD_GET_FEATURES 0x0d
|
||||
|
||||
/* Supported features */
|
||||
enum ec_feature_code {
|
||||
/*
|
||||
* This image contains a limited set of features. Another image
|
||||
* in RW partition may support more features.
|
||||
*/
|
||||
EC_FEATURE_LIMITED = 0,
|
||||
/*
|
||||
* Commands for probing/reading/writing/erasing the flash in the
|
||||
* EC are present.
|
||||
*/
|
||||
EC_FEATURE_FLASH = 1,
|
||||
/*
|
||||
* Can control the fan speed directly.
|
||||
*/
|
||||
EC_FEATURE_PWM_FAN = 2,
|
||||
/*
|
||||
* Can control the intensity of the keyboard backlight.
|
||||
*/
|
||||
EC_FEATURE_PWM_KEYB = 3,
|
||||
/*
|
||||
* Support Google lightbar, introduced on Pixel.
|
||||
*/
|
||||
EC_FEATURE_LIGHTBAR = 4,
|
||||
/* Control of LEDs */
|
||||
EC_FEATURE_LED = 5,
|
||||
/* Exposes an interface to control gyro and sensors.
|
||||
* The host goes through the EC to access these sensors.
|
||||
* In addition, the EC may provide composite sensors, like lid angle.
|
||||
*/
|
||||
EC_FEATURE_MOTION_SENSE = 6,
|
||||
/* The keyboard is controlled by the EC */
|
||||
EC_FEATURE_KEYB = 7,
|
||||
/* The AP can use part of the EC flash as persistent storage. */
|
||||
EC_FEATURE_PSTORE = 8,
|
||||
/* The EC monitors BIOS port 80h, and can return POST codes. */
|
||||
EC_FEATURE_PORT80 = 9,
|
||||
/*
|
||||
* Thermal management: include TMP specific commands.
|
||||
* Higher level than direct fan control.
|
||||
*/
|
||||
EC_FEATURE_THERMAL = 10,
|
||||
/* Can switch the screen backlight on/off */
|
||||
EC_FEATURE_BKLIGHT_SWITCH = 11,
|
||||
/* Can switch the wifi module on/off */
|
||||
EC_FEATURE_WIFI_SWITCH = 12,
|
||||
/* Monitor host events, through for example SMI or SCI */
|
||||
EC_FEATURE_HOST_EVENTS = 13,
|
||||
/* The EC exposes GPIO commands to control/monitor connected devices. */
|
||||
EC_FEATURE_GPIO = 14,
|
||||
/* The EC can send i2c messages to downstream devices. */
|
||||
EC_FEATURE_I2C = 15,
|
||||
/* Command to control charger are included */
|
||||
EC_FEATURE_CHARGER = 16,
|
||||
/* Simple battery support. */
|
||||
EC_FEATURE_BATTERY = 17,
|
||||
/*
|
||||
* Support Smart battery protocol
|
||||
* (Common Smart Battery System Interface Specification)
|
||||
*/
|
||||
EC_FEATURE_SMART_BATTERY = 18,
|
||||
/* EC can dectect when the host hangs. */
|
||||
EC_FEATURE_HANG_DETECT = 19,
|
||||
/* Report power information, for pit only */
|
||||
EC_FEATURE_PMU = 20,
|
||||
/* Another Cros EC device is present downstream of this one */
|
||||
EC_FEATURE_SUB_MCU = 21,
|
||||
/* Support USB Power delivery (PD) commands */
|
||||
EC_FEATURE_USB_PD = 22,
|
||||
/* Control USB multiplexer, for audio through USB port for instance. */
|
||||
EC_FEATURE_USB_MUX = 23,
|
||||
/* Motion Sensor code has an internal software FIFO */
|
||||
EC_FEATURE_MOTION_SENSE_FIFO = 24,
|
||||
};
|
||||
|
||||
#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
|
||||
#define EC_FEATURE_MASK_1(event_code) (1UL << (event_code - 32))
|
||||
struct ec_response_get_features {
|
||||
uint32_t flags[2];
|
||||
} __packed;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Flash commands */
|
||||
|
@ -1315,6 +1399,24 @@ enum motionsense_command {
|
|||
*/
|
||||
MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5,
|
||||
|
||||
/*
|
||||
* Returns a single sensor data.
|
||||
*/
|
||||
MOTIONSENSE_CMD_DATA = 6,
|
||||
|
||||
/*
|
||||
* Perform low level calibration.. On sensors that support it, ask to
|
||||
* do offset calibration.
|
||||
*/
|
||||
MOTIONSENSE_CMD_PERFORM_CALIB = 10,
|
||||
|
||||
/*
|
||||
* Sensor Offset command is a setter/getter command for the offset used
|
||||
* for calibration. The offsets can be calculated by the host, or via
|
||||
* PERFORM_CALIB command.
|
||||
*/
|
||||
MOTIONSENSE_CMD_SENSOR_OFFSET = 11,
|
||||
|
||||
/* Number of motionsense sub-commands. */
|
||||
MOTIONSENSE_NUM_CMDS
|
||||
};
|
||||
|
@ -1335,12 +1437,18 @@ enum motionsensor_id {
|
|||
enum motionsensor_type {
|
||||
MOTIONSENSE_TYPE_ACCEL = 0,
|
||||
MOTIONSENSE_TYPE_GYRO = 1,
|
||||
MOTIONSENSE_TYPE_MAG = 2,
|
||||
MOTIONSENSE_TYPE_PROX = 3,
|
||||
MOTIONSENSE_TYPE_LIGHT = 4,
|
||||
MOTIONSENSE_TYPE_ACTIVITY = 5,
|
||||
MOTIONSENSE_TYPE_MAX
|
||||
};
|
||||
|
||||
/* List of motion sensor locations. */
|
||||
enum motionsensor_location {
|
||||
MOTIONSENSE_LOC_BASE = 0,
|
||||
MOTIONSENSE_LOC_LID = 1,
|
||||
MOTIONSENSE_LOC_MAX,
|
||||
};
|
||||
|
||||
/* List of motion sensor chips. */
|
||||
|
@ -1361,6 +1469,31 @@ enum motionsensor_chip {
|
|||
*/
|
||||
#define EC_MOTION_SENSE_NO_VALUE -1
|
||||
|
||||
#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000
|
||||
|
||||
/* Set Calibration information */
|
||||
#define MOTION_SENSE_SET_OFFSET 1
|
||||
|
||||
struct ec_response_motion_sensor_data {
|
||||
/* Flags for each sensor. */
|
||||
uint8_t flags;
|
||||
/* Sensor number the data comes from */
|
||||
uint8_t sensor_num;
|
||||
/* Each sensor is up to 3-axis. */
|
||||
union {
|
||||
int16_t data[3];
|
||||
struct {
|
||||
uint16_t rsvd;
|
||||
uint32_t timestamp;
|
||||
} __packed;
|
||||
struct {
|
||||
uint8_t activity; /* motionsensor_activity */
|
||||
uint8_t state;
|
||||
int16_t add_info[2];
|
||||
};
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct ec_params_motion_sense {
|
||||
uint8_t cmd;
|
||||
union {
|
||||
|
@ -1378,9 +1511,37 @@ struct ec_params_motion_sense {
|
|||
int16_t data;
|
||||
} ec_rate, kb_wake_angle;
|
||||
|
||||
/* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
|
||||
struct {
|
||||
uint8_t sensor_num;
|
||||
|
||||
/*
|
||||
* bit 0: If set (MOTION_SENSE_SET_OFFSET), set
|
||||
* the calibration information in the EC.
|
||||
* If unset, just retrieve calibration information.
|
||||
*/
|
||||
uint16_t flags;
|
||||
|
||||
/*
|
||||
* Temperature at calibration, in units of 0.01 C
|
||||
* 0x8000: invalid / unknown.
|
||||
* 0x0: 0C
|
||||
* 0x7fff: +327.67C
|
||||
*/
|
||||
int16_t temp;
|
||||
|
||||
/*
|
||||
* Offset for calibration.
|
||||
* Unit:
|
||||
* Accelerometer: 1/1024 g
|
||||
* Gyro: 1/1024 deg/s
|
||||
* Compass: 1/16 uT
|
||||
*/
|
||||
int16_t offset[3];
|
||||
} __packed sensor_offset;
|
||||
|
||||
/* Used for MOTIONSENSE_CMD_INFO. */
|
||||
struct {
|
||||
/* Should be element of enum motionsensor_id. */
|
||||
uint8_t sensor_num;
|
||||
} info;
|
||||
|
||||
|
@ -1410,11 +1571,14 @@ struct ec_response_motion_sense {
|
|||
/* Flags representing the motion sensor module. */
|
||||
uint8_t module_flags;
|
||||
|
||||
/* Flags for each sensor in enum motionsensor_id. */
|
||||
uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT];
|
||||
/* Number of sensors managed directly by the EC. */
|
||||
uint8_t sensor_count;
|
||||
|
||||
/* Array of all sensor data. Each sensor is 3-axis. */
|
||||
int16_t data[3*EC_MOTION_SENSOR_COUNT];
|
||||
/*
|
||||
* Sensor data is truncated if response_max is too small
|
||||
* for holding all the data.
|
||||
*/
|
||||
struct ec_response_motion_sensor_data sensor[0];
|
||||
} dump;
|
||||
|
||||
/* Used for MOTIONSENSE_CMD_INFO. */
|
||||
|
@ -1429,6 +1593,9 @@ struct ec_response_motion_sense {
|
|||
uint8_t chip;
|
||||
} info;
|
||||
|
||||
/* Used for MOTIONSENSE_CMD_DATA */
|
||||
struct ec_response_motion_sensor_data data;
|
||||
|
||||
/*
|
||||
* Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR,
|
||||
* MOTIONSENSE_CMD_SENSOR_RANGE, and
|
||||
|
@ -1438,6 +1605,12 @@ struct ec_response_motion_sense {
|
|||
/* Current value of the parameter queried. */
|
||||
int32_t ret;
|
||||
} ec_rate, sensor_odr, sensor_range, kb_wake_angle;
|
||||
|
||||
/* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
|
||||
struct {
|
||||
int16_t temp;
|
||||
int16_t offset[3];
|
||||
} sensor_offset, perform_calib;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#define REG_IRQENABLE 0x02C
|
||||
#define REG_IRQCLR 0x030
|
||||
#define REG_IRQWAKEUP 0x034
|
||||
#define REG_DMAENABLE_SET 0x038
|
||||
#define REG_DMAENABLE_CLEAR 0x03c
|
||||
#define REG_CTRL 0x040
|
||||
#define REG_ADCFSM 0x044
|
||||
#define REG_CLKDIV 0x04C
|
||||
|
@ -36,6 +38,7 @@
|
|||
#define REG_FIFO0THR 0xE8
|
||||
#define REG_FIFO1CNT 0xF0
|
||||
#define REG_FIFO1THR 0xF4
|
||||
#define REG_DMA1REQ 0xF8
|
||||
#define REG_FIFO0 0x100
|
||||
#define REG_FIFO1 0x200
|
||||
|
||||
|
@ -126,6 +129,10 @@
|
|||
#define FIFOREAD_DATA_MASK (0xfff << 0)
|
||||
#define FIFOREAD_CHNLID_MASK (0xf << 16)
|
||||
|
||||
/* DMA ENABLE/CLEAR Register */
|
||||
#define DMA_FIFO0 BIT(0)
|
||||
#define DMA_FIFO1 BIT(1)
|
||||
|
||||
/* Sequencer Status */
|
||||
#define SEQ_STATUS BIT(5)
|
||||
#define CHARGE_STEP 0x11
|
||||
|
@ -155,6 +162,7 @@ struct ti_tscadc_dev {
|
|||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
void __iomem *tscadc_base;
|
||||
phys_addr_t tscadc_phys_base;
|
||||
int irq;
|
||||
int used_cells; /* 1-2 */
|
||||
int tsc_wires;
|
||||
|
|
Loading…
Reference in a new issue