From d4032cce4538a9d9e8e21e62608a111712842b77 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:55 +0500 Subject: dt-bindings: vendor-prefixes: Document liteon vendor prefix Add vendor prefix for LITE-ON Technology Corp. Signed-off-by: Nikita Travkin Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211006163058.145842-1-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 42b529a8e5db..be42b4b2fcd5 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -653,6 +653,8 @@ patternProperties: description: Linux-specific binding "^linx,.*": description: Linx Technologies + "^liteon,.*": + description: LITE-ON Technology Corp. "^litex,.*": description: LiteX SoC builder "^lltc,.*": -- cgit v1.2.3 From ec39f1ead4e4c0a9e66c066d6e566fe1362d1c6e Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:56 +0500 Subject: dt-bindings: iio: light: Document ltr501 light sensor bindings Add devicetree bindings for ltr501, ltr559 and ltr301. Signed-off-by: Nikita Travkin Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211006163058.145842-2-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/liteon,ltr501.yaml | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml diff --git a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml new file mode 100644 index 000000000000..db0407bc9209 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/liteon,ltr501.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LiteON LTR501 I2C Proximity and Light sensor + +maintainers: + - Nikita Travkin + +properties: + compatible: + enum: + - liteon,ltr501 + - liteon,ltr559 + - liteon,ltr301 + + reg: + maxItems: 1 + + vdd-supply: true + vddio-supply: true + + interrupts: + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - reg + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@23 { + compatible = "liteon,ltr559"; + reg = <0x23>; + vdd-supply = <&pm8916_l17>; + vddio-supply = <&pm8916_l6>; + + interrupt-parent = <&msmgpio>; + interrupts = <115 IRQ_TYPE_EDGE_FALLING>; + }; + }; -- cgit v1.2.3 From f6ec898c9ab9ec3c7dad5cc41fc3f873cdb7f502 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:57 +0500 Subject: iio: light: ltr501: Add rudimentary regulator support On some platforms the supply regulators must be enabled before the sensor can work. Add vdd and vddio regulators for the sensor and IO bus power respectively. Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20211006163058.145842-3-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 1830221da48d..57851c8ef1c6 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -151,6 +152,7 @@ struct ltr501_chip_info { struct ltr501_data { struct i2c_client *client; + struct regulator_bulk_data regulators[2]; struct mutex lock_als, lock_ps; const struct ltr501_chip_info *chip_info; u8 als_contr, ps_contr; @@ -1379,6 +1381,13 @@ static const struct regmap_config ltr501_regmap_config = { .volatile_reg = ltr501_is_volatile_reg, }; +static void ltr501_disable_regulators(void *d) +{ + struct ltr501_data *data = d; + + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); +} + static int ltr501_powerdown(struct ltr501_data *data) { return ltr501_write_contr(data, data->als_contr & @@ -1423,6 +1432,25 @@ static int ltr501_probe(struct i2c_client *client, mutex_init(&data->lock_als); mutex_init(&data->lock_ps); + data->regulators[0].supply = "vdd"; + data->regulators[1].supply = "vddio"; + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, + ltr501_disable_regulators, data); + if (ret) + return ret; + data->reg_it = devm_regmap_field_alloc(&client->dev, regmap, reg_field_it); if (IS_ERR(data->reg_it)) { -- cgit v1.2.3 From 26fa68c1d7a1d6504d6cc158db015acc1a8bc0fd Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:58 +0500 Subject: iio: light: ltr501: Add of_device_id table Add of_device_id table so the driver can be used on DT platforms without relying on i2c_device_id fallback. (So DT schema validation is possible) Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20211006163058.145842-4-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 57851c8ef1c6..7e51aaac0bf8 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1609,9 +1609,18 @@ static const struct i2c_device_id ltr501_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltr501_id); +static const struct of_device_id ltr501_of_match[] = { + { .compatible = "liteon,ltr501", }, + { .compatible = "liteon,ltr559", }, + { .compatible = "liteon,ltr301", }, + {} +}; +MODULE_DEVICE_TABLE(of, ltr501_of_match); + static struct i2c_driver ltr501_driver = { .driver = { .name = LTR501_DRV_NAME, + .of_match_table = ltr501_of_match, .pm = <r501_pm_ops, .acpi_match_table = ACPI_PTR(ltr_acpi_match), }, -- cgit v1.2.3 From 0be844470eb929dd2a1388c6dc6d7d6ea2edaf2e Mon Sep 17 00:00:00 2001 From: André Gustavo Nakagomi Lopez Date: Fri, 15 Oct 2021 15:47:12 -0300 Subject: iio: adc: lpc18xx_adc: Convert probe to device managed version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove function and the goto sections are not necessary if devm functions are used. Convert device register to devm version. Add hook functions to release device resources, and use them inside probe with devm_add_action, which will release resources on driver detach. To maintain the order of which device resources were released/reseted, register the hook functions as soon as resources are obtained/initialized. Since devres actions are called on driver detach, the remove function and the error-handling goto sections are no longer necessary. Signed-off-by: André Gustavo Nakagomi Lopez Link: https://lore.kernel.org/r/YWnMsGlc/I35gYvy@Andryuu.br Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc18xx_adc.c | 61 ++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index 3566990ae87d..7d50107427ac 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -115,6 +115,23 @@ static const struct iio_info lpc18xx_adc_info = { .read_raw = lpc18xx_adc_read_raw, }; +static void lpc18xx_clear_cr_reg(void *data) +{ + struct lpc18xx_adc *adc = data; + + writel(0, adc->base + LPC18XX_ADC_CR); +} + +static void lpc18xx_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + +static void lpc18xx_regulator_disable(void *vref) +{ + regulator_disable(vref); +} + static int lpc18xx_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -127,7 +144,6 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) if (!indio_dev) return -ENOMEM; - platform_set_drvdata(pdev, indio_dev); adc = iio_priv(indio_dev); adc->dev = &pdev->dev; mutex_init(&adc->lock); @@ -163,44 +179,30 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) return ret; } + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_regulator_disable, adc->vref); + if (ret) + return ret; + ret = clk_prepare_enable(adc->clk); if (ret) { dev_err(&pdev->dev, "unable to enable clock\n"); - goto dis_reg; + return ret; } + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clk_disable, + adc->clk); + if (ret) + return ret; + adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) | LPC18XX_ADC_CR_PDN; writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "unable to register device\n"); - goto dis_clk; - } - - return 0; - -dis_clk: - writel(0, adc->base + LPC18XX_ADC_CR); - clk_disable_unprepare(adc->clk); -dis_reg: - regulator_disable(adc->vref); - return ret; -} - -static int lpc18xx_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct lpc18xx_adc *adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - writel(0, adc->base + LPC18XX_ADC_CR); - clk_disable_unprepare(adc->clk); - regulator_disable(adc->vref); + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clear_cr_reg, adc); + if (ret) + return ret; - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static const struct of_device_id lpc18xx_adc_match[] = { @@ -211,7 +213,6 @@ MODULE_DEVICE_TABLE(of, lpc18xx_adc_match); static struct platform_driver lpc18xx_adc_driver = { .probe = lpc18xx_adc_probe, - .remove = lpc18xx_adc_remove, .driver = { .name = "lpc18xx-adc", .of_match_table = lpc18xx_adc_match, -- cgit v1.2.3 From eeb82b54bb03a0899505996e8da7d67b4ff4aec2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Oct 2021 16:32:54 +0100 Subject: iio: buffer: Fix uninitialized variable ret When !iio_buffer_space_available(rb) is true and signal_pending(current) is false the end of the do-while loop is reached and the uninitialized variable ret is zero checked. Fix this by initializing variable ret to zero. Addresses-Coverity: ("Uninitialized scalar variable") Fixes: 9eeee3b0bf19 ("iio: Add output buffer support") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20211015153254.33783-1-colin.king@canonical.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index b884d78657cb..c97fb8462395 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -179,7 +179,7 @@ static ssize_t iio_buffer_write(struct file *filp, const char __user *buf, struct iio_buffer *rb = ib->buffer; struct iio_dev *indio_dev = ib->indio_dev; DEFINE_WAIT_FUNC(wait, woken_wake_function); - int ret; + int ret = 0; size_t written; if (!indio_dev->info) -- cgit v1.2.3 From 08e9734afc7f4bf01d3011215405d77c98613827 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:49 +0800 Subject: iio: adc: ab8500-gpadc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ab8500-gpadc.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 7b5212ba5501..4c46a201d4ef 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -1103,17 +1103,15 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) return ret; gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); - if (gpadc->irq_sw < 0) { - dev_err(dev, "failed to get platform sw_conv_end irq\n"); - return gpadc->irq_sw; - } + if (gpadc->irq_sw < 0) + return dev_err_probe(dev, gpadc->irq_sw, + "failed to get platform sw_conv_end irq\n"); if (is_ab8500(gpadc->ab8500)) { gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); - if (gpadc->irq_hw < 0) { - dev_err(dev, "failed to get platform hw_conv_end irq\n"); - return gpadc->irq_hw; - } + if (gpadc->irq_hw < 0) + return dev_err_probe(dev, gpadc->irq_hw, + "failed to get platform hw_conv_end irq\n"); } else { gpadc->irq_hw = 0; } @@ -1146,11 +1144,9 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) /* The VTVout LDO used to power the AB8500 GPADC */ gpadc->vddadc = devm_regulator_get(dev, "vddadc"); - if (IS_ERR(gpadc->vddadc)) { - ret = PTR_ERR(gpadc->vddadc); - dev_err(dev, "failed to get vddadc\n"); - return ret; - } + if (IS_ERR(gpadc->vddadc)) + return dev_err_probe(dev, PTR_ERR(gpadc->vddadc), + "failed to get vddadc\n"); ret = regulator_enable(gpadc->vddadc); if (ret) { -- cgit v1.2.3 From 1c17abbc953e287a27f660d08822051013e509ce Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:50 +0800 Subject: iio: adc: imx7d_adc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 4969a5f941e3..092f8d296527 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -493,22 +493,16 @@ static int imx7d_adc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return irq; + return dev_err_probe(dev, irq, "Failed getting irq\n"); info->clk = devm_clk_get(dev, "adc"); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_err(dev, "Failed getting clock, err = %d\n", ret); - return ret; - } + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n"); info->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(info->vref)) { - ret = PTR_ERR(info->vref); - dev_err(dev, - "Failed getting reference voltage, err = %d\n", ret); - return ret; - } + if (IS_ERR(info->vref)) + return dev_err_probe(dev, PTR_ERR(info->vref), + "Failed getting reference voltage\n"); platform_set_drvdata(pdev, indio_dev); -- cgit v1.2.3 From 922f694b582286ce82e1140a522d20a203fddba9 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:51 +0800 Subject: iio: adc: lpc18xx_adc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-3-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc18xx_adc.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index 7d50107427ac..ceefa4d793cf 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -153,19 +153,17 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) return PTR_ERR(adc->base); adc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(adc->clk)) { - dev_err(&pdev->dev, "error getting clock\n"); - return PTR_ERR(adc->clk); - } + if (IS_ERR(adc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk), + "error getting clock\n"); rate = clk_get_rate(adc->clk); clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); adc->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(adc->vref)) { - dev_err(&pdev->dev, "error getting regulator\n"); - return PTR_ERR(adc->vref); - } + if (IS_ERR(adc->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc->vref), + "error getting regulator\n"); indio_dev->name = dev_name(&pdev->dev); indio_dev->info = &lpc18xx_adc_info; -- cgit v1.2.3 From 9444794b58bf25787baf4c4c69e05077b3795547 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:52 +0800 Subject: iio: adc: max1118: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-4-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1118.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 8cec9d949083..a41bc570be21 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -221,10 +221,9 @@ static int max1118_probe(struct spi_device *spi) if (id->driver_data == max1118) { adc->reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(adc->reg)) { - dev_err(&spi->dev, "failed to get vref regulator\n"); - return PTR_ERR(adc->reg); - } + if (IS_ERR(adc->reg)) + return dev_err_probe(&spi->dev, PTR_ERR(adc->reg), + "failed to get vref regulator\n"); ret = regulator_enable(adc->reg); if (ret) return ret; -- cgit v1.2.3 From 070a83ff635cc0d4b895ccf8eefd70aa09cbae14 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:53 +0800 Subject: iio: adc: max1241: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-5-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1241.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index b60f8448f21a..a5afd84af58b 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -148,10 +148,9 @@ static int max1241_probe(struct spi_device *spi) mutex_init(&adc->lock); adc->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(adc->vdd)) { - dev_err(dev, "failed to get vdd regulator\n"); - return PTR_ERR(adc->vdd); - } + if (IS_ERR(adc->vdd)) + return dev_err_probe(dev, PTR_ERR(adc->vdd), + "failed to get vdd regulator\n"); ret = regulator_enable(adc->vdd); if (ret) @@ -164,10 +163,9 @@ static int max1241_probe(struct spi_device *spi) } adc->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(adc->vref)) { - dev_err(dev, "failed to get vref regulator\n"); - return PTR_ERR(adc->vref); - } + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get vref regulator\n"); ret = regulator_enable(adc->vref); if (ret) @@ -182,7 +180,8 @@ static int max1241_probe(struct spi_device *spi) adc->shutdown = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(adc->shutdown)) - return PTR_ERR(adc->shutdown); + return dev_err_probe(dev, PTR_ERR(adc->shutdown), + "cannot get shutdown gpio\n"); if (adc->shutdown) dev_dbg(dev, "shutdown pin passed, low-power mode enabled"); -- cgit v1.2.3 From a5999024b5ba0e6e00a8b95f1d758c69d470b0a3 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:54 +0800 Subject: iio: adc: meson_saradc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Reviewed-by: Martin Blumenstingl Tested-by: Martin Blumenstingl #Meson8b Odroid-C1 Link: https://lore.kernel.org/r/20211008092858.495-6-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 705d5e11a54b..62cc6fb0ef85 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1230,35 +1230,31 @@ static int meson_sar_adc_probe(struct platform_device *pdev) return ret; priv->clkin = devm_clk_get(&pdev->dev, "clkin"); - if (IS_ERR(priv->clkin)) { - dev_err(&pdev->dev, "failed to get clkin\n"); - return PTR_ERR(priv->clkin); - } + if (IS_ERR(priv->clkin)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clkin), + "failed to get clkin\n"); priv->core_clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(priv->core_clk)) { - dev_err(&pdev->dev, "failed to get core clk\n"); - return PTR_ERR(priv->core_clk); - } + if (IS_ERR(priv->core_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->core_clk), + "failed to get core clk\n"); priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk"); if (IS_ERR(priv->adc_clk)) { - if (PTR_ERR(priv->adc_clk) == -ENOENT) { + if (PTR_ERR(priv->adc_clk) == -ENOENT) priv->adc_clk = NULL; - } else { - dev_err(&pdev->dev, "failed to get adc clk\n"); - return PTR_ERR(priv->adc_clk); - } + else + return dev_err_probe(&pdev->dev, PTR_ERR(priv->adc_clk), + "failed to get adc clk\n"); } priv->adc_sel_clk = devm_clk_get(&pdev->dev, "adc_sel"); if (IS_ERR(priv->adc_sel_clk)) { - if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) { + if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) priv->adc_sel_clk = NULL; - } else { - dev_err(&pdev->dev, "failed to get adc_sel clk\n"); - return PTR_ERR(priv->adc_sel_clk); - } + else + return dev_err_probe(&pdev->dev, PTR_ERR(priv->adc_sel_clk), + "failed to get adc_sel clk\n"); } /* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */ @@ -1269,10 +1265,9 @@ static int meson_sar_adc_probe(struct platform_device *pdev) } priv->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(priv->vref)) { - dev_err(&pdev->dev, "failed to get vref regulator\n"); - return PTR_ERR(priv->vref); - } + if (IS_ERR(priv->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->vref), + "failed to get vref regulator\n"); priv->calibscale = MILLION; -- cgit v1.2.3 From 94f08a06685e9ef724d9a55a5192b38cb9e4f71e Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:55 +0800 Subject: iio: adc: qcom-pm8xxx-xoadc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. BTW, change the return value from 'ENXIO' to 'ENODEV', perfer ENODEV which means no such device. Signed-off-by: Cai Huoqing Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20211008092858.495-7-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 0610bf254771..21d7eff645c3 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -910,16 +910,15 @@ static int pm8xxx_xoadc_probe(struct platform_device *pdev) map = dev_get_regmap(dev->parent, NULL); if (!map) { dev_err(dev, "parent regmap unavailable.\n"); - return -ENXIO; + return -ENODEV; } adc->map = map; /* Bring up regulator */ adc->vref = devm_regulator_get(dev, "xoadc-ref"); - if (IS_ERR(adc->vref)) { - dev_err(dev, "failed to get XOADC VREF regulator\n"); - return PTR_ERR(adc->vref); - } + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get XOADC VREF regulator\n"); ret = regulator_enable(adc->vref); if (ret) { dev_err(dev, "failed to enable XOADC VREF regulator\n"); -- cgit v1.2.3 From 8f46a93bdc730dd2022a83ccde66c36a6599ecd6 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:56 +0800 Subject: iio: adc: rockchip_saradc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-8-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rockchip_saradc.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index a56a0d7337ca..14b8df4ca9c8 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -360,7 +360,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev) if (IS_ERR(info->reset)) { ret = PTR_ERR(info->reset); if (ret != -ENOENT) - return ret; + return dev_err_probe(&pdev->dev, ret, + "failed to get saradc-apb\n"); dev_dbg(&pdev->dev, "no reset control found\n"); info->reset = NULL; @@ -370,7 +371,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return irq; + return dev_err_probe(&pdev->dev, irq, "failed to get irq\n"); ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr, 0, dev_name(&pdev->dev), info); @@ -380,23 +381,19 @@ static int rockchip_saradc_probe(struct platform_device *pdev) } info->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); - if (IS_ERR(info->pclk)) { - dev_err(&pdev->dev, "failed to get pclk\n"); - return PTR_ERR(info->pclk); - } + if (IS_ERR(info->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->pclk), + "failed to get pclk\n"); info->clk = devm_clk_get(&pdev->dev, "saradc"); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed to get adc clock\n"); - return PTR_ERR(info->clk); - } + if (IS_ERR(info->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), + "failed to get adc clock\n"); info->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(info->vref)) { - dev_err(&pdev->dev, "failed to get regulator, %ld\n", - PTR_ERR(info->vref)); - return PTR_ERR(info->vref); - } + if (IS_ERR(info->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->vref), + "failed to get regulator\n"); if (info->reset) rockchip_saradc_reset_controller(info->reset); -- cgit v1.2.3 From 42351035dc1530363b323b4583307e866dc4464e Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:57 +0800 Subject: iio: adc: ti-ads7950: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-9-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index a2b83f0bd526..a7efa3eada2c 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -600,8 +600,8 @@ static int ti_ads7950_probe(struct spi_device *spi) st->reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(st->reg)) { - dev_err(&spi->dev, "Failed to get regulator \"vref\"\n"); - ret = PTR_ERR(st->reg); + ret = dev_err_probe(&spi->dev, PTR_ERR(st->reg), + "Failed to get regulator \"vref\"\n"); goto error_destroy_mutex; } -- cgit v1.2.3 From 0d31d91e614505803a0788b92f9f1a83178d8a9a Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:26:52 +0800 Subject: iio: light: cm3605: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20211008092656.421-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm3605.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 4c83953672be..3e7fb16ab1f6 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -159,6 +159,7 @@ static int cm3605_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; enum iio_chan_type ch_type; u32 rset; + int irq; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*cm3605)); @@ -195,12 +196,9 @@ static int cm3605_probe(struct platform_device *pdev) cm3605->aout = devm_iio_channel_get(dev, "aout"); if (IS_ERR(cm3605->aout)) { - if (PTR_ERR(cm3605->aout) == -ENODEV) { - dev_err(dev, "no ADC, deferring...\n"); - return -EPROBE_DEFER; - } - dev_err(dev, "failed to get AOUT ADC channel\n"); - return PTR_ERR(cm3605->aout); + ret = PTR_ERR(cm3605->aout); + ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret; + return dev_err_probe(dev, ret, "failed to get AOUT ADC channel\n"); } ret = iio_get_channel_type(cm3605->aout, &ch_type); if (ret < 0) @@ -211,10 +209,10 @@ static int cm3605_probe(struct platform_device *pdev) } cm3605->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(cm3605->vdd)) { - dev_err(dev, "failed to get VDD regulator\n"); - return PTR_ERR(cm3605->vdd); - } + if (IS_ERR(cm3605->vdd)) + return dev_err_probe(dev, PTR_ERR(cm3605->vdd), + "failed to get VDD regulator\n"); + ret = regulator_enable(cm3605->vdd); if (ret) { dev_err(dev, "failed to enable VDD regulator\n"); @@ -223,13 +221,16 @@ static int cm3605_probe(struct platform_device *pdev) cm3605->aset = devm_gpiod_get(dev, "aset", GPIOD_OUT_HIGH); if (IS_ERR(cm3605->aset)) { - dev_err(dev, "no ASET GPIO\n"); - ret = PTR_ERR(cm3605->aset); + ret = dev_err_probe(dev, PTR_ERR(cm3605->aset), "no ASET GPIO\n"); goto out_disable_vdd; } - ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), - cm3605_prox_irq, NULL, 0, "cm3605", indio_dev); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return dev_err_probe(dev, irq, "failed to get irq\n"); + + ret = devm_request_threaded_irq(dev, irq, cm3605_prox_irq, + NULL, 0, "cm3605", indio_dev); if (ret) { dev_err(dev, "unable to request IRQ\n"); goto out_disable_aset; -- cgit v1.2.3 From 77b91b1cbc26f2d7eebe7294035325076de6e1cc Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:26:54 +0800 Subject: iio: light: gp2ap002: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092656.421-3-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index f960be7d4001..c6d1d88d3775 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -503,12 +503,9 @@ static int gp2ap002_probe(struct i2c_client *client, if (!gp2ap002->is_gp2ap002s00f) { gp2ap002->alsout = devm_iio_channel_get(dev, "alsout"); if (IS_ERR(gp2ap002->alsout)) { - if (PTR_ERR(gp2ap002->alsout) == -ENODEV) { - dev_err(dev, "no ADC, deferring...\n"); - return -EPROBE_DEFER; - } - dev_err(dev, "failed to get ALSOUT ADC channel\n"); - return PTR_ERR(gp2ap002->alsout); + ret = PTR_ERR(gp2ap002->alsout); + ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret; + return dev_err_probe(dev, ret, "failed to get ALSOUT ADC channel\n"); } ret = iio_get_channel_type(gp2ap002->alsout, &ch_type); if (ret < 0) @@ -521,15 +518,14 @@ static int gp2ap002_probe(struct i2c_client *client, } gp2ap002->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(gp2ap002->vdd)) { - dev_err(dev, "failed to get VDD regulator\n"); - return PTR_ERR(gp2ap002->vdd); - } + if (IS_ERR(gp2ap002->vdd)) + return dev_err_probe(dev, PTR_ERR(gp2ap002->vdd), + "failed to get VDD regulator\n"); + gp2ap002->vio = devm_regulator_get(dev, "vio"); - if (IS_ERR(gp2ap002->vio)) { - dev_err(dev, "failed to get VIO regulator\n"); - return PTR_ERR(gp2ap002->vio); - } + if (IS_ERR(gp2ap002->vio)) + return dev_err_probe(dev, PTR_ERR(gp2ap002->vio), + "failed to get VIO regulator\n"); /* Operating voltage 2.4V .. 3.6V according to datasheet */ ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000); -- cgit v1.2.3 From 94be878c882d8d784ff44c639bf55f3b029f85af Mon Sep 17 00:00:00 2001 From: Teng Qi Date: Mon, 11 Oct 2021 19:40:03 +0800 Subject: iio: imu: st_lsm6dsx: Avoid potential array overflow in st_lsm6dsx_set_odr() The length of hw->settings->odr_table is 2 and ref_sensor->id is an enum variable whose value is between 0 and 5. However, the value ST_LSM6DSX_ID_MAX (i.e. 5) is not caught properly in switch (sensor->id) { If ref_sensor->id is ST_LSM6DSX_ID_MAX, an array overflow will ocurrs in function st_lsm6dsx_check_odr(): odr_table = &sensor->hw->settings->odr_table[sensor->id]; and in function st_lsm6dsx_set_odr(): reg = &hw->settings->odr_table[ref_sensor->id].reg; To avoid this array overflow, handle ST_LSM6DSX_ID_GYRO explicitly and return -EINVAL for the default case. The enum value ST_LSM6DSX_ID_MAX is only present as an easy way to check the limit and as such is never used, however this is not locally obvious. Reported-by: TOTE Robot Signed-off-by: Teng Qi Acked-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/20211011114003.976221-1-starmiku1207184332@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 0f54df85134a..f2cbbc756459 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1281,6 +1281,8 @@ st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr) int err; switch (sensor->id) { + case ST_LSM6DSX_ID_GYRO: + break; case ST_LSM6DSX_ID_EXT0: case ST_LSM6DSX_ID_EXT1: case ST_LSM6DSX_ID_EXT2: @@ -1306,8 +1308,8 @@ st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr) } break; } - default: - break; + default: /* should never occur */ + return -EINVAL; } if (req_odr > 0) { -- cgit v1.2.3 From dd4efd05c565529dad8722301d69e0c19a3292e9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 17 Oct 2021 14:15:51 +0200 Subject: iio: xilinx-xadc: Remove `irq` field from state struct Since commit 2a9685d1a3b7 ("iio: adc: xilinx: use more devres helpers and remove remove()") the `irq` field from XADC driver state struct is only used in the `probe()` function. Use the local `irq` variable throughout the `probe()` function and remove the now unused field from the state struct. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211017121551.24063-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 5 ++--- drivers/iio/adc/xilinx-xadc.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 198d2916266d..83bea5ef765d 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1332,7 +1332,6 @@ static int xadc_probe(struct platform_device *pdev) xadc = iio_priv(indio_dev); xadc->ops = id->data; - xadc->irq = irq; init_completion(&xadc->completion); mutex_init(&xadc->mutex); spin_lock_init(&xadc->lock); @@ -1397,7 +1396,7 @@ static int xadc_probe(struct platform_device *pdev) } } - ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0, + ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, 0, dev_name(dev), indio_dev); if (ret) return ret; @@ -1407,7 +1406,7 @@ static int xadc_probe(struct platform_device *pdev) if (ret) return ret; - ret = xadc->ops->setup(pdev, indio_dev, xadc->irq); + ret = xadc->ops->setup(pdev, indio_dev, irq); if (ret) return ret; diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h index 8b80195725e9..7d78ce698967 100644 --- a/drivers/iio/adc/xilinx-xadc.h +++ b/drivers/iio/adc/xilinx-xadc.h @@ -67,7 +67,6 @@ struct xadc { spinlock_t lock; struct completion completion; - int irq; }; enum xadc_type { -- cgit v1.2.3 From 1ea3615b6168eaaf07445c8d32a4c6bc965d9579 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 17 Oct 2021 17:03:03 +0100 Subject: iio: accel: sca3000: Use sign_extend32() instead of opencoding sign extension. Whilst nice to get rid of this non obvious code, this also clears a static checker warning: drivers/iio/accel/sca3000.c:734 sca3000_read_raw() warn: no-op. '((*val) << 19) >> 19' Reported-by: Dan Carpenter Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211017160303.72441-1-jic23@kernel.org --- drivers/iio/accel/sca3000.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index cb753a43533c..c6b75308148a 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -731,8 +731,7 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, return ret; } *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF; - *val = ((*val) << (sizeof(*val) * 8 - 13)) >> - (sizeof(*val) * 8 - 13); + *val = sign_extend32(*val, 12); } else { /* get the temperature when available */ ret = sca3000_read_data_short(st, -- cgit v1.2.3 From 664b9879f56ef97d2ac98ec66e846814669b7e4a Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:22 +0200 Subject: dt-bindings: iio: stm32-adc: add generic channel binding Add ADC generic channel binding. This binding should be used as an alternate to legacy channel properties whenever possible. ADC generic channel binding allows to identify supported internal channels through the following reserved label names: "vddcore", "vrefint" and "vbat". This binding also allows to set a different sampling time for each channel. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211014131228.4692-2-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/st,stm32-adc.yaml | 100 +++++++++++++++++++-- 1 file changed, 93 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml index a58334c3bb76..2083e1723970 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -222,6 +222,12 @@ patternProperties: '#io-channel-cells': const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + interrupts: description: | IRQ Line for the ADC instance. Valid values are: @@ -256,6 +262,7 @@ patternProperties: - 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7 and stm32mp1. $ref: /schemas/types.yaml#/definitions/uint32-array + deprecated: true st,adc-diff-channels: description: | @@ -265,7 +272,9 @@ patternProperties: , ,... vinp and vinn are numbered from 0 to 19. Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is - required. Both properties can be used together. Some channels can be + required if no adc generic channel is defined. These legacy channel + properties are exclusive with adc generic channel bindings. + Both properties can be used together. Some channels can be used as single-ended and some other ones as differential (mixed). But channels can't be configured both as single-ended and differential. $ref: /schemas/types.yaml#/definitions/uint32-matrix @@ -279,6 +288,7 @@ patternProperties: "vinn" indicates negative input number minimum: 0 maximum: 19 + deprecated: true st,min-sample-time-nsecs: description: @@ -289,6 +299,42 @@ patternProperties: list, to set sample time resp. for all channels, or independently for each channel. $ref: /schemas/types.yaml#/definitions/uint32-array + deprecated: true + + patternProperties: + "^channel@([0-9]|1[0-9])$": + type: object + $ref: "adc.yaml" + description: Represents the external channels which are connected to the ADC. + + properties: + reg: + items: + minimum: 0 + maximum: 19 + + label: + description: | + Unique name to identify which channel this is. + Reserved label names "vddcore", "vrefint" and "vbat" + are used to identify internal channels with matching names. + + diff-channels: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + minimum: 0 + maximum: 19 + + st,min-sample-time-ns: + description: | + Minimum sampling time in nanoseconds. Depending on hardware (board) + e.g. high/low analog input source impedance, fine tune of ADC + sampling time may be recommended. + + required: + - reg + + additionalProperties: false allOf: - if: @@ -369,12 +415,6 @@ patternProperties: additionalProperties: false - anyOf: - - required: - - st,adc-channels - - required: - - st,adc-diff-channels - required: - compatible - reg @@ -451,4 +491,50 @@ examples: // other adc child node follow... }; + - | + // Example 3: with stm32mp157c to setup ADC2 with: + // - internal channels 13, 14, 15. + #include + #include + adc122: adc@48003000 { + compatible = "st,stm32mp1-adc-core"; + reg = <0x48003000 0x400>; + interrupts = , + ; + clocks = <&rcc ADC12>, <&rcc ADC12_K>; + clock-names = "bus", "adc"; + booster-supply = <&booster>; + vdd-supply = <&vdd>; + vdda-supply = <&vdda>; + vref-supply = <&vref>; + st,syscfg = <&syscfg>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + adc@100 { + compatible = "st,stm32mp1-adc"; + #io-channel-cells = <1>; + reg = <0x100>; + interrupts = <1>; + #address-cells = <1>; + #size-cells = <0>; + channel@13 { + reg = <13>; + label = "vrefint"; + st,min-sample-time-ns = <9000>; + }; + channel@14 { + reg = <14>; + label = "vddcore"; + st,min-sample-time-ns = <9000>; + }; + channel@15 { + reg = <15>; + label = "vbat"; + st,min-sample-time-ns = <9000>; + }; + }; + }; + ... -- cgit v1.2.3 From 6cd4ed8eb89348fa5cf0b87bb420fe39531c9b59 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:23 +0200 Subject: dt-bindings: iio: stm32-adc: add nvmem support for vrefint internal channel Add support of nvmem. This allows to retrieve calibration data from OTP for vrefint internal channel. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211014131228.4692-3-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml index 2083e1723970..ec0450d111a9 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -301,6 +301,14 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/uint32-array deprecated: true + nvmem-cells: + items: + - description: Phandle to the calibration vrefint data provided by otp + + nvmem-cell-names: + items: + - const: vrefint + patternProperties: "^channel@([0-9]|1[0-9])$": type: object -- cgit v1.2.3 From 45571a361c09fe89ebdefe533b3800d89d5934e3 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:24 +0200 Subject: iio: adc: stm32-adc: split channel init into several routines Split stm32_adc_chan_of_init channel initialization function into several routines to increase readability and prepare channel generic binding handling. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-4-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 131 +++++++++++++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 44 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5088de835bb1..2f137d14f141 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -194,6 +194,7 @@ struct stm32_adc_cfg { * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) * @cal: optional calibration data on some devices * @chan_name: channel name array + * @num_diff: number of differential channels */ struct stm32_adc { struct stm32_adc_common *common; @@ -217,6 +218,7 @@ struct stm32_adc { u32 smpr_val[2]; struct stm32_adc_calib cal; char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; + u32 num_diff; }; struct stm32_adc_diff_channel { @@ -1706,17 +1708,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, } } -static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) +static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc) { struct device_node *node = indio_dev->dev.of_node; - struct stm32_adc *adc = iio_priv(indio_dev); const struct stm32_adc_info *adc_info = adc->cfg->adc_info; - struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; - struct property *prop; - const __be32 *cur; - struct iio_chan_spec *channels; - int scan_index = 0, num_channels = 0, num_diff = 0, ret, i; - u32 val, smp = 0; + int num_channels = 0, ret; ret = of_property_count_u32_elems(node, "st,adc-channels"); if (ret > adc_info->max_channels) { @@ -1727,41 +1723,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) } ret = of_property_count_elems_of_size(node, "st,adc-diff-channels", - sizeof(*diff)); + sizeof(struct stm32_adc_diff_channel)); if (ret > adc_info->max_channels) { dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); return -EINVAL; } else if (ret > 0) { - int size = ret * sizeof(*diff) / sizeof(u32); - - num_diff = ret; + adc->num_diff = ret; num_channels += ret; + } + + return num_channels; +} + +static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, + struct stm32_adc *adc, + struct iio_chan_spec *channels) +{ + struct device_node *node = indio_dev->dev.of_node; + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; + u32 num_diff = adc->num_diff; + int size = num_diff * sizeof(*diff) / sizeof(u32); + int scan_index = 0, val, ret, i; + struct property *prop; + const __be32 *cur; + + if (num_diff) { ret = of_property_read_u32_array(node, "st,adc-diff-channels", (u32 *)diff, size); - if (ret) + if (ret) { + dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret); return ret; - } + } - if (!num_channels) { - dev_err(&indio_dev->dev, "No channels configured\n"); - return -ENODATA; - } + for (i = 0; i < num_diff; i++) { + if (diff[i].vinp >= adc_info->max_channels || + diff[i].vinn >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", + diff[i].vinp, diff[i].vinn); + return -EINVAL; + } - /* Optional sample time is provided either for each, or all channels */ - ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); - if (ret > 1 && ret != num_channels) { - dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); - return -EINVAL; + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + diff[i].vinp, diff[i].vinn, + scan_index, true); + scan_index++; + } } - if (timestamping) - num_channels++; - - channels = devm_kcalloc(&indio_dev->dev, num_channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); - if (!channels) - return -ENOMEM; - of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) { if (val >= adc_info->max_channels) { dev_err(&indio_dev->dev, "Invalid channel %d\n", val); @@ -1771,8 +1780,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) /* Channel can't be configured both as single-ended & diff */ for (i = 0; i < num_diff; i++) { if (val == diff[i].vinp) { - dev_err(&indio_dev->dev, - "channel %d miss-configured\n", val); + dev_err(&indio_dev->dev, "channel %d misconfigured\n", val); return -EINVAL; } } @@ -1781,19 +1789,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) scan_index++; } - for (i = 0; i < num_diff; i++) { - if (diff[i].vinp >= adc_info->max_channels || - diff[i].vinn >= adc_info->max_channels) { - dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", - diff[i].vinp, diff[i].vinn); - return -EINVAL; - } - stm32_adc_chan_init_one(indio_dev, &channels[scan_index], - diff[i].vinp, diff[i].vinn, scan_index, - true); - scan_index++; + return scan_index; +} + +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) +{ + struct device_node *node = indio_dev->dev.of_node; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct iio_chan_spec *channels; + int scan_index = 0, num_channels = 0, ret, i; + u32 smp = 0; + + ret = stm32_adc_get_legacy_chan_count(indio_dev, adc); + if (ret < 0) + return ret; + num_channels = ret; + + if (!num_channels) { + dev_err(&indio_dev->dev, "No channels configured\n"); + return -ENODATA; } + if (num_channels > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n", + num_channels, adc_info->max_channels); + return -EINVAL; + } + + /* Optional sample time is provided either for each, or all channels */ + ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); + if (ret > 1 && ret != num_channels) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); + return -EINVAL; + } + + if (timestamping) + num_channels++; + + channels = devm_kcalloc(&indio_dev->dev, num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels); + if (ret < 0) + return ret; + scan_index = ret; + for (i = 0; i < scan_index; i++) { /* * Using of_property_read_u32_index(), smp value will only be -- cgit v1.2.3 From 95bc818404b2c1a61611365ecf434a52f01ee97d Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:25 +0200 Subject: iio: adc: stm32-adc: add support of generic channels binding Add support of generic IIO channel binding: ./devicetree/bindings/iio/adc/adc.yaml Keep support of st,adc-channels and st,adc-diff-channels for backward compatibility. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-5-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 95 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 2f137d14f141..85d09cbd41ae 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -35,7 +35,7 @@ #define STM32H7_BOOST_CLKRATE 20000000UL #define STM32_ADC_CH_MAX 20 /* max number of channels */ -#define STM32_ADC_CH_SZ 10 /* max channel name size */ +#define STM32_ADC_CH_SZ 16 /* max channel name size */ #define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ #define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */ #define STM32_ADC_TIMEOUT_US 100000 @@ -1792,6 +1792,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, return scan_index; } +static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, + struct stm32_adc *adc, + struct iio_chan_spec *channels) +{ + struct device_node *node = indio_dev->dev.of_node; + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct device_node *child; + const char *name; + int val, scan_index = 0, ret; + bool differential; + u32 vin[2]; + + for_each_available_child_of_node(node, child) { + ret = of_property_read_u32(child, "reg", &val); + if (ret) { + dev_err(&indio_dev->dev, "Missing channel index %d\n", ret); + goto err; + } + + ret = of_property_read_string(child, "label", &name); + /* label is optional */ + if (!ret) { + if (strlen(name) >= STM32_ADC_CH_SZ) { + dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n", + name, STM32_ADC_CH_SZ); + return -EINVAL; + } + strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); + } else if (ret != -EINVAL) { + dev_err(&indio_dev->dev, "Invalid label %d\n", ret); + goto err; + } + + if (val >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel %d\n", val); + ret = -EINVAL; + goto err; + } + + differential = false; + ret = of_property_read_u32_array(child, "diff-channels", vin, 2); + /* diff-channels is optional */ + if (!ret) { + differential = true; + if (vin[0] != val || vin[1] >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", + vin[0], vin[1]); + goto err; + } + } else if (ret != -EINVAL) { + dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret); + goto err; + } + + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, + vin[1], scan_index, differential); + scan_index++; + } + + return scan_index; + +err: + of_node_put(child); + + return ret; +} + static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) { struct device_node *node = indio_dev->dev.of_node; @@ -1800,15 +1867,22 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) struct iio_chan_spec *channels; int scan_index = 0, num_channels = 0, ret, i; u32 smp = 0; + bool legacy = false; - ret = stm32_adc_get_legacy_chan_count(indio_dev, adc); - if (ret < 0) - return ret; - num_channels = ret; - + num_channels = of_get_available_child_count(node); + /* If no channels have been found, fallback to channels legacy properties. */ if (!num_channels) { - dev_err(&indio_dev->dev, "No channels configured\n"); - return -ENODATA; + legacy = true; + + ret = stm32_adc_get_legacy_chan_count(indio_dev, adc); + if (!ret) { + dev_err(indio_dev->dev.parent, "No channel found\n"); + return -ENODATA; + } else if (ret < 0) { + return ret; + } + + num_channels = ret; } if (num_channels > adc_info->max_channels) { @@ -1832,7 +1906,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) if (!channels) return -ENOMEM; - ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels); + if (legacy) + ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels); + else + ret = stm32_adc_generic_chan_init(indio_dev, adc, channels); if (ret < 0) return ret; scan_index = ret; -- cgit v1.2.3 From aec6e0d8f0fe2e99c6486ebf1979fe2a03890e39 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:26 +0200 Subject: iio: adc: stm32-adc: add support of internal channels Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT. The reserved label name "vddcore", "vrefint" and "vbat" must be used in Device Tree channel node, to enable the corresponding internal channel. Note: This patch does not provide support of internal channels for F4 and H7. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-6-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 1 + drivers/iio/adc/stm32-adc-core.h | 10 +++ drivers/iio/adc/stm32-adc.c | 138 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index c088cb990193..b6e18eb101f7 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -659,6 +659,7 @@ static int stm32_adc_probe(struct platform_device *pdev) priv->cfg = (const struct stm32_adc_priv_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; + spin_lock_init(&priv->common.lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->common.base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index 2322809bfd2f..faedf7a49555 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -102,6 +102,9 @@ #define STM32H7_ADC_CALFACT 0xC4 #define STM32H7_ADC_CALFACT2 0xC8 +/* STM32MP1 - ADC2 instance option register */ +#define STM32MP1_ADC2_OR 0xD0 + /* STM32H7 - common registers for all ADC instances */ #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) @@ -168,23 +171,30 @@ enum stm32h7_adc_dmngt { #define STM32H7_EOC_MST BIT(2) /* STM32H7_ADC_CCR - bit fields */ +#define STM32H7_VBATEN BIT(24) +#define STM32H7_VREFEN BIT(22) #define STM32H7_PRESC_SHIFT 18 #define STM32H7_PRESC_MASK GENMASK(21, 18) #define STM32H7_CKMODE_SHIFT 16 #define STM32H7_CKMODE_MASK GENMASK(17, 16) +/* STM32MP1_ADC2_OR - bit fields */ +#define STM32MP1_VDDCOREEN BIT(0) + /** * struct stm32_adc_common - stm32 ADC driver common data (for all instances) * @base: control registers base cpu addr * @phys_base: control registers base physical addr * @rate: clock rate used for analog circuitry * @vref_mv: vref voltage (mv) + * @lock: spinlock */ struct stm32_adc_common { void __iomem *base; phys_addr_t phys_base; unsigned long rate; int vref_mv; + spinlock_t lock; /* lock for common register */ }; #endif diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 85d09cbd41ae..943ca03f4d31 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -77,6 +77,30 @@ enum stm32_adc_extsel { STM32_EXT20, }; +enum stm32_adc_int_ch { + STM32_ADC_INT_CH_NONE = -1, + STM32_ADC_INT_CH_VDDCORE, + STM32_ADC_INT_CH_VREFINT, + STM32_ADC_INT_CH_VBAT, + STM32_ADC_INT_CH_NB, +}; + +/** + * struct stm32_adc_ic - ADC internal channels + * @name: name of the internal channel + * @idx: internal channel enum index + */ +struct stm32_adc_ic { + const char *name; + u32 idx; +}; + +static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = { + { "vddcore", STM32_ADC_INT_CH_VDDCORE }, + { "vrefint", STM32_ADC_INT_CH_VREFINT }, + { "vbat", STM32_ADC_INT_CH_VBAT }, +}; + /** * struct stm32_adc_trig_info - ADC trigger info * @name: name of the trigger, corresponding to its source @@ -126,6 +150,9 @@ struct stm32_adc_regs { * @res: resolution selection register & bitfield * @smpr: smpr1 & smpr2 registers offset array * @smp_bits: smpr1 & smpr2 index and bitfields + * @or_vdd: option register & vddcore bitfield + * @ccr_vbat: common register & vbat bitfield + * @ccr_vref: common register & vrefint bitfield */ struct stm32_adc_regspec { const u32 dr; @@ -139,6 +166,9 @@ struct stm32_adc_regspec { const struct stm32_adc_regs res; const u32 smpr[2]; const struct stm32_adc_regs *smp_bits; + const struct stm32_adc_regs or_vdd; + const struct stm32_adc_regs ccr_vbat; + const struct stm32_adc_regs ccr_vref; }; struct stm32_adc; @@ -195,6 +225,7 @@ struct stm32_adc_cfg { * @cal: optional calibration data on some devices * @chan_name: channel name array * @num_diff: number of differential channels + * @int_ch: internal channel indexes array */ struct stm32_adc { struct stm32_adc_common *common; @@ -219,6 +250,7 @@ struct stm32_adc { struct stm32_adc_calib cal; char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; u32 num_diff; + int int_ch[STM32_ADC_INT_CH_NB]; }; struct stm32_adc_diff_channel { @@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = { .smp_bits = stm32h7_smp_bits, }; +static const struct stm32_adc_regspec stm32mp1_adc_regspec = { + .dr = STM32H7_ADC_DR, + .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE }, + .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR }, + .sqr = stm32h7_sq, + .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT }, + .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK, + STM32H7_EXTSEL_SHIFT }, + .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, + .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, + .smp_bits = stm32h7_smp_bits, + .or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN }, + .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN }, + .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN }, +}; + /* * STM32 ADC registers access routines * @adc: stm32 adc instance @@ -489,6 +539,14 @@ static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits) spin_unlock_irqrestore(&adc->lock, flags); } +static void stm32_adc_set_bits_common(struct stm32_adc *adc, u32 reg, u32 bits) +{ + spin_lock(&adc->common->lock); + writel_relaxed(readl_relaxed(adc->common->base + reg) | bits, + adc->common->base + reg); + spin_unlock(&adc->common->lock); +} + static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits) { unsigned long flags; @@ -498,6 +556,14 @@ static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits) spin_unlock_irqrestore(&adc->lock, flags); } +static void stm32_adc_clr_bits_common(struct stm32_adc *adc, u32 reg, u32 bits) +{ + spin_lock(&adc->common->lock); + writel_relaxed(readl_relaxed(adc->common->base + reg) & ~bits, + adc->common->base + reg); + spin_unlock(&adc->common->lock); +} + /** * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt * @adc: stm32 adc instance @@ -579,6 +645,60 @@ err_clk_dis: return ret; } +static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 i; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE) + continue; + + switch (i) { + case STM32_ADC_INT_CH_VDDCORE: + dev_dbg(&indio_dev->dev, "Enable VDDCore\n"); + stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg, + adc->cfg->regs->or_vdd.mask); + break; + case STM32_ADC_INT_CH_VREFINT: + dev_dbg(&indio_dev->dev, "Enable VREFInt\n"); + stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vref.reg, + adc->cfg->regs->ccr_vref.mask); + break; + case STM32_ADC_INT_CH_VBAT: + dev_dbg(&indio_dev->dev, "Enable VBAT\n"); + stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, + adc->cfg->regs->ccr_vbat.mask); + break; + } + } +} + +static void stm32_adc_int_ch_disable(struct stm32_adc *adc) +{ + u32 i; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE) + continue; + + switch (i) { + case STM32_ADC_INT_CH_VDDCORE: + stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg, + adc->cfg->regs->or_vdd.mask); + break; + case STM32_ADC_INT_CH_VREFINT: + stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vref.reg, + adc->cfg->regs->ccr_vref.mask); + break; + case STM32_ADC_INT_CH_VBAT: + stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, + adc->cfg->regs->ccr_vbat.mask); + break; + } + } +} + /** * stm32f4_adc_start_conv() - Start conversions for regular channels. * @indio_dev: IIO device instance @@ -947,11 +1067,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev) goto pwr_dwn; calib = ret; + stm32_adc_int_ch_enable(indio_dev); + stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); ret = stm32h7_adc_enable(indio_dev); if (ret) - goto pwr_dwn; + goto ch_disable; /* Either restore or read calibration result for future reference */ if (calib) @@ -967,6 +1089,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev) disable: stm32h7_adc_disable(indio_dev); +ch_disable: + stm32_adc_int_ch_disable(adc); pwr_dwn: stm32h7_adc_enter_pwr_down(adc); @@ -978,6 +1102,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) struct stm32_adc *adc = iio_priv(indio_dev); stm32h7_adc_disable(indio_dev); + stm32_adc_int_ch_disable(adc); stm32h7_adc_enter_pwr_down(adc); } @@ -1800,7 +1925,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, const struct stm32_adc_info *adc_info = adc->cfg->adc_info; struct device_node *child; const char *name; - int val, scan_index = 0, ret; + int val, scan_index = 0, ret, i; bool differential; u32 vin[2]; @@ -1820,6 +1945,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, return -EINVAL; } strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ)) + adc->int_ch[i] = val; + } } else if (ret != -EINVAL) { dev_err(&indio_dev->dev, "Invalid label %d\n", ret); goto err; @@ -1869,6 +1998,9 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) u32 smp = 0; bool legacy = false; + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) + adc->int_ch[i] = STM32_ADC_INT_CH_NONE; + num_channels = of_get_available_child_count(node); /* If no channels have been found, fallback to channels legacy properties. */ if (!num_channels) { @@ -2219,7 +2351,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { }; static const struct stm32_adc_cfg stm32mp1_adc_cfg = { - .regs = &stm32h7_adc_regspec, + .regs = &stm32mp1_adc_regspec, .adc_info = &stm32h7_adc_info, .trigs = stm32h7_adc_trigs, .has_vregready = true, -- cgit v1.2.3 From 0e346b2cfa855d6e6990dbdb220983fbf2bb0b92 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:27 +0200 Subject: iio: adc: stm32-adc: add vrefint calibration support Add support of vrefint calibration. If a channel is labeled as vrefint, get vrefint calibration from non volatile memory for this channel. vrefint channel is exposed as a processed channel returning the actual value of vrefp: vrefp = 3.3 x vrefint_cal / vrefint_data A conversion on vrefint channel allows to update scale factor according to vrefint deviation, compared to vrefint calibration value. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-7-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 74 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 943ca03f4d31..5269bb87d094 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #define STM32_ADC_TIMEOUT_US 100000 #define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000)) #define STM32_ADC_HW_STOP_DELAY_MS 100 +#define STM32_ADC_VREFINT_VOLTAGE 3300 #define STM32_DMA_BUFFER_SIZE PAGE_SIZE @@ -137,6 +139,16 @@ struct stm32_adc_regs { int shift; }; +/** + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data + * @vrefint_cal: vrefint calibration value from nvmem + * @vrefint_data: vrefint actual value + */ +struct stm32_adc_vrefint { + u32 vrefint_cal; + u32 vrefint_data; +}; + /** * struct stm32_adc_regspec - stm32 registers definition * @dr: data register offset @@ -186,6 +198,7 @@ struct stm32_adc; * @unprepare: optional unprepare routine (disable, power-down) * @irq_clear: routine to clear irqs * @smp_cycles: programmable sampling time (ADC clock cycles) + * @ts_vrefint_ns: vrefint minimum sampling time in ns */ struct stm32_adc_cfg { const struct stm32_adc_regspec *regs; @@ -199,6 +212,7 @@ struct stm32_adc_cfg { void (*unprepare)(struct iio_dev *); void (*irq_clear)(struct iio_dev *indio_dev, u32 msk); const unsigned int *smp_cycles; + const unsigned int ts_vrefint_ns; }; /** @@ -223,6 +237,7 @@ struct stm32_adc_cfg { * @pcsel: bitmask to preselect channels on some devices * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) * @cal: optional calibration data on some devices + * @vrefint: internal reference voltage data * @chan_name: channel name array * @num_diff: number of differential channels * @int_ch: internal channel indexes array @@ -248,6 +263,7 @@ struct stm32_adc { u32 pcsel; u32 smpr_val[2]; struct stm32_adc_calib cal; + struct stm32_adc_vrefint vrefint; char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; u32 num_diff; int int_ch[STM32_ADC_INT_CH_NB]; @@ -1339,6 +1355,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; @@ -1346,6 +1363,10 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, ret = stm32_adc_single_conv(indio_dev, chan, val); else ret = -EINVAL; + + if (mask == IIO_CHAN_INFO_PROCESSED && adc->vrefint.vrefint_cal) + *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val; + iio_device_release_direct_mode(indio_dev); return ret; @@ -1815,7 +1836,10 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, chan->datasheet_name = name; chan->scan_index = scan_index; chan->indexed = 1; - chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + if (chan->channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) + chan->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED); + else + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET); chan->scan_type.sign = 'u'; @@ -1917,6 +1941,36 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, return scan_index; } +static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_name, + int chan) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u16 vrefint; + int i, ret; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) { + adc->int_ch[i] = chan; + + if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) + continue; + + /* Get calibration data for vrefint channel */ + ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint); + if (ret && ret != -ENOENT) { + return dev_err_probe(&indio_dev->dev, ret, + "nvmem access error\n"); + } + if (ret == -ENOENT) + dev_dbg(&indio_dev->dev, "vrefint calibration not found\n"); + else + adc->vrefint.vrefint_cal = vrefint; + } + } + + return 0; +} + static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, struct stm32_adc *adc, struct iio_chan_spec *channels) @@ -1925,7 +1979,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, const struct stm32_adc_info *adc_info = adc->cfg->adc_info; struct device_node *child; const char *name; - int val, scan_index = 0, ret, i; + int val, scan_index = 0, ret; bool differential; u32 vin[2]; @@ -1945,10 +1999,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, return -EINVAL; } strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); - for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { - if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ)) - adc->int_ch[i] = val; - } + ret = stm32_adc_populate_int_ch(indio_dev, name, val); + if (ret) + goto err; } else if (ret != -EINVAL) { dev_err(&indio_dev->dev, "Invalid label %d\n", ret); goto err; @@ -2055,6 +2108,14 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) */ of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp); + + /* + * For vrefint channel, ensure that the sampling time cannot + * be lower than the one specified in the datasheet + */ + if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) + smp = max(smp, adc->cfg->ts_vrefint_ns); + /* Prepare sampling time settings */ stm32_adc_smpr_init(adc, channels[i].channel, smp); } @@ -2361,6 +2422,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .unprepare = stm32h7_adc_unprepare, .smp_cycles = stm32h7_adc_smp_cycles, .irq_clear = stm32h7_adc_irq_clear, + .ts_vrefint_ns = 4300, }; static const struct of_device_id stm32_adc_of_match[] = { -- cgit v1.2.3 From 796e5d0b1e9baa2c80c234efd54d561e6d1cf1e7 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:28 +0200 Subject: iio: adc: stm32-adc: use generic binding for sample-time Add st,min-sample-time-nsecs to channel generic binding. Sample time can be defined par channel node. If a channel is configured as differential, the same sample time applies for both inputs. Keep support of legacy st,min-sample-time-nsecs property for backward compatibility. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-8-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 70 ++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5269bb87d094..6245434f8377 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1805,6 +1805,13 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns) u32 period_ns, shift = smpr->shift, mask = smpr->mask; unsigned int smp, r = smpr->reg; + /* + * For vrefint channel, ensure that the sampling time cannot + * be lower than the one specified in the datasheet + */ + if (channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) + smp_ns = max(smp_ns, adc->cfg->ts_vrefint_ns); + /* Determine sampling time (ADC clock cycles) */ period_ns = NSEC_PER_SEC / adc->common->rate; for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++) @@ -1881,6 +1888,13 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm num_channels += ret; } + /* Optional sample time is provided either for each, or all channels */ + ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); + if (ret > 1 && ret != num_channels) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); + return -EINVAL; + } + return num_channels; } @@ -1896,6 +1910,7 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, int scan_index = 0, val, ret, i; struct property *prop; const __be32 *cur; + u32 smp = 0; if (num_diff) { ret = of_property_read_u32_array(node, "st,adc-diff-channels", @@ -1938,6 +1953,19 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, scan_index++; } + for (i = 0; i < scan_index; i++) { + /* + * Using of_property_read_u32_index(), smp value will only be + * modified if valid u32 value can be decoded. This allows to + * get either no value, 1 shared value for all indexes, or one + * value per channel. + */ + of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp); + + /* Prepare sampling time settings */ + stm32_adc_smpr_init(adc, channels[i].channel, smp); + } + return scan_index; } @@ -2030,6 +2058,19 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, vin[1], scan_index, differential); + + ret = of_property_read_u32(child, "st,min-sample-time-ns", &val); + /* st,min-sample-time-ns is optional */ + if (!ret) { + stm32_adc_smpr_init(adc, channels[scan_index].channel, val); + if (differential) + stm32_adc_smpr_init(adc, vin[1], val); + } else if (ret != -EINVAL) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-ns property %d\n", + ret); + goto err; + } + scan_index++; } @@ -2048,7 +2089,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) const struct stm32_adc_info *adc_info = adc->cfg->adc_info; struct iio_chan_spec *channels; int scan_index = 0, num_channels = 0, ret, i; - u32 smp = 0; bool legacy = false; for (i = 0; i < STM32_ADC_INT_CH_NB; i++) @@ -2076,13 +2116,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) return -EINVAL; } - /* Optional sample time is provided either for each, or all channels */ - ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); - if (ret > 1 && ret != num_channels) { - dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); - return -EINVAL; - } - if (timestamping) num_channels++; @@ -2099,27 +2132,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) return ret; scan_index = ret; - for (i = 0; i < scan_index; i++) { - /* - * Using of_property_read_u32_index(), smp value will only be - * modified if valid u32 value can be decoded. This allows to - * get either no value, 1 shared value for all indexes, or one - * value per channel. - */ - of_property_read_u32_index(node, "st,min-sample-time-nsecs", - i, &smp); - - /* - * For vrefint channel, ensure that the sampling time cannot - * be lower than the one specified in the datasheet - */ - if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) - smp = max(smp, adc->cfg->ts_vrefint_ns); - - /* Prepare sampling time settings */ - stm32_adc_smpr_init(adc, channels[i].channel, smp); - } - if (timestamping) { struct iio_chan_spec *timestamp = &channels[scan_index]; -- cgit v1.2.3 From 17b5b576ff5faff99a4c8140d521cd4d7fff5c16 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 7 Oct 2021 15:46:39 +0200 Subject: mux: add support for delay after muxing Hardware may require some time for the muxed analog signals to settle after the muxing is changed. Allow users of the mux subsystem to specify this delay with the new mux_control_select_delay() function (and the _try equivalent). Signed-off-by: Vincent Whitchurch Reviewed-by: Lars-Peter Clausen Tested-by: Lars-Peter Clausen Acked-by: Peter Rosin Link: https://lore.kernel.org/r/20211007134641.13417-2-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron --- drivers/mux/core.c | 38 ++++++++++++++++++++++++++++++++------ include/linux/mux/consumer.h | 23 +++++++++++++++++++---- include/linux/mux/driver.h | 4 ++++ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index 1fb22388e7e0..22f4709768d1 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -9,6 +9,7 @@ #define pr_fmt(fmt) "mux-core: " fmt +#include #include #include #include @@ -116,6 +117,7 @@ struct mux_chip *mux_chip_alloc(struct device *dev, sema_init(&mux->lock, 1); mux->cached_state = MUX_CACHE_UNKNOWN; mux->idle_state = MUX_IDLE_AS_IS; + mux->last_change = ktime_get(); } device_initialize(&mux_chip->dev); @@ -129,6 +131,8 @@ static int mux_control_set(struct mux_control *mux, int state) int ret = mux->chip->ops->set(mux, state); mux->cached_state = ret < 0 ? MUX_CACHE_UNKNOWN : state; + if (ret >= 0) + mux->last_change = ktime_get(); return ret; } @@ -314,10 +318,25 @@ static int __mux_control_select(struct mux_control *mux, int state) return ret; } +static void mux_control_delay(struct mux_control *mux, unsigned int delay_us) +{ + ktime_t delayend; + s64 remaining; + + if (!delay_us) + return; + + delayend = ktime_add_us(mux->last_change, delay_us); + remaining = ktime_us_delta(delayend, ktime_get()); + if (remaining > 0) + fsleep(remaining); +} + /** - * mux_control_select() - Select the given multiplexer state. + * mux_control_select_delay() - Select the given multiplexer state. * @mux: The mux-control to request a change of state from. * @state: The new requested state. + * @delay_us: The time to delay (in microseconds) if the mux state is changed. * * On successfully selecting the mux-control state, it will be locked until * there is a call to mux_control_deselect(). If the mux-control is already @@ -331,7 +350,8 @@ static int __mux_control_select(struct mux_control *mux, int state) * Return: 0 when the mux-control state has the requested state or a negative * errno on error. */ -int mux_control_select(struct mux_control *mux, unsigned int state) +int mux_control_select_delay(struct mux_control *mux, unsigned int state, + unsigned int delay_us) { int ret; @@ -340,18 +360,21 @@ int mux_control_select(struct mux_control *mux, unsigned int state) return ret; ret = __mux_control_select(mux, state); + if (ret >= 0) + mux_control_delay(mux, delay_us); if (ret < 0) up(&mux->lock); return ret; } -EXPORT_SYMBOL_GPL(mux_control_select); +EXPORT_SYMBOL_GPL(mux_control_select_delay); /** - * mux_control_try_select() - Try to select the given multiplexer state. + * mux_control_try_select_delay() - Try to select the given multiplexer state. * @mux: The mux-control to request a change of state from. * @state: The new requested state. + * @delay_us: The time to delay (in microseconds) if the mux state is changed. * * On successfully selecting the mux-control state, it will be locked until * mux_control_deselect() called. @@ -363,7 +386,8 @@ EXPORT_SYMBOL_GPL(mux_control_select); * Return: 0 when the mux-control state has the requested state or a negative * errno on error. Specifically -EBUSY if the mux-control is contended. */ -int mux_control_try_select(struct mux_control *mux, unsigned int state) +int mux_control_try_select_delay(struct mux_control *mux, unsigned int state, + unsigned int delay_us) { int ret; @@ -371,13 +395,15 @@ int mux_control_try_select(struct mux_control *mux, unsigned int state) return -EBUSY; ret = __mux_control_select(mux, state); + if (ret >= 0) + mux_control_delay(mux, delay_us); if (ret < 0) up(&mux->lock); return ret; } -EXPORT_SYMBOL_GPL(mux_control_try_select); +EXPORT_SYMBOL_GPL(mux_control_try_select_delay); /** * mux_control_deselect() - Deselect the previously selected multiplexer state. diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h index 5fc6bb2fefad..7a09b040ac39 100644 --- a/include/linux/mux/consumer.h +++ b/include/linux/mux/consumer.h @@ -16,10 +16,25 @@ struct device; struct mux_control; unsigned int mux_control_states(struct mux_control *mux); -int __must_check mux_control_select(struct mux_control *mux, - unsigned int state); -int __must_check mux_control_try_select(struct mux_control *mux, - unsigned int state); +int __must_check mux_control_select_delay(struct mux_control *mux, + unsigned int state, + unsigned int delay_us); +int __must_check mux_control_try_select_delay(struct mux_control *mux, + unsigned int state, + unsigned int delay_us); + +static inline int __must_check mux_control_select(struct mux_control *mux, + unsigned int state) +{ + return mux_control_select_delay(mux, state, 0); +} + +static inline int __must_check mux_control_try_select(struct mux_control *mux, + unsigned int state) +{ + return mux_control_try_select_delay(mux, state, 0); +} + int mux_control_deselect(struct mux_control *mux); struct mux_control *mux_control_get(struct device *dev, const char *mux_name); diff --git a/include/linux/mux/driver.h b/include/linux/mux/driver.h index 627a2c6bc02d..18824064f8c0 100644 --- a/include/linux/mux/driver.h +++ b/include/linux/mux/driver.h @@ -12,6 +12,7 @@ #include #include +#include #include struct mux_chip; @@ -33,6 +34,7 @@ struct mux_control_ops { * @states: The number of mux controller states. * @idle_state: The mux controller state to use when inactive, or one * of MUX_IDLE_AS_IS and MUX_IDLE_DISCONNECT. + * @last_change: Timestamp of last change * * Mux drivers may only change @states and @idle_state, and may only do so * between allocation and registration of the mux controller. Specifically, @@ -47,6 +49,8 @@ struct mux_control { unsigned int states; int idle_state; + + ktime_t last_change; }; /** -- cgit v1.2.3 From b9221f71c285d4ce557c0a2a95e168f641f3396d Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 7 Oct 2021 15:46:40 +0200 Subject: dt-bindings: iio: io-channel-mux: Add property for settle time Hardware may require some time for the muxed analog signals to settle after the muxing is changed. Allow this time to be specified in the devicetree. Signed-off-by: Vincent Whitchurch Reviewed-by: Rob Herring Reviewed-by: Lars-Peter Clausen Tested-by: Lars-Peter Clausen Acked-by: Peter Rosin Link: https://lore.kernel.org/r/20211007134641.13417-3-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/multiplexer/io-channel-mux.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml index 870b043406d8..5a7328042c76 100644 --- a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml @@ -39,6 +39,11 @@ properties: description: List of strings, labeling the mux controller states. + settle-time-us: + default: 0 + description: + Time required for analog signals to settle after muxing. + required: - compatible - io-channels -- cgit v1.2.3 From 39aa50400794080e401a0334fbf0b058a67fc16c Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 7 Oct 2021 15:46:41 +0200 Subject: iio: multiplexer: iio-mux: Support settle-time-us property If the devicetree specifies that the hardware requires a settle time, pass this time on to the mux APIs. Signed-off-by: Vincent Whitchurch Reviewed-by: Lars-Peter Clausen Tested-by: Lars-Peter Clausen Acked-by: Peter Rosin Link: https://lore.kernel.org/r/20211007134641.13417-4-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron --- drivers/iio/multiplexer/iio-mux.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index d54ae5cbe51b..f422d44377df 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -33,6 +33,7 @@ struct mux { struct iio_chan_spec *chan; struct iio_chan_spec_ext_info *ext_info; struct mux_child *child; + u32 delay_us; }; static int iio_mux_select(struct mux *mux, int idx) @@ -42,7 +43,8 @@ static int iio_mux_select(struct mux *mux, int idx) int ret; int i; - ret = mux_control_select(mux->control, chan->channel); + ret = mux_control_select_delay(mux->control, chan->channel, + mux->delay_us); if (ret < 0) { mux->cached_state = -1; return ret; @@ -392,6 +394,9 @@ static int mux_probe(struct platform_device *pdev) mux->parent = parent; mux->cached_state = -1; + mux->delay_us = 0; + of_property_read_u32(np, "settle-time-us", &mux->delay_us); + indio_dev->name = dev_name(dev); indio_dev->info = &mux_info; indio_dev->modes = INDIO_DIRECT_MODE; -- cgit v1.2.3 From 16cc9aa4a8a4dcd40eabd245f4533e1ea1b1e37f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 26 Aug 2021 20:36:22 +0200 Subject: iio: adc: adc128s052: Simplify adc128_probe() Turn 'adc128_probe()' into a full resource managed function to simplify the code. This way, the .remove function can be removed. Doing so, the only 'spi_get_drvdata()' call is removed and the corresponding 'spi_set_drvdata()' can be removed as well. Suggested-by: Alexandru Ardelean Signed-off-by: Christophe JAILLET Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/4fa7fcc59c40e27af0569138d656c698a53dbd44.1630002770.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc128s052.c | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 83c1ae07b3e9..8e7adec87755 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -132,6 +132,11 @@ static const struct iio_info adc128_info = { .read_raw = adc128_read_raw, }; +static void adc128_disable_regulator(void *reg) +{ + regulator_disable(reg); +} + static int adc128_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -151,8 +156,6 @@ static int adc128_probe(struct spi_device *spi) adc = iio_priv(indio_dev); adc->spi = spi; - spi_set_drvdata(spi, indio_dev); - indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &adc128_info; @@ -167,29 +170,14 @@ static int adc128_probe(struct spi_device *spi) ret = regulator_enable(adc->reg); if (ret < 0) return ret; - - mutex_init(&adc->lock); - - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(&spi->dev, adc128_disable_regulator, + adc->reg); if (ret) - goto err_disable_regulator; - - return 0; - -err_disable_regulator: - regulator_disable(adc->reg); - return ret; -} - -static int adc128_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct adc128 *adc = iio_priv(indio_dev); + return ret; - iio_device_unregister(indio_dev); - regulator_disable(adc->reg); + mutex_init(&adc->lock); - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id adc128_of_match[] = { @@ -231,7 +219,6 @@ static struct spi_driver adc128_driver = { .acpi_match_table = ACPI_PTR(adc128_acpi_match), }, .probe = adc128_probe, - .remove = adc128_remove, .id_table = adc128_id, }; module_spi_driver(adc128_driver); -- cgit v1.2.3 From 86477c231c16a41d9b9805ce43c7a140a0dae4c0 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Sat, 9 Oct 2021 00:11:21 +0200 Subject: dt-bindings: iio: io-channel-mux: add optional #io-channel-cells Needed for in-kernel use of the child channels of the mux. Fixes problems like this, reported by dtbs_check: adc0mux: '#io-channel-cells' does not match any of the regexes: 'pinctrl-[0-9]+' arch/arm/boot/dts/aspeed-bmc-ampere-mtjade.dt.yaml Suggested-by: Vincent Whitchurch Signed-off-by: Peter Rosin Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml index 5a7328042c76..80b6229a6fad 100644 --- a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml @@ -44,6 +44,9 @@ properties: description: Time required for analog signals to settle after muxing. + "#io-channel-cells": + const: 1 + required: - compatible - io-channels -- cgit v1.2.3 From 392998a8032a59bff4ec14dc9d5098c60b50baf9 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Sat, 9 Oct 2021 00:11:30 +0200 Subject: dt-bindings: iio: io-channel-mux: allow duplicate channel, labels This is needed since an empty channel label is used to indicate an unused channel, and there can be more that one of those. Fixes the following problem reported by dtbs_check: envelope-detector-mux: channels: ['', '', 'sync-1', 'in', 'out', 'sync-2', 'sys-reg', 'ana-reg'] has non-unique elements arch/arm/boot/dts/at91-tse850-3.dt.yaml Suggested-by: Rob Herring Signed-off-by: Peter Rosin Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/multiplexer/io-channel-mux.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml index 80b6229a6fad..611ad4444cf0 100644 --- a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml @@ -35,9 +35,10 @@ properties: mux-control-names: true channels: - $ref: /schemas/types.yaml#/definitions/string-array + $ref: /schemas/types.yaml#/definitions/non-unique-string-array description: - List of strings, labeling the mux controller states. + List of strings, labeling the mux controller states. An empty + string for a state means that the channel is not available. settle-time-us: default: 0 -- cgit v1.2.3 From e46e2512ac84bd914212dc4b639d596db543f5b1 Mon Sep 17 00:00:00 2001 From: Roan van Dijk Date: Thu, 21 Oct 2021 16:00:18 +0200 Subject: iio: chemical: scd4x: Add a scale for the co2 concentration reading This patch adds a scale for the co2 concentration reading. The reading is expressed in percent after applying the scale to the raw value. Signed-off-by: Roan van Dijk Link: https://lore.kernel.org/r/20211021140018.3051213-1-roan@protonic.nl Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd4x.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c index ebebcb117ba2..267bc3c05338 100644 --- a/drivers/iio/chemical/scd4x.c +++ b/drivers/iio/chemical/scd4x.c @@ -353,7 +353,11 @@ static int scd4x_read_raw(struct iio_dev *indio_dev, *val = ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_TEMP) { + if (chan->type == IIO_CONCENTRATION) { + *val = 0; + *val2 = 100; + return IIO_VAL_INT_PLUS_MICRO; + } else if (chan->type == IIO_TEMP) { *val = 175000; *val2 = 65536; return IIO_VAL_FRACTIONAL; @@ -503,7 +507,8 @@ static const struct iio_chan_spec scd4x_channels[] = { .type = IIO_CONCENTRATION, .channel2 = IIO_MOD_CO2, .modified = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), .address = SCD4X_CO2, .scan_index = SCD4X_CO2, .scan_type = { -- cgit v1.2.3 From 63aaf6d06d87866dd6b58265711a979ed4968420 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 21 Oct 2021 14:32:43 +0300 Subject: iio: frequency: adrf6780: add support for ADRF6780 The ADRF6780 is a silicon germanium (SiGe) design, wideband, microwave upconverter optimized for point to point microwave radio designs operating in the 5.9 GHz to 23.6 GHz frequency range. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADRF6780.pdf Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20211021113244.56936-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/Kconfig | 12 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/adrf6780.c | 527 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 540 insertions(+) create mode 100644 drivers/iio/frequency/adrf6780.c diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 240b81502512..2c9e0559e8a4 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -49,5 +49,17 @@ config ADF4371 To compile this driver as a module, choose M here: the module will be called adf4371. + +config ADRF6780 + tristate "Analog Devices ADRF6780 Microwave Upconverter" + depends on SPI + depends on COMMON_CLK + help + Say yes here to build support for Analog Devices ADRF6780 + 5.9 GHz to 23.6 GHz, Wideband, Microwave Upconverter. + + To compile this driver as a module, choose M here: the + module will be called adrf6780. + endmenu endmenu diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 518b1e50caef..ae3136c79202 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_AD9523) += ad9523.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o +obj-$(CONFIG_ADRF6780) += adrf6780.o diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c new file mode 100644 index 000000000000..abe8b30fceca --- /dev/null +++ b/drivers/iio/frequency/adrf6780.c @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADRF6780 driver + * + * Copyright 2021 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ADRF6780 Register Map */ +#define ADRF6780_REG_CONTROL 0x00 +#define ADRF6780_REG_ALARM_READBACK 0x01 +#define ADRF6780_REG_ALARM_MASKS 0x02 +#define ADRF6780_REG_ENABLE 0x03 +#define ADRF6780_REG_LINEARIZE 0x04 +#define ADRF6780_REG_LO_PATH 0x05 +#define ADRF6780_REG_ADC_CONTROL 0x06 +#define ADRF6780_REG_ADC_OUTPUT 0x0C + +/* ADRF6780_REG_CONTROL Map */ +#define ADRF6780_PARITY_EN_MSK BIT(15) +#define ADRF6780_SOFT_RESET_MSK BIT(14) +#define ADRF6780_CHIP_ID_MSK GENMASK(11, 4) +#define ADRF6780_CHIP_ID 0xA +#define ADRF6780_CHIP_REVISION_MSK GENMASK(3, 0) + +/* ADRF6780_REG_ALARM_READBACK Map */ +#define ADRF6780_PARITY_ERROR_MSK BIT(15) +#define ADRF6780_TOO_FEW_ERRORS_MSK BIT(14) +#define ADRF6780_TOO_MANY_ERRORS_MSK BIT(13) +#define ADRF6780_ADDRESS_RANGE_ERROR_MSK BIT(12) + +/* ADRF6780_REG_ENABLE Map */ +#define ADRF6780_VGA_BUFFER_EN_MSK BIT(8) +#define ADRF6780_DETECTOR_EN_MSK BIT(7) +#define ADRF6780_LO_BUFFER_EN_MSK BIT(6) +#define ADRF6780_IF_MODE_EN_MSK BIT(5) +#define ADRF6780_IQ_MODE_EN_MSK BIT(4) +#define ADRF6780_LO_X2_EN_MSK BIT(3) +#define ADRF6780_LO_PPF_EN_MSK BIT(2) +#define ADRF6780_LO_EN_MSK BIT(1) +#define ADRF6780_UC_BIAS_EN_MSK BIT(0) + +/* ADRF6780_REG_LINEARIZE Map */ +#define ADRF6780_RDAC_LINEARIZE_MSK GENMASK(7, 0) + +/* ADRF6780_REG_LO_PATH Map */ +#define ADRF6780_LO_SIDEBAND_MSK BIT(10) +#define ADRF6780_Q_PATH_PHASE_ACCURACY_MSK GENMASK(7, 4) +#define ADRF6780_I_PATH_PHASE_ACCURACY_MSK GENMASK(3, 0) + +/* ADRF6780_REG_ADC_CONTROL Map */ +#define ADRF6780_VDET_OUTPUT_SELECT_MSK BIT(3) +#define ADRF6780_ADC_START_MSK BIT(2) +#define ADRF6780_ADC_EN_MSK BIT(1) +#define ADRF6780_ADC_CLOCK_EN_MSK BIT(0) + +/* ADRF6780_REG_ADC_OUTPUT Map */ +#define ADRF6780_ADC_STATUS_MSK BIT(8) +#define ADRF6780_ADC_VALUE_MSK GENMASK(7, 0) + +struct adrf6780_state { + struct spi_device *spi; + struct clk *clkin; + /* Protect against concurrent accesses to the device */ + struct mutex lock; + bool vga_buff_en; + bool lo_buff_en; + bool if_mode_en; + bool iq_mode_en; + bool lo_x2_en; + bool lo_ppf_en; + bool lo_en; + bool uc_bias_en; + bool lo_sideband; + bool vdet_out_en; + u8 data[3] ____cacheline_aligned; +}; + +static int __adrf6780_spi_read(struct adrf6780_state *st, unsigned int reg, + unsigned int *val) +{ + int ret; + struct spi_transfer t = {0}; + + st->data[0] = 0x80 | (reg << 1); + st->data[1] = 0x0; + st->data[2] = 0x0; + + t.rx_buf = &st->data[0]; + t.tx_buf = &st->data[0]; + t.len = 3; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + *val = (get_unaligned_be24(&st->data[0]) >> 1) & GENMASK(15, 0); + + return ret; +} + +static int adrf6780_spi_read(struct adrf6780_state *st, unsigned int reg, + unsigned int *val) +{ + int ret; + + mutex_lock(&st->lock); + ret = adrf6780_spi_read(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __adrf6780_spi_write(struct adrf6780_state *st, + unsigned int reg, + unsigned int val) +{ + put_unaligned_be24((val << 1) | (reg << 17), &st->data[0]); + + return spi_write(st->spi, &st->data[0], 3); +} + +static int adrf6780_spi_write(struct adrf6780_state *st, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = adrf6780_spi_write(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __adrf6780_spi_update_bits(struct adrf6780_state *st, + unsigned int reg, unsigned int mask, + unsigned int val) +{ + int ret; + unsigned int data, temp; + + ret = __adrf6780_spi_read(st, reg, &data); + if (ret) + return ret; + + temp = (data & ~mask) | (val & mask); + + return __adrf6780_spi_write(st, reg, temp); +} + +static int adrf6780_spi_update_bits(struct adrf6780_state *st, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __adrf6780_spi_update_bits(st, reg, mask, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int adrf6780_read_adc_raw(struct adrf6780_state *st, unsigned int *read_val) +{ + int ret; + + mutex_lock(&st->lock); + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL, + ADRF6780_ADC_EN_MSK | + ADRF6780_ADC_CLOCK_EN_MSK | + ADRF6780_ADC_START_MSK, + FIELD_PREP(ADRF6780_ADC_EN_MSK, 1) | + FIELD_PREP(ADRF6780_ADC_CLOCK_EN_MSK, 1) | + FIELD_PREP(ADRF6780_ADC_START_MSK, 1)); + if (ret) + goto exit; + + /* Recommended delay for the ADC to be ready*/ + usleep_range(200, 250); + + ret = __adrf6780_spi_read(st, ADRF6780_REG_ADC_OUTPUT, read_val); + if (ret) + goto exit; + + if (!(*read_val & ADRF6780_ADC_STATUS_MSK)) { + ret = -EINVAL; + goto exit; + } + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL, + ADRF6780_ADC_START_MSK, + FIELD_PREP(ADRF6780_ADC_START_MSK, 0)); + if (ret) + goto exit; + + ret = __adrf6780_spi_read(st, ADRF6780_REG_ADC_OUTPUT, read_val); + +exit: + mutex_unlock(&st->lock); + return ret; +} + +static int adrf6780_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adrf6780_state *dev = iio_priv(indio_dev); + unsigned int data; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = adrf6780_read_adc_raw(dev, &data); + if (ret) + return ret; + + *val = data & ADRF6780_ADC_VALUE_MSK; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = adrf6780_spi_read(dev, ADRF6780_REG_LINEARIZE, &data); + if (ret) + return ret; + + *val = data & ADRF6780_RDAC_LINEARIZE_MSK; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_PHASE: + ret = adrf6780_spi_read(dev, ADRF6780_REG_LO_PATH, &data); + if (ret) + return ret; + + switch (chan->channel2) { + case IIO_MOD_I: + *val = data & ADRF6780_I_PATH_PHASE_ACCURACY_MSK; + + return IIO_VAL_INT; + case IIO_MOD_Q: + *val = FIELD_GET(ADRF6780_Q_PATH_PHASE_ACCURACY_MSK, + data); + + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adrf6780_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adrf6780_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return adrf6780_spi_write(st, ADRF6780_REG_LINEARIZE, val); + case IIO_CHAN_INFO_PHASE: + switch (chan->channel2) { + case IIO_MOD_I: + return adrf6780_spi_update_bits(st, + ADRF6780_REG_LO_PATH, + ADRF6780_I_PATH_PHASE_ACCURACY_MSK, + FIELD_PREP(ADRF6780_I_PATH_PHASE_ACCURACY_MSK, val)); + case IIO_MOD_Q: + return adrf6780_spi_update_bits(st, + ADRF6780_REG_LO_PATH, + ADRF6780_Q_PATH_PHASE_ACCURACY_MSK, + FIELD_PREP(ADRF6780_Q_PATH_PHASE_ACCURACY_MSK, val)); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adrf6780_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct adrf6780_state *st = iio_priv(indio_dev); + + if (read_val) + return adrf6780_spi_read(st, reg, read_val); + else + return adrf6780_spi_write(st, reg, write_val); +} + +static const struct iio_info adrf6780_info = { + .read_raw = adrf6780_read_raw, + .write_raw = adrf6780_write_raw, + .debugfs_reg_access = &adrf6780_reg_access, +}; + +#define ADRF6780_CHAN_ADC(_channel) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ +} + +#define ADRF6780_CHAN_RDAC(_channel) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) \ +} + +#define ADRF6780_CHAN_IQ_PHASE(_channel, rf_comp) { \ + .type = IIO_ALTVOLTAGE, \ + .modified = 1, \ + .output = 1, \ + .indexed = 1, \ + .channel2 = IIO_MOD_##rf_comp, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) \ +} + +static const struct iio_chan_spec adrf6780_channels[] = { + ADRF6780_CHAN_ADC(0), + ADRF6780_CHAN_RDAC(0), + ADRF6780_CHAN_IQ_PHASE(0, I), + ADRF6780_CHAN_IQ_PHASE(0, Q), +}; + +static int adrf6780_reset(struct adrf6780_state *st) +{ + int ret; + struct spi_device *spi = st->spi; + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, + ADRF6780_SOFT_RESET_MSK, + FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADRF6780 SPI software reset failed.\n"); + return ret; + } + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, + ADRF6780_SOFT_RESET_MSK, + FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 0)); + if (ret) { + dev_err(&spi->dev, "ADRF6780 SPI software reset disable failed.\n"); + return ret; + } + + return 0; +} + +static int adrf6780_init(struct adrf6780_state *st) +{ + int ret; + unsigned int chip_id, enable_reg, enable_reg_msk; + struct spi_device *spi = st->spi; + + /* Perform a software reset */ + ret = adrf6780_reset(st); + if (ret) + return ret; + + ret = __adrf6780_spi_read(st, ADRF6780_REG_CONTROL, &chip_id); + if (ret) + return ret; + + chip_id = FIELD_GET(ADRF6780_CHIP_ID_MSK, chip_id); + if (chip_id != ADRF6780_CHIP_ID) { + dev_err(&spi->dev, "ADRF6780 Invalid Chip ID.\n"); + return -EINVAL; + } + + enable_reg_msk = ADRF6780_VGA_BUFFER_EN_MSK | + ADRF6780_DETECTOR_EN_MSK | + ADRF6780_LO_BUFFER_EN_MSK | + ADRF6780_IF_MODE_EN_MSK | + ADRF6780_IQ_MODE_EN_MSK | + ADRF6780_LO_X2_EN_MSK | + ADRF6780_LO_PPF_EN_MSK | + ADRF6780_LO_EN_MSK | + ADRF6780_UC_BIAS_EN_MSK; + + enable_reg = FIELD_PREP(ADRF6780_VGA_BUFFER_EN_MSK, st->vga_buff_en) | + FIELD_PREP(ADRF6780_DETECTOR_EN_MSK, 1) | + FIELD_PREP(ADRF6780_LO_BUFFER_EN_MSK, st->lo_buff_en) | + FIELD_PREP(ADRF6780_IF_MODE_EN_MSK, st->if_mode_en) | + FIELD_PREP(ADRF6780_IQ_MODE_EN_MSK, st->iq_mode_en) | + FIELD_PREP(ADRF6780_LO_X2_EN_MSK, st->lo_x2_en) | + FIELD_PREP(ADRF6780_LO_PPF_EN_MSK, st->lo_ppf_en) | + FIELD_PREP(ADRF6780_LO_EN_MSK, st->lo_en) | + FIELD_PREP(ADRF6780_UC_BIAS_EN_MSK, st->uc_bias_en); + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ENABLE, + enable_reg_msk, enable_reg); + if (ret) + return ret; + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_LO_PATH, + ADRF6780_LO_SIDEBAND_MSK, + FIELD_PREP(ADRF6780_LO_SIDEBAND_MSK, st->lo_sideband)); + if (ret) + return ret; + + return __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL, + ADRF6780_VDET_OUTPUT_SELECT_MSK, + FIELD_PREP(ADRF6780_VDET_OUTPUT_SELECT_MSK, st->vdet_out_en)); +} + +static void adrf6780_properties_parse(struct adrf6780_state *st) +{ + struct spi_device *spi = st->spi; + + st->vga_buff_en = device_property_read_bool(&spi->dev, "adi,vga-buff-en"); + st->lo_buff_en = device_property_read_bool(&spi->dev, "adi,lo-buff-en"); + st->if_mode_en = device_property_read_bool(&spi->dev, "adi,if-mode-en"); + st->iq_mode_en = device_property_read_bool(&spi->dev, "adi,iq-mode-en"); + st->lo_x2_en = device_property_read_bool(&spi->dev, "adi,lo-x2-en"); + st->lo_ppf_en = device_property_read_bool(&spi->dev, "adi,lo-ppf-en"); + st->lo_en = device_property_read_bool(&spi->dev, "adi,lo-en"); + st->uc_bias_en = device_property_read_bool(&spi->dev, "adi,uc-bias-en"); + st->lo_sideband = device_property_read_bool(&spi->dev, "adi,lo-sideband"); + st->vdet_out_en = device_property_read_bool(&spi->dev, "adi,vdet-out-en"); +} + +static void adrf6780_clk_disable(void *data) +{ + clk_disable_unprepare(data); +} + +static void adrf6780_powerdown(void *data) +{ + /* Disable all components in the Enable Register */ + adrf6780_spi_write(data, ADRF6780_REG_ENABLE, 0x0); +} + +static int adrf6780_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adrf6780_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + indio_dev->info = &adrf6780_info; + indio_dev->name = "adrf6780"; + indio_dev->channels = adrf6780_channels; + indio_dev->num_channels = ARRAY_SIZE(adrf6780_channels); + + st->spi = spi; + + adrf6780_properties_parse(st); + + st->clkin = devm_clk_get(&spi->dev, "lo_in"); + if (IS_ERR(st->clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + "failed to get the LO input clock\n"); + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, adrf6780_clk_disable, + st->clkin); + if (ret) + return ret; + + mutex_init(&st->lock); + + ret = adrf6780_init(st); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, adrf6780_powerdown, st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id adrf6780_id[] = { + { "adrf6780", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adrf6780_id); + +static const struct of_device_id adrf6780_of_match[] = { + { .compatible = "adi,adrf6780" }, + {} +}; +MODULE_DEVICE_TABLE(of, adrf6780_of_match); + +static struct spi_driver adrf6780_driver = { + .driver = { + .name = "adrf6780", + .of_match_table = adrf6780_of_match, + }, + .probe = adrf6780_probe, + .id_table = adrf6780_id, +}; +module_spi_driver(adrf6780_driver); + +MODULE_AUTHOR("Antoniu Miclaus Date: Thu, 21 Oct 2021 14:32:44 +0300 Subject: dt-bindings: iio: frequency: add adrf6780 doc Add device tree bindings for the ADRF6780 Upconverter. Reviewed-by: Rob Herring Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20211021113244.56936-2-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/frequency/adi,adrf6780.yaml | 131 +++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml new file mode 100644 index 000000000000..3a8ea93f4e0c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,adrf6780.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADRF6780 Microwave Upconverter + +maintainers: + - Antoniu Miclaus + +description: | + Wideband, microwave upconverter optimized for point to point microwave + radio designs operating in the 5.9 GHz to 23.6 GHz frequency range. + + https://www.analog.com/en/products/adrf6780.html + +properties: + compatible: + enum: + - adi,adrf6780 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 1000000 + + clocks: + description: + Definition of the external clock. + minItems: 1 + + clock-names: + items: + - const: lo_in + + clock-output-names: + maxItems: 1 + + adi,vga-buff-en: + description: + RF Variable Gain Amplifier Buffer Enable. Gain is controlled by + the voltage on the VATT pin. + type: boolean + + adi,lo-buff-en: + description: + Local Oscillator Amplifier Enable. Disable to put the part in + a power down state. + type: boolean + + adi,if-mode-en: + description: + Intermediate Frequency Mode Enable. Either IF Mode or I/Q Mode + can be enabled at a time. + type: boolean + + adi,iq-mode-en: + description: + I/Q Mode Enable. Either IF Mode or I/Q Mode can be enabled at a + time. + type: boolean + + adi,lo-x2-en: + description: + Double the Local Oscillator output frequency from the Local + Oscillator Input Frequency. Either LOx1 or LOx2 can be enabled + at a time. + type: boolean + + adi,lo-ppf-en: + description: + Local Oscillator input frequency equal to the Local Oscillator + output frequency (LO x1). Either LOx1 or LOx2 can be enabled + at a time. + type: boolean + + adi,lo-en: + description: + Enable additional cirtuitry in the LO chain. Disable to put the + part in a power down state. + type: boolean + + adi,uc-bias-en: + description: + Enable all bias circuitry thourghout the entire part. + Disable to put the part in a power down state. + type: boolean + + adi,lo-sideband: + description: + Switch to the Lower LO Sideband. By default the Upper LO + sideband is enabled. + type: boolean + + adi,vdet-out-en: + description: + VDET Output Select Enable. Expose the RF detector output to the + VDET external pin. + type: boolean + + '#clock-cells': + const: 0 + +dependencies: + adi,lo-x2-en: [ "adi,lo-en" ] + adi,lo-ppf-en: [ "adi,lo-en" ] + +required: + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + adrf6780@0 { + compatible = "adi,adrf6780"; + reg = <0>; + spi-max-frequency = <1000000>; + clocks = <&adrf6780_lo>; + clock-names = "lo_in"; + }; + }; +... -- cgit v1.2.3