diff options
Diffstat (limited to 'drivers')
32 files changed, 1733 insertions, 211 deletions
diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index bbc930a5962c..28b63645c411 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -347,7 +347,7 @@ static const struct iio_chan_spec stm32_lptim_cnt_channels = { }; /** - * stm32_lptim_cnt_function - enumerates stm32 LPTimer counter & encoder modes + * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature) */ diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 644ba18a72ad..b61135b63ee8 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -28,7 +28,7 @@ struct stm32_timer_cnt { }; /** - * stm32_count_function - enumerates stm32 timer counter encoder modes + * enum stm32_count_function - enumerates stm32 timer counter encoder modes * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1 * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level @@ -219,8 +219,8 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter, if (enable) { regmap_read(priv->regmap, TIM_CR1, &cr1); - if (!(cr1 & TIM_CR1_CEN)) - clk_enable(priv->clk); + if (!(cr1 & TIM_CR1_CEN)) + clk_enable(priv->clk); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index ac0ffff6c5ae..5c2b3446fa4a 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -54,38 +54,20 @@ struct ad7949_adc_chip { u8 resolution; u16 cfg; unsigned int current_channel; - u32 buffer ____cacheline_aligned; + u16 buffer ____cacheline_aligned; }; -static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc) -{ - if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK)) - return true; - - return false; -} - -static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc) -{ - int ret = ad7949_adc->resolution; - - if (ad7949_spi_cfg_is_read_back(ad7949_adc)) - ret += AD7949_CFG_REG_SIZE_BITS; - - return ret; -} - static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, u16 mask) { int ret; - int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int bits_per_word = ad7949_adc->resolution; int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; struct spi_message msg; struct spi_transfer tx[] = { { .tx_buf = &ad7949_adc->buffer, - .len = 4, + .len = 2, .bits_per_word = bits_per_word, }, }; @@ -107,13 +89,13 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, unsigned int channel) { int ret; - int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int bits_per_word = ad7949_adc->resolution; int mask = GENMASK(ad7949_adc->resolution, 0); struct spi_message msg; struct spi_transfer tx[] = { { .rx_buf = &ad7949_adc->buffer, - .len = 4, + .len = 2, .bits_per_word = bits_per_word, }, }; @@ -138,10 +120,7 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, ad7949_adc->current_channel = channel; - if (ad7949_spi_cfg_is_read_back(ad7949_adc)) - *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask; - else - *val = ad7949_adc->buffer & mask; + *val = ad7949_adc->buffer & mask; return 0; } diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 2640b75fb774..8ba90486c787 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -205,7 +205,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, } EXPORT_SYMBOL_GPL(ad_sd_reset); -static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, +int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { int ret; @@ -242,6 +242,7 @@ out: return ret; } +EXPORT_SYMBOL_GPL(ad_sd_calibrate); /** * ad_sd_calibrate_all() - Performs channel calibration diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index e234970b7150..7a53c2f8d438 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -25,9 +25,13 @@ #define JZ_ADC_REG_ADSDAT 0x20 #define JZ_ADC_REG_ADCLK 0x28 +#define JZ_ADC_REG_ENABLE_PD BIT(7) +#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1)) #define JZ_ADC_REG_CFG_BAT_MD BIT(4) #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0 -#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB 16 +#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16 +#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8 +#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16 #define JZ_ADC_AUX_VREF 3300 #define JZ_ADC_AUX_VREF_BITS 12 @@ -37,6 +41,8 @@ #define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10 #define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 +#define JZ4770_ADC_BATTERY_VREF 6600 +#define JZ4770_ADC_BATTERY_VREF_BITS 12 struct ingenic_adc; @@ -47,6 +53,8 @@ struct ingenic_adc_soc_data { size_t battery_raw_avail_size; const int *battery_scale_avail; size_t battery_scale_avail_size; + unsigned int battery_vref_mode: 1; + unsigned int has_aux2: 1; int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc); }; @@ -54,6 +62,7 @@ struct ingenic_adc { void __iomem *base; struct clk *clk; struct mutex lock; + struct mutex aux_lock; const struct ingenic_adc_soc_data *soc_data; bool low_vref_mode; }; @@ -120,6 +129,8 @@ static int ingenic_adc_write_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->channel) { case INGENIC_ADC_BATTERY: + if (!adc->soc_data->battery_vref_mode) + return -EINVAL; if (val > JZ_ADC_BATTERY_LOW_VREF) { ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_BAT_MD, @@ -158,6 +169,14 @@ static const int jz4740_adc_battery_scale_avail[] = { JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, }; +static const int jz4770_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1, +}; + +static const int jz4770_adc_battery_scale_avail[] = { + JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS, +}; + static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) { struct clk *parent_clk; @@ -187,7 +206,45 @@ static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) /* We also need a divider that produces a 10us clock. */ div_10us = DIV_ROUND_UP(rate, 100000); - writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) | + writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) | + (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, + adc->base + JZ_ADC_REG_ADCLK); + + return 0; +} + +static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) +{ + struct clk *parent_clk; + unsigned long parent_rate, rate; + unsigned int div_main, div_ms, div_10us; + + parent_clk = clk_get_parent(adc->clk); + if (!parent_clk) { + dev_err(dev, "ADC clock has no parent\n"); + return -ENODEV; + } + parent_rate = clk_get_rate(parent_clk); + + /* + * The JZ4770 ADC works at 20 kHz to 200 kHz. + * We pick the highest rate possible. + */ + div_main = DIV_ROUND_UP(parent_rate, 200000); + div_main = clamp(div_main, 1u, 256u); + rate = parent_rate / div_main; + if (rate < 20000 || rate > 200000) { + dev_err(dev, "No valid divider for ADC main clock\n"); + return -EINVAL; + } + + /* We also need a divider that produces a 10us clock. */ + div_10us = DIV_ROUND_UP(rate, 10000); + /* And another, which produces a 1ms clock. */ + div_ms = DIV_ROUND_UP(rate, 1000); + + writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) | + ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) | (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, adc->base + JZ_ADC_REG_ADCLK); @@ -201,6 +258,8 @@ static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), .battery_scale_avail = jz4725b_adc_battery_scale_avail, .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), + .battery_vref_mode = true, + .has_aux2 = false, .init_clk_div = jz4725b_adc_init_clk_div, }; @@ -211,9 +270,23 @@ static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), .battery_scale_avail = jz4740_adc_battery_scale_avail, .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), + .battery_vref_mode = true, + .has_aux2 = false, .init_clk_div = NULL, /* no ADCLK register on JZ4740 */ }; +static const struct ingenic_adc_soc_data jz4770_adc_soc_data = { + .battery_high_vref = JZ4770_ADC_BATTERY_VREF, + .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, + .battery_raw_avail = jz4770_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), + .battery_scale_avail = jz4770_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail), + .battery_vref_mode = false, + .has_aux2 = true, + .init_clk_div = jz4770_adc_init_clk_div, +}; + static int ingenic_adc_read_avail(struct iio_dev *iio_dev, struct iio_chan_spec const *chan, const int **vals, @@ -239,6 +312,42 @@ static int ingenic_adc_read_avail(struct iio_dev *iio_dev, }; } +static int ingenic_adc_read_chan_info_raw(struct ingenic_adc *adc, + struct iio_chan_spec const *chan, + int *val) +{ + int bit, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); + + /* We cannot sample AUX/AUX2 in parallel. */ + mutex_lock(&adc->aux_lock); + if (adc->soc_data->has_aux2 && engine == 0) { + bit = BIT(chan->channel == INGENIC_ADC_AUX2); + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, bit); + } + + clk_enable(adc->clk); + ret = ingenic_adc_capture(adc, engine); + if (ret) + goto out; + + switch (chan->channel) { + case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: + *val = readw(adc->base + JZ_ADC_REG_ADSDAT); + break; + case INGENIC_ADC_BATTERY: + *val = readw(adc->base + JZ_ADC_REG_ADBDAT); + break; + } + + ret = IIO_VAL_INT; +out: + clk_disable(adc->clk); + mutex_unlock(&adc->aux_lock); + + return ret; +} + static int ingenic_adc_read_raw(struct iio_dev *iio_dev, struct iio_chan_spec const *chan, int *val, @@ -246,32 +355,14 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev, long m) { struct ingenic_adc *adc = iio_priv(iio_dev); - int ret; switch (m) { case IIO_CHAN_INFO_RAW: - clk_enable(adc->clk); - ret = ingenic_adc_capture(adc, chan->channel); - if (ret) { - clk_disable(adc->clk); - return ret; - } - - switch (chan->channel) { - case INGENIC_ADC_AUX: - *val = readw(adc->base + JZ_ADC_REG_ADSDAT); - break; - case INGENIC_ADC_BATTERY: - *val = readw(adc->base + JZ_ADC_REG_ADBDAT); - break; - } - - clk_disable(adc->clk); - - return IIO_VAL_INT; + return ingenic_adc_read_chan_info_raw(adc, chan, val); case IIO_CHAN_INFO_SCALE: switch (chan->channel) { case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: *val = JZ_ADC_AUX_VREF; *val2 = JZ_ADC_AUX_VREF_BITS; break; @@ -322,6 +413,14 @@ static const struct iio_chan_spec ingenic_channels[] = { .indexed = 1, .channel = INGENIC_ADC_BATTERY, }, + { /* Must always be last in the array. */ + .extend_name = "aux2", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX2, + }, }; static int ingenic_adc_probe(struct platform_device *pdev) @@ -343,6 +442,7 @@ static int ingenic_adc_probe(struct platform_device *pdev) adc = iio_priv(iio_dev); mutex_init(&adc->lock); + mutex_init(&adc->aux_lock); adc->soc_data = soc_data; mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -374,6 +474,7 @@ static int ingenic_adc_probe(struct platform_device *pdev) /* Put hardware in a known passive state. */ writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); writeb(0xff, adc->base + JZ_ADC_REG_CTRL); + usleep_range(2000, 3000); /* Must wait at least 2ms. */ clk_disable(adc->clk); ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk); @@ -387,6 +488,9 @@ static int ingenic_adc_probe(struct platform_device *pdev) iio_dev->modes = INDIO_DIRECT_MODE; iio_dev->channels = ingenic_channels; iio_dev->num_channels = ARRAY_SIZE(ingenic_channels); + /* Remove AUX2 from the list of supported channels. */ + if (!adc->soc_data->has_aux2) + iio_dev->num_channels -= 1; iio_dev->info = &ingenic_adc_info; ret = devm_iio_device_register(dev, iio_dev); @@ -400,6 +504,7 @@ static int ingenic_adc_probe(struct platform_device *pdev) static const struct of_device_id ingenic_adc_of_match[] = { { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, }, { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, }, + { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, }, { }, }; MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index a6c046575ec3..66b387f9b36d 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -477,13 +477,6 @@ static void sc27xx_adc_disable(void *_data) SC27XX_MODULE_ADC_EN, 0); } -static void sc27xx_adc_free_hwlock(void *_data) -{ - struct hwspinlock *hwlock = _data; - - hwspin_lock_free(hwlock); -} - static int sc27xx_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -520,19 +513,12 @@ static int sc27xx_adc_probe(struct platform_device *pdev) return ret; } - sc27xx_data->hwlock = hwspin_lock_request_specific(ret); + sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); if (!sc27xx_data->hwlock) { dev_err(dev, "failed to request hwspinlock\n"); return -ENXIO; } - ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock, - sc27xx_data->hwlock); - if (ret) { - dev_err(dev, "failed to add hwspinlock action\n"); - return ret; - } - sc27xx_data->dev = dev; ret = sc27xx_adc_enable(sc27xx_data); diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index 3a20cb5d9bff..6c175eb1c7a7 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -323,16 +323,16 @@ static int atlas_buffer_predisable(struct iio_dev *indio_dev) struct atlas_data *data = iio_priv(indio_dev); int ret; - ret = iio_triggered_buffer_predisable(indio_dev); + ret = atlas_set_interrupt(data, false); if (ret) return ret; - ret = atlas_set_interrupt(data, false); + pm_runtime_mark_last_busy(&data->client->dev); + ret = pm_runtime_put_autosuspend(&data->client->dev); if (ret) return ret; - pm_runtime_mark_last_busy(&data->client->dev); - return pm_runtime_put_autosuspend(&data->client->dev); + return iio_triggered_buffer_predisable(indio_dev); } static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index 236220d6de02..1b84b8e112fe 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -38,10 +38,12 @@ struct adis16080_chip_info { * @us: actual spi_device to write data * @info: chip specific parameters * @buf: transmit or receive buffer + * @lock lock to protect buffer during reads **/ struct adis16080_state { struct spi_device *us; const struct adis16080_chip_info *info; + struct mutex lock; __be16 buf ____cacheline_aligned; }; @@ -82,9 +84,9 @@ static int adis16080_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = adis16080_read_sample(indio_dev, chan->address, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret ? ret : IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (chan->type) { @@ -196,6 +198,8 @@ static int adis16080_probe(struct spi_device *spi) /* this is only used for removal purposes */ spi_set_drvdata(spi, indio_dev); + mutex_init(&st->lock); + /* Allocate the comms buffers */ st->us = spi; st->info = &adis16080_chip_info[id->driver_data]; diff --git a/drivers/iio/gyro/adis16130.c b/drivers/iio/gyro/adis16130.c index de3f66f89496..79e63c8a2ea8 100644 --- a/drivers/iio/gyro/adis16130.c +++ b/drivers/iio/gyro/adis16130.c @@ -76,9 +76,7 @@ static int adis16130_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: /* Take the iio_dev status lock */ - mutex_lock(&indio_dev->mlock); ret = adis16130_spi_read(indio_dev, chan->address, &temp); - mutex_unlock(&indio_dev->mlock); if (ret) return ret; *val = temp; diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index 998fb8d66fe3..981ae2291505 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -154,7 +154,7 @@ static int itg3200_write_raw(struct iio_dev *indio_dev, t); mutex_unlock(&indio_dev->mlock); - return ret; + return ret; default: return -EINVAL; diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile index 70ffe0d13d8c..c103441a906b 100644 --- a/drivers/iio/imu/inv_mpu6050/Makefile +++ b/drivers/iio/imu/inv_mpu6050/Makefile @@ -4,10 +4,11 @@ # obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o -inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o +inv-mpu6050-y := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o \ + inv_mpu_aux.o inv_mpu_magn.o obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o -inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o +inv-mpu6050-i2c-y := inv_mpu_i2c.o inv_mpu_acpi.o obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o -inv-mpu6050-spi-objs := inv_mpu_spi.o +inv-mpu6050-spi-y := inv_mpu_spi.o diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c new file mode 100644 index 000000000000..7327e5723f96 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/delay.h> + +#include "inv_mpu_aux.h" +#include "inv_mpu_iio.h" + +/* + * i2c master auxiliary bus transfer function. + * Requires the i2c operations to be correctly setup before. + */ +static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st) +{ + /* use 50hz frequency for xfer */ + const unsigned int freq = 50; + const unsigned int period_ms = 1000 / freq; + uint8_t d; + unsigned int user_ctrl; + int ret; + + /* set sample rate */ + d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq); + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + return ret; + + /* start i2c master */ + user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + goto error_restore_rate; + + /* wait for xfer: 1 period + half-period margin */ + msleep(period_ms + period_ms / 2); + + /* stop i2c master */ + user_ctrl = st->chip_config.user_ctrl; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + goto error_stop_i2c; + + /* restore sample rate */ + d = st->chip_config.divider; + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + goto error_restore_rate; + + return 0; + +error_stop_i2c: + regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); +error_restore_rate: + regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider); + return ret; +} + +/** + * inv_mpu_aux_init() - init i2c auxiliary bus + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_init(const struct inv_mpu6050_state *st) +{ + unsigned int val; + int ret; + + /* configure i2c master */ + val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ | + INV_MPU6050_BIT_WAIT_FOR_ES; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_MST_CTRL, val); + if (ret) + return ret; + + /* configure i2c master delay */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, 0); + if (ret) + return ret; + + val = INV_MPU6050_BIT_I2C_SLV0_DLY_EN | + INV_MPU6050_BIT_I2C_SLV1_DLY_EN | + INV_MPU6050_BIT_I2C_SLV2_DLY_EN | + INV_MPU6050_BIT_I2C_SLV3_DLY_EN | + INV_MPU6050_BIT_DELAY_ES_SHADOW; + return regmap_write(st->map, INV_MPU6050_REG_I2C_MST_DELAY_CTRL, val); +} + +/** + * inv_mpu_aux_read() - read register function for i2c auxiliary bus + * @st: driver internal state. + * @addr: chip i2c Address + * @reg: chip register address + * @val: buffer for storing read bytes + * @size: number of bytes to read + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t *val, size_t size) +{ + unsigned int status; + int ret; + + if (size > 0x0F) + return -EINVAL; + + /* setup i2c SLV0 control: i2c addr, register, enable + size */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), + INV_MPU6050_BIT_I2C_SLV_RNW | addr); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | size); + if (ret) + return ret; + + /* do i2c xfer */ + ret = inv_mpu_i2c_master_xfer(st); + if (ret) + goto error_disable_i2c; + + /* disable i2c slave */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + if (ret) + goto error_disable_i2c; + + /* check i2c status */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) + return -EIO; + + /* read data in registers */ + return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, + val, size); + +error_disable_i2c: + regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + return ret; +} + +/** + * inv_mpu_aux_write() - write register function for i2c auxiliary bus + * @st: driver internal state. + * @addr: chip i2c Address + * @reg: chip register address + * @val: 1 byte value to write + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t val) +{ + unsigned int status; + int ret; + + /* setup i2c SLV0 control: i2c addr, register, value, enable + size */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), addr); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(0), val); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | 1); + if (ret) + return ret; + + /* do i2c xfer */ + ret = inv_mpu_i2c_master_xfer(st); + if (ret) + goto error_disable_i2c; + + /* disable i2c slave */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + if (ret) + goto error_disable_i2c; + + /* check i2c status */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) + return -EIO; + + return 0; + +error_disable_i2c: + regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + return ret; +} diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h new file mode 100644 index 000000000000..b66997545762 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#ifndef INV_MPU_AUX_H_ +#define INV_MPU_AUX_H_ + +#include "inv_mpu_iio.h" + +int inv_mpu_aux_init(const struct inv_mpu6050_state *st); + +int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t *val, size_t size); + +int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t val); + +#endif /* INV_MPU_AUX_H_ */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index b17f060b52fc..354030e9bed5 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include "inv_mpu_iio.h" +#include "inv_mpu_magn.h" /* * this is the gyro scale translated from dynamic range plus/minus @@ -103,6 +104,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = { .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE), .gyro_fifo_enable = false, .accl_fifo_enable = false, + .magn_fifo_enable = false, .accl_fs = INV_MPU6050_FS_02G, .user_ctrl = 0, }; @@ -332,6 +334,11 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) */ st->chip_period = NSEC_PER_MSEC; + /* magn chip init, noop if not present in the chip */ + result = inv_mpu_magn_probe(st); + if (result) + goto error_power_off; + return inv_mpu6050_set_power_itg(st, false); error_power_off: @@ -411,6 +418,9 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, ret = inv_mpu6050_sensor_show(st, st->reg->temperature, IIO_MOD_X, val); break; + case IIO_MAGN: + ret = inv_mpu_magn_read(st, chan->channel2, val); + break; default: ret = -EINVAL; break; @@ -469,6 +479,8 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, *val2 = INV_MPU6050_TEMP_SCALE; return IIO_VAL_INT_PLUS_MICRO; + case IIO_MAGN: + return inv_mpu_magn_get_scale(st, chan, val, val2); default: |