From a3e9625ec0c523cba3847ae21c382ae745e71d19 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 25 Aug 2023 18:57:44 +0800 Subject: iio: adc: mt6577_auxadc: Use devm_clk_get_enabled() helper function The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. This simplifies the code. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230825105746.2999548-3-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6577_auxadc.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c index 0e134777bdd2..ea42fd7a8c99 100644 --- a/drivers/iio/adc/mt6577_auxadc.c +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -270,23 +270,16 @@ static int mt6577_auxadc_probe(struct platform_device *pdev) return PTR_ERR(adc_dev->reg_base); } - adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main"); + adc_dev->adc_clk = devm_clk_get_enabled(&pdev->dev, "main"); if (IS_ERR(adc_dev->adc_clk)) { - dev_err(&pdev->dev, "failed to get auxadc clock\n"); - return PTR_ERR(adc_dev->adc_clk); - } - - ret = clk_prepare_enable(adc_dev->adc_clk); - if (ret) { dev_err(&pdev->dev, "failed to enable auxadc clock\n"); - return ret; + return PTR_ERR(adc_dev->adc_clk); } adc_clk_rate = clk_get_rate(adc_dev->adc_clk); if (!adc_clk_rate) { - ret = -EINVAL; dev_err(&pdev->dev, "null clock rate\n"); - goto err_disable_clk; + return -EINVAL; } adc_dev->dev_comp = device_get_match_data(&pdev->dev); @@ -310,8 +303,6 @@ static int mt6577_auxadc_probe(struct platform_device *pdev) err_power_off: mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, 0, MT6577_AUXADC_PDN_EN); -err_disable_clk: - clk_disable_unprepare(adc_dev->adc_clk); return ret; } @@ -325,8 +316,6 @@ static int mt6577_auxadc_remove(struct platform_device *pdev) mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, 0, MT6577_AUXADC_PDN_EN); - clk_disable_unprepare(adc_dev->adc_clk); - return 0; } -- cgit v1.2.3 From 3878ae2a1a7636be7009e20b48d4fc00adab8cf4 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sat, 26 Aug 2023 14:27:31 +0800 Subject: iio: adc: spear_adc: Use device managed function The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. Switch to devm_iio_device_register() and drop the remove function. This simplifies the code. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230826062733.3714169-2-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/spear_adc.c | 43 ++++++++----------------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index ad54ef798109..d24adacfdf53 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -297,36 +297,27 @@ static int spear_adc_probe(struct platform_device *pdev) st->adc_base_spear3xx = (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx; - st->clk = devm_clk_get(dev, NULL); + st->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(st->clk)) { - dev_err(dev, "failed getting clock\n"); - return PTR_ERR(st->clk); - } - - ret = clk_prepare_enable(st->clk); - if (ret) { dev_err(dev, "failed enabling clock\n"); - return ret; + return PTR_ERR(st->clk); } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto errout2; - } + if (irq < 0) + return irq; ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME, st); if (ret < 0) { dev_err(dev, "failed requesting interrupt\n"); - goto errout2; + return ret; } if (of_property_read_u32(np, "sampling-frequency", &st->sampling_freq)) { dev_err(dev, "sampling-frequency missing in DT\n"); - ret = -EINVAL; - goto errout2; + return -EINVAL; } /* @@ -343,8 +334,6 @@ static int spear_adc_probe(struct platform_device *pdev) spear_adc_configure(st); - platform_set_drvdata(pdev, indio_dev); - init_completion(&st->completion); indio_dev->name = SPEAR_ADC_MOD_NAME; @@ -353,27 +342,12 @@ static int spear_adc_probe(struct platform_device *pdev) indio_dev->channels = spear_adc_iio_channels; indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels); - ret = iio_device_register(indio_dev); + ret = devm_iio_device_register(dev, indio_dev); if (ret) - goto errout2; + return ret; dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq); - return 0; - -errout2: - clk_disable_unprepare(st->clk); - return ret; -} - -static int spear_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct spear_adc_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - clk_disable_unprepare(st->clk); - return 0; } @@ -387,7 +361,6 @@ MODULE_DEVICE_TABLE(of, spear_adc_dt_ids); static struct platform_driver spear_adc_driver = { .probe = spear_adc_probe, - .remove = spear_adc_remove, .driver = { .name = SPEAR_ADC_MOD_NAME, .of_match_table = of_match_ptr(spear_adc_dt_ids), -- cgit v1.2.3 From b564b99de79cdc87008dab3b19ae67a011f3f2fa Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sat, 26 Aug 2023 14:27:32 +0800 Subject: iio: adc: spear_adc: Use dev_err_probe() Use the dev_err_probe() helper to simplify error handling during probe. This also handle scenario, when EDEFER is returned and useless error is printed. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230826062733.3714169-3-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/spear_adc.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index d24adacfdf53..71362c2ddf89 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -274,10 +274,9 @@ static int spear_adc_probe(struct platform_device *pdev) int irq; indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state)); - if (!indio_dev) { - dev_err(dev, "failed allocating iio device\n"); - return -ENOMEM; - } + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "failed allocating iio device\n"); st = iio_priv(indio_dev); @@ -298,10 +297,9 @@ static int spear_adc_probe(struct platform_device *pdev) (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx; st->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(st->clk)) { - dev_err(dev, "failed enabling clock\n"); - return PTR_ERR(st->clk); - } + if (IS_ERR(st->clk)) + return dev_err_probe(dev, PTR_ERR(st->clk), + "failed enabling clock\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -309,16 +307,13 @@ static int spear_adc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME, st); - if (ret < 0) { - dev_err(dev, "failed requesting interrupt\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed requesting interrupt\n"); if (of_property_read_u32(np, "sampling-frequency", - &st->sampling_freq)) { - dev_err(dev, "sampling-frequency missing in DT\n"); - return -EINVAL; - } + &st->sampling_freq)) + return dev_err_probe(dev, -EINVAL, + "sampling-frequency missing in DT\n"); /* * Optional avg_samples defaults to 0, resulting in single data -- cgit v1.2.3 From 8cbba23e43eb182c75656c0dba40f890feea6400 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sat, 26 Aug 2023 11:54:01 +0800 Subject: iio: adc: mt6577_auxadc: Simplify with dev_err_probe() Use the dev_err_probe() helper to simplify error handling during probe. This also handle scenario, when EDEFER is returned and useless error is printed. Signed-off-by: Jinjie Ruan Suggested-by: Jonathan Cameron Link: https://lore.kernel.org/r/20230826035402.3512033-2-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6577_auxadc.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c index ea42fd7a8c99..935cf560e238 100644 --- a/drivers/iio/adc/mt6577_auxadc.c +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -265,22 +265,18 @@ static int mt6577_auxadc_probe(struct platform_device *pdev) indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels); adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(adc_dev->reg_base)) { - dev_err(&pdev->dev, "failed to get auxadc base address\n"); - return PTR_ERR(adc_dev->reg_base); - } + if (IS_ERR(adc_dev->reg_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc_dev->reg_base), + "failed to get auxadc base address\n"); adc_dev->adc_clk = devm_clk_get_enabled(&pdev->dev, "main"); - if (IS_ERR(adc_dev->adc_clk)) { - dev_err(&pdev->dev, "failed to enable auxadc clock\n"); - return PTR_ERR(adc_dev->adc_clk); - } + if (IS_ERR(adc_dev->adc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc_dev->adc_clk), + "failed to enable auxadc clock\n"); adc_clk_rate = clk_get_rate(adc_dev->adc_clk); - if (!adc_clk_rate) { - dev_err(&pdev->dev, "null clock rate\n"); - return -EINVAL; - } + if (!adc_clk_rate) + return dev_err_probe(&pdev->dev, -EINVAL, "null clock rate\n"); adc_dev->dev_comp = device_get_match_data(&pdev->dev); -- cgit v1.2.3 From a2d518fbe3760f66576a5c4f627aa3f126271c8b Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sat, 26 Aug 2023 11:54:02 +0800 Subject: iio: adc: mt6577_auxadc: Simplify with device managed function Add a device managed hook, via devm_add_action_or_reset() and mt6577_power_off(), to power off on device detach. Replace iio_device_register() by devm_iio_device_register() and remove the mt6577_auxadc_remove() function used to unregister the device and power off the device. Remove platform_set_drvdata() from the probe function, since platform_get_drvdata() is not used anymore. Remove mt6577_auxadc_mod_reg() call from the probe function error path. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230826035402.3512033-3-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6577_auxadc.c | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c index 935cf560e238..370b84c2d0ba 100644 --- a/drivers/iio/adc/mt6577_auxadc.c +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -246,6 +246,14 @@ static int mt6577_auxadc_suspend(struct device *dev) return 0; } +static void mt6577_power_off(void *data) +{ + struct mt6577_auxadc_device *adc_dev = data; + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); +} + static int mt6577_auxadc_probe(struct platform_device *pdev) { struct mt6577_auxadc_device *adc_dev; @@ -286,31 +294,14 @@ static int mt6577_auxadc_probe(struct platform_device *pdev) MT6577_AUXADC_PDN_EN, 0); mdelay(MT6577_AUXADC_POWER_READY_MS); - platform_set_drvdata(pdev, indio_dev); - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register iio device\n"); - goto err_power_off; - } - - return 0; + ret = devm_add_action_or_reset(&pdev->dev, mt6577_power_off, adc_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to add action to managed power off\n"); -err_power_off: - mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, - 0, MT6577_AUXADC_PDN_EN); - return ret; -} - -static int mt6577_auxadc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, - 0, MT6577_AUXADC_PDN_EN); + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to register iio device\n"); return 0; } @@ -337,7 +328,6 @@ static struct platform_driver mt6577_auxadc_driver = { .pm = pm_sleep_ptr(&mt6577_auxadc_pm_ops), }, .probe = mt6577_auxadc_probe, - .remove = mt6577_auxadc_remove, }; module_platform_driver(mt6577_auxadc_driver); -- cgit v1.2.3 From 5ff46635e4c51a2aeafb50a06c36bfdd0cda4203 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 25 Aug 2023 17:56:12 +0800 Subject: staging: iio: Use devm_clk_get_enabled() helper function The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. This simplifies the code and avoids the need of a dedicated function used with devm_add_action_or_reset(). Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230825095612.2972892-1-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9832.c | 15 +-------------- drivers/staging/iio/frequency/ad9834.c | 21 ++------------------- drivers/staging/iio/impedance-analyzer/ad5933.c | 22 ++-------------------- 3 files changed, 5 insertions(+), 53 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 6f9eebd6c7ee..6c390c4eb26d 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -299,11 +299,6 @@ static void ad9832_reg_disable(void *reg) regulator_disable(reg); } -static void ad9832_clk_disable(void *clk) -{ - clk_disable_unprepare(clk); -} - static int ad9832_probe(struct spi_device *spi) { struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev); @@ -350,18 +345,10 @@ static int ad9832_probe(struct spi_device *spi) if (ret) return ret; - st->mclk = devm_clk_get(&spi->dev, "mclk"); + st->mclk = devm_clk_get_enabled(&spi->dev, "mclk"); if (IS_ERR(st->mclk)) return PTR_ERR(st->mclk); - ret = clk_prepare_enable(st->mclk); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, ad9832_clk_disable, st->mclk); - if (ret) - return ret; - st->spi = spi; mutex_init(&st->lock); diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 285df0e489a6..a7a5cdcc6590 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -394,13 +394,6 @@ static void ad9834_disable_reg(void *data) regulator_disable(reg); } -static void ad9834_disable_clk(void *data) -{ - struct clk *clk = data; - - clk_disable_unprepare(clk); -} - static int ad9834_probe(struct spi_device *spi) { struct ad9834_state *st; @@ -429,22 +422,12 @@ static int ad9834_probe(struct spi_device *spi) } st = iio_priv(indio_dev); mutex_init(&st->lock); - st->mclk = devm_clk_get(&spi->dev, NULL); + st->mclk = devm_clk_get_enabled(&spi->dev, NULL); if (IS_ERR(st->mclk)) { - ret = PTR_ERR(st->mclk); - return ret; - } - - ret = clk_prepare_enable(st->mclk); - if (ret) { dev_err(&spi->dev, "Failed to enable master clock\n"); - return ret; + return PTR_ERR(st->mclk); } - ret = devm_add_action_or_reset(&spi->dev, ad9834_disable_clk, st->mclk); - if (ret) - return ret; - st->spi = spi; st->devid = spi_get_device_id(spi)->driver_data; indio_dev->name = spi_get_device_id(spi)->name; diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 46db6d91542a..e748a5d04e97 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -667,13 +667,6 @@ static void ad5933_reg_disable(void *data) regulator_disable(st->reg); } -static void ad5933_clk_disable(void *data) -{ - struct ad5933_state *st = data; - - clk_disable_unprepare(st->mclk); -} - static int ad5933_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -712,23 +705,12 @@ static int ad5933_probe(struct i2c_client *client) st->vref_mv = ret / 1000; - st->mclk = devm_clk_get(&client->dev, "mclk"); + st->mclk = devm_clk_get_enabled(&client->dev, "mclk"); if (IS_ERR(st->mclk) && PTR_ERR(st->mclk) != -ENOENT) return PTR_ERR(st->mclk); - if (!IS_ERR(st->mclk)) { - ret = clk_prepare_enable(st->mclk); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&client->dev, - ad5933_clk_disable, - st); - if (ret) - return ret; - + if (!IS_ERR(st->mclk)) ext_clk_hz = clk_get_rate(st->mclk); - } if (ext_clk_hz) { st->mclk_hz = ext_clk_hz; -- cgit v1.2.3 From 655be10df27d98f014fd4691909e15d56834020e Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 22 Aug 2023 21:22:54 +0200 Subject: dt-bindings: iio: adc: mcp3911: add support for the whole MCP39xx family Microchip does have many similar chips, add those to the compatible string as the driver support is extended. The new supported chips are: - microchip,mcp3910 - microchip,mcp3912 - microchip,mcp3913 - microchip,mcp3914 - microchip,mcp3918 - microchip,mcp3919 Signed-off-by: Marcus Folkesson Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230822192259.1125792-2-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml index f7b3fde4115a..06951ec5f5da 100644 --- a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml +++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml @@ -18,7 +18,13 @@ description: | properties: compatible: enum: + - microchip,mcp3910 - microchip,mcp3911 + - microchip,mcp3912 + - microchip,mcp3913 + - microchip,mcp3914 + - microchip,mcp3918 + - microchip,mcp3919 reg: maxItems: 1 -- cgit v1.2.3 From 46d1bfa04f61eb9533859d50d33c6f1c3ef98b80 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 22 Aug 2023 21:22:55 +0200 Subject: iio: adc: mcp3911: make use of dev_err_probe() Simplify code by switch to dev_err_probe(). Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Marcus Folkesson Link: https://lore.kernel.org/r/20230822192259.1125792-3-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3911.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 974c5bd923a6..7fb3ab4a4256 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -277,10 +277,7 @@ static int mcp3911_calc_scale_table(struct mcp3911 *adc) if (adc->vref) { ret = regulator_get_voltage(adc->vref); if (ret < 0) { - dev_err(&adc->spi->dev, - "failed to get vref voltage: %d\n", - ret); - return ret; + return dev_err_probe(&adc->spi->dev, ret, "failed to get vref voltage\n"); } ref = ret / 1000; @@ -396,10 +393,9 @@ static int mcp3911_config(struct mcp3911 *adc) if (ret) device_property_read_u32(dev, "device-addr", &adc->dev_addr); if (adc->dev_addr > 3) { - dev_err(&adc->spi->dev, - "invalid device address (%i). Must be in range 0-3.\n", - adc->dev_addr); - return -EINVAL; + return dev_err_probe(dev, -EINVAL, + "invalid device address (%i). Must be in range 0-3.\n", + adc->dev_addr); } dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); @@ -466,6 +462,7 @@ static const struct iio_trigger_ops mcp3911_trigger_ops = { static int mcp3911_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct mcp3911 *adc; int ret; @@ -482,10 +479,7 @@ static int mcp3911_probe(struct spi_device *spi) if (PTR_ERR(adc->vref) == -ENODEV) { adc->vref = NULL; } else { - dev_err(&adc->spi->dev, - "failed to get regulator (%ld)\n", - PTR_ERR(adc->vref)); - return PTR_ERR(adc->vref); + return dev_err_probe(dev, PTR_ERR(adc->vref), "failed to get regulator\n"); } } else { @@ -504,10 +498,7 @@ static int mcp3911_probe(struct spi_device *spi) if (PTR_ERR(adc->clki) == -ENOENT) { adc->clki = NULL; } else { - dev_err(&adc->spi->dev, - "failed to get adc clk (%ld)\n", - PTR_ERR(adc->clki)); - return PTR_ERR(adc->clki); + return dev_err_probe(dev, PTR_ERR(adc->clki), "failed to get adc clk\n"); } } -- cgit v1.2.3 From d1f6a2ac592241215f9229e68eb951211d08695e Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 22 Aug 2023 21:22:56 +0200 Subject: iio: adc: mcp3911: simplify usage of spi->dev Replace the usage of `adc->spi->dev` with `dev` to make the code prettier. Reviewed-by: Andy Shevchenko Suggested-by: Andy Shevchenko Signed-off-by: Marcus Folkesson Link: https://lore.kernel.org/r/20230822192259.1125792-4-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3911.c | 50 ++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 7fb3ab4a4256..31ee8d7f81de 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -269,6 +269,7 @@ out: static int mcp3911_calc_scale_table(struct mcp3911 *adc) { + struct device *dev = &adc->spi->dev; u32 ref = MCP3911_INT_VREF_MV; u32 div; int ret; @@ -277,7 +278,7 @@ static int mcp3911_calc_scale_table(struct mcp3911 *adc) if (adc->vref) { ret = regulator_get_voltage(adc->vref); if (ret < 0) { - return dev_err_probe(&adc->spi->dev, ret, "failed to get vref voltage\n"); + return dev_err_probe(dev, ret, "failed to get vref voltage\n"); } ref = ret / 1000; @@ -334,6 +335,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct mcp3911 *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; struct spi_transfer xfer[] = { { .tx_buf = &adc->tx_buf, @@ -351,8 +353,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p) adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr); ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer)); if (ret < 0) { - dev_warn(&adc->spi->dev, - "failed to get conversion data\n"); + dev_warn(dev, "failed to get conversion data\n"); goto out; } @@ -397,7 +398,7 @@ static int mcp3911_config(struct mcp3911 *adc) "invalid device address (%i). Must be in range 0-3.\n", adc->dev_addr); } - dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); + dev_dbg(dev, "use device address %i\n", adc->dev_addr); ret = mcp3911_read(adc, MCP3911_REG_CONFIG, ®val, 2); if (ret) @@ -405,21 +406,19 @@ static int mcp3911_config(struct mcp3911 *adc) regval &= ~MCP3911_CONFIG_VREFEXT; if (adc->vref) { - dev_dbg(&adc->spi->dev, "use external voltage reference\n"); + dev_dbg(dev, "use external voltage reference\n"); regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1); } else { - dev_dbg(&adc->spi->dev, - "use internal voltage reference (1.2V)\n"); + dev_dbg(dev, "use internal voltage reference (1.2V)\n"); regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 0); } regval &= ~MCP3911_CONFIG_CLKEXT; if (adc->clki) { - dev_dbg(&adc->spi->dev, "use external clock as clocksource\n"); + dev_dbg(dev, "use external clock as clocksource\n"); regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 1); } else { - dev_dbg(&adc->spi->dev, - "use crystal oscillator as clocksource\n"); + dev_dbg(dev, "use crystal oscillator as clocksource\n"); regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 0); } @@ -467,14 +466,14 @@ static int mcp3911_probe(struct spi_device *spi) struct mcp3911 *adc; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); if (!indio_dev) return -ENOMEM; adc = iio_priv(indio_dev); adc->spi = spi; - adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref"); + adc->vref = devm_regulator_get_optional(dev, "vref"); if (IS_ERR(adc->vref)) { if (PTR_ERR(adc->vref) == -ENODEV) { adc->vref = NULL; @@ -487,13 +486,12 @@ static int mcp3911_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, - mcp3911_cleanup_regulator, adc->vref); + ret = devm_add_action_or_reset(dev, mcp3911_cleanup_regulator, adc->vref); if (ret) return ret; } - adc->clki = devm_clk_get_enabled(&adc->spi->dev, NULL); + adc->clki = devm_clk_get_enabled(dev, NULL); if (IS_ERR(adc->clki)) { if (PTR_ERR(adc->clki) == -ENOENT) { adc->clki = NULL; @@ -506,7 +504,7 @@ static int mcp3911_probe(struct spi_device *spi) if (ret) return ret; - if (device_property_read_bool(&adc->spi->dev, "microchip,data-ready-hiz")) + if (device_property_read_bool(dev, "microchip,data-ready-hiz")) ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, 0, 2); else @@ -540,15 +538,14 @@ static int mcp3911_probe(struct spi_device *spi) mutex_init(&adc->lock); if (spi->irq > 0) { - adc->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", - indio_dev->name, - iio_device_id(indio_dev)); + adc->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); if (!adc->trig) return -ENOMEM; adc->trig->ops = &mcp3911_trigger_ops; iio_trigger_set_drvdata(adc->trig, adc); - ret = devm_iio_trigger_register(&spi->dev, adc->trig); + ret = devm_iio_trigger_register(dev, adc->trig); if (ret) return ret; @@ -557,20 +554,19 @@ static int mcp3911_probe(struct spi_device *spi) * Some platforms might not allow the option to power it down so * don't enable the interrupt to avoid extra load on the system. */ - ret = devm_request_irq(&spi->dev, spi->irq, - &iio_trigger_generic_data_rdy_poll, IRQF_NO_AUTOEN | IRQF_ONESHOT, - indio_dev->name, adc->trig); + ret = devm_request_irq(dev, spi->irq, &iio_trigger_generic_data_rdy_poll, + IRQF_NO_AUTOEN | IRQF_ONESHOT, + indio_dev->name, adc->trig); if (ret) return ret; } - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, - NULL, - mcp3911_trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + mcp3911_trigger_handler, NULL); if (ret) return ret; - return devm_iio_device_register(&adc->spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id mcp3911_dt_ids[] = { -- cgit v1.2.3 From 7e5047334f04d2c8588ee23eadb88ad52998745b Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 22 Aug 2023 21:22:57 +0200 Subject: iio: adc: mcp3911: fix indentation The whole file does not make use of indentation properly. Do something about it. Reviewed-by: Andy Shevchenko Signed-off-by: Marcus Folkesson Link: https://lore.kernel.org/r/20230822192259.1125792-5-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3911.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 31ee8d7f81de..f9db08813645 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -33,7 +33,7 @@ #define MCP3911_GAIN_VAL(ch, val) ((val << 3 * ch) & MCP3911_GAIN_MASK(ch)) #define MCP3911_REG_STATUSCOM 0x0a -#define MCP3911_STATUSCOM_DRHIZ BIT(12) +#define MCP3911_STATUSCOM_DRHIZ BIT(12) #define MCP3911_STATUSCOM_READ GENMASK(7, 6) #define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) #define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) @@ -111,8 +111,7 @@ static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len) return spi_write(adc->spi, &val, len + 1); } -static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, - u32 val, u8 len) +static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, u32 val, u8 len) { u32 tmp; int ret; @@ -127,8 +126,8 @@ static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, } static int mcp3911_write_raw_get_fmt(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - long mask) + struct iio_chan_spec const *chan, + long mask) { switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -141,9 +140,9 @@ static int mcp3911_write_raw_get_fmt(struct iio_dev *indio_dev, } static int mcp3911_read_avail(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - const int **vals, int *type, int *length, - long info) + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) { switch (info) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -212,8 +211,8 @@ out: } static int mcp3911_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *channel, int val, - int val2, long mask) + struct iio_chan_spec const *channel, int val, + int val2, long mask) { struct mcp3911 *adc = iio_priv(indio_dev); int ret = -EINVAL; @@ -223,12 +222,12 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: for (int i = 0; i < MCP3911_NUM_SCALES; i++) { if (val == mcp3911_scale_table[i][0] && - val2 == mcp3911_scale_table[i][1]) { + val2 == mcp3911_scale_table[i][1]) { adc->gain[channel->channel] = BIT(i); ret = mcp3911_update(adc, MCP3911_REG_GAIN, - MCP3911_GAIN_MASK(channel->channel), - MCP3911_GAIN_VAL(channel->channel, i), 1); + MCP3911_GAIN_MASK(channel->channel), + MCP3911_GAIN_VAL(channel->channel, i), 1); } } break; @@ -246,8 +245,8 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, /* Enable offset*/ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, - MCP3911_STATUSCOM_EN_OFFCAL, - MCP3911_STATUSCOM_EN_OFFCAL, 2); + MCP3911_STATUSCOM_EN_OFFCAL, + MCP3911_STATUSCOM_EN_OFFCAL, 2); break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -255,7 +254,7 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, if (val == mcp3911_osr_table[i]) { val = FIELD_PREP(MCP3911_CONFIG_OSR, i); ret = mcp3911_update(adc, MCP3911_REG_CONFIG, MCP3911_CONFIG_OSR, - val, 2); + val, 2); break; } } @@ -506,10 +505,10 @@ static int mcp3911_probe(struct spi_device *spi) if (device_property_read_bool(dev, "microchip,data-ready-hiz")) ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, - 0, 2); + 0, 2); else ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, - MCP3911_STATUSCOM_DRHIZ, 2); + MCP3911_STATUSCOM_DRHIZ, 2); if (ret) return ret; @@ -517,12 +516,12 @@ static int mcp3911_probe(struct spi_device *spi) if (ret) return ret; - /* Set gain to 1 for all channels */ + /* Set gain to 1 for all channels */ for (int i = 0; i < MCP3911_NUM_CHANNELS; i++) { adc->gain[i] = 1; ret = mcp3911_update(adc, MCP3911_REG_GAIN, - MCP3911_GAIN_MASK(i), - MCP3911_GAIN_VAL(i, 0), 1); + MCP3911_GAIN_MASK(i), + MCP3911_GAIN_VAL(i, 0), 1); if (ret) return ret; } -- cgit v1.2.3 From 593d73629a441bccad21a5bffc126df8f9280831 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 22 Aug 2023 21:22:58 +0200 Subject: iio: adc: mcp3911: avoid ambiguity parameters in macros Name macro parameters after what they represent instead of 'x' and make sure the evaluation of that will have no side effects. Reviewed-by: Andy Shevchenko Signed-off-by: Marcus Folkesson Link: https://lore.kernel.org/r/20230822192259.1125792-6-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3911.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index f9db08813645..281cc1211fd6 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -29,8 +29,8 @@ #define MCP3911_REG_MOD 0x06 #define MCP3911_REG_PHASE 0x07 #define MCP3911_REG_GAIN 0x09 -#define MCP3911_GAIN_MASK(ch) (GENMASK(2, 0) << 3 * ch) -#define MCP3911_GAIN_VAL(ch, val) ((val << 3 * ch) & MCP3911_GAIN_MASK(ch)) +#define MCP3911_GAIN_MASK(ch) (GENMASK(2, 0) << 3 * (ch)) +#define MCP3911_GAIN_VAL(ch, val) ((val << 3 * (ch)) & MCP3911_GAIN_MASK(ch)) #define MCP3911_REG_STATUSCOM 0x0a #define MCP3911_STATUSCOM_DRHIZ BIT(12) @@ -51,8 +51,8 @@ #define MCP3911_REG_GAINCAL_CH1 0x17 #define MCP3911_REG_VREFCAL 0x1a -#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3) -#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6) +#define MCP3911_CHANNEL(ch) (MCP3911_REG_CHANNEL0 + (ch) * 3) +#define MCP3911_OFFCAL(ch) (MCP3911_REG_OFFCAL_CH0 + (ch) * 6) /* Internal voltage reference in mV */ #define MCP3911_INT_VREF_MV 1200 -- cgit v1.2.3 From 732ad34260d367661e2f20a5c5a4d54dbc856ac4 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 22 Aug 2023 21:22:59 +0200 Subject: iio: adc: mcp3911: add support for the whole MCP39xx family Microchip does have many similar chips, add support for those. The new supported chips are: - microchip,mcp3910 - microchip,mcp3912 - microchip,mcp3913 - microchip,mcp3914 - microchip,mcp3918 - microchip,mcp3919 Signed-off-by: Marcus Folkesson Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230822192259.1125792-7-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 6 +- drivers/iio/adc/mcp3911.c | 466 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 415 insertions(+), 57 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 517b3db114b8..432d3f9bfde1 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -785,8 +785,10 @@ config MCP3911 select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say yes here to build support for Microchip Technology's MCP3911 - analog to digital converter. + Say yes here to build support for one of the following + Microchip Technology's analog to digital converters: + MCP3910, MCP3911, MCP3912, MCP3913, MCP3914, + MCP3918 and MCP3919. This driver can also be built as a module. If so, the module will be called mcp3911. diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 281cc1211fd6..d864558bc087 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -61,12 +61,56 @@ #define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 6) | (0 << 0)) & 0xff) #define MCP3911_REG_MASK GENMASK(4, 1) -#define MCP3911_NUM_CHANNELS 2 #define MCP3911_NUM_SCALES 6 +/* Registers compatible with MCP3910 */ +#define MCP3910_REG_STATUSCOM 0x0c +#define MCP3910_STATUSCOM_READ GENMASK(23, 22) +#define MCP3910_STATUSCOM_DRHIZ BIT(20) + +#define MCP3910_REG_GAIN 0x0b + +#define MCP3910_REG_CONFIG0 0x0d +#define MCP3910_CONFIG0_EN_OFFCAL BIT(23) +#define MCP3910_CONFIG0_OSR GENMASK(15, 13) + +#define MCP3910_REG_CONFIG1 0x0e +#define MCP3910_CONFIG1_CLKEXT BIT(6) +#define MCP3910_CONFIG1_VREFEXT BIT(7) + +#define MCP3910_REG_OFFCAL_CH0 0x0f +#define MCP3910_OFFCAL(ch) (MCP3910_REG_OFFCAL_CH0 + (ch) * 6) + +/* Maximal number of channels used by the MCP39XX family */ +#define MCP39XX_MAX_NUM_CHANNELS 8 + static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 }; static u32 mcp3911_scale_table[MCP3911_NUM_SCALES][2]; +enum mcp3911_id { + MCP3910, + MCP3911, + MCP3912, + MCP3913, + MCP3914, + MCP3918, + MCP3919, +}; + +struct mcp3911; +struct mcp3911_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + + int (*config)(struct mcp3911 *adc); + int (*get_osr)(struct mcp3911 *adc, u32 *val); + int (*set_osr)(struct mcp3911 *adc, u32 val); + int (*enable_offset)(struct mcp3911 *adc, bool enable); + int (*get_offset)(struct mcp3911 *adc, int channel, int *val); + int (*set_offset)(struct mcp3911 *adc, int channel, int val); + int (*set_scale)(struct mcp3911 *adc, int channel, u32 val); +}; + struct mcp3911 { struct spi_device *spi; struct mutex lock; @@ -74,14 +118,15 @@ struct mcp3911 { struct clk *clki; u32 dev_addr; struct iio_trigger *trig; - u32 gain[MCP3911_NUM_CHANNELS]; + u32 gain[MCP39XX_MAX_NUM_CHANNELS]; + const struct mcp3911_chip_info *chip; struct { - u32 channels[MCP3911_NUM_CHANNELS]; + u32 channels[MCP39XX_MAX_NUM_CHANNELS]; s64 ts __aligned(8); } scan; u8 tx_buf __aligned(IIO_DMA_MINALIGN); - u8 rx_buf[MCP3911_NUM_CHANNELS * 3]; + u8 rx_buf[MCP39XX_MAX_NUM_CHANNELS * 3]; }; static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len) @@ -125,6 +170,112 @@ static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, u32 val, u8 len return mcp3911_write(adc, reg, val, len); } +static int mcp3910_enable_offset(struct mcp3911 *adc, bool enable) +{ + unsigned int mask = MCP3910_CONFIG0_EN_OFFCAL; + unsigned int value = enable ? mask : 0; + + return mcp3911_update(adc, MCP3910_REG_CONFIG0, mask, value, 3); +} + +static int mcp3910_get_offset(struct mcp3911 *adc, int channel, int *val) +{ + return mcp3911_read(adc, MCP3910_OFFCAL(channel), val, 3); +} + +static int mcp3910_set_offset(struct mcp3911 *adc, int channel, int val) +{ + int ret; + + ret = mcp3911_write(adc, MCP3910_OFFCAL(channel), val, 3); + if (ret) + return ret; + + return adc->chip->enable_offset(adc, 1); +} + +static int mcp3911_enable_offset(struct mcp3911 *adc, bool enable) +{ + unsigned int mask = MCP3911_STATUSCOM_EN_OFFCAL; + unsigned int value = enable ? mask : 0; + + return mcp3911_update(adc, MCP3911_REG_STATUSCOM, mask, value, 2); +} + +static int mcp3911_get_offset(struct mcp3911 *adc, int channel, int *val) +{ + return mcp3911_read(adc, MCP3911_OFFCAL(channel), val, 3); +} + +static int mcp3911_set_offset(struct mcp3911 *adc, int channel, int val) +{ + int ret; + + ret = mcp3911_write(adc, MCP3911_OFFCAL(channel), val, 3); + if (ret) + return ret; + + return adc->chip->enable_offset(adc, 1); +} + +static int mcp3910_get_osr(struct mcp3911 *adc, u32 *val) +{ + int ret; + unsigned int osr; + + ret = mcp3911_read(adc, MCP3910_REG_CONFIG0, val, 3); + if (ret) + return ret; + + osr = FIELD_GET(MCP3910_CONFIG0_OSR, *val); + *val = 32 << osr; + return 0; +} + +static int mcp3910_set_osr(struct mcp3911 *adc, u32 val) +{ + unsigned int osr = FIELD_PREP(MCP3910_CONFIG0_OSR, val); + unsigned int mask = MCP3910_CONFIG0_OSR; + + return mcp3911_update(adc, MCP3910_REG_CONFIG0, mask, osr, 3); +} + +static int mcp3911_set_osr(struct mcp3911 *adc, u32 val) +{ + unsigned int osr = FIELD_PREP(MCP3911_CONFIG_OSR, val); + unsigned int mask = MCP3911_CONFIG_OSR; + + return mcp3911_update(adc, MCP3911_REG_CONFIG, mask, osr, 2); +} + +static int mcp3911_get_osr(struct mcp3911 *adc, u32 *val) +{ + int ret; + unsigned int osr; + + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, val, 2); + if (ret) + return ret; + + osr = FIELD_GET(MCP3911_CONFIG_OSR, *val); + *val = 32 << osr; + return ret; +} + +static int mcp3910_set_scale(struct mcp3911 *adc, int channel, u32 val) +{ + return mcp3911_update(adc, MCP3910_REG_GAIN, + MCP3911_GAIN_MASK(channel), + MCP3911_GAIN_VAL(channel, val), 3); +} + +static int mcp3911_set_scale(struct mcp3911 *adc, int channel, u32 val) +{ + return mcp3911_update(adc, MCP3911_REG_GAIN, + MCP3911_GAIN_MASK(channel), + MCP3911_GAIN_VAL(channel, val), 1); +} + static int mcp3911_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) @@ -181,20 +332,18 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_OFFSET: - ret = mcp3911_read(adc, - MCP3911_OFFCAL(channel->channel), val, 3); + + ret = adc->chip->get_offset(adc, channel->channel, val); if (ret) goto out; ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - ret = mcp3911_read(adc, MCP3911_REG_CONFIG, val, 2); + ret = adc->chip->get_osr(adc, val); if (ret) goto out; - *val = FIELD_GET(MCP3911_CONFIG_OSR, *val); - *val = 32 << *val; ret = IIO_VAL_INT; break; @@ -225,9 +374,7 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, val2 == mcp3911_scale_table[i][1]) { adc->gain[channel->channel] = BIT(i); - ret = mcp3911_update(adc, MCP3911_REG_GAIN, - MCP3911_GAIN_MASK(channel->channel), - MCP3911_GAIN_VAL(channel->channel, i), 1); + ret = adc->chip->set_scale(adc, channel->channel, i); } } break; @@ -237,24 +384,13 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, goto out; } - /* Write offset */ - ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val, - 3); - if (ret) - goto out; - - /* Enable offset*/ - ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, - MCP3911_STATUSCOM_EN_OFFCAL, - MCP3911_STATUSCOM_EN_OFFCAL, 2); + ret = adc->chip->set_offset(adc, channel->channel, val); break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: for (int i = 0; i < ARRAY_SIZE(mcp3911_osr_table); i++) { if (val == mcp3911_osr_table[i]) { - val = FIELD_PREP(MCP3911_CONFIG_OSR, i); - ret = mcp3911_update(adc, MCP3911_REG_CONFIG, MCP3911_CONFIG_OSR, - val, 2); + ret = adc->chip->set_osr(adc, i); break; } } @@ -323,12 +459,60 @@ static int mcp3911_calc_scale_table(struct mcp3911 *adc) }, \ } +static const struct iio_chan_spec mcp3910_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + static const struct iio_chan_spec mcp3911_channels[] = { MCP3911_CHAN(0), MCP3911_CHAN(1), IIO_CHAN_SOFT_TIMESTAMP(2), }; +static const struct iio_chan_spec mcp3912_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + MCP3911_CHAN(3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec mcp3913_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + MCP3911_CHAN(3), + MCP3911_CHAN(4), + MCP3911_CHAN(5), + IIO_CHAN_SOFT_TIMESTAMP(6), +}; + +static const struct iio_chan_spec mcp3914_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + MCP3911_CHAN(3), + MCP3911_CHAN(4), + MCP3911_CHAN(5), + MCP3911_CHAN(6), + MCP3911_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec mcp3918_channels[] = { + MCP3911_CHAN(0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec mcp3919_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + static irqreturn_t mcp3911_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -341,7 +525,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p) .len = 1, }, { .rx_buf = adc->rx_buf, - .len = sizeof(adc->rx_buf), + .len = (adc->chip->num_channels - 1) * 3, }, }; int scan_index; @@ -384,21 +568,6 @@ static int mcp3911_config(struct mcp3911 *adc) u32 regval; int ret; - ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr); - - /* - * Fallback to "device-addr" due to historical mismatch between - * dt-bindings and implementation - */ - if (ret) - device_property_read_u32(dev, "device-addr", &adc->dev_addr); - if (adc->dev_addr > 3) { - return dev_err_probe(dev, -EINVAL, - "invalid device address (%i). Must be in range 0-3.\n", - adc->dev_addr); - } - dev_dbg(dev, "use device address %i\n", adc->dev_addr); - ret = mcp3911_read(adc, MCP3911_REG_CONFIG, ®val, 2); if (ret) return ret; @@ -433,7 +602,97 @@ static int mcp3911_config(struct mcp3911 *adc) regval &= ~MCP3911_STATUSCOM_READ; regval |= FIELD_PREP(MCP3911_STATUSCOM_READ, 0x02); - return mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2); + regval &= ~MCP3911_STATUSCOM_DRHIZ; + if (device_property_read_bool(dev, "microchip,data-ready-hiz")) + regval |= FIELD_PREP(MCP3911_STATUSCOM_DRHIZ, 0); + else + regval |= FIELD_PREP(MCP3911_STATUSCOM_DRHIZ, 1); + + /* Disable offset to ignore any old values in offset register */ + regval &= ~MCP3911_STATUSCOM_EN_OFFCAL; + + ret = mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2); + if (ret) + return ret; + + /* Set gain to 1 for all channels */ + ret = mcp3911_read(adc, MCP3911_REG_GAIN, ®val, 1); + if (ret) + return ret; + + for (int i = 0; i < adc->chip->num_channels - 1; i++) { + adc->gain[i] = 1; + regval &= ~MCP3911_GAIN_MASK(i); + } + + return mcp3911_write(adc, MCP3911_REG_GAIN, regval, 1); +} + +static int mcp3910_config(struct mcp3911 *adc) +{ + struct device *dev = &adc->spi->dev; + u32 regval; + int ret; + + ret = mcp3911_read(adc, MCP3910_REG_CONFIG1, ®val, 3); + if (ret) + return ret; + + regval &= ~MCP3910_CONFIG1_VREFEXT; + if (adc->vref) { + dev_dbg(dev, "use external voltage reference\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_VREFEXT, 1); + } else { + dev_dbg(dev, "use internal voltage reference (1.2V)\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_VREFEXT, 0); + } + + regval &= ~MCP3910_CONFIG1_CLKEXT; + if (adc->clki) { + dev_dbg(dev, "use external clock as clocksource\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_CLKEXT, 1); + } else { + dev_dbg(dev, "use crystal oscillator as clocksource\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_CLKEXT, 0); + } + + ret = mcp3911_write(adc, MCP3910_REG_CONFIG1, regval, 3); + if (ret) + return ret; + + ret = mcp3911_read(adc, MCP3910_REG_STATUSCOM, ®val, 3); + if (ret) + return ret; + + /* Address counter incremented, cycle through register types */ + regval &= ~MCP3910_STATUSCOM_READ; + regval |= FIELD_PREP(MCP3910_STATUSCOM_READ, 0x02); + + regval &= ~MCP3910_STATUSCOM_DRHIZ; + if (device_property_read_bool(dev, "microchip,data-ready-hiz")) + regval |= FIELD_PREP(MCP3910_STATUSCOM_DRHIZ, 0); + else + regval |= FIELD_PREP(MCP3910_STATUSCOM_DRHIZ, 1); + + ret = mcp3911_write(adc, MCP3910_REG_STATUSCOM, regval, 3); + if (ret) + return ret; + + /* Set gain to 1 for all channels */ + ret = mcp3911_read(adc, MCP3910_REG_GAIN, ®val, 3); + if (ret) + return ret; + + for (int i = 0; i < adc->chip->num_channels - 1; i++) { + adc->gain[i] = 1; + regval &= ~MCP3911_GAIN_MASK(i); + } + ret = mcp3911_write(adc, MCP3910_REG_GAIN, regval, 3); + if (ret) + return ret; + + /* Disable offset to ignore any old values in offset register */ + return adc->chip->enable_offset(adc, 0); } static void mcp3911_cleanup_regulator(void *vref) @@ -471,6 +730,7 @@ static int mcp3911_probe(struct spi_device *spi) adc = iio_priv(indio_dev); adc->spi = spi; + adc->chip = spi_get_device_match_data(spi); adc->vref = devm_regulator_get_optional(dev, "vref"); if (IS_ERR(adc->vref)) { @@ -499,16 +759,21 @@ static int mcp3911_probe(struct spi_device *spi) } } - ret = mcp3911_config(adc); + /* + * Fallback to "device-addr" due to historical mismatch between + * dt-bindings and implementation. + */ + ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr); if (ret) - return ret; + device_property_read_u32(dev, "device-addr", &adc->dev_addr); + if (adc->dev_addr > 3) { + return dev_err_probe(dev, -EINVAL, + "invalid device address (%i). Must be in range 0-3.\n", + adc->dev_addr); + } + dev_dbg(dev, "use device address %i\n", adc->dev_addr); - if (device_property_read_bool(dev, "microchip,data-ready-hiz")) - ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, - 0, 2); - else - ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, - MCP3911_STATUSCOM_DRHIZ, 2); + ret = adc->chip->config(adc); if (ret) return ret; @@ -517,7 +782,7 @@ static int mcp3911_probe(struct spi_device *spi) return ret; /* Set gain to 1 for all channels */ - for (int i = 0; i < MCP3911_NUM_CHANNELS; i++) { + for (int i = 0; i < adc->chip->num_channels - 1; i++) { adc->gain[i] = 1; ret = mcp3911_update(adc, MCP3911_REG_GAIN, MCP3911_GAIN_MASK(i), @@ -531,8 +796,8 @@ static int mcp3911_probe(struct spi_device *spi) indio_dev->info = &mcp3911_info; spi_set_drvdata(spi, indio_dev); - indio_dev->channels = mcp3911_channels; - indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels); + indio_dev->channels = adc->chip->channels; + indio_dev->num_channels = adc->chip->num_channels; mutex_init(&adc->lock); @@ -568,14 +833,105 @@ static int mcp3911_probe(struct spi_device *spi) return devm_iio_device_register(dev, indio_dev); } +static const struct mcp3911_chip_info mcp3911_chip_info[] = { + [MCP3910] = { + .channels = mcp3910_channels, + .num_channels = ARRAY_SIZE(mcp3910_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + }, + [MCP3911] = { + .channels = mcp3911_channels, + .num_channels = ARRAY_SIZE(mcp3911_channels), + .config = mcp3911_config, + .get_osr = mcp3911_get_osr, + .set_osr = mcp3911_set_osr, + .enable_offset = mcp3911_enable_offset, + .get_offset = mcp3911_get_offset, + .set_offset = mcp3911_set_offset, + .set_scale = mcp3911_set_scale, + }, + [MCP3912] = { + .channels = mcp3912_channels, + .num_channels = ARRAY_SIZE(mcp3912_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + }, + [MCP3913] = { + .channels = mcp3913_channels, + .num_channels = ARRAY_SIZE(mcp3913_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + }, + [MCP3914] = { + .channels = mcp3914_channels, + .num_channels = ARRAY_SIZE(mcp3914_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + }, + [MCP3918] = { + .channels = mcp3918_channels, + .num_channels = ARRAY_SIZE(mcp3918_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + }, + [MCP3919] = { + .channels = mcp3919_channels, + .num_channels = ARRAY_SIZE(mcp3919_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + }, +}; static const struct of_device_id mcp3911_dt_ids[] = { - { .compatible = "microchip,mcp3911" }, + { .compatible = "microchip,mcp3910", .data = &mcp3911_chip_info[MCP3910] }, + { .compatible = "microchip,mcp3911", .data = &mcp3911_chip_info[MCP3911] }, + { .compatible = "microchip,mcp3912", .data = &mcp3911_chip_info[MCP3912] }, + { .compatible = "microchip,mcp3913", .data = &mcp3911_chip_info[MCP3913] }, + { .compatible = "microchip,mcp3914", .data = &mcp3911_chip_info[MCP3914] }, + { .compatible = "microchip,mcp3918", .data = &mcp3911_chip_info[MCP3918] }, + { .compatible = "microchip,mcp3919", .data = &mcp3911_chip_info[MCP3919] }, { } }; MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); static const struct spi_device_id mcp3911_id[] = { - { "mcp3911", 0 }, + { "mcp3910", (kernel_ulong_t)&mcp3911_chip_info[MCP3910] }, + { "mcp3911", (kernel_ulong_t)&mcp3911_chip_info[MCP3911] }, + { "mcp3912", (kernel_ulong_t)&mcp3911_chip_info[MCP3912] }, + { "mcp3913", (kernel_ulong_t)&mcp3911_chip_info[MCP3913] }, + { "mcp3914", (kernel_ulong_t)&mcp3911_chip_info[MCP3914] }, + { "mcp3918", (kernel_ulong_t)&mcp3911_chip_info[MCP3918] }, + { "mcp3919", (kernel_ulong_t)&mcp3911_chip_info[MCP3919] }, { } }; MODULE_DEVICE_TABLE(spi, mcp3911_id); -- cgit v1.2.3 From da2737c96b6bf5f4090979b0dddd998b7b5b4362 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sun, 20 Aug 2023 14:32:29 +0200 Subject: dt-bindings: iio: adc: Add TI TWL603X GPADC Document TI TWL603X GPADC devicetree bindings. A driver is already there, the compatibles are used, but not documented. Signed-off-by: Andreas Kemnade Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230820123229.530371-1-andreas@kemnade.info Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/ti,twl6030-gpadc.yaml | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ti,twl6030-gpadc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/ti,twl6030-gpadc.yaml b/Documentation/devicetree/bindings/iio/adc/ti,twl6030-gpadc.yaml new file mode 100644 index 000000000000..e779a8986e0b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,twl6030-gpadc.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,twl6030-gpadc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPADC subsystem in the TWL6030 power module + +maintainers: + - Andreas Kemnade + +description: + The GPADC subsystem in the TWL603X consists of a 10-bit ADC + combined with a 15-input analog multiplexer in the TWL6030 resp. a + 19-input analog muliplexer in the TWL6032. + +properties: + compatible: + enum: + - ti,twl6030-gpadc + - ti,twl6032-gpadc + + interrupts: + maxItems: 1 + + "#io-channel-cells": + const: 1 + +required: + - compatible + - interrupts + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + gpadc { + compatible = "ti,twl6030-gpadc"; + interrupts = <3>; + #io-channel-cells = <1>; + }; +... -- cgit v1.2.3 From 9979cc64853b598518a485c2e554657d5c7a00c8 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 28 Aug 2023 14:27:16 +0800 Subject: iio: frequency: adf4350: Use device managed functions and fix power down issue. The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. Also replace devm_regulator_get() and regulator_enable() with devm_regulator_get_enable() helper and remove regulator_disable(). Replace iio_device_register() with devm_iio_device_register() and remove iio_device_unregister(). And st->reg is not used anymore, so remove it. As Jonathan pointed out, couple of things that are wrong: 1) The device is powered down 'before' we unregister it with the subsystem and as such userspace interfaces are still exposed which probably won't do the right thing if the chip is powered down. 2) This isn't done in the error paths in probe. To solve this problem, register a new callback adf4350_power_down() with devm_add_action_or_reset(), to enable software power down in both error and device detach path. So the remove function can be removed. Remove spi_set_drvdata() from the probe function, since spi_get_drvdata() is not used anymore. Fixes: e31166f0fd48 ("iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers") Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230828062717.2310219-1-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 75 +++++++++++++---------------------------- 1 file changed, 23 insertions(+), 52 deletions(-) diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 85e289700c3c..4abf80f75ef5 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -33,7 +33,6 @@ enum { struct adf4350_state { struct spi_device *spi; - struct regulator *reg; struct gpio_desc *lock_detect_gpiod; struct adf4350_platform_data *pdata; struct clk *clk; @@ -469,6 +468,15 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) return pdata; } +static void adf4350_power_down(void *data) +{ + struct iio_dev *indio_dev = data; + struct adf4350_state *st = iio_priv(indio_dev); + + st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN; + adf4350_sync_config(st); +} + static int adf4350_probe(struct spi_device *spi) { struct adf4350_platform_data *pdata; @@ -491,31 +499,21 @@ static int adf4350_probe(struct spi_device *spi) } if (!pdata->clkin) { - clk = devm_clk_get(&spi->dev, "clkin"); + clk = devm_clk_get_enabled(&spi->dev, "clkin"); if (IS_ERR(clk)) - return -EPROBE_DEFER; - - ret = clk_prepare_enable(clk); - if (ret < 0) - return ret; + return PTR_ERR(clk); } indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_disable_clk; - } + if (indio_dev == NULL) + return -ENOMEM; st = iio_priv(indio_dev); - st->reg = devm_regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - goto error_disable_clk; - } + ret = devm_regulator_get_enable(&spi->dev, "vcc"); + if (ret) + return ret; - spi_set_drvdata(spi, indio_dev); st->spi = spi; st->pdata = pdata; @@ -544,47 +542,21 @@ static int adf4350_probe(struct spi_device *spi) st->lock_detect_gpiod = devm_gpiod_get_optional(&spi->dev, NULL, GPIOD_IN); - if (IS_ERR(st->lock_detect_gpiod)) { - ret = PTR_ERR(st->lock_detect_gpiod); - goto error_disable_reg; - } + if (IS_ERR(st->lock_detect_gpiod)) + return PTR_ERR(st->lock_detect_gpiod); if (pdata->power_up_frequency) { ret = adf4350_set_freq(st, pdata->power_up_frequency); if (ret) - goto error_disable_reg; + return ret; } - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(&spi->dev, adf4350_power_down, indio_dev); if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); -error_disable_clk: - clk_disable_unprepare(clk); - - return ret; -} - -static void adf4350_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct adf4350_state *st = iio_priv(indio_dev); - struct regulator *reg = st->reg; - - st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN; - adf4350_sync_config(st); - - iio_device_unregister(indio_dev); - - clk_disable_unprepare(st->clk); + return dev_err_probe(&spi->dev, ret, + "Failed to add action to managed power down\n"); - if (!IS_ERR(reg)) - regulator_disable(reg); + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id adf4350_of_match[] = { @@ -607,7 +579,6 @@ static struct spi_driver adf4350_driver = { .of_match_table = adf4350_of_match, }, .probe = adf4350_probe, - .remove = adf4350_remove, .id_table = adf4350_id, }; module_spi_driver(adf4350_driver); -- cgit v1.2.3 From 449635ec210eb50ee5d11be934350d994080ff40 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 28 Aug 2023 14:45:44 +0800 Subject: iio: adc: at91_adc: Use devm_request_irq() helper function Use devm_request_irq() to request the interrupt, so we can avoid having to manually clean this up. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230828064546.2383857-2-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index de6650f9c4b1..771ad5c4a131 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1077,11 +1077,13 @@ static int at91_adc_probe(struct platform_device *pdev) at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF); if (st->caps->has_tsmr) - ret = request_irq(st->irq, at91_adc_9x5_interrupt, 0, - pdev->dev.driver->name, idev); + ret = devm_request_irq(&pdev->dev, st->irq, + at91_adc_9x5_interrupt, 0, + pdev->dev.driver->name, idev); else - ret = request_irq(st->irq, at91_adc_rl_interrupt, 0, - pdev->dev.driver->name, idev); + ret = devm_request_irq(&pdev->dev, st->irq, + at91_adc_rl_interrupt, 0, + pdev->dev.driver->name, idev); if (ret) { dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); return ret; @@ -1090,15 +1092,14 @@ static int at91_adc_probe(struct platform_device *pdev) st->clk = devm_clk_get(&pdev->dev, "adc_clk"); if (IS_ERR(st->clk)) { dev_err(&pdev->dev, "Failed to get the clock.\n"); - ret = PTR_ERR(st->clk); - goto error_free_irq; + return PTR_ERR(st->clk); } ret = clk_prepare_enable(st->clk); if (ret) { dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); - goto error_free_irq; + return ret; } st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk"); @@ -1211,8 +1212,6 @@ error_disable_adc_clk: clk_disable_unprepare(st->adc_clk); error_disable_clk: clk_disable_unprepare(st->clk); -error_free_irq: - free_irq(st->irq, idev); return ret; } @@ -1230,7 +1229,6 @@ static int at91_adc_remove(struct platform_device *pdev) } clk_disable_unprepare(st->adc_clk); clk_disable_unprepare(st->clk); - free_irq(st->irq, idev); return 0; } -- cgit v1.2.3 From 892de7031e905f242ea4e851abe5f4f7222e7455 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 28 Aug 2023 14:45:45 +0800 Subject: iio: adc: at91_adc: Use devm_clk_get_enabled() helper function The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. This simplifies the code. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230828064546.2383857-3-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 771ad5c4a131..e5610722ce0b 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1089,31 +1089,18 @@ static int at91_adc_probe(struct platform_device *pdev) return ret; } - st->clk = devm_clk_get(&pdev->dev, "adc_clk"); + st->clk = devm_clk_get_enabled(&pdev->dev, "adc_clk"); if (IS_ERR(st->clk)) { - dev_err(&pdev->dev, "Failed to get the clock.\n"); - return PTR_ERR(st->clk); - } - - ret = clk_prepare_enable(st->clk); - if (ret) { dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); - return ret; + return PTR_ERR(st->clk); } - st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk"); + st->adc_clk = devm_clk_get_enabled(&pdev->dev, "adc_op_clk"); if (IS_ERR(st->adc_clk)) { - dev_err(&pdev->dev, "Failed to get the ADC clock.\n"); - ret = PTR_ERR(st->adc_clk); - goto error_disable_clk; - } - - ret = clk_prepare_enable(st->adc_clk); - if (ret) { dev_err(&pdev->dev, "Could not prepare or enable the ADC clock.\n"); - goto error_disable_clk; + return PTR_ERR(st->adc_clk); } /* @@ -1132,8 +1119,7 @@ static int at91_adc_probe(struct platform_device *pdev) if (!st->startup_time) { dev_err(&pdev->dev, "No startup time available.\n"); - ret = -EINVAL; - goto error_disable_adc_clk; + return -EINVAL; } ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz); @@ -1161,7 +1147,7 @@ static int at91_adc_probe(struct platform_device *pdev) ret = at91_adc_channel_init(idev); if (ret < 0) { dev_err(&pdev->dev, "Couldn't initialize the channels.\n"); - goto error_disable_adc_clk; + return ret; } init_waitqueue_head(&st->wq_data_avail); @@ -1176,19 +1162,19 @@ static int at91_adc_probe(struct platform_device *pdev) ret = at91_adc_buffer_init(idev); if (ret < 0) { dev_err(&pdev->dev, "Couldn't initialize the buffer.\n"); - goto error_disable_adc_clk; + return ret; } ret = at91_adc_trigger_init(idev); if (ret < 0) { dev_err(&pdev->dev, "Couldn't setup the triggers.\n"); at91_adc_buffer_remove(idev); - goto error_disable_adc_clk; + return ret; } } else { ret = at91_ts_register(idev, pdev); if (ret) - goto error_disable_adc_clk; + return ret; at91_ts_hw_init(idev, adc_clk_khz); } @@ -1208,10 +1194,6 @@ error_iio_device_register: } else { at91_ts_unregister(st); } -error_disable_adc_clk: - clk_disable_unprepare(st->adc_clk); -error_disable_clk: - clk_disable_unprepare(st->clk); return ret; } @@ -1227,8 +1209,6 @@ static int at91_adc_remove(struct platform_device *pdev) } else { at91_ts_unregister(st); } - clk_disable_unprepare(st->adc_clk); - clk_disable_unprepare(st->clk); return 0; } -- cgit v1.2.3 From 974a6c27b4d707f75d283805e7fc8aa54686f198 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 28 Aug 2023 14:45:46 +0800 Subject: iio: adc: at91_adc: Simplify with dev_err_probe() Use the dev_err_probe() helper to simplify error handling during probe. This also handle scenario, when EDEFER is returned and useless error is printed. Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230828064546.2383857-4-ruanjinjie@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 66 +++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index e5610722ce0b..200c4599530b 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1013,28 +1013,25 @@ static int at91_adc_probe(struct platform_device *pdev) st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers"); - if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) { - dev_err(&idev->dev, "Missing adc-channels-used property in the DT.\n"); - return -EINVAL; - } + if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) + return dev_err_probe(&idev->dev, -EINVAL, + "Missing adc-channels-used property in the DT.\n"); st->channels_mask = prop; st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode"); - if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) { - dev_err(&idev->dev, "Missing adc-startup-time property in the DT.\n"); - return -EINVAL; - } + if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) + return dev_err_probe(&idev->dev, -EINVAL, + "Missing adc-startup-time property in the DT.\n"); st->startup_time = prop; prop = 0; of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop); st->sample_hold_time = prop; - if (of_property_read_u32(node, "atmel,adc-vref", &prop)) { - dev_err(&idev->dev, "Missing adc-vref property in the DT.\n"); - return -EINVAL; - } + if (of_property_read_u32(node, "atmel,adc-vref", &prop)) + return dev_err_probe(&idev->dev, -EINVAL, + "Missing adc-vref property in the DT.\n"); st->vref_mv = prop; st->res = st->caps->high_res_bits; @@ -1069,7 +1066,6 @@ static int at91_adc_probe(struct platform_device *pdev) if (IS_ERR(st->reg_base)) return PTR_ERR(st->reg_base); - /* * Disable all IRQs before setting up the handler */ @@ -1084,24 +1080,19 @@ static int at91_adc_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_rl_interrupt, 0, pdev->dev.driver->name, idev); - if (ret) { - dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to allocate IRQ.\n"); st->clk = devm_clk_get_enabled(&pdev->dev, "adc_clk"); - if (IS_ERR(st->clk)) { - dev_err(&pdev->dev, - "Could not prepare or enable the clock.\n"); - return PTR_ERR(st->clk); - } + if (IS_ERR(st->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(st->clk), + "Could not prepare or enable the clock.\n"); st->adc_clk = devm_clk_get_enabled(&pdev->dev, "adc_op_clk"); - if (IS_ERR(st->adc_clk)) { - dev_err(&pdev->dev, - "Could not prepare or enable the ADC clock.\n"); - return PTR_ERR(st->adc_clk); - } + if (IS_ERR(st->adc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(st->adc_clk), + "Could not prepare or enable the ADC clock.\n"); /* * Prescaler rate computation using the formula from the Atmel's @@ -1117,10 +1108,9 @@ static int at91_adc_probe(struct platform_device *pdev) prsc = (mstrclk / (2 * adc_clk)) - 1; - if (!st->startup_time) { - dev_err(&pdev->dev, "No startup time available.\n"); - return -EINVAL; - } + if (!st->startup_time) + return dev_err_probe(&pdev->dev, -EINVAL, + "No startup time available.\n"); ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz); /* @@ -1145,10 +1135,9 @@ static int at91_adc_probe(struct platform_device *pdev) /* Setup the ADC channels available on the board */ ret = at91_adc_channel_init(idev); - if (ret < 0) { - dev_err(&pdev->dev, "Couldn't initialize the channels.\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Couldn't initialize the channels.\n"); init_waitqueue_head(&st->wq_data_avail); mutex_init(&st->lock); @@ -1160,10 +1149,9 @@ static int at91_adc_probe(struct platform_device *pdev) */ if (!st->touchscreen_type) { ret = at91_adc_buffer_init(idev); - if (ret < 0) { - dev_err(&pdev->dev, "Couldn't initialize the buffer.\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Couldn't initialize the buffer.\n"); ret = at91_adc_trigger_init(idev); if (ret < 0) { -- cgit v1.2.3 From 0f8eaeda7659d02063278c7eb72bbcd2d2819375 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 19:40:32 +0100 Subject: iio: accel: mma8452: Convert enum->pointer for data in the ID table Convert enum->pointer for data in the ID table, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Replace enum->struct *mma_chip_info for data in the ID table and simplify mma8452_probe() by replacing device_get_match_data() with i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230818184033.335502-2-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index f42a88711486..5864ad726e97 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1547,7 +1547,6 @@ MODULE_DEVICE_TABLE(of, mma8452_dt_ids); static int mma8452_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct mma8452_data *data; struct iio_dev *indio_dev; int ret; @@ -1560,15 +1559,10 @@ static int mma8452_probe(struct i2c_client *client) data->client = client; mutex_init(&data->lock); - data->chip_info = device_get_match_data(&client->dev); - if (!data->chip_info) { - if (id) { - data->chip_info = &mma_chip_info_table[id->driver_data]; - } else { - dev_err(&client->dev, "unknown device model\n"); - return -ENODEV; - } - } + data->chip_info = i2c_get_match_data(client); + if (!data->chip_info) + return dev_err_probe(&client->dev, -ENODEV, + "unknown device model\n"); ret = iio_read_mount_matrix(&client->dev, &data->orientation); if (ret) @@ -1830,12 +1824,12 @@ static const struct dev_pm_ops mma8452_pm_ops = { }; static const struct i2c_device_id mma8452_id[] = { - { "mma8451", mma8451 }, - { "mma8452", mma8452 }, - { "mma8453", mma8453 }, - { "mma8652", mma8652 }, - { "mma8653", mma8653 }, - { "fxls8471", fxls8471 }, + { "mma8451", (kernel_ulong_t)&mma_chip_info_table[mma8451] }, + { "mma8452", (kernel_ulong_t)&mma_chip_info_table[mma8452] }, + { "mma8453", (kernel_ulong_t)&mma_chip_info_table[mma8453] }, + { "mma8652", (kernel_ulong_t)&mma_chip_info_table[mma8652] }, + { "mma8653", (kernel_ulong_t)&mma_chip_info_table[mma8653] }, + { "fxls8471", (kernel_ulong_t)&mma_chip_info_table[fxls8471] }, { } }; MODULE_DEVICE_TABLE(i2c, mma8452_id); -- cgit v1.2.3 From 6915f0b98b71e5f6a5e49b0ec3ab98a387cef0f3 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 19:40:33 +0100 Subject: iio: accel: mma8452: Sort match tables Sort ID table alphabetically by name and OF table by compatible. Suggested-by: Andy Shevchenko Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230818184033.335502-3-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 5864ad726e97..d3fd0318e47b 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1535,12 +1535,12 @@ static int mma8452_reset(struct i2c_client *client) } static const struct of_device_id mma8452_dt_ids[] = { + { .compatible = "fsl,fxls8471", .data = &mma_chip_info_table[fxls8471] }, { .compatible = "fsl,mma8451", .data = &mma_chip_info_table[mma8451] }, { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] }, { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] }, { .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] }, { .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] }, - { .compatible = "fsl,fxls8471", .data = &mma_chip_info_table[fxls8471] }, { } }; MODULE_DEVICE_TABLE(of, mma8452_dt_ids); @@ -1824,12 +1824,12 @@ static const struct dev_pm_ops mma8452_pm_ops = { }; static const struct i2c_device_id mma8452_id[] = { + { "fxls8471", (kernel_ulong_t)&mma_chip_info_table[fxls8471] }, { "mma8451", (kernel_ulong_t)&mma_chip_info_table[mma8451] }, { "mma8452", (kernel_ulong_t)&mma_chip_info_table[mma8452] }, { "mma8453", (kernel_ulong_t)&mma_chip_info_table[mma8453] }, { "mma8652", (kernel_ulong_t)&mma_chip_info_table[mma8652] }, { "mma8653", (kernel_ulong_t)&mma_chip_info_table[mma8653] }, - { "fxls8471", (kernel_ulong_t)&mma_chip_info_table[fxls8471] }, { } }; MODULE_DEVICE_TABLE(i2c, mma8452_id); -- cgit v1.2.3 From 2b0ddc83dcb51737ef52fb1f42ec651a94aafa2c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 20:04:29 +0100 Subject: iio: chemical: vz89x: Convert enum->pointer for data in the match tables Convert enum->pointer for data in the match tables, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Replace enum->struct *vz89x_chip_data for data in the match table. Simplify the probe() by replacing device_get_match_data() and ID lookup for retrieving data by i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230818190429.338065-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/vz89x.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 13555f4f401a..5b358bcd311b 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -342,19 +342,17 @@ static const struct vz89x_chip_data vz89x_chips[] = { }; static const struct of_device_id vz89x_dt_ids[] = { - { .compatible = "sgx,vz89x", .data = (void *) VZ89X }, - { .compatible = "sgx,vz89te", .data = (void *) VZ89TE }, + { .compatible = "sgx,vz89x", .data = &vz89x_chips[VZ89X] }, + { .compatible = "sgx,vz89te", .data = &vz89x_chips[VZ89TE] }, { } }; MODULE_DEVICE_TABLE(of, vz89x_dt_ids); static int vz89x_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device *dev = &client->dev; struct iio_dev *indio_dev; struct vz89x_data *data; - int chip_id; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -369,14 +367,10 @@ static int vz89x_probe(struct i2c_client *client) else return -EOPNOTSUPP; - if (!dev_fwnode(dev)) - chip_id = id->driver_data; - else - chip_id = (unsigned long)device_get_match_data(dev); + data->chip = i2c_get_match_data(client); i2c_set_clientdata(client, indio_dev); data->client = client; - data->chip = &vz89x_chips[chip_id]; data->last_update = jiffies - HZ; mutex_init(&data->lock); @@ -391,8 +385,8 @@ static int vz89x_probe(struct i2c_client *client) } static const struct i2c_device_id vz89x_id[] = { - { "vz89x", VZ89X }, - { "vz89te", VZ89TE }, + { "vz89x", (kernel_ulong_t)&vz89x_chips[VZ89X] }, + { "vz89te", (kernel_ulong_t)&vz89x_chips[VZ89TE] }, { } }; MODULE_DEVICE_TABLE(i2c, vz89x_id); -- cgit v1.2.3 From ab3555b4b5b08fb03b8da904cc3fedbba91560df Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 19:55:31 +0100 Subject: iio: chemical: atlas-sensor: Convert enum->pointer for data in the match tables Convert enum->pointer for data in the match tables, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Replace enum->struct *atlas_device for data in the match table. Simplify the probe() by replacing device_get_match_data() and ID lookup for retrieving data by i2c_get_match_data(). While at it, add const qualifier to struct atlas_device and drop inner trailing commas from OF table. Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230818185531.336672-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index fb15bb216019..baf93e5e3ca7 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -87,7 +87,7 @@ enum { struct atlas_data { struct i2c_client *client; struct iio_trigger *trig; - struct atlas_device *chip; + const struct atlas_device *chip; struct regmap *regmap; struct irq_work work; unsigned int interrupt_enabled; @@ -353,7 +353,7 @@ struct atlas_device { int delay; }; -static struct atlas_device atlas_devices[] = { +static const struct atlas_device atlas_devices[] = { [ATLAS_PH_SM] = { .channels = atlas_ph_channels, .num_channels = 3, @@ -589,30 +589,29 @@ static const struct iio_info atlas_info = { }; static const struct i2c_device_id atlas_id[] = { - { "atlas-ph-sm", ATLAS_PH_SM }, - { "atlas-ec-sm", ATLAS_EC_SM }, - { "atlas-orp-sm", ATLAS_ORP_SM }, - { "atlas-do-sm", ATLAS_DO_SM }, - { "atlas-rtd-sm", ATLAS_RTD_SM }, + { "atlas-ph-sm", (kernel_ulong_t)&atlas_devices[ATLAS_PH_SM] }, + { "atlas-ec-sm", (kernel_ulong_t)&atlas_devices[ATLAS_EC_SM] }, + { "atlas-orp-sm", (kernel_ulong_t)&atlas_devices[ATLAS_ORP_SM] }, + { "atlas-do-sm", (kernel_ulong_t)&atlas_devices[ATLAS_DO_SM] }, + { "atlas-rtd-sm", (kernel_ulong_t)&atlas_devices[ATLAS_RTD_SM] }, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); static const struct of_device_id atlas_dt_ids[] = { - { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, - { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, - { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, }, - { .compatible = "atlas,do-sm", .data = (void *)ATLAS_DO_SM, }, - { .compatible = "atlas,rtd-sm", .data = (void *)ATLAS_RTD_SM, }, + { .compatible = "atlas,ph-sm", .data = &atlas_devices[ATLAS_PH_SM] }, + { .compatible = "atlas,ec-sm", .data = &atlas_devices[ATLAS_EC_SM] }, + { .compatible = "atlas,orp-sm", .data = &atlas_devices[ATLAS_ORP_SM] }, + { .compatible = "atlas,do-sm", .data = &atlas_devices[ATLAS_DO_SM] }, + { .compatible = "atlas,rtd-sm", .data = &atlas_devices[ATLAS_RTD_SM] }, { } }; MODULE_DEVICE_TABLE(of, atlas_dt_ids); static int atlas_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct atlas_data *data; - struct atlas_device *chip; + const struct atlas_device *chip; struct iio_trigger *trig; struct iio_dev *indio_dev; int ret; @@ -621,10 +620,7 @@ static int atlas_probe(struct i2c_client *client) if (!indio_dev) return -ENOMEM; - if (!dev_fwnode(&client->dev)) - chip = &atlas_devices[id->driver_data]; - else - chip = &atlas_devices[(unsigned long)device_get_match_data(&client->dev)]; + chip = i2c_get_match_data(client); indio_dev->info = &atlas_info; indio_dev->name = ATLAS_DRV_NAME; -- cgit v1.2.3 From 7f8643aa5e0fbf540eeba412fc2a7d4dfc2f22f8 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 19:31:28 +0100 Subject: iio: chemical: atlas-ezo-sensor: Simplify probe() Simplify the probe() by replacing device_get_match_data() and ID lookup match by i2c_get_match_data() as we have similar I2C and DT-based matching table. Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230818183128.334233-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-ezo-sensor.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/chemical/atlas-ezo-sensor.c b/drivers/iio/chemical/atlas-ezo-sensor.c index 8fc926a2d33b..761a853a4d17 100644 --- a/drivers/iio/chemical/atlas-ezo-sensor.c +++ b/drivers/iio/chemical/atlas-ezo-sensor.c @@ -203,7 +203,6 @@ MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids); static int atlas_ezo_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); const struct atlas_ezo_device *chip; struct atlas_ezo_data *data; struct iio_dev *indio_dev; @@ -212,10 +211,7 @@ static int atlas_ezo_probe(struct i2c_client *client) if (!indio_dev) return -ENOMEM; - if (dev_fwnode(&client->dev)) - chip = device_get_match_data(&client->dev); - else - chip = (const struct atlas_ezo_device *)id->driver_data; + chip = i2c_get_match_data(client); if (!chip) return -EINVAL; -- cgit v1.2.3 From 21fd3b1373f65c6a9d910642ecef194a077d1624 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 18:58:19 +0100 Subject: iio: proximity: sx9310: Convert enum->pointer for match data table Convert enum->pointer for data in match data table, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Add struct sx931x_info and replace enum->sx931x_info in the match table and simplify sx9310_check_whoami(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20230818175819.325663-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9310.c | 46 ++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index d977aacb7491..0d230a0dff56 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -159,6 +159,11 @@ static_assert(SX9310_NUM_CHANNELS <= SX_COMMON_MAX_NUM_CHANNELS); } #define SX9310_CHANNEL(idx) SX9310_NAMED_CHANNEL(idx, NULL) +struct sx931x_info { + const char *name; + unsigned int whoami; +}; + static const struct iio_chan_spec sx9310_channels[] = { SX9310_CHANNEL(0), /* CS0 */ SX9310_CHANNEL(1), /* CS1 */ @@ -902,7 +907,7 @@ static int sx9310_check_whoami(struct device *dev, struct iio_dev *indio_dev) { struct sx_common_data *data = iio_priv(indio_dev); - unsigned int long ddata; + const struct sx931x_info *ddata; unsigned int whoami; int ret; @@ -910,20 +915,11 @@ static int sx9310_check_whoami(struct device *dev, if (ret) return ret; - ddata = (uintptr_t)device_get_match_data(dev); - if (ddata != whoami) - return -EINVAL; - - switch (whoami) { - case SX9310_WHOAMI_VALUE: - indio_dev->name = "sx9310"; - break; - case SX9311_WHOAMI_VALUE: - indio_dev->name = "sx9311"; - break; - default: + ddata = device_get_match_data(dev); + if (ddata->whoami != whoami) return -ENODEV; - } + + indio_dev->name = ddata->name; return 0; } @@ -1015,23 +1011,33 @@ out: static DEFINE_SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume); +static const struct sx931x_info sx9310_info = { + .name = "sx9310", + .whoami = SX9310_WHOAMI_VALUE, +}; + +static const struct sx931x_info sx9311_info = { + .name = "sx9311", + .whoami = SX9311_WHOAMI_VALUE, +}; + static const struct acpi_device_id sx9310_acpi_match[] = { - { "STH9310", SX9310_WHOAMI_VALUE }, - { "STH9311", SX9311_WHOAMI_VALUE }, + { "STH9310", (kernel_ulong_t)&sx9310_info }, + { "STH9311", (kernel_ulong_t)&sx9311_info }, {} }; MODULE_DEVICE_TABLE(acpi, sx9310_acpi_match); static const struct of_device_id sx9310_of_match[] = { - { .compatible = "semtech,sx9310", (void *)SX9310_WHOAMI_VALUE }, - { .compatible = "semtech,sx9311", (void *)SX9311_WHOAMI_VALUE }, + { .compatible = "semtech,sx9310", &sx9310_info }, + { .compatible = "semtech,sx9311", &sx9311_info }, {} }; MODULE_DEVICE_TABLE(of, sx9310_of_match); static const struct i2c_device_id sx9310_id[] = { - { "sx9310", SX9310_WHOAMI_VALUE }, - { "sx9311", SX9311_WHOAMI_VALUE }, + { "sx9310", (kernel_ulong_t)&sx9310_info }, + { "sx9311", (kernel_ulong_t)&sx9311_info }, {} }; MODULE_DEVICE_TABLE(i2c, sx9310_id); -- cgit v1.2.3 From 17dc571687c5cc60e88fe29a33da0b9c64647086 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 18:39:06 +0100 Subject: iio: dac: ti-dac5571: Use i2c_get_match_data() Replace device_get_match_data() and id lookup for retrieving match data by i2c_get_match_data() by converting enum->pointer for data in the match table. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230818173907.323640-2-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ti-dac5571.c | 48 +++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index bab11b9adc25..2bb3f76569ee 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -313,7 +313,6 @@ static int dac5571_probe(struct i2c_client *client) const struct dac5571_spec *spec; struct dac5571_data *data; struct iio_dev *indio_dev; - enum chip_id chip_id; int ret, i; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); @@ -329,12 +328,7 @@ static int dac5571_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = dac5571_channels; - if (dev_fwnode(dev)) - chip_id = (uintptr_t)device_get_match_data(dev); - else - chip_id = id->driver_data; - - spec = &dac5571_spec[chip_id]; + spec = i2c_get_match_data(client); indio_dev->num_channels = spec->num_channels; data->spec = spec; @@ -392,31 +386,31 @@ static void dac5571_remove(struct i2c_client *i2c) } static const struct of_device_id dac5571_of_id[] = { - {.compatible = "ti,dac5571", .data = (void *)single_8bit}, - {.compatible = "ti,dac6571", .data = (void *)single_10bit}, - {.compatible = "ti,dac7571", .data = (void *)single_12bit}, - {.compatible = "ti,dac5574", .data = (void *)quad_8bit}, - {.compatible = "ti,dac6574", .data = (void *)quad_10bit}, - {.compatible = "ti,dac7574", .data = (void *)quad_12bit}, - {.compatible = "ti,dac5573", .data = (void *)quad_8bit}, - {.compatible = "ti,dac6573", .data = (void *)quad_10bit}, - {.compatible = "ti,dac7573", .data = (void *)quad_12bit}, - {.compatible = "ti,dac121c081", .data = (void *)single_12bit}, + {.compatible = "ti,dac5571", .data = &dac5571_spec[single_8bit] }, + {.compatible = "ti,dac6571", .data = &dac5571_spec[single_10bit] }, + {.compatible = "ti,dac7571", .data = &dac5571_spec[single_12bit] }, + {.compatible = "ti,dac5574", .data = &dac5571_spec[quad_8bit] }, + {.compatible = "ti,dac6574", .data = &dac5571_spec[quad_10bit] }, + {.compatible = "ti,dac7574", .data = &dac5571_spec[quad_12bit] }, + {.compatible = "ti,dac5573", .data = &dac5571_spec[quad_8bit] }, + {.compatible = "ti,dac6573", .data = &dac5571_spec[quad_10bit] }, + {.compatible = "ti,dac7573", .data = &dac5571_spec[quad_12bit] }, + {.compatible = "ti,dac121c081", .data = &dac5571_spec[single_12bit] }, {} }; MODULE_DEVICE_TABLE(of, dac5571_of_id); static const struct i2c_device_id dac5571_id[] = { - {"dac5571", single_8bit}, - {"dac6571", single_10bit}, - {"dac7571", single_12bit}, - {"dac5574", quad_8bit}, - {"dac6574", quad_10bit}, - {"dac7574", quad_12bit}, - {"dac5573", quad_8bit}, - {"dac6573", quad_10bit}, - {"dac7573", quad_12bit}, - {"dac121c081", single_12bit}, + {"dac5571", (kernel_ulong_t)&dac5571_spec[single_8bit] }, + {"dac6571", (kernel_ulong_t)&dac5571_spec[single_10bit] }, + {"dac7571", (kernel_ulong_t)&dac5571_spec[single_12bit] }, + {"dac5574", (kernel_ulong_t)&dac5571_spec[quad_8bit] }, + {"dac6574", (kernel_ulong_t)&dac5571_spec[quad_10bit] }, + {"dac7574", (kernel_ulong_t)&dac5571_spec[quad_12bit] }, + {"dac5573", (kernel_ulong_t)&dac5571_spec[quad_8bit] }, + {"dac6573", (kernel_ulong_t)&dac5571_spec[quad_10bit] }, + {"dac7573", (kernel_ulong_t)&dac5571_spec[quad_12bit] }, + {"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] }, {} }; MODULE_DEVICE_TABLE(i2c, dac5571_id); -- cgit v1.2.3 From 541d803abf33caa7243c99361a47f7b998966815 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 18:39:07 +0100 Subject: iio: dac: ti-dac5571: Sort match tables Sort ID table alphabetically by name and OF table by compatible for single_*bit enums. Suggested-by: Andy Shevchenko Signed-off-by: Biju Das Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20230818173907.323640-3-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ti-dac5571.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index 2bb3f76569ee..efb1269a77c1 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -386,6 +386,7 @@ static void dac5571_remove(struct i2c_client *i2c) } static const struct of_device_id dac5571_of_id[] = { + {.compatible = "ti,dac121c081", .data = &dac5571_spec[single_12bit] }, {.compatible = "ti,dac5571", .data = &dac5571_spec[single_8bit] }, {.compatible = "ti,dac6571", .data = &dac5571_spec[single_10bit] }, {.compatible = "ti,dac7571", .data = &dac5571_spec[single_12bit] }, @@ -395,12 +396,12 @@ static const struct of_device_id dac5571_of_id[] = { {.compatible = "ti,dac5573", .data = &dac5571_spec[quad_8bit] }, {.compatible = "ti,dac6573", .data = &dac5571_spec[quad_10bit] }, {.compatible = "ti,dac7573", .data = &dac5571_spec[quad_12bit] }, - {.compatible = "ti,dac121c081", .data = &dac5571_spec[single_12bit] }, {} }; MODULE_DEVICE_TABLE(of, dac5571_of_id); static const struct i2c_device_id dac5571_id[] = { + {"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] }, {"dac5571", (kernel_ulong_t)&dac5571_spec[single_8bit] }, {"dac6571", (kernel_ulong_t)&dac5571_spec[single_10bit] }, {"dac7571", (kernel_ulong_t)&dac5571_spec[single_12bit] }, @@ -410,7 +411,6 @@ static const struct i2c_device_id dac5571_id[] = { {"dac5573", (kernel_ulong_t)&dac5571_spec[quad_8bit] }, {"dac6573", (kernel_ulong_t)&dac5571_spec[quad_10bit] }, {"dac7573", (kernel_ulong_t)&dac5571_spec[quad_12bit] }, - {"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] }, {} }; MODULE_DEVICE_TABLE(i2c, dac5571_id); -- cgit v1.2.3 From 72d365398d96293291abdab23ab6be08e86001fa Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 07:57:40 +0100 Subject: iio: magnetometer: yamaha-yas530: Use i2c_get_match_data() Simplify the probe() by replacing device_get_match_data() with i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230812065741.20990-2-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index c5e485bfc6fc..7b041bb38693 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -1434,9 +1434,7 @@ static int yas5xx_probe(struct i2c_client *i2c) goto assert_reset; } - ci = device_get_match_data(dev); - if (!ci) - ci = (const struct yas5xx_chip_info *)id->driver_data; + ci = i2c_get_match_data(i2c); yas5xx->chip_info = ci; ret = regmap_read(yas5xx->map, YAS5XX_DEVICE_ID, &id_check); -- cgit v1.2.3 From 9f6001e390927aa3173cfd4c7d325b8f1d93ccf8 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 08:24:19 +0100 Subject: iio: adc: max1363: Use i2c_get_match_data() Replace device_get_match_data() and i2c_match_id() by i2c_get_match_data() by making similar I2C and DT-based matching table. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230812072419.42645-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 87 ++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index b31581616ce3..7c2a98b8c3a9 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1599,9 +1599,7 @@ static int max1363_probe(struct i2c_client *client) if (ret) return ret; - st->chip_info = device_get_match_data(&client->dev); - if (!st->chip_info) - st->chip_info = &max1363_chip_info_tbl[id->driver_data]; + st->chip_info = i2c_get_match_data(client); st->client = client; st->vref_uv = st->chip_info->int_vref_mv * 1000; @@ -1669,46 +1667,51 @@ static int max1363_probe(struct i2c_client *client) return devm_iio_device_register(&client->dev, indio_dev); } +#define MAX1363_ID_TABLE(_name, cfg) { \ + .name = _name, \ + .driver_data = (kernel_ulong_t)&max1363_chip_info_tbl[cfg], \ +} + static const struct i2c_device_id max1363_id[] = { - { "max1361", max1361 }, - { "max1362", max1362 }, - { "max1363", max1363 }, - { "max1364", max1364 }, - { "max1036", max1036 }, - { "max1037", max1037 }, - { "max1038", max1038 }, - { "max1039", max1039 }, - { "max1136", max1136 }, - { "max1137", max1137 }, - { "max1138", max1138 }, - { "max1139", max1139 }, - { "max1236", max1236 }, - { "max1237", max1237 }, - { "max1238", max1238 }, - { "max1239", max1239 }, - { "max11600", max11600 }, - { "max11601", max11601 }, - { "max11602", max11602 }, - { "max11603", max11603 }, - { "max11604", max11604 }, - { "max11605", max11605 }, - { "max11606", max11606 }, - { "max11607", max11607 }, - { "max11608", max11608 }, - { "max11609", max11609 }, - { "max11610", max11610 }, - { "max11611", max11611 }, - { "max11612", max11612 }, - { "max11613", max11613 }, - { "max11614", max11614 }, - { "max11615", max11615 }, - { "max11616", max11616 }, - { "max11617", max11617 }, - { "max11644", max11644 }, - { "max11645", max11645 }, - { "max11646", max11646 }, - { "max11647", max11647 }, - {} + MAX1363_ID_TABLE("max1361", max1361), + MAX1363_ID_TABLE("max1362", max1362), + MAX1363_ID_TABLE("max1363", max1363), + MAX1363_ID_TABLE("max1364", max1364), + MAX1363_ID_TABLE("max1036", max1036), + MAX1363_ID_TABLE("max1037", max1037), + MAX1363_ID_TABLE("max1038", max1038), + MAX1363_ID_TABLE("max1039", max1039), + MAX1363_ID_TABLE("max1136", max1136), + MAX1363_ID_TABLE("max1137", max1137), + MAX1363_ID_TABLE("max1138", max1138), + MAX1363_ID_TABLE("max1139", max1139), + MAX1363_ID_TABLE("max1236", max1236), + MAX1363_ID_TABLE("max1237", max1237), + MAX1363_ID_TABLE("max1238", max1238), + MAX1363_ID_TABLE("max1239", max1239), + MAX1363_ID_TABLE("max11600", max11600), + MAX1363_ID_TABLE("max11601", max11601), + MAX1363_ID_TABLE("max11602", max11602), + MAX1363_ID_TABLE("max11603", max11603), + MAX1363_ID_TABLE("max11604", max11604), + MAX1363_ID_TABLE("max11605", max11605), + MAX1363_ID_TABLE("max11606", max11606), + MAX1363_ID_TABLE("max11607", max11607), + MAX1363_ID_TABLE("max11608", max11608), + MAX1363_ID_TABLE("max11609", max11609), + MAX1363_ID_TABLE("max11610", max11610), + MAX1363_ID_TABLE("max11611", max11611), + MAX1363_ID_TABLE("max11612", max11612), + MAX1363_ID_TABLE("max11613", max11613), + MAX1363_ID_TABLE("max11614", max11614), + MAX1363_ID_TABLE("max11615", max11615), + MAX1363_ID_TABLE("max11616", max11616), + MAX1363_ID_TABLE("max11617", max11617), + MAX1363_ID_TABLE("max11644", max11644), + MAX1363_ID_TABLE("max11645", max11645), + MAX1363_ID_TABLE("max11646", max11646), + MAX1363_ID_TABLE("max11647", max11647), + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, max1363_id); -- cgit v1.2.3 From fe11e389117a6d98a8a5c894f69975877c612d67 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 15:10:44 +0100 Subject: iio: accel: bma180: Convert enum->pointer for data in the match table Convert enum->pointer for data in the match table, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Replace enum->struct *bma180_part_info for data in the match table and simplify bma180_probe(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230812141044.151520-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma180.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 13439f52d26d..ab4fccb24b6c 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -926,7 +926,6 @@ static int bma180_probe(struct i2c_client *client) struct device *dev = &client->dev; struct bma180_data *data; struct iio_dev *indio_dev; - enum chip_ids chip; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); @@ -936,11 +935,7 @@ static int bma180_probe(struct i2c_client *client) data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - if (client->dev.of_node) - chip = (uintptr_t)of_device_get_match_data(dev); - else - chip = id->driver_data; - data->part_info = &bma180_part_info[chip]; + data->part_info = i2c_get_match_data(client); ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) @@ -1092,11 +1087,11 @@ static int bma180_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); static const struct i2c_device_id bma180_ids[] = { - { "bma023", BMA023 }, - { "bma150", BMA150 }, - { "bma180", BMA180 }, - { "bma250", BMA250 }, - { "smb380", BMA150 }, + { "bma023", (kernel_ulong_t)&bma180_part_info[BMA023] }, + { "bma150", (kernel_ulong_t)&bma180_part_info[BMA150] }, + { "bma180", (kernel_ulong_t)&bma180_part_info[BMA180] }, + { "bma250", (kernel_ulong_t)&bma180_part_info[BMA250] }, + { "smb380", (kernel_ulong_t)&bma180_part_info[BMA150] }, { } }; @@ -1105,23 +1100,23 @@ MODULE_DEVICE_TABLE(i2c, bma180_ids); static const struct of_device_id bma180_of_match[] = { { .compatible = "bosch,bma023", - .data = (void *)BMA023 + .data = &bma180_part_info[BMA023] }, { .compatible = "bosch,bma150", - .data = (void *)BMA150 + .data = &bma180_part_info[BMA150] }, { .compatible = "bosch,bma180", - .data = (void *)BMA180 + .data = &bma180_part_info[BMA180] }, { .compatible = "bosch,bma250", - .data = (void *)BMA250 + .data = &bma180_part_info[BMA250] }, { .compatible = "bosch,smb380", - .data = (void *)BMA150 + .data = &bma180_part_info[BMA150] }, { } }; -- cgit v1.2.3 From 4545d4777d9ede0f8061edf9a08c842d719c0fe6 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 17:22:22 +0100 Subject: iio: mlx90614: Use i2c_get_match_data() Replace device_get_match_data()->i2c_get_match_data() to extend matching support for ID table. Signed-off-by: Biju Das Acked-by: "Crt Mori " Link: https://lore.kernel.org/r/20230812162222.200004-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/mlx90614.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index 07bb5df24ab3..740018d4b3df 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -600,7 +600,7 @@ static int mlx90614_probe(struct i2c_client *client) data->client = client; mutex_init(&data->lock); data->wakeup_gpio = mlx90614_probe_wakeup(client); - data->chip_info = device_get_match_data(&client->dev); + data->chip_info = i2c_get_match_data(client); mlx90614_wakeup(data); -- cgit v1.2.3 From 4f9ea93afde190a0f906ee624fc9a45cf784551b Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 08:55:56 +0100 Subject: iio: magnetometer: ak8975: Convert enum->pointer for data in the match tables Convert enum->pointer for data in the match tables to simplify the probe() by replacing device_get_match_data() and i2c_client_get_device_id by i2c_get_match_data() as we have similar I2C, ACPI and DT matching table. Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230818075600.24277-2-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 75 ++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index eb706d0bf70b..104798549de1 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -813,13 +813,13 @@ static const struct iio_info ak8975_info = { }; static const struct acpi_device_id ak_acpi_match[] = { - {"AK8975", AK8975}, - {"AK8963", AK8963}, - {"INVN6500", AK8963}, - {"AK009911", AK09911}, - {"AK09911", AK09911}, - {"AKM9911", AK09911}, - {"AK09912", AK09912}, + {"AK8975", (kernel_ulong_t)&ak_def_array[AK8975] }, + {"AK8963", (kernel_ulong_t)&ak_def_array[AK8963] }, + {"INVN6500", (kernel_ulong_t)&ak_def_array[AK8963] }, + {"AK009911", (kernel_ulong_t)&ak_def_array[AK09911] }, + {"AK09911", (kernel_ulong_t)&ak_def_array[AK09911] }, + {"AKM9911", (kernel_ulong_t)&ak_def_array[AK09911] }, + {"AK09912", (kernel_ulong_t)&ak_def_array[AK09912] }, { } }; MODULE_DEVICE_TABLE(acpi, ak_acpi_match); @@ -883,10 +883,7 @@ static int ak8975_probe(struct i2c_client *client) struct iio_dev *indio_dev; struct gpio_desc *eoc_gpiod; struct gpio_desc *reset_gpiod; - const void *match; - unsigned int i; int err; - enum asahi_compass_chipset chipset; const char *name = NULL; /* @@ -928,27 +925,15 @@ static int ak8975_probe(struct i2c_client *client) return err; /* id will be NULL when enumerated via ACPI */ - match = device_get_match_data(&client->dev); - if (match) { - chipset = (uintptr_t)match; - name = dev_name(&client->dev); - } else if (id) { - chipset = (enum asahi_compass_chipset)(id->driver_data); - name = id->name; - } else - return -ENOSYS; - - for (i = 0; i < ARRAY_SIZE(ak_def_array); i++) - if (ak_def_array[i].type == chipset) - break; - - if (i == ARRAY_SIZE(ak_def_array)) { - dev_err(&client->dev, "AKM device type unsupported: %d\n", - chipset); + data->def = i2c_get_match_data(client); + if (!data->def) return -ENODEV; - } - data->def = &ak_def_array[i]; + /* If enumerated via firmware node, fix the ABI */ + if (dev_fwnode(&client->dev)) + name = dev_name(&client->dev); + else + name = id->name; /* Fetch the regulators */ data->vdd = devm_regulator_get(&client->dev, "vdd"); @@ -1077,28 +1062,28 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ak8975_dev_pm_ops, ak8975_runtime_suspend, ak8975_runtime_resume, NULL); static const struct i2c_device_id ak8975_id[] = { - {"ak8975", AK8975}, - {"ak8963", AK8963}, - {"AK8963", AK8963}, - {"ak09911", AK09911}, - {"ak09912", AK09912}, - {"ak09916", AK09916}, + {"ak8975", (kernel_ulong_t)&ak_def_array[AK8975] }, + {"ak8963", (kernel_ulong_t)&ak_def_array[AK8963] }, + {"AK8963", (kernel_ulong_t)&ak_def_array[AK8963] }, + {"ak09911", (kernel_ulong_t)&ak_def_array[AK09911] }, + {"ak09912", (kernel_ulong_t)&ak_def_array[AK09912] }, + {"ak09916", (kernel_ulong_t)&ak_def_array[AK09916] }, {} }; MODULE_DEVICE_TABLE(i2c, ak8975_id); static const struct of_device_id ak8975_of_match[] = { - { .compatible = "asahi-kasei,ak8975", }, - { .compatible = "ak8975", }, - { .compatible = "asahi-kasei,ak8963", }, - { .compatible = "ak8963", }, - { .compatible = "asahi-kasei,ak09911", }, - { .compatible = "ak09911", }, - { .compatible = "asahi-kasei,ak09912", }, - { .compatible = "ak09912", }, - { .compatible = "asahi-kasei,ak09916", }, - { .compatible = "ak09916", }, + { .compatible = "asahi-kasei,ak8975", .data = &ak_def_array[AK8975] }, + { .compatible = "ak8975", .data = &ak_def_array[AK8975] }, + { .compatible = "asahi-kasei,ak8963", .data = &ak_def_array[AK8963] }, + { .compatible = "ak8963", .data = &ak_def_array[AK8963] }, + { .compatible = "asahi-kasei,ak09911", .data = &ak_def_array[AK09911] }, + { .compatible = "ak09911", .data = &ak_def_array[AK09911] }, + { .compatible = "asahi-kasei,ak09912", .data = &ak_def_array[AK09912] }, + { .compatible = "ak09912", .data = &ak_def_array[AK09912] }, + { .compatible = "asahi-kasei,ak09916", .data = &ak_def_array[AK09916] }, + { .compatible = "ak09916", .data = &ak_def_array[AK09916] }, {} }; MODULE_DEVICE_TABLE(of, ak8975_of_match); -- cgit v1.2.3 From 680b2f21c31d4e0af10cf0f732c2c972a8d7489d Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 08:55:57 +0100 Subject: iio: magnetometer: ak8975: Sort ID and ACPI tables Sort ID table alphabetically by name and ACPI table by HID. While at it, drop blank line before ID table. Suggested-by: Andy Shevchenko Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230818075600.24277-3-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 104798549de1..8cfceb007936 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -812,18 +812,6 @@ static const struct iio_info ak8975_info = { .read_raw = &ak8975_read_raw, }; -static const struct acpi_device_id ak_acpi_match[] = { - {"AK8975", (kernel_ulong_t)&ak_def_array[AK8975] }, - {"AK8963", (kernel_ulong_t)&ak_def_array[AK8963] }, - {"INVN6500", (kernel_ulong_t)&ak_def_array[AK8963] }, - {"AK009911", (kernel_ulong_t)&ak_def_array[AK09911] }, - {"AK09911", (kernel_ulong_t)&ak_def_array[AK09911] }, - {"AKM9911", (kernel_ulong_t)&ak_def_array[AK09911] }, - {"AK09912", (kernel_ulong_t)&ak_def_array[AK09912] }, - { } -}; -MODULE_DEVICE_TABLE(acpi, ak_acpi_match); - static void ak8975_fill_buffer(struct iio_dev *indio_dev) { struct ak8975_data *data = iio_priv(indio_dev); @@ -1061,16 +1049,27 @@ static int ak8975_runtime_resume(struct device *dev) static DEFINE_RUNTIME_DEV_PM_OPS(ak8975_dev_pm_ops, ak8975_runtime_suspend, ak8975_runtime_resume, NULL); +static const struct acpi_device_id ak_acpi_match[] = { + {"AK8963", (kernel_ulong_t)&ak_def_array[AK8963] }, + {"AK8975", (kernel_ulong_t)&ak_def_array[AK8975] }, + {"AK009911", (kernel_ulong_t)&ak_def_array[AK09911] }, + {"AK09911", (kernel_ulong_t)&ak_def_array[AK09911] }, + {"AK09912", (kernel_ulong_t)&ak_def_array[AK09912] }, + {"AKM9911", (kernel_ulong_t)&ak_def_array[AK09911] }, + {"INVN6500", (kernel_ulong_t)&ak_def_array[AK8963] }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ak_acpi_match); + static const struct i2c_device_id ak8975_id[] = { - {"ak8975", (kernel_ulong_t)&ak_def_array[AK8975] }, - {"ak8963", (kernel_ulong_t)&ak_def_array[AK8963] }, {"AK8963", (kernel_ulong_t)&ak_def_array[AK8963] }, + {"ak8963", (kernel_ulong_t)&ak_def_array[AK8963] }, + {"ak8975", (kernel_ulong_t)&ak_def_array[AK8975] }, {"ak09911", (kernel_ulong_t)&ak_def_array[AK09911] }, {"ak09912", (kernel_ulong_t)&ak_def_array[AK09912] }, {"ak09916", (kernel_ulong_t)&ak_def_array[AK09916] }, {} }; - MODULE_DEVICE_TABLE(i2c, ak8975_id); static const struct of_device_id ak8975_of_match[] = { -- cgit v1.2.3 From 711fb79a1ea8e79dc600f25d9f8c1ac25870b4de Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 18 Aug 2023 08:55:58 +0100 Subject: dt-bindings: iio: magnetometer: asahi-kasei,ak8975: Drop deprecated enums Drop deprecated enums from bindings as it is been here for a long time. Signed-off-by: Biju Das Acked-by: Rob Herring Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230818075600.24277-4-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml index 9790f75fc669..ee77558e9800 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml @@ -18,13 +18,6 @@ properties: - asahi-kasei,ak09911 - asahi-kasei,ak09912 - asahi-kasei,ak09916 - - enum: - - ak8975 - - ak8963 - - ak09911 - - ak09912 - - ak09916 - deprecated: true reg: maxItems: 1 -- cgit v1.2.3 From 77865a8f9f7085a51188b3fb95368c73a7dd12f3 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 16 Aug 2023 14:09:05 +0300 Subject: iio: amplifiers: hmc425a: Add Support HMC540S 4-bit Attenuator This adds support for the Analog Devices HMC540s 1 dB LSB Silicon MMIC 4-Bit Digital Positive Control Attenuator, 0.1 - 8 GHz Signed-off-by: Michael Hennerich Signed-off-by: Ana-Maria Cusco Link: https://lore.kernel.org/r/20230816110906.144540-1-ana-maria.cusco@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/hmc425a.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index 108f0f1685ef..e87d35d50a95 100644 --- a/drivers/iio/amplifiers/hmc425a.c +++ b/drivers/iio/amplifiers/hmc425a.c @@ -21,6 +21,7 @@ enum hmc425a_type { ID_HMC425A, + ID_HMC540S, }; struct hmc425a_chip_info { @@ -70,6 +71,9 @@ static int hmc425a_read_raw(struct iio_dev *indio_dev, case ID_HMC425A: gain = ~code * -500; break; + case ID_HMC540S: + gain = ~code * -1000; + break; } *val = gain / 1000; @@ -106,6 +110,9 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev, case ID_HMC425A: code = ~((abs(gain) / 500) & 0x3F); break; + case ID_HMC540S: + code = ~((abs(gain) / 1000) & 0xF); + break; } mutex_lock(&st->lock); @@ -157,6 +164,7 @@ static const struct iio_chan_spec hmc425a_channels[] = { /* Match table for of_platform binding */ static const struct of_device_id hmc425a_of_match[] = { { .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A }, + { .compatible = "adi,hmc540s", .data = (void *)ID_HMC540S }, {}, }; MODULE_DEVICE_TABLE(of, hmc425a_of_match); @@ -171,6 +179,15 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { .gain_max = 0, .default_gain = -0x40, /* set default gain -31.5db*/ }, + [ID_HMC540S] = { + .name = "hmc540s", + .channels = hmc425a_channels, + .num_channels = ARRAY_SIZE(hmc425a_channels), + .num_gpios = 4, + .gain_min = -15000, + .gain_max = 0, + .default_gain = -0x10, /* set default gain -15.0db*/ + }, }; static int hmc425a_probe(struct platform_device *pdev) -- cgit v1.2.3 From 20f87a9a26bea28df91506f3c2747e636d5d589c Mon Sep 17 00:00:00 2001 From: Ana-Maria Cusco Date: Wed, 16 Aug 2023 14:09:06 +0300 Subject: dt-bindings: iio: hmc425a: add entry for HMC540S Added support for HMC540SLP3E broadband 4-bit Silicon IC digital attenuator with a 15 dB control range and wide frequency coverage (0.1 to 8 GHz). Signed-off-by: Ana-Maria Cusco Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20230816110906.144540-2-ana-maria.cusco@analog.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml index 9fda56fa49c3..2ee6080deac7 100644 --- a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml @@ -4,20 +4,26 @@ $id: http://devicetree.org/schemas/iio/amplifiers/adi,hmc425a.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: HMC425A 6-bit Digital Step Attenuator +title: Analog Devices HMC425A and similar Digital Step Attenuators maintainers: - Michael Hennerich description: | - Digital Step Attenuator IIO device with gpio interface. + Digital Step Attenuator IIO devices with gpio interface. + Offer various frequency and attenuation ranges. HMC425A 0.5 dB LSB GaAs MMIC 6-BIT DIGITAL POSITIVE CONTROL ATTENUATOR, 2.2 - 8.0 GHz - https://www.analog.com/media/en/technical-documentation/data-sheets/hmc425A.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/hmc425A.pdf + + HMC540S 1 dB LSB Silicon MMIC 4-Bit Digital Positive Control Attenuator, 0.1 - 8 GHz + https://www.analog.com/media/en/technical-documentation/data-sheets/hmc540s.pdf + properties: compatible: enum: - adi,hmc425a + - adi,hmc540s vcc-supply: true -- cgit v1.2.3 From 8c89edc1ac9e3be816cd37e0df2afe040283a9d3 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 17:57:30 +0100 Subject: iio: chemical: sgp30: Convert enum->pointer for data in the match tables Convert enum->pointer for data in the match tables, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Add product_id variable to struct sgp_device and remove the local variable product_id in probe() and replace enum->struct *sgp_device for data in the match table. Simplify theprobe() by replacing device_get_match_data() and ID lookup for retrieving data by i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230812165730.216180-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/sgp30.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index b509cff9ce37..21730d62b5c8 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -114,6 +114,7 @@ struct sgp_data { }; struct sgp_device { + unsigned long product_id; const struct iio_chan_spec *channels; int num_channels; }; @@ -182,10 +183,12 @@ static const struct iio_chan_spec sgpc3_channels[] = { static const struct sgp_device sgp_devices[] = { [SGP30] = { + .product_id = SGP30, .channels = sgp30_channels, .num_channels = ARRAY_SIZE(sgp30_channels), }, [SGPC3] = { + .product_id = SGPC3, .channels = sgpc3_channels, .num_channels = ARRAY_SIZE(sgpc3_channels), }, @@ -491,28 +494,25 @@ static const struct iio_info sgp_info = { }; static const struct of_device_id sgp_dt_ids[] = { - { .compatible = "sensirion,sgp30", .data = (void *)SGP30 }, - { .compatible = "sensirion,sgpc3", .data = (void *)SGPC3 }, + { .compatible = "sensirion,sgp30", .data = &sgp_devices[SGP30] }, + { .compatible = "sensirion,sgpc3", .data = &sgp_devices[SGPC3] }, { } }; static int sgp_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct sgp_device *match_data; struct device *dev = &client->dev; struct iio_dev *indio_dev; struct sgp_data *data; - unsigned long product_id; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; - if (dev_fwnode(dev)) - product_id = (unsigned long)device_get_match_data(dev); - else - product_id = id->driver_data; + match_data = i2c_get_match_data(client); data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); @@ -528,15 +528,15 @@ static int sgp_probe(struct i2c_client *client) data->feature_set = be16_to_cpu(data->buffer.raw_words[0].value); - ret = sgp_check_compat(data, product_id); + ret = sgp_check_compat(data, match_data->product_id); if (ret) return ret; indio_dev->info = &sgp_info; indio_dev->name = id->name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = sgp_devices[product_id].channels; - indio_dev->num_channels = sgp_devices[product_id].num_channels; + indio_dev->channels = match_data->channels; + indio_dev->num_channels = match_data->num_channels; sgp_init(data); @@ -562,8 +562,8 @@ static void sgp_remove(struct i2c_client *client) } static const struct i2c_device_id sgp_id[] = { - { "sgp30", SGP30 }, - { "sgpc3", SGPC3 }, + { "sgp30", (kernel_ulong_t)&sgp_devices[SGP30] }, + { "sgpc3", (kernel_ulong_t)&sgp_devices[SGPC3] }, { } }; -- cgit v1.2.3 From 0f5cecd14f42a2cbd10f7670c025dfb98a817f7b Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 15:41:06 +0100 Subject: iio: potentiometer: ds1803: Convert enum->pointer for data in the ID table Convert enum->pointer for data in the ID table, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Replace enum->struct *ds1803_cfg for data in the ID table and simplify ds1803_probe() by replacing device_get_match_data() with i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230812144106.163355-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/ds1803.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/iio/potentiometer/ds1803.c b/drivers/iio/potentiometer/ds1803.c index fc183e0790da..e0526dd0e3cb 100644 --- a/drivers/iio/potentiometer/ds1803.c +++ b/drivers/iio/potentiometer/ds1803.c @@ -204,7 +204,6 @@ static const struct iio_info ds1803_info = { static int ds1803_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device *dev = &client->dev; struct ds1803_data *data; struct iio_dev *indio_dev; @@ -217,9 +216,7 @@ static int ds1803_probe(struct i2c_client *client) data = iio_priv(indio_dev); data->client = client; - data->cfg = device_get_match_data(dev); - if (!data->cfg) - data->cfg = &ds1803_cfg[id->driver_data]; + data->cfg = i2c_get_match_data(client); indio_dev->info = &ds1803_info; indio_dev->channels = data->cfg->channels; @@ -239,10 +236,10 @@ static const struct of_device_id ds1803_dt_ids[] = { MODULE_DEVICE_TABLE(of, ds1803_dt_ids); static const struct i2c_device_id ds1803_id[] = { - { "ds1803-010", DS1803_010 }, - { "ds1803-050", DS1803_050 }, - { "ds1803-100", DS1803_100 }, - { "ds3502", DS3502 }, + { "ds1803-010", (kernel_ulong_t)&ds1803_cfg[DS1803_010] }, + { "ds1803-050", (kernel_ulong_t)&ds1803_cfg[DS1803_050] }, + { "ds1803-100", (kernel_ulong_t)&ds1803_cfg[DS1803_100] }, + { "ds3502", (kernel_ulong_t)&ds1803_cfg[DS3502] }, {} }; MODULE_DEVICE_TABLE(i2c, ds1803_id); -- cgit v1.2.3 From c4153b5720e62be957f3497049cb2706f0b8a5b1 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 16:08:38 +0100 Subject: iio: potentiometer: ad5110: Use i2c_get_match_data() Replace device_get_match_data()->i2c_get_match_data by making similar I2C and DT-based matching table to extend matching support for ID table. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230812150838.185055-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/ad5110.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/iio/potentiometer/ad5110.c b/drivers/iio/potentiometer/ad5110.c index 991e745c4f93..aaf02cc7aeba 100644 --- a/drivers/iio/potentiometer/ad5110.c +++ b/drivers/iio/potentiometer/ad5110.c @@ -278,14 +278,19 @@ static const struct of_device_id ad5110_of_match[] = { }; MODULE_DEVICE_TABLE(of, ad5110_of_match); +#define AD5110_ID_TABLE(_name, cfg) { \ + .name = _name, \ + .driver_data = (kernel_ulong_t)&ad5110_cfg[cfg], \ +} + static const struct i2c_device_id ad5110_id[] = { - { "ad5110-10", AD5110_10 }, - { "ad5110-80", AD5110_80 }, - { "ad5112-05", AD5112_05 }, - { "ad5112-10", AD5112_10 }, - { "ad5112-80", AD5112_80 }, - { "ad5114-10", AD5114_10 }, - { "ad5114-80", AD5114_80 }, + AD5110_ID_TABLE("ad5110-10", AD5110_10), + AD5110_ID_TABLE("ad5110-80", AD5110_80), + AD5110_ID_TABLE("ad5112-05", AD5112_05), + AD5110_ID_TABLE("ad5112-10", AD5112_10), + AD5110_ID_TABLE("ad5112-80", AD5112_80), + AD5110_ID_TABLE("ad5114-10", AD5114_10), + AD5110_ID_TABLE("ad5114-80", AD5114_80), { } }; MODULE_DEVICE_TABLE(i2c, ad5110_id); @@ -305,7 +310,7 @@ static int ad5110_probe(struct i2c_client *client) data->client = client; mutex_init(&data->lock); data->enable = 1; - data->cfg = device_get_match_data(dev); + data->cfg = i2c_get_match_data(client); /* refresh RDAC register with EEPROM */ ret = ad5110_write(data, AD5110_RESET, 0); -- cgit v1.2.3 From 5a4ef20aab63dacdce77c97eaf1847355cc9e66e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 16:19:08 +0100 Subject: iio: light: opt4001: Use i2c_get_match_data() Replace device_get_match_data()->i2c_get_match_data() to extend matching support for ID table. Signed-off-by: Biju Das Reviewed-by: Stefan Windfeldt-Prytz Tested-by: Stefan Windfeldt-Prytz Link: https://lore.kernel.org/r/20230812151908.188696-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt4001.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index 502946bf9f94..6cf60151b3d8 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -412,7 +412,7 @@ static int opt4001_probe(struct i2c_client *client) if (dev_id != OPT4001_DEVICE_ID_VAL) dev_warn(&client->dev, "Device ID: %#04x unknown\n", dev_id); - chip->chip_info = device_get_match_data(&client->dev); + chip->chip_info = i2c_get_match_data(client); indio_dev->channels = opt4001_channels; indio_dev->num_channels = ARRAY_SIZE(opt4001_channels); -- cgit v1.2.3 From fc1d297b928b98f2290c1d80f6bb7ff12d278a3a Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 12 Aug 2023 17:11:54 +0100 Subject: iio: temperature: tmp117: Convert enum->pointer for data in the match tables Convert enum->pointer for data in the match tables, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Add struct tmp11x_info and replace enum->struct *tmp11x_info for data in the match table. Drop tmp117_identify() and simplify tmp117_probe() by replacing device_get_match_data() and id lookup for retrieving data by i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Marco Felsch Link: https://lore.kernel.org/r/20230812161154.196555-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tmp117.c | 94 +++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index fc02f491688b..059953015ae7 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -42,6 +42,12 @@ struct tmp117_data { s16 calibbias; }; +struct tmp11x_info { + const char *name; + struct iio_chan_spec const *channels; + int num_channels; +}; + static int tmp117_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel, int *val, int *val2, long mask) @@ -119,57 +125,54 @@ static const struct iio_chan_spec tmp116_channels[] = { }, }; +static const struct tmp11x_info tmp116_channels_info = { + .name = "tmp116", + .channels = tmp116_channels, + .num_channels = ARRAY_SIZE(tmp116_channels) +}; + +static const struct tmp11x_info tmp117_channels_info = { + .name = "tmp117", + .channels = tmp117_channels, + .num_channels = ARRAY_SIZE(tmp117_channels) +}; + static const struct iio_info tmp117_info = { .read_raw = tmp117_read_raw, .write_raw = tmp117_write_raw, }; -static int tmp117_identify(struct i2c_client *client) +static int tmp117_probe(struct i2c_client *client) { - const struct i2c_device_id *id; - unsigned long match_data; + const struct tmp11x_info *match_data; + struct tmp117_data *data; + struct iio_dev *indio_dev; int dev_id; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID); if (dev_id < 0) return dev_id; switch (dev_id) { case TMP116_DEVICE_ID: + match_data = &tmp116_channels_info; + break; case TMP117_DEVICE_ID: - return dev_id; + match_data = &tmp117_channels_info; + break; + default: + dev_info(&client->dev, + "Unknown device id (0x%x), use fallback compatible\n", + dev_id); + match_data = i2c_get_match_data(client); } - dev_info(&client->dev, "Unknown device id (0x%x), use fallback compatible\n", - dev_id); - - match_data = (uintptr_t)device_get_match_data(&client->dev); - if (match_data) - return match_data; - - id = i2c_client_get_device_id(client); - if (id) - return id->driver_data; - - dev_err(&client->dev, "Failed to identify unsupported device\n"); - - return -ENODEV; -} - -static int tmp117_probe(struct i2c_client *client) -{ - struct tmp117_data *data; - struct iio_dev *indio_dev; - int ret, dev_id; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -EOPNOTSUPP; - - ret = tmp117_identify(client); - if (ret < 0) - return ret; - - dev_id = ret; + if (!match_data) + return dev_err_probe(&client->dev, -ENODEV, + "Failed to identify unsupported device\n"); indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -181,33 +184,24 @@ static int tmp117_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &tmp117_info; + indio_dev->channels = match_data->channels; + indio_dev->num_channels = match_data->num_channels; + indio_dev->name = match_data->name; - switch (dev_id) { - case TMP116_DEVICE_ID: - indio_dev->channels = tmp116_channels; - indio_dev->num_channels = ARRAY_SIZE(tmp116_channels); - indio_dev->name = "tmp116"; - break; - case TMP117_DEVICE_ID: - indio_dev->channels = tmp117_channels; - indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); - indio_dev->name = "tmp117"; - break; - } return devm_iio_device_register(&client->dev, indio_dev); } static const struct of_device_id tmp117_of_match[] = { - { .compatible = "ti,tmp116", .data = (void *)TMP116_DEVICE_ID }, - { .compatible = "ti,tmp117", .data = (void *)TMP117_DEVICE_ID }, + { .compatible = "ti,tmp116", .data = &tmp116_channels_info }, + { .compatible = "ti,tmp117", .data = &tmp117_channels_info }, { } }; MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { - { "tmp116", TMP116_DEVICE_ID }, - { "tmp117", TMP117_DEVICE_ID }, + { "tmp116", (kernel_ulong_t)&tmp116_channels_info }, + { "tmp117", (kernel_ulong_t)&tmp117_channels_info }, { } }; MODULE_DEVICE_TABLE(i2c, tmp117_id); -- cgit v1.2.3 From 4eaf928622abc5dabc9b42a0de4dafbe29ddf491 Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Fri, 11 Aug 2023 17:57:01 +0800 Subject: iio: Remove unused declarations Commit 0f3a8c3f34f7 ("iio: Add support for creating IIO devices via configfs") declared but never implemented iio_sw_device_type_configfs_{un}register(). Commit b662f809d410 ("iio: core: Introduce IIO software triggers") declared but never implemented iio_sw_trigger_type_configfs_{un}register(). Commit a3e0b51884ee ("iio: accel: add support for FXLS8962AF/FXLS8964AF accelerometers") declared but never implemented fxls8962af_core_remove(). Commit 8dedcc3eee3a ("iio: core: centralize ioctl() calls to the main chardev") declared but never implemented iio_device_ioctl(). Commit d430f3c36ca6 ("iio: imu: inv_mpu6050: Use regmap instead of i2c specific functions") removed inv_mpu6050_write_reg() but not its declaration. Signed-off-by: Yue Haibing Link: https://lore.kernel.org/r/20230811095701.35372-1-yuehaibing@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/fxls8962af.h | 1 - drivers/iio/iio_core.h | 3 --- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 1 - include/linux/iio/sw_device.h | 3 --- include/linux/iio/sw_trigger.h | 3 --- 5 files changed, 11 deletions(-) diff --git a/drivers/iio/accel/fxls8962af.h b/drivers/iio/accel/fxls8962af.h index 9cbe98c3ba9a..6eaa2803b26f 100644 --- a/drivers/iio/accel/fxls8962af.h +++ b/drivers/iio/accel/fxls8962af.h @@ -14,7 +14,6 @@ enum { }; int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq); -int fxls8962af_core_remove(struct device *dev); extern const struct dev_pm_ops fxls8962af_pm_ops; extern const struct regmap_config fxls8962af_i2c_regmap_conf; diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 501e286702ef..1a38b1915e7a 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -30,9 +30,6 @@ struct iio_ioctl_handler { unsigned int cmd, unsigned long arg); }; -long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp, - unsigned int cmd, unsigned long arg); - void iio_device_ioctl_handler_register(struct iio_dev *indio_dev, struct iio_ioctl_handler *h); void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index ed5a96e78df0..95f548235de7 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -464,7 +464,6 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type); int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable); int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, unsigned int mask); -int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); int inv_mpu_acpi_create_mux_client(struct i2c_client *client); void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, diff --git a/include/linux/iio/sw_device.h b/include/linux/iio/sw_device.h index eff1e6b2595c..0f7fe7b522e3 100644 --- a/include/linux/iio/sw_device.h +++ b/include/linux/iio/sw_device.h @@ -51,9 +51,6 @@ void iio_unregister_sw_device_type(struct iio_sw_device_type *dt); struct iio_sw_device *iio_sw_device_create(const char *, const char *); void iio_sw_device_destroy(struct iio_sw_device *); -int iio_sw_device_type_configfs_register(struct iio_sw_device_type *dt); -void iio_sw_device_type_configfs_unregister(struct iio_sw_device_type *dt); - static inline void iio_swd_group_init_type_name(struct iio_sw_device *d, const char *name, diff --git a/include/linux/iio/sw_trigger.h b/include/linux/iio/sw_trigger.h index 47de2443e984..bc77f88df303 100644 --- a/include/linux/iio/sw_trigger.h +++ b/include/linux/iio/sw_trigger.h @@ -51,9 +51,6 @@ void iio_unregister_sw_trigger_type(struct iio_sw_trigger_type *tt); struct iio_sw_trigger *iio_sw_trigger_create(const char *, const char *); void iio_sw_trigger_destroy(struct iio_sw_trigger *); -int iio_sw_trigger_type_configfs_register(struct iio_sw_trigger_type *tt); -void iio_sw_trigger_type_configfs_unregister(struct iio_sw_trigger_type *tt); - static inline void iio_swt_group_init_type_name(struct iio_sw_trigger *t, const char *name, -- cgit v1.2.3 From b1209cf096358cfb9c538def71ff039fcb8631bd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 8 Aug 2023 19:42:04 +0300 Subject: iio: accel: kionix-kx022a: Use correct header(s) instead of string_helpers.h There is nothing from string_helpers.h used in the driver, correct the header inclusion block accordingly. Signed-off-by: Andy Shevchenko Acked-by: Matti Vaittinen Link: https://lore.kernel.org/r/20230808164204.66818-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kionix-kx022a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 4ea3c6718ed4..f164b09520c9 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From de39695dd1fb753a6040053483213ebe7a960af2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 8 Aug 2023 19:41:52 +0300 Subject: iio: accel: msa311: Use correct header(s) instead of string_helpers.h There is nothing from string_helpers.h used in the driver, correct the header inclusion block accordingly. Signed-off-by: Andy Shevchenko Reviewed-by: Dmitry Rokosov Link: https://lore.kernel.org/r/20230808164152.66748-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/msa311.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index 6ddcc3c2f840..b8ddbfd98f11 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From 8c337436e6da6ac1dcc2d2f990489c9ebfe429f1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 8 Aug 2023 19:41:37 +0300 Subject: iio: dac: stm32-dac: Use correct header(s) instead of string_helpers.h There is nothing from string_helpers.h used in the driver, correct the header inclusion block accordingly. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230808164137.66663-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/stm32-dac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index 15eb44075107..3cab28c7ee3b 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -11,12 +11,13 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include "stm32-dac-core.h" -- cgit v1.2.3 From 744f4990ee0519ebef21fa7db094240bbaf3d746 Mon Sep 17 00:00:00 2001 From: Ramona Bolboaca Date: Tue, 8 Aug 2023 10:50:57 +0300 Subject: iio: Add IIO_DELTA_ANGL channel type The delta angle is defined as a piece-wise integration of angular velocity data. The delta angle represents the amount of angular displacement between two consecutive measurements and it is measured in radians. In order to track the total angular displacement during a desired period of time, simply sum-up the delta angle samples acquired during that time. IIO currently does not offer a suitable channel type for this type of measurements hence this patch adds it. Signed-off-by: Ramona Bolboaca Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230808075059.645525-2-ramona.bolboaca@analog.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 22 ++++++++++++++++++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 2 ++ 4 files changed, 26 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index a2854dc9a839..363dd4b09930 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -279,6 +279,20 @@ Description: but should match other such assignments on device). Units after application of scale and offset are m/s^2. +What: /sys/bus/iio/devices/iio:deviceX/in_deltaangl_x_raw +What: /sys/bus/iio/devices/iio:deviceX/in_deltaangl_y_raw +What: /sys/bus/iio/devices/iio:deviceX/in_deltaangl_z_raw +KernelVersion: 6.5 +Contact: linux-iio@vger.kernel.org +Description: + Angular displacement between two consecutive samples on x, y or + z (may be arbitrarily assigned but should match other such + assignments on device). + In order to compute the total angular displacement during a + desired period of time, the application should sum-up the delta + angle samples acquired during that time. + Units after application of scale and offset are radians. + What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw What: /sys/bus/iio/devices/iio:deviceX/in_anglY_raw KernelVersion: 4.17 @@ -461,6 +475,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_deltaangl_scale What: /sys/bus/iio/devices/iio:deviceX/in_angl_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_x_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_y_scale @@ -1332,6 +1347,9 @@ Description: What: /sys/.../iio:deviceX/bufferY/in_accel_x_en What: /sys/.../iio:deviceX/bufferY/in_accel_y_en What: /sys/.../iio:deviceX/bufferY/in_accel_z_en +What: /sys/.../iio:deviceX/bufferY/in_deltaangl_x_en +What: /sys/.../iio:deviceX/bufferY/in_deltaangl_y_en +What: /sys/.../iio:deviceX/bufferY/in_deltaangl_z_en What: /sys/.../iio:deviceX/bufferY/in_anglvel_x_en What: /sys/.../iio:deviceX/bufferY/in_anglvel_y_en What: /sys/.../iio:deviceX/bufferY/in_anglvel_z_en @@ -1362,6 +1380,7 @@ Description: Scan element control for triggered data capture. What: /sys/.../iio:deviceX/bufferY/in_accel_type +What: /sys/.../iio:deviceX/bufferY/in_deltaangl_type What: /sys/.../iio:deviceX/bufferY/in_anglvel_type What: /sys/.../iio:deviceX/bufferY/in_magn_type What: /sys/.../iio:deviceX/bufferY/in_incli_type @@ -1416,6 +1435,9 @@ What: /sys/.../iio:deviceX/bufferY/in_voltage_q_index What: /sys/.../iio:deviceX/bufferY/in_accel_x_index What: /sys/.../iio:deviceX/bufferY/in_accel_y_index What: /sys/.../iio:deviceX/bufferY/in_accel_z_index +What: /sys/.../iio:deviceX/bufferY/in_deltaangl_x_index +What: /sys/.../iio:deviceX/bufferY/in_deltaangl_y_index +What: /sys/.../iio:deviceX/bufferY/in_deltaangl_z_index What: /sys/.../iio:deviceX/bufferY/in_anglvel_x_index What: /sys/.../iio:deviceX/bufferY/in_anglvel_y_index What: /sys/.../iio:deviceX/bufferY/in_anglvel_z_index diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index d752e9c0499b..11a65030186d 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -90,6 +90,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_POSITIONRELATIVE] = "positionrelative", [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", + [IIO_DELTA_ANGL] = "deltaangl", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index c79f2f046a0b..55666a17d311 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -47,6 +47,7 @@ enum iio_chan_type { IIO_POSITIONRELATIVE, IIO_PHASE, IIO_MASSCONCENTRATION, + IIO_DELTA_ANGL, }; enum iio_modifier { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 0a5c2bb60030..3505450060e6 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -59,6 +59,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_POSITIONRELATIVE] = "positionrelative", [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", + [IIO_DELTA_ANGL] = "deltaangl", }; static const char * const iio_ev_type_text[] = { @@ -173,6 +174,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_POSITIONRELATIVE: case IIO_PHASE: case IIO_MASSCONCENTRATION: + case IIO_DELTA_ANGL: break; default: return false; -- cgit v1.2.3 From 94a39f2c443b3fc5efe31a68502991020eefa3d2 Mon Sep 17 00:00:00 2001 From: Ramona Bolboaca Date: Tue, 8 Aug 2023 10:50:58 +0300 Subject: iio: Add IIO_DELTA_VELOCITY channel type The delta velocity is defined as a piece-wise integration of acceleration data. The delta velocity represents the linear velocity change between two consecutive measurements and it is measured in m / s (meters per second). In order to track the total linear velocity change during a desired period of time, simply sum-up the delta velocity samples acquired during that time. IIO currently does not offer a suitable channel type for this type of measurements hence this patch adds it. Signed-off-by: Ramona Bolboaca Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230808075059.645525-3-ramona.bolboaca@analog.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 23 +++++++++++++++++++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 2 ++ 4 files changed, 27 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 363dd4b09930..59968b745518 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -293,6 +293,21 @@ Description: angle samples acquired during that time. Units after application of scale and offset are radians. +What: /sys/bus/iio/devices/iio:deviceX/in_deltavelocity_x_raw +What: /sys/bus/iio/devices/iio:deviceX/in_deltavelocity_y_raw +What: /sys/bus/iio/devices/iio:deviceX/in_deltavelocity_z_raw +KernelVersion: 6.5 +Contact: linux-iio@vger.kernel.org +Description: + The linear velocity change between two consecutive samples on x, + y or z (may be arbitrarily assigned but should match other such + assignments on device). + In order to compute the total linear velocity change during a + desired period of time, the application should sum-up the delta + velocity samples acquired during that time. + Units after application of scale and offset are meters per + second. + What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw What: /sys/bus/iio/devices/iio:deviceX/in_anglY_raw KernelVersion: 4.17 @@ -476,6 +491,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale What: /sys/bus/iio/devices/iio:deviceX/in_deltaangl_scale +What: /sys/bus/iio/devices/iio:deviceX/in_deltavelocity_scale What: /sys/bus/iio/devices/iio:deviceX/in_angl_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_x_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_y_scale @@ -1350,6 +1366,9 @@ What: /sys/.../iio:deviceX/bufferY/in_accel_z_en What: /sys/.../iio:deviceX/bufferY/in_deltaangl_x_en What: /sys/.../iio:deviceX/bufferY/in_deltaangl_y_en What: /sys/.../iio:deviceX/bufferY/in_deltaangl_z_en +What: /sys/.../iio:deviceX/bufferY/in_deltavelocity_x_en +What: /sys/.../iio:deviceX/bufferY/in_deltavelocity_y_en +What: /sys/.../iio:deviceX/bufferY/in_deltavelocity_z_en What: /sys/.../iio:deviceX/bufferY/in_anglvel_x_en What: /sys/.../iio:deviceX/bufferY/in_anglvel_y_en What: /sys/.../iio:deviceX/bufferY/in_anglvel_z_en @@ -1381,6 +1400,7 @@ Description: What: /sys/.../iio:deviceX/bufferY/in_accel_type What: /sys/.../iio:deviceX/bufferY/in_deltaangl_type +What: /sys/.../iio:deviceX/bufferY/in_deltavelocity_type What: /sys/.../iio:deviceX/bufferY/in_anglvel_type What: /sys/.../iio:deviceX/bufferY/in_magn_type What: /sys/.../iio:deviceX/bufferY/in_incli_type @@ -1438,6 +1458,9 @@ What: /sys/.../iio:deviceX/bufferY/in_accel_z_index What: /sys/.../iio:deviceX/bufferY/in_deltaangl_x_index What: /sys/.../iio:deviceX/bufferY/in_deltaangl_y_index What: /sys/.../iio:deviceX/bufferY/in_deltaangl_z_index +What: /sys/.../iio:deviceX/bufferY/in_deltavelocity_x_index +What: /sys/.../iio:deviceX/bufferY/in_deltavelocity_y_index +What: /sys/.../iio:deviceX/bufferY/in_deltavelocity_z_index What: /sys/.../iio:deviceX/bufferY/in_anglvel_x_index What: /sys/.../iio:deviceX/bufferY/in_anglvel_y_index What: /sys/.../iio:deviceX/bufferY/in_anglvel_z_index diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 11a65030186d..2ea0bf08caf3 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -91,6 +91,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", [IIO_DELTA_ANGL] = "deltaangl", + [IIO_DELTA_VELOCITY] = "deltavelocity", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 55666a17d311..9a341bd07702 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -48,6 +48,7 @@ enum iio_chan_type { IIO_PHASE, IIO_MASSCONCENTRATION, IIO_DELTA_ANGL, + IIO_DELTA_VELOCITY, }; enum iio_modifier { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 3505450060e6..7e6761612246 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -60,6 +60,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", [IIO_DELTA_ANGL] = "deltaangl", + [IIO_DELTA_VELOCITY] = "deltavelocity", }; static const char * const iio_ev_type_text[] = { @@ -175,6 +176,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_PHASE: case IIO_MASSCONCENTRATION: case IIO_DELTA_ANGL: + case IIO_DELTA_VELOCITY: break; default: return false; -- cgit v1.2.3 From 8f6bc87d67c0309a98546d933bfefd2837154c8e Mon Sep 17 00:00:00 2001 From: Ramona Bolboaca Date: Tue, 8 Aug 2023 10:50:59 +0300 Subject: iio: imu: adis16475.c: Add delta angle and delta velocity channels Add support for delta angle and delta velocity raw and buffer readings to adis16475 driver. Signed-off-by: Ramona Bolboaca Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230808075059.645525-4-ramona.bolboaca@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16475.c | 165 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 146 insertions(+), 19 deletions(-) diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 17275a53ca2c..00e4e09cdafb 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -31,6 +31,12 @@ #define ADIS16475_REG_Y_ACCEL_L 0x14 #define ADIS16475_REG_Z_ACCEL_L 0x18 #define ADIS16475_REG_TEMP_OUT 0x1c +#define ADIS16475_REG_X_DELTANG_L 0x24 +#define ADIS16475_REG_Y_DELTANG_L 0x28 +#define ADIS16475_REG_Z_DELTANG_L 0x2C +#define ADIS16475_REG_X_DELTVEL_L 0x30 +#define ADIS16475_REG_Y_DELTVEL_L 0x34 +#define ADIS16475_REG_Z_DELTVEL_L 0x38 #define ADIS16475_REG_X_GYRO_BIAS_L 0x40 #define ADIS16475_REG_Y_GYRO_BIAS_L 0x44 #define ADIS16475_REG_Z_GYRO_BIAS_L 0x48 @@ -55,6 +61,7 @@ #define ADIS16475_REG_PROD_ID 0x72 #define ADIS16475_REG_SERIAL_NUM 0x74 #define ADIS16475_REG_FLASH_CNT 0x7c +#define ADIS16500_BURST_DATA_SEL_MASK BIT(8) #define ADIS16500_BURST32_MASK BIT(9) #define ADIS16500_BURST32(x) FIELD_PREP(ADIS16500_BURST32_MASK, x) /* number of data elements in burst mode */ @@ -65,6 +72,8 @@ #define ADIS16475_BURST_MAX_SPEED 1000000 #define ADIS16475_LSB_DEC_MASK BIT(0) #define ADIS16475_LSB_FIR_MASK BIT(1) +#define ADIS16500_BURST_DATA_SEL_0_CHN_MASK GENMASK(5, 0) +#define ADIS16500_BURST_DATA_SEL_1_CHN_MASK GENMASK(12, 7) enum { ADIS16475_SYNC_DIRECT = 1, @@ -84,16 +93,20 @@ struct adis16475_chip_info { const struct adis16475_sync *sync; const struct adis_data adis_data; const char *name; +#define ADIS16475_HAS_BURST32 BIT(0) +#define ADIS16475_HAS_BURST_DELTA_DATA BIT(1) + const long flags; u32 num_channels; u32 gyro_max_val; u32 gyro_max_scale; u32 accel_max_val; u32 accel_max_scale; u32 temp_scale; + u32 deltang_max_val; + u32 deltvel_max_val; u32 int_clk; u16 max_dec; u8 num_sync; - bool has_burst32; }; struct adis16475 { @@ -115,6 +128,12 @@ enum { ADIS16475_SCAN_ACCEL_Y, ADIS16475_SCAN_ACCEL_Z, ADIS16475_SCAN_TEMP, + ADIS16475_SCAN_DELTANG_X, + ADIS16475_SCAN_DELTANG_Y, + ADIS16475_SCAN_DELTANG_Z, + ADIS16475_SCAN_DELTVEL_X, + ADIS16475_SCAN_DELTVEL_Y, + ADIS16475_SCAN_DELTVEL_Z, }; static bool low_rate_allow; @@ -451,6 +470,14 @@ static int adis16475_read_raw(struct iio_dev *indio_dev, case IIO_TEMP: *val = st->info->temp_scale; return IIO_VAL_INT; + case IIO_DELTA_ANGL: + *val = st->info->deltang_max_val; + *val2 = 31; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_DELTA_VELOCITY: + *val = st->info->deltvel_max_val; + *val2 = 31; + return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } @@ -551,6 +578,32 @@ static int adis16475_write_raw(struct iio_dev *indio_dev, }, \ } +#define ADIS16475_MOD_CHAN_DELTA(_type, _mod, _address, _si, _r_bits, _s_bits) { \ + .type = (_type), \ + .modified = 1, \ + .channel2 = (_mod), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .address = (_address), \ + .scan_index = _si, \ + .scan_type = { \ + .sign = 's', \ + .realbits = (_r_bits), \ + .storagebits = (_s_bits), \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADIS16475_DELTANG_CHAN(_mod) \ + ADIS16475_MOD_CHAN_DELTA(IIO_DELTA_ANGL, IIO_MOD_ ## _mod, \ + ADIS16475_REG_ ## _mod ## _DELTANG_L, ADIS16475_SCAN_DELTANG_ ## _mod, 32, 32) + +#define ADIS16475_DELTVEL_CHAN(_mod) \ + ADIS16475_MOD_CHAN_DELTA(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \ + ADIS16475_REG_ ## _mod ## _DELTVEL_L, ADIS16475_SCAN_DELTVEL_ ## _mod, 32, 32) + static const struct iio_chan_spec adis16475_channels[] = { ADIS16475_GYRO_CHANNEL(X), ADIS16475_GYRO_CHANNEL(Y), @@ -559,7 +612,13 @@ static const struct iio_chan_spec adis16475_channels[] = { ADIS16475_ACCEL_CHANNEL(Y), ADIS16475_ACCEL_CHANNEL(Z), ADIS16475_TEMP_CHANNEL(), - IIO_CHAN_SOFT_TIMESTAMP(7) + ADIS16475_DELTANG_CHAN(X), + ADIS16475_DELTANG_CHAN(Y), + ADIS16475_DELTANG_CHAN(Z), + ADIS16475_DELTVEL_CHAN(X), + ADIS16475_DELTVEL_CHAN(Y), + ADIS16475_DELTVEL_CHAN(Z), + IIO_CHAN_SOFT_TIMESTAMP(13) }; enum adis16475_variant { @@ -662,6 +721,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(800 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -677,6 +738,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(4000 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(360), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -692,6 +755,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(4000 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -707,6 +772,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(4000 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -722,11 +789,13 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(800 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(360), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, .num_sync = ARRAY_SIZE(adis16475_sync_mode), - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts), }, [ADIS16477_2] = { @@ -738,11 +807,13 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(800 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, .num_sync = ARRAY_SIZE(adis16475_sync_mode), - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts), }, [ADIS16477_3] = { @@ -754,11 +825,13 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(800 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, .num_sync = ARRAY_SIZE(adis16475_sync_mode), - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts), }, [ADIS16465_1] = { @@ -770,6 +843,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(4000 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(360), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -785,6 +860,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(4000 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -800,6 +877,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(4000 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -815,6 +894,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(800 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(360), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -830,6 +911,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(800 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -845,6 +928,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 1, .accel_max_scale = IIO_M_S_2_TO_G(800 << 16), .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, @@ -860,12 +945,14 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 392, .accel_max_scale = 32000 << 16, .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, /* pulse sync not supported */ .num_sync = ARRAY_SIZE(adis16475_sync_mode) - 1, - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16500, &adis1650x_timeouts), }, [ADIS16505_1] = { @@ -877,12 +964,14 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 78, .accel_max_scale = 32000 << 16, .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(360), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, /* pulse sync not supported */ .num_sync = ARRAY_SIZE(adis16475_sync_mode) - 1, - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16505, &adis1650x_timeouts), }, [ADIS16505_2] = { @@ -894,12 +983,14 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 78, .accel_max_scale = 32000 << 16, .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, /* pulse sync not supported */ .num_sync = ARRAY_SIZE(adis16475_sync_mode) - 1, - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16505, &adis1650x_timeouts), }, [ADIS16505_3] = { @@ -911,12 +1002,14 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 78, .accel_max_scale = 32000 << 16, .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 100, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, /* pulse sync not supported */ .num_sync = ARRAY_SIZE(adis16475_sync_mode) - 1, - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16505, &adis1650x_timeouts), }, [ADIS16507_1] = { @@ -928,12 +1021,14 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 392, .accel_max_scale = 32000 << 16, .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(360), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, /* pulse sync not supported */ .num_sync = ARRAY_SIZE(adis16475_sync_mode) - 1, - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16507, &adis1650x_timeouts), }, [ADIS16507_2] = { @@ -945,12 +1040,14 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 392, .accel_max_scale = 32000 << 16, .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, /* pulse sync not supported */ .num_sync = ARRAY_SIZE(adis16475_sync_mode) - 1, - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16507, &adis1650x_timeouts), }, [ADIS16507_3] = { @@ -962,20 +1059,47 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .accel_max_val = 392, .accel_max_scale = 32000 << 16, .temp_scale = 100, + .deltang_max_val = IIO_DEGREE_TO_RAD(2160), + .deltvel_max_val = 400, .int_clk = 2000, .max_dec = 1999, .sync = adis16475_sync_mode, /* pulse sync not supported */ .num_sync = ARRAY_SIZE(adis16475_sync_mode) - 1, - .has_burst32 = true, + .flags = ADIS16475_HAS_BURST32 | ADIS16475_HAS_BURST_DELTA_DATA, .adis_data = ADIS16475_DATA(16507, &adis1650x_timeouts), }, }; +static int adis16475_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + u16 en; + int ret; + struct adis16475 *st = iio_priv(indio_dev); + + if (st->info->flags & ADIS16475_HAS_BURST_DELTA_DATA) { + if ((*scan_mask & ADIS16500_BURST_DATA_SEL_0_CHN_MASK) && + (*scan_mask & ADIS16500_BURST_DATA_SEL_1_CHN_MASK)) + return -EINVAL; + if (*scan_mask & ADIS16500_BURST_DATA_SEL_0_CHN_MASK) + en = FIELD_PREP(ADIS16500_BURST_DATA_SEL_MASK, 0); + else + en = FIELD_PREP(ADIS16500_BURST_DATA_SEL_MASK, 1); + + ret = __adis_update_bits(&st->adis, ADIS16475_REG_MSG_CTRL, + ADIS16500_BURST_DATA_SEL_MASK, en); + if (ret) + return ret; + } + + return adis_update_scan_mode(indio_dev, scan_mask); +} + static const struct iio_info adis16475_info = { .read_raw = &adis16475_read_raw, .write_raw = &adis16475_write_raw, - .update_scan_mode = adis_update_scan_mode, + .update_scan_mode = adis16475_update_scan_mode, .debugfs_reg_access = adis_debugfs_reg_access, }; @@ -998,7 +1122,7 @@ static void adis16475_burst32_check(struct adis16475 *st) int ret; struct adis *adis = &st->adis; - if (!st->info->has_burst32) + if (!(st->info->flags & ADIS16475_HAS_BURST32)) return; if (st->lsb_flag && !st->burst32) { @@ -1044,7 +1168,7 @@ static irqreturn_t adis16475_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct adis16475 *st = iio_priv(indio_dev); struct adis *adis = &st->adis; - int ret, bit, i = 0; + int ret, bit, buff_offset = 0, i = 0; __be16 *buffer; u16 crc; bool valid; @@ -1074,6 +1198,9 @@ static irqreturn_t adis16475_trigger_handler(int irq, void *p) case ADIS16475_SCAN_TEMP: st->data[i++] = buffer[offset]; break; + case ADIS16475_SCAN_DELTANG_X ... ADIS16475_SCAN_DELTVEL_Z: + buff_offset = ADIS16475_SCAN_DELTANG_X; + fallthrough; case ADIS16475_SCAN_GYRO_X ... ADIS16475_SCAN_ACCEL_Z: /* * The first 2 bytes on the received data are the @@ -1081,18 +1208,18 @@ static irqreturn_t adis16475_trigger_handler(int irq, void *p) */ if (st->burst32) { /* upper 16 */ - st->data[i++] = buffer[bit * 2 + 2]; + st->data[i++] = buffer[(bit - buff_offset) * 2 + 2]; /* lower 16 */ - st->data[i++] = buffer[bit * 2 + 1]; + st->data[i++] = buffer[(bit - buff_offset) * 2 + 1]; } else { - st->data[i++] = buffer[bit + 1]; + st->data[i++] = buffer[(bit - buff_offset) + 1]; /* * Don't bother in doing the manual read if the * device supports burst32. burst32 will be * enabled in the next call to * adis16475_burst32_check()... */ - if (st->lsb_flag && !st->info->has_burst32) { + if (st->lsb_flag && !(st->info->flags & ADIS16475_HAS_BURST32)) { u16 val = 0; const u32 reg = ADIS16475_REG_X_GYRO_L + bit * 4; -- cgit v1.2.3 From 3a23b384e7e3d64d5587ad10729a34d4f761517e Mon Sep 17 00:00:00 2001 From: Zhang Shurong Date: Sat, 15 Jul 2023 23:55:50 +0800 Subject: iio: adc: stm32-adc: harden against NULL pointer deref in stm32_adc_probe() of_match_device() may fail and returns a NULL pointer. In practice there is no known reasonable way to trigger this, but in case one is added in future, harden the code by adding the check Signed-off-by: Zhang Shurong Link: https://lore.kernel.org/r/tencent_994DA85912C937E3B5405BA960B31ED90A08@qq.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 2f082006550f..bbd5bdd732f0 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -708,6 +708,8 @@ static int stm32_adc_probe(struct platform_device *pdev) struct stm32_adc_priv *priv; struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + struct resource *res; u32 max_rate; int ret; @@ -720,8 +722,11 @@ static int stm32_adc_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, &priv->common); - priv->cfg = (const struct stm32_adc_priv_cfg *) - of_match_device(dev->driver->of_match_table, dev)->data; + of_id = of_match_device(dev->driver->of_match_table, dev); + if (!of_id) + return -ENODEV; + + priv->cfg = (const struct stm32_adc_priv_cfg *)of_id->data; priv->nb_adc_max = priv->cfg->num_adcs; spin_lock_init(&priv->common.lock); -- cgit v1.2.3 From 8aa6e6682f3624dca0cfbc3824e2d9937e58ca59 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Wed, 30 Aug 2023 12:43:14 +0300 Subject: iio: addac: ad74413r: fix function prefix typo Use complete device name prefix in the input current offset getter function. Signed-off-by: Antoniu Miclaus Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230830094314.26353-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 6b0e8218f150..75216ece7ad6 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -705,8 +705,8 @@ static int ad74413r_get_input_current_scale(struct ad74413r_state *st, return IIO_VAL_FRACTIONAL; } -static int ad74413_get_input_current_offset(struct ad74413r_state *st, - unsigned int channel, int *val) +static int ad74413r_get_input_current_offset(struct ad74413r_state *st, + unsigned int channel, int *val) { unsigned int range; int voltage_range; @@ -991,7 +991,7 @@ static int ad74413r_read_raw(struct iio_dev *indio_dev, return ad74413r_get_input_voltage_offset(st, chan->channel, val); case IIO_CURRENT: - return ad74413_get_input_current_offset(st, + return ad74413r_get_input_current_offset(st, chan->channel, val); default: return -EINVAL; -- cgit v1.2.3 From 0679ea0be2c86e384bc7fef46ef94cdd0aa73aa3 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Mon, 28 Aug 2023 22:41:34 -0400 Subject: dt-bindings: iio: adc: add lltc,ltc2309 bindings Add devicetree bindings for the Linear Technology LTC2309 ADC driver. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Liam Beguin Link: https://lore.kernel.org/r/20230828-ltc2309-v3-1-338b3a8fab8b@gmail.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/lltc,ltc2497.yaml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml index 875f394576c2..5cc6a9684077 100644 --- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml +++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml @@ -4,21 +4,31 @@ $id: http://devicetree.org/schemas/iio/adc/lltc,ltc2497.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Linear Technology / Analog Devices LTC2497 ADC +title: Linear Technology / Analog Devices LTC2497 and LTC2309 ADC maintainers: - Michael Hennerich + - Liam Beguin description: | - 16bit ADC supporting up to 16 single ended or 8 differential inputs. - I2C interface. + LTC2309: + low noise, low power, 8-channel, 12-bit successive approximation ADC with an + I2C compatible serial interface. - https://www.analog.com/media/en/technical-documentation/data-sheets/2497fb.pdf - https://www.analog.com/media/en/technical-documentation/data-sheets/2499fe.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf + + LTC2497: + LTC2499: + 16bit ADC supporting up to 16 single ended or 8 differential inputs. + I2C interface. + + https://www.analog.com/media/en/technical-documentation/data-sheets/2497fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/2499fe.pdf properties: compatible: enum: + - lltc,ltc2309 - lltc,ltc2497 - lltc,ltc2499 -- cgit v1.2.3 From 733e0fed9c275b7d9e07041cfca8b221c71e3bd2 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Mon, 28 Aug 2023 22:41:35 -0400 Subject: iio: adc: add ltc2309 support The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface. This implements support for all single-ended and differential channels, in unipolar mode only. Signed-off-by: Liam Beguin Link: https://lore.kernel.org/r/20230828-ltc2309-v3-2-338b3a8fab8b@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2309.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 drivers/iio/adc/ltc2309.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 432d3f9bfde1..bb3786745ab6 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -607,6 +607,16 @@ config LPC32XX_ADC activate only one via device tree selection. Provides direct access via sysfs. +config LTC2309 + tristate "Linear Technology LTC2309 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2309, a low + noise, low power, 8-channel, 12-bit SAR ADC + + This driver can also be built as a module. If so, the module will + be called ltc2309. + config LTC2471 tristate "Linear Technology LTC2471 and LTC2473 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 2facf979327d..2d5b2a181380 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o +obj-$(CONFIG_LTC2309) += ltc2309.o obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c new file mode 100644 index 000000000000..8b3a89c1b840 --- /dev/null +++ b/drivers/iio/adc/ltc2309.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface. + * + * Datasheet: + * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf + * + * Copyright (c) 2023, Liam Beguin + */ +#include +#include +#include +#include +#include +#include +#include + +#define LTC2309_ADC_RESOLUTION 12 + +#define LTC2309_DIN_CH_MASK GENMASK(7, 4) +#define LTC2309_DIN_SDN BIT(7) +#define LTC2309_DIN_OSN BIT(6) +#define LTC2309_DIN_S1 BIT(5) +#define LTC2309_DIN_S0 BIT(4) +#define LTC2309_DIN_UNI BIT(3) +#define LTC2309_DIN_SLEEP BIT(2) + +/** + * struct ltc2309 - internal device data structure + * @dev: Device reference + * @client: I2C reference + * @vref: External reference source + * @lock: Lock to serialize data access + * @vref_mv: Internal voltage reference + */ +struct ltc2309 { + struct device *dev; + struct i2c_client *client; + struct regulator *vref; + struct mutex lock; /* serialize data access */ + int vref_mv; +}; + +/* Order matches expected channel address, See datasheet Table 1. */ +enum ltc2309_channels { + LTC2309_CH0_CH1 = 0, + LTC2309_CH2_CH3, + LTC2309_CH4_CH5, + LTC2309_CH6_CH7, + LTC2309_CH1_CH0, + LTC2309_CH3_CH2, + LTC2309_CH5_CH4, + LTC2309_CH7_CH6, + LTC2309_CH0, + LTC2309_CH2, + LTC2309_CH4, + LTC2309_CH6, + LTC2309_CH1, + LTC2309_CH3, + LTC2309_CH5, + LTC2309_CH7, +}; + +#define LTC2309_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +#define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec ltc2309_channels[] = { + LTC2309_CHAN(0, LTC2309_CH0), + LTC2309_CHAN(1, LTC2309_CH1), + LTC2309_CHAN(2, LTC2309_CH2), + LTC2309_CHAN(3, LTC2309_CH3), + LTC2309_CHAN(4, LTC2309_CH4), + LTC2309_CHAN(5, LTC2309_CH5), + LTC2309_CHAN(6, LTC2309_CH6), + LTC2309_CHAN(7, LTC2309_CH7), + LTC2309_DIFF_CHAN(0, 1, LTC2309_CH0_CH1), + LTC2309_DIFF_CHAN(2, 3, LTC2309_CH2_CH3), + LTC2309_DIFF_CHAN(4, 5, LTC2309_CH4_CH5), + LTC2309_DIFF_CHAN(6, 7, LTC2309_CH6_CH7), + LTC2309_DIFF_CHAN(1, 0, LTC2309_CH1_CH0), + LTC2309_DIFF_CHAN(3, 2, LTC2309_CH3_CH2), + LTC2309_DIFF_CHAN(5, 4, LTC2309_CH5_CH4), + LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6), +}; + +static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, + unsigned long address, int *val) +{ + int ret; + u16 buf; + u8 din; + + din = FIELD_PREP(LTC2309_DIN_CH_MASK, address & 0x0f) | + FIELD_PREP(LTC2309_DIN_UNI, 1) | + FIELD_PREP(LTC2309_DIN_SLEEP, 0); + + ret = i2c_smbus_write_byte(ltc2309->client, din); + if (ret < 0) { + dev_err(ltc2309->dev, "i2c command failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2); + if (ret < 0) { + dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret)); + return ret; + } + + *val = be16_to_cpu(buf) >> 4; + + return ret; +} + +static int ltc2309_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ltc2309 *ltc2309 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(<c2309->lock); + ret = ltc2309_read_raw_channel(ltc2309, chan->address, val); + mutex_unlock(<c2309->lock); + if (ret < 0) + return -EINVAL; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = ltc2309->vref_mv; + *val2 = LTC2309_ADC_RESOLUTION; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info ltc2309_info = { + .read_raw = ltc2309_read_raw, +}; + +static void ltc2309_regulator_disable(void *regulator) +{ + regulator_disable(regulator); +} + +static int ltc2309_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ltc2309 *ltc2309; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ltc2309)); + if (!indio_dev) + return -ENOMEM; + + ltc2309 = iio_priv(indio_dev); + ltc2309->dev = &indio_dev->dev; + ltc2309->client = client; + ltc2309->vref_mv = 4096; /* Default to the internal ref */ + + indio_dev->name = "ltc2309"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2309_channels; + indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels); + indio_dev->info = <c2309_info; + + ltc2309->vref = devm_regulator_get_optional(&client->dev, "vref"); + if (IS_ERR(ltc2309->vref)) { + ret = PTR_ERR(ltc2309->vref); + if (ret == -ENODEV) + ltc2309->vref = NULL; + else + return ret; + } + + if (ltc2309->vref) { + ret = regulator_enable(ltc2309->vref); + if (ret) + return dev_err_probe(ltc2309->dev, ret, + "failed to enable vref\n"); + + ret = devm_add_action_or_reset(ltc2309->dev, + ltc2309_regulator_disable, + ltc2309->vref); + if (ret) { + return dev_err_probe(ltc2309->dev, ret, + "failed to add regulator_disable action: %d\n", + ret); + } + + ret = regulator_get_voltage(ltc2309->vref); + if (ret < 0) + return ret; + + ltc2309->vref_mv = ret / 1000; + } + + mutex_init(<c2309->lock); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id ltc2309_of_match[] = { + { .compatible = "lltc,ltc2309" }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2309_of_match); + +static const struct i2c_device_id ltc2309_id[] = { + { "ltc2309" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2309_id); + +static struct i2c_driver ltc2309_driver = { + .driver = { + .name = "ltc2309", + .of_match_table = ltc2309_of_match, + }, + .probe = ltc2309_probe, + .id_table = ltc2309_id, +}; +module_i2c_driver(ltc2309_driver); + +MODULE_AUTHOR("Liam Beguin "); +MODULE_DESCRIPTION("Linear Technology LTC2309 ADC"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From df2ece7aec158bd2b62a75b37bcc230bbd2695f4 Mon Sep 17 00:00:00 2001 From: Marius Cristea Date: Tue, 29 Aug 2023 18:41:32 +0300 Subject: dt-bindings: iio: adc: adding MCP3564 ADC This is the device tree schema for iio driver for Microchip family of 153.6 ksps, Low-Noise 16/24-Bit Delta-Sigma ADCs with an SPI interface (Microchip's MCP3461, MCP3462, MCP3464, MCP3461R, MCP3462R, MCP3464R, MCP3561, MCP3562, MCP3564, MCP3561R, MCP3562R and MCP3564R analog to digital converters). Signed-off-by: Marius Cristea Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230829154133.40716-2-marius.cristea@microchip.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/microchip,mcp3564.yaml | 205 +++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml new file mode 100644 index 000000000000..675319276197 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml @@ -0,0 +1,205 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/microchip,mcp3564.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MCP346X and MCP356X ADC Family + +maintainers: + - Marius Cristea + +description: | + Bindings for the Microchip family of 153.6 ksps, Low-Noise 16/24-Bit + Delta-Sigma ADCs with an SPI interface. Datasheet can be found here: + Datasheet for MCP3561, MCP3562, MCP3564 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf + Datasheet for MCP3561R, MCP3562R, MCP3564R can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3561_2_4R-Data-Sheet-DS200006391C.pdf + Datasheet for MCP3461, MCP3462, MCP3464 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4-Two-Four-Eight-Channel-153.6-ksps-Low-Noise-16-Bit-Delta-Sigma-ADC-Data-Sheet-20006180D.pdf + Datasheet for MCP3461R, MCP3462R, MCP3464R can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4R-Family-Data-Sheet-DS20006404C.pdf + +properties: + compatible: + enum: + - microchip,mcp3461 + - microchip,mcp3462 + - microchip,mcp3464 + - microchip,mcp3461r + - microchip,mcp3462r + - microchip,mcp3464r + - microchip,mcp3561 + - microchip,mcp3562 + - microchip,mcp3564 + - microchip,mcp3561r + - microchip,mcp3562r + - microchip,mcp3564r + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 20000000 + + spi-cpha: true + + spi-cpol: true + + vdd-supply: true + + avdd-supply: true + + clocks: + description: + Phandle and clock identifier for external sampling clock. + If not specified, the internal crystal oscillator will be used. + maxItems: 1 + + interrupts: + description: IRQ line of the ADC + maxItems: 1 + + drive-open-drain: + description: + Whether to drive the IRQ signal as push-pull (default) or open-drain. Note + that the device requires this pin to become "high", otherwise it will stop + converting. + type: boolean + + vref-supply: + description: + Some devices have a specific reference voltage supplied on a different + pin to the other supplies. Needed to be able to establish channel scaling + unless there is also an internal reference available (e.g. mcp3564r). In + case of "r" devices (e. g. mcp3564r), if it does not exists the internal + reference will be used. + + microchip,hw-device-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + description: + The address is set on a per-device basis by fuses in the factory, + configured on request. If not requested, the fuses are set for 0x1. + The device address is part of the device markings to avoid + potential confusion. This address is coded on two bits, so four possible + addresses are available when multiple devices are present on the same + SPI bus with only one Chip Select line for all devices. + Each device communication starts by a CS falling edge, followed by the + clocking of the device address (BITS[7:6] - top two bits of COMMAND BYTE + which is first one on the wire). + + "#io-channel-cells": + const: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^channel@([0-9]|([1-7][0-9]))$": + $ref: adc.yaml + type: object + unevaluatedProperties: false + description: Represents the external channels which are connected to the ADC. + + properties: + reg: + description: The channel number in single-ended and differential mode. + minimum: 0 + maximum: 79 + + required: + - reg + +dependencies: + spi-cpol: [ spi-cpha ] + spi-cpha: [ spi-cpol ] + +required: + - compatible + - reg + - microchip,hw-device-address + - spi-max-frequency + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - # External vref, no internal reference + if: + properties: + compatible: + contains: + enum: + - microchip,mcp3461 + - microchip,mcp3462 + - microchip,mcp3464 + - microchip,mcp3561 + - microchip,mcp3562 + - microchip,mcp3564 + then: + required: + - vref-supply + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "microchip,mcp3564r"; + reg = <0>; + vref-supply = <&vref_reg>; + spi-cpha; + spi-cpol; + spi-max-frequency = <10000000>; + microchip,hw-device-address = <1>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + /* CH0 to AGND */ + reg = <0>; + label = "CH0"; + }; + + channel@1 { + /* CH1 to AGND */ + reg = <1>; + label = "CH1"; + }; + + /* diff-channels */ + channel@11 { + reg = <11>; + + /* CN0, CN1 */ + diff-channels = <0 1>; + label = "CH0_CH1"; + }; + + channel@22 { + reg = <0x22>; + + /* CN1, CN2 */ + diff-channels = <1 2>; + label = "CH1_CH3"; + }; + + channel@23 { + reg = <0x23>; + + /* CN1, CN3 */ + diff-channels = <1 3>; + label = "CH1_CH3"; + }; + }; + }; +... -- cgit v1.2.3 From 33ec3e5fc1ea0e0c5c93425170602533c07f55d4 Mon Sep 17 00:00:00 2001 From: Marius Cristea Date: Tue, 29 Aug 2023 18:41:33 +0300 Subject: iio: adc: adding support for MCP3564 ADC This is the iio driver for Microchip family of 153.6 ksps, Low-Noise 16/24-Bit Delta-Sigma ADCs with an SPI interface (Microchip's MCP3461, MCP3462, MCP3464, MCP3461R, MCP3462R, MCP3464R, MCP3561, MCP3562, MCP3564, MCP3561R, MCP3562R and MCP3564R analog to digital converters). Signed-off-by: Marius Cristea Link: https://lore.kernel.org/r/20230829154133.40716-3-marius.cristea@microchip.com Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-adc-mcp3564 | 53 + MAINTAINERS | 7 + drivers/iio/adc/Kconfig | 13 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mcp3564.c | 1516 ++++++++++++++++++++ 5 files changed, 1590 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 create mode 100644 drivers/iio/adc/mcp3564.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 b/Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 new file mode 100644 index 000000000000..b168aa44b233 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 @@ -0,0 +1,53 @@ +What: /sys/bus/iio/devices/iio:deviceX/boost_current_gain +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is used to set the gain of the biasing current + circuit of the Delta-Sigma modulator. The different BOOST + settings are applied to the entire modulator circuit, including + the voltage reference buffers. + +What: /sys/bus/iio/devices/iio:deviceX/boost_current_gain_available +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns a list with the possible gain values for + the current biasing circuit of the Delta-Sigma modulator. + +What: /sys/bus/iio/devices/iio:deviceX/auto_zeroing_mux_enable +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is used to enable the analog input multiplexer + auto-zeroing algorithm (the input multiplexer and the ADC + include an offset cancellation algorithm that cancels the offset + contribution of the ADC). When the offset cancellation algorithm + is enabled, ADC takes two conversions, one with the differential + input as VIN+/VIN-, one with VIN+/VIN- inverted. In this case the + conversion time is multiplied by two compared to the default + case where the algorithm is disabled. This technique allows the + cancellation of the ADC offset error and the achievement of + ultra-low offset without any digital calibration. The resulting + offset is the residue of the difference between the two + conversions, which is on the order of magnitude of the noise + floor. This offset is effectively canceled at every conversion, + so the residual offset error temperature drift is extremely low. + Write '1' to enable it, write '0' to disable it. + +What: /sys/bus/iio/devices/iio:deviceX/auto_zeroing_ref_enable +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is used to enable the chopping algorithm for the + internal voltage reference buffer. This setting has no effect + when external voltage reference is selected. + Internal voltage reference buffer injects a certain quantity of + 1/f noise into the system that can be modulated with the + incoming input signals and can limit the SNR performance at + higher Oversampling Ratio values (over 256). To overcome this + limitation, the buffer includes an auto-zeroing algorithm that + greatly reduces (cancels out) the 1/f noise and cancels the + offset value of the reference buffer. As a result, the SNR of + the system is not affected by this 1/f noise component of the + reference buffer, even at maximum oversampling ratio values. + Write '1' to enable it, write '0' to disable it. diff --git a/MAINTAINERS b/MAINTAINERS index 90f13281d297..337a4244ee30 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14019,6 +14019,13 @@ S: Supported F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt F: drivers/regulator/mcp16502.c +MICROCHIP MCP3564 ADC DRIVER +M: Marius Cristea +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml +F: drivers/iio/adc/mcp3564.c + MICROCHIP MCP3911 ADC DRIVER M: Marcus Folkesson M: Kent Gustavsson diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index bb3786745ab6..35f9867da12c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -789,6 +789,19 @@ config MCP3422 This driver can also be built as a module. If so, the module will be called mcp3422. +config MCP3564 + tristate "Microchip Technology MCP3461/2/4/R, MCP3561/2/4/R driver" + depends on SPI + depends on IIO + help + Say yes here to build support for Microchip Technology's MCP3461, + MCP3462, MCP3464, MCP3461R, MCP3462R, MCP3464R, MCP3561, MCP3562, + MCP3564, MCP3561R, MCP3562R and MCP3564R analog to digital + converters. + + This driver can also be built as a module. If so, the module will be + called mcp3564. + config MCP3911 tristate "Microchip Technology MCP3911 driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 2d5b2a181380..bee11d442af4 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MCP3564) += mcp3564.o obj-$(CONFIG_MCP3911) += mcp3911.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c new file mode 100644 index 000000000000..64145f4ae55c --- /dev/null +++ b/drivers/iio/adc/mcp3564.c @@ -0,0 +1,1516 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for MCP356X/MCP356XR and MCP346X/MCP346XR series ADC chip family + * + * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries + * + * Author: Marius Cristea + * + * Datasheet for MCP3561, MCP3562, MCP3564 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf + * Datasheet for MCP3561R, MCP3562R, MCP3564R can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3561_2_4R-Data-Sheet-DS200006391C.pdf + * Datasheet for MCP3461, MCP3462, MCP3464 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4-Two-Four-Eight-Channel-153.6-ksps-Low-Noise-16-Bit-Delta-Sigma-ADC-Data-Sheet-20006180D.pdf + * Datasheet for MCP3461R, MCP3462R, MCP3464R can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4R-Family-Data-Sheet-DS20006404C.pdf + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MCP3564_ADCDATA_REG 0x00 + +#define MCP3564_CONFIG0_REG 0x01 +#define MCP3564_CONFIG0_ADC_MODE_MASK GENMASK(1, 0) +/* Current Source/Sink Selection Bits for Sensor Bias */ +#define MCP3564_CONFIG0_CS_SEL_MASK GENMASK(3, 2) +/* Internal clock is selected and AMCLK is present on the analog master clock output pin */ +#define MCP3564_CONFIG0_USE_INT_CLK_OUTPUT_EN 0x03 +/* Internal clock is selected and no clock output is present on the CLK pin */ +#define MCP3564_CONFIG0_USE_INT_CLK 0x02 +/* External digital clock */ +#define MCP3564_CONFIG0_USE_EXT_CLK 0x01 +/* External digital clock (default) */ +#define MCP3564_CONFIG0_USE_EXT_CLK_DEFAULT 0x00 +#define MCP3564_CONFIG0_CLK_SEL_MASK GENMASK(5, 4) +#define MCP3456_CONFIG0_BIT6_DEFAULT BIT(6) +#define MCP3456_CONFIG0_VREF_MASK BIT(7) + +#define MCP3564_CONFIG1_REG 0x02 +#define MCP3564_CONFIG1_OVERSPL_RATIO_MASK GENMASK(5, 2) + +#define MCP3564_CONFIG2_REG 0x03 +#define MCP3564_CONFIG2_AZ_REF_MASK BIT(1) +#define MCP3564_CONFIG2_AZ_MUX_MASK BIT(2) + +#define MCP3564_CONFIG2_HARDWARE_GAIN_MASK GENMASK(5, 3) +#define MCP3564_DEFAULT_HARDWARE_GAIN 0x01 +#define MCP3564_CONFIG2_BOOST_CURRENT_MASK GENMASK(7, 6) + +#define MCP3564_CONFIG3_REG 0x04 +#define MCP3464_CONFIG3_EN_GAINCAL_MASK BIT(0) +#define MCP3464_CONFIG3_EN_OFFCAL_MASK BIT(1) +#define MCP3464_CONFIG3_EN_CRCCOM_MASK BIT(2) +#define MCP3464_CONFIG3_CRC_FORMAT_MASK BIT(3) +/* + * ADC Output Data Format 32-bit (25-bit right justified data + Channel ID): + * CHID[3:0] + SGN extension (4 bits) + 24-bit ADC data. + * It allows overrange with the SGN extension. + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_WITH_CH_ID 3 +/* + * ADC Output Data Format 32-bit (25-bit right justified data): + * SGN extension (8-bit) + 24-bit ADC data. + * It allows overrange with the SGN extension. + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT 2 +/* + * ADC Output Data Format 32-bit (24-bit left justified data): + * 24-bit ADC data + 0x00 (8-bit). + * It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000). + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_LEFT_JUSTIFIED 1 +/* + * ADC Output Data Format 24-bit (default ADC coding): + * 24-bit ADC data. + * It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000). + */ +#define MCP3464_CONFIG3_DATA_FMT_24B 0 +#define MCP3464_CONFIG3_DATA_FORMAT_MASK GENMASK(5, 4) + +/* Continuous Conversion mode or continuous conversion cycle in SCAN mode. */ +#define MCP3464_CONFIG3_CONV_MODE_CONTINUOUS 3 +/* + * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘10’ + * (standby) at the end of the conversion or at the end of the conversion cycle in SCAN mode. + */ +#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY 2 +/* + * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘0x’ (ADC + * Shutdown) at the end of the conversion or at the end of the conversion cycle in SCAN + * mode (default). + */ +#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_SHUTDOWN 0 +#define MCP3464_CONFIG3_CONV_MODE_MASK GENMASK(7, 6) + +#define MCP3564_IRQ_REG 0x05 +#define MCP3464_EN_STP_MASK BIT(0) +#define MCP3464_EN_FASTCMD_MASK BIT(1) +#define MCP3464_IRQ_MODE_0_MASK BIT(2) +#define MCP3464_IRQ_MODE_1_MASK BIT(3) +#define MCP3564_POR_STATUS_MASK BIT(4) +#define MCP3564_CRCCFG_STATUS_MASK BIT(5) +#define MCP3564_DATA_READY_MASK BIT(6) + +#define MCP3564_MUX_REG 0x06 +#define MCP3564_MUX_VIN_P_MASK GENMASK(7, 4) +#define MCP3564_MUX_VIN_N_MASK GENMASK(3, 0) +#define MCP3564_MUX_SET(x, y) (FIELD_PREP(MCP3564_MUX_VIN_P_MASK, (x)) | \ + FIELD_PREP(MCP3564_MUX_VIN_N_MASK, (y))) + +#define MCP3564_SCAN_REG 0x07 +#define MCP3564_SCAN_CH_SEL_MASK GENMASK(15, 0) +#define MCP3564_SCAN_CH_SEL_SET(x) FIELD_PREP(MCP3564_SCAN_CH_SEL_MASK, (x)) +#define MCP3564_SCAN_DELAY_TIME_MASK GENMASK(23, 21) +#define MCP3564_SCAN_DELAY_TIME_SET(x) FIELD_PREP(MCP3564_SCAN_DELAY_TIME_MASK, (x)) +#define MCP3564_SCAN_DEFAULT_VALUE 0 + +#define MCP3564_TIMER_REG 0x08 +#define MCP3564_TIMER_DEFAULT_VALUE 0 + +#define MCP3564_OFFSETCAL_REG 0x09 +#define MCP3564_DEFAULT_OFFSETCAL 0 + +#define MCP3564_GAINCAL_REG 0x0A +#define MCP3564_DEFAULT_GAINCAL 0x00800000 + +#define MCP3564_RESERVED_B_REG 0x0B + +#define MCP3564_RESERVED_C_REG 0x0C +#define MCP3564_C_REG_DEFAULT 0x50 +#define MCP3564R_C_REG_DEFAULT 0x30 + +#define MCP3564_LOCK_REG 0x0D +#define MCP3564_LOCK_WRITE_ACCESS_PASSWORD 0xA5 +#define MCP3564_RESERVED_E_REG 0x0E +#define MCP3564_CRCCFG_REG 0x0F + +#define MCP3564_CMD_HW_ADDR_MASK GENMASK(7, 6) +#define MCP3564_CMD_ADDR_MASK GENMASK(5, 2) + +#define MCP3564_HW_ADDR_MASK GENMASK(1, 0) + +#define MCP3564_FASTCMD_START 0x0A +#define MCP3564_FASTCMD_RESET 0x0E + +#define MCP3461_HW_ID 0x0008 +#define MCP3462_HW_ID 0x0009 +#define MCP3464_HW_ID 0x000B + +#define MCP3561_HW_ID 0x000C +#define MCP3562_HW_ID 0x000D +#define MCP3564_HW_ID 0x000F +#define MCP3564_HW_ID_MASK GENMASK(3, 0) + +#define MCP3564R_INT_VREF_MV 2400 + +#define MCP3564_DATA_READY_TIMEOUT_MS 2000 + +#define MCP3564_MAX_PGA 8 +#define MCP3564_MAX_BURNOUT_IDX 4 +#define MCP3564_MAX_CHANNELS 66 + +enum mcp3564_ids { + mcp3461, + mcp3462, + mcp3464, + mcp3561, + mcp3562, + mcp3564, + mcp3461r, + mcp3462r, + mcp3464r, + mcp3561r, + mcp3562r, + mcp3564r, +}; + +enum mcp3564_delay_time { + MCP3564_NO_DELAY, + MCP3564_DELAY_8_DMCLK, + MCP3564_DELAY_16_DMCLK, + MCP3564_DELAY_32_DMCLK, + MCP3564_DELAY_64_DMCLK, + MCP3564_DELAY_128_DMCLK, + MCP3564_DELAY_256_DMCLK, + MCP3564_DELAY_512_DMCLK +}; + +enum mcp3564_adc_conversion_mode { + MCP3564_ADC_MODE_DEFAULT, + MCP3564_ADC_MODE_SHUTDOWN, + MCP3564_ADC_MODE_STANDBY, + MCP3564_ADC_MODE_CONVERSION +}; + +enum mcp3564_adc_bias_current { + MCP3564_BOOST_CURRENT_x0_50, + MCP3564_BOOST_CURRENT_x0_66, + MCP3564_BOOST_CURRENT_x1_00, + MCP3564_BOOST_CURRENT_x2_00 +}; + +enum mcp3564_burnout { + MCP3564_CONFIG0_CS_SEL_0_0_uA, + MCP3564_CONFIG0_CS_SEL_0_9_uA, + MCP3564_CONFIG0_CS_SEL_3_7_uA, + MCP3564_CONFIG0_CS_SEL_15_uA +}; + +enum mcp3564_channel_names { + MCP3564_CH0, + MCP3564_CH1, + MCP3564_CH2, + MCP3564_CH3, + MCP3564_CH4, + MCP3564_CH5, + MCP3564_CH6, + MCP3564_CH7, + MCP3564_AGND, + MCP3564_AVDD, + MCP3564_RESERVED, /* do not use */ + MCP3564_REFIN_POZ, + MCP3564_REFIN_NEG, + MCP3564_TEMP_DIODE_P, + MCP3564_TEMP_DIODE_M, + MCP3564_INTERNAL_VCM, +}; + +enum mcp3564_oversampling { + MCP3564_OVERSAMPLING_RATIO_32, + MCP3564_OVERSAMPLING_RATIO_64, + MCP3564_OVERSAMPLING_RATIO_128, + MCP3564_OVERSAMPLING_RATIO_256, + MCP3564_OVERSAMPLING_RATIO_512, + MCP3564_OVERSAMPLING_RATIO_1024, + MCP3564_OVERSAMPLING_RATIO_2048, + MCP3564_OVERSAMPLING_RATIO_4096, + MCP3564_OVERSAMPLING_RATIO_8192, + MCP3564_OVERSAMPLING_RATIO_16384, + MCP3564_OVERSAMPLING_RATIO_20480, + MCP3564_OVERSAMPLING_RATIO_24576, + MCP3564_OVERSAMPLING_RATIO_40960, + MCP3564_OVERSAMPLING_RATIO_49152, + MCP3564_OVERSAMPLING_RATIO_81920, + MCP3564_OVERSAMPLING_RATIO_98304 +}; + +static const unsigned int mcp3564_oversampling_avail[] = { + [MCP3564_OVERSAMPLING_RATIO_32] = 32, + [MCP3564_OVERSAMPLING_RATIO_64] = 64, + [MCP3564_OVERSAMPLING_RATIO_128] = 128, + [MCP3564_OVERSAMPLING_RATIO_256] = 256, + [MCP3564_OVERSAMPLING_RATIO_512] = 512, + [MCP3564_OVERSAMPLING_RATIO_1024] = 1024, + [MCP3564_OVERSAMPLING_RATIO_2048] = 2048, + [MCP3564_OVERSAMPLING_RATIO_4096] = 4096, + [MCP3564_OVERSAMPLING_RATIO_8192] = 8192, + [MCP3564_OVERSAMPLING_RATIO_16384] = 16384, + [MCP3564_OVERSAMPLING_RATIO_20480] = 20480, + [MCP3564_OVERSAMPLING_RATIO_24576] = 24576, + [MCP3564_OVERSAMPLING_RATIO_40960] = 40960, + [MCP3564_OVERSAMPLING_RATIO_49152] = 49152, + [MCP3564_OVERSAMPLING_RATIO_81920] = 81920, + [MCP3564_OVERSAMPLING_RATIO_98304] = 98304 +}; + +/* + * Current Source/Sink Selection Bits for Sensor Bias (source on VIN+/sink on VIN-) + */ +static const int mcp3564_burnout_avail[][2] = { + [MCP3564_CONFIG0_CS_SEL_0_0_uA] = { 0, 0 }, + [MCP3564_CONFIG0_CS_SEL_0_9_uA] = { 0, 900 }, + [MCP3564_CONFIG0_CS_SEL_3_7_uA] = { 0, 3700 }, + [MCP3564_CONFIG0_CS_SEL_15_uA] = { 0, 15000 } +}; + +/* + * BOOST[1:0]: ADC Bias Current Selection + */ +static const char * const mcp3564_boost_current_avail[] = { + [MCP3564_BOOST_CURRENT_x0_50] = "0.5", + [MCP3564_BOOST_CURRENT_x0_66] = "0.66", + [MCP3564_BOOST_CURRENT_x1_00] = "1", + [MCP3564_BOOST_CURRENT_x2_00] = "2", +}; + +/* + * Calibration bias values + */ +static const int mcp3564_calib_bias[] = { + -8388608, /* min: -2^23 */ + 1, /* step: 1 */ + 8388607 /* max: 2^23 - 1 */ +}; + +/* + * Calibration scale values + * The Gain Error Calibration register (GAINCAL) is an + * unsigned 24-bit register that holds the digital gain error + * calibration value, GAINCAL which could be calculated by + * GAINCAL (V/V) = (GAINCAL[23:0])/8388608 + * The gain error calibration value range in equivalent voltage is [0; 2-2^(-23)] + */ +static const unsigned int mcp3564_calib_scale[] = { + 0, /* min: 0 */ + 1, /* step: 1/8388608 */ + 16777215 /* max: 2 - 2^(-23) */ +}; + +/* Programmable hardware gain x1/3, x1, x2, x4, x8, x16, x32, x64 */ +static const int mcp3564_hwgain_frac[] = { + 3, 10, + 1, 1, + 2, 1, + 4, 1, + 8, 1, + 16, 1, + 32, 1, + 64, 1 +}; + +static const char *mcp3564_channel_labels[2] = { + "burnout_current", "temperature", +}; + +/** + * struct mcp3564_chip_info - chip specific data + * @name: device name + * @num_channels: number of channels + * @resolution: ADC resolution + * @have_vref: does the hardware have an internal voltage reference? + */ +struct mcp3564_chip_info { + const char *name; + unsigned int num_channels; + unsigned int resolution; + bool have_vref; +}; + +/** + * struct mcp3564_state - working data for a ADC device + * @chip_info: chip specific data + * @spi: SPI device structure + * @vref: the regulator device used as a voltage reference in case + * external voltage reference is used + * @vref_mv: voltage reference value in miliVolts + * @lock: synchronize access to driver's state members + * @dev_addr: hardware device address + * @oversampling: the index inside oversampling list of the ADC + * @hwgain: the index inside hardware gain list of the ADC + * @scale_tbls: table with precalculated scale + * @calib_bias: calibration bias value + * @calib_scale: calibration scale value + * @current_boost_mode: the index inside current boost list of the ADC + * @burnout_mode: the index inside current bias list of the ADC + * @auto_zeroing_mux: set if ADC auto-zeroing algorithm is enabled + * @auto_zeroing_ref: set if ADC auto-Zeroing Reference Buffer Setting is enabled + * @have_vref: does the ADC have an internal voltage reference? + * @labels: table with channels labels + */ +struct mcp3564_state { + const struct mcp3564_chip_info *chip_info; + struct spi_device *spi; + struct regulator *vref; + unsigned short vref_mv; + struct mutex lock; /* Synchronize access to driver's state members */ + u8 dev_addr; + enum mcp3564_oversampling oversampling; + unsigned int hwgain; + unsigned int scale_tbls[MCP3564_MAX_PGA][2]; + int calib_bias; + int calib_scale; + unsigned int current_boost_mode; + enum mcp3564_burnout burnout_mode; + bool auto_zeroing_mux; + bool auto_zeroing_ref; + bool have_vref; + const char *labels[MCP3564_MAX_CHANNELS]; +}; + +static inline u8 mcp3564_cmd_write(u8 chip_addr, u8 reg) +{ + return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) | + BIT(1); +} + +static inline u8 mcp3564_cmd_read(u8 chip_addr, u8 reg) +{ + return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) | + BIT(0); +} + +static int mcp3564_read_8bits(struct mcp3564_state *adc, u8 reg, u8 *val) +{ + int ret; + u8 tx_buf; + u8 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = rx_buf; + + return ret; +} + +static int mcp3564_read_16bits(struct mcp3564_state *adc, u8 reg, u16 *val) +{ + int ret; + u8 tx_buf; + __be16 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = be16_to_cpu(rx_buf); + + return ret; +} + +static int mcp3564_read_32bits(struct mcp3564_state *adc, u8 reg, u32 *val) +{ + int ret; + u8 tx_buf; + __be32 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = be32_to_cpu(rx_buf); + + return ret; +} + +static int mcp3564_write_8bits(struct mcp3564_state *adc, u8 reg, u8 val) +{ + u8 tx_buf[2]; + + tx_buf[0] = mcp3564_cmd_write(adc->dev_addr, reg); + tx_buf[1] = val; + + return spi_write_then_read(adc->spi, tx_buf, sizeof(tx_buf), NULL, 0); +} + +static int mcp3564_write_24bits(struct mcp3564_state *adc, u8 reg, u32 val) +{ + __be32 val_be; + + val |= (mcp3564_cmd_write(adc->dev_addr, reg) << 24); + val_be = cpu_to_be32(val); + + return spi_write_then_read(adc->spi, &val_be, sizeof(val_be), NULL, 0); +} + +static int mcp3564_fast_cmd(struct mcp3564_state *adc, u8 fast_cmd) +{ + u8 val; + + val = FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, adc->dev_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, fast_cmd); + + return spi_write_then_read(adc->spi, &val, 1, NULL, 0); +} + +static int mcp3564_update_8bits(struct mcp3564_state *adc, u8 reg, u32 mask, u8 val) +{ + u8 tmp; + int ret; + + val &= mask; + + ret = mcp3564_read_8bits(adc, reg, &tmp); + if (ret < 0) + return ret; + + tmp &= ~mask; + tmp |= val; + + return mcp3564_write_8bits(adc, reg, tmp); +} + +static int mcp3564_set_current_boost_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + + dev_dbg(&indio_dev->dev, "%s: %d\n", __func__, mode); + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_BOOST_CURRENT_MASK, + FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, mode)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to configure CONFIG2 register\n"); + else + adc->current_boost_mode = mode; + + mutex_unlock(&adc->lock); + + return ret; +} + +static int mcp3564_get_current_boost_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + return adc->current_boost_mode; +} + +static const struct iio_enum mcp3564_current_boost_mode_enum = { + .items = mcp3564_boost_current_avail, + .num_items = ARRAY_SIZE(mcp3564_boost_current_avail), + .set = mcp3564_set_current_boost_mode, + .get = mcp3564_get_current_boost_mode, +}; + +static const struct iio_chan_spec_ext_info mcp3564_ext_info[] = { + IIO_ENUM("boost_current_gain", IIO_SHARED_BY_ALL, &mcp3564_current_boost_mode_enum), + { + .name = "boost_current_gain_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&mcp3564_current_boost_mode_enum, + }, + { } +}; + +static ssize_t mcp3564_auto_zeroing_mux_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", adc->auto_zeroing_mux); +} + +static ssize_t mcp3564_auto_zeroing_mux_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + bool auto_zero; + int ret; + + ret = kstrtobool(buf, &auto_zero); + if (ret) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_MUX_MASK, + FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, auto_zero)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); + else + adc->auto_zeroing_mux = auto_zero; + + mutex_unlock(&adc->lock); + + return ret ? ret : len; +} + +static ssize_t mcp3564_auto_zeroing_ref_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", adc->auto_zeroing_ref); +} + +static ssize_t mcp3564_auto_zeroing_ref_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + bool auto_zero; + int ret; + + ret = kstrtobool(buf, &auto_zero); + if (ret) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_REF_MASK, + FIELD_PREP(MCP3564_CONFIG2_AZ_REF_MASK, auto_zero)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); + else + adc->auto_zeroing_ref = auto_zero; + + mutex_unlock(&adc->lock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec mcp3564_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = mcp3564_ext_info, +}; + +static const struct iio_chan_spec mcp3564_temp_channel_template = { + .type = IIO_TEMP, + .channel = 0, + .address = ((MCP3564_TEMP_DIODE_P << 4) | MCP3564_TEMP_DIODE_M), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), +}; + +static const struct iio_chan_spec mcp3564_burnout_channel_template = { + .type = IIO_CURRENT, + .output = true, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), +}; + +/* + * Number of channels could be calculated: + * num_channels = single_ended_input + differential_input + temperature + burnout + * Eg. for MCP3561 (only 2 channels available: CH0 and CH1) + * single_ended_input = (CH0 - GND), (CH1 - GND) = 2 + * differential_input = (CH0 - CH1), (CH0 - CH0) = 2 + * num_channels = 2 + 2 + 2 + * Generic formula is: + * num_channels = P^R(Number_of_single_ended_channels, 2) + 2 (temperature + burnout channels) + * P^R(Number_of_single_ended_channels, 2) is Permutations with Replacement of + * Number_of_single_ended_channels taken by 2 + */ +static const struct mcp3564_chip_info mcp3564_chip_infos_tbl[] = { + [mcp3461] = { + .name = "mcp3461", + .num_channels = 6, + .resolution = 16, + .have_vref = false, + }, + [mcp3462] = { + .name = "mcp3462", + .num_channels = 18, + .resolution = 16, + .have_vref = false, + }, + [mcp3464] = { + .name = "mcp3464", + .num_channels = 66, + .resolution = 16, + .have_vref = false, + }, + [mcp3561] = { + .name = "mcp3561", + .num_channels = 6, + .resolution = 24, + .have_vref = false, + }, + [mcp3562] = { + .name = "mcp3562", + .num_channels = 18, + .resolution = 24, + .have_vref = false, + }, + [mcp3564] = { + .name = "mcp3564", + .num_channels = 66, + .resolution = 24, + .have_vref = false, + }, + [mcp3461r] = { + .name = "mcp3461r", + .num_channels = 6, + .resolution = 16, + .have_vref = false, + }, + [mcp3462r] = { + .name = "mcp3462r", + .num_channels = 18, + .resolution = 16, + .have_vref = true, + }, + [mcp3464r] = { + .name = "mcp3464r", + .num_channels = 66, + .resolution = 16, + .have_vref = true, + }, + [mcp3561r] = { + .name = "mcp3561r", + .num_channels = 6, + .resolution = 24, + .have_vref = true, + }, + [mcp3562r] = { + .name = "mcp3562r", + .num_channels = 18, + .resolution = 24, + .have_vref = true, + }, + [mcp3564r] = { + .name = "mcp3564r", + .num_channels = 66, + .resolution = 24, + .have_vref = true, + }, +}; + +static int mcp3564_read_single_value(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + int *val) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + u8 tmp; + int ret_read = 0; + + ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, channel->address); + if (ret) + return ret; + + /* Start ADC Conversion using fast command (overwrites ADC_MODE[1:0] = 11) */ + ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_START); + if (ret) + return ret; + + /* + * Check if the conversion is ready. If not, wait a little bit, and + * in case of timeout exit with an error. + */ + ret = read_poll_timeout(mcp3564_read_8bits, ret_read, + ret_read || !(tmp & MCP3564_DATA_READY_MASK), + 20000, MCP3564_DATA_READY_TIMEOUT_MS * 1000, true, + adc, MCP3564_IRQ_REG, &tmp); + + /* failed to read status register */ + if (ret_read) + return ret_read; + + if (ret) + return ret; + + if (tmp & MCP3564_DATA_READY_MASK) + /* failing to finish conversion */ + return -EBUSY; + + return mcp3564_read_32bits(adc, MCP3564_ADCDATA_REG, val); +} + +static int mcp3564_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + const int **vals, int *type, + int *length, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!channel->output) + return -EINVAL; + + *vals = mcp3564_burnout_avail[0]; + *length = ARRAY_SIZE(mcp3564_burnout_avail) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = mcp3564_oversampling_avail; + *length = ARRAY_SIZE(mcp3564_oversampling_avail); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (int *)adc->scale_tbls; + *length = ARRAY_SIZE(adc->scale_tbls) * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_CALIBBIAS: + *vals = mcp3564_calib_bias; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_CALIBSCALE: + *vals = mcp3564_calib_scale; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int mcp3564_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + int *val, int *val2, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (channel->output) { + mutex_lock(&adc->lock); + *val = mcp3564_burnout_avail[adc->burnout_mode][0]; + *val2 = mcp3564_burnout_avail[adc->burnout_mode][1]; + mutex_unlock(&adc->lock); + return IIO_VAL_INT_PLUS_MICRO; + } + + ret = mcp3564_read_single_value(indio_dev, channel, val); + if (ret) + return -EINVAL; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + mutex_lock(&adc->lock); + *val = adc->scale_tbls[adc->hwgain][0]; + *val2 = adc->scale_tbls[adc->hwgain][1]; + mutex_unlock(&adc->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = mcp3564_oversampling_avail[adc->oversampling]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + *val = adc->calib_bias; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val = adc->calib_scale; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int mcp3564_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int mcp3564_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int tmp; + unsigned int hwgain; + enum mcp3564_burnout burnout; + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!channel->output) + return -EINVAL; + + for (burnout = 0; burnout < MCP3564_MAX_BURNOUT_IDX; burnout++) + if (val == mcp3564_burnout_avail[burnout][0] && + val2 == mcp3564_burnout_avail[burnout][1]) + break; + + if (burnout == MCP3564_MAX_BURNOUT_IDX) + return -EINVAL; + + if (burnout == adc->burnout_mode) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG0_REG, + MCP3564_CONFIG0_CS_SEL_MASK, + FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, burnout)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to configure burnout current\n"); + else + adc->burnout_mode = burnout; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_CALIBBIAS: + if (val < mcp3564_calib_bias[0] && val > mcp3564_calib_bias[2]) + return -EINVAL; + + mutex_lock(&adc->lock); + ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, val); + if (!ret) + adc->calib_bias = val; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_CALIBSCALE: + if (val < mcp3564_calib_scale[0] && val > mcp3564_calib_scale[2]) + return -EINVAL; + + if (adc->calib_scale == val) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, val); + if (!ret) + adc->calib_scale = val; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val < 0) + return -EINVAL; + + tmp = find_closest(val, mcp3564_oversampling_avail, + ARRAY_SIZE(mcp3564_oversampling_avail)); + + if (adc->oversampling == tmp) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG1_REG, + MCP3564_CONFIG1_OVERSPL_RATIO_MASK, + FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK, + adc->oversampling)); + if (!ret) + adc->oversampling = tmp; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_SCALE: + for (hwgain = 0; hwgain < MCP3564_MAX_PGA; hwgain++) + if (val == adc->scale_tbls[hwgain][0] && + val2 == adc->scale_tbls[hwgain][1]) + break; + + if (hwgain == MCP3564_MAX_PGA) + return -EINVAL; + + if (hwgain == adc->hwgain) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, + MCP3564_CONFIG2_HARDWARE_GAIN_MASK, + FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, hwgain)); + if (!ret) + adc->hwgain = hwgain; + + mutex_unlock(&adc->lock); + return ret; + default: + return -EINVAL; + } +} + +static int mcp3564_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sprintf(label, "%s\n", adc->labels[chan->scan_index]); +} + +static int mcp3564_parse_fw_children(struct iio_dev *indio_dev) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; + struct iio_chan_spec *channels; + struct fwnode_handle *child; + struct iio_chan_spec chanspec = mcp3564_channel_template; + struct iio_chan_spec temp_chanspec = mcp3564_temp_channel_template; + struct iio_chan_spec burnout_chanspec = mcp3564_burnout_channel_template; + int chan_idx = 0; + unsigned int num_ch; + u32 inputs[2]; + const char *node_name; + const char *label; + int ret; + + num_ch = device_get_child_node_count(dev); + if (num_ch == 0) + return dev_err_probe(&indio_dev->dev, -ENODEV, + "FW has no channels defined\n"); + + /* Reserve space for burnout and temperature channel */ + num_ch += 2; + + if (num_ch > adc->chip_info->num_channels) + return dev_err_probe(dev, -EINVAL, "Too many channels %d > %d\n", + num_ch, adc->chip_info->num_channels); + + channels = devm_kcalloc(dev, num_ch, sizeof(*channels), GFP_KERNEL); + if (!channels) + return dev_err_probe(dev, -ENOMEM, "Can't allocate memory\n"); + + device_for_each_child_node(dev, child) { + node_name = fwnode_get_name(child); + + if (fwnode_property_present(child, "diff-channels")) { + ret = fwnode_property_read_u32_array(child, + "diff-channels", + inputs, + ARRAY_SIZE(inputs)); + chanspec.differential = 1; + } else { + ret = fwnode_property_read_u32(child, "reg", &inputs[0]); + + chanspec.differential = 0; + inputs[1] = MCP3564_AGND; + } + if (ret) { + fwnode_handle_put(child); + return ret; + } + + if (inputs[0] > MCP3564_INTERNAL_VCM || + inputs[1] > MCP3564_INTERNAL_VCM) { + fwnode_handle_put(child); + return dev_err_probe(&indio_dev->dev, -EINVAL, + "Channel index > %d, for %s\n", + MCP3564_INTERNAL_VCM + 1, + node_name); + } + + chanspec.address = (inputs[0] << 4) | inputs[1]; + chanspec.channel = inputs[0]; + chanspec.channel2 = inputs[1]; + chanspec.scan_index = chan_idx; + + if (fwnode_property_present(child, "label")) { + fwnode_property_read_string(child, "label", &label); + adc->labels[chan_idx] = label; + } + + channels[chan_idx] = chanspec; + chan_idx++; + } + + /* Add burnout current channel */ + burnout_chanspec.scan_index = chan_idx; + channels[chan_idx] = burnout_chanspec; + adc->labels[chan_idx] = mcp3564_channel_labels[0]; + chanspec.scan_index = chan_idx; + chan_idx++; + + /* Add temperature channel */ + temp_chanspec.scan_index = chan_idx; + channels[chan_idx] = temp_chanspec; + adc->labels[chan_idx] = mcp3564_channel_labels[1]; + chan_idx++; + + indio_dev->num_channels = chan_idx; + indio_dev->channels = channels; + + return 0; +} + +static void mcp3564_disable_reg(void *reg) +{ + regulator_disable(reg); +} + +static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc) +{ + unsigned int pow = adc->chip_info->resolution - 1; + int ref; + unsigned int i; + int tmp0; + u64 tmp1; + + for (i = 0; i < MCP3564_MAX_PGA; i++) { + ref = adc->vref_mv; + tmp1 = shift_right((u64)ref * NANO, pow); + div_u64_rem(tmp1, NANO, &tmp0); + + tmp1 = tmp1 * mcp3564_hwgain_frac[(2 * i) + 1]; + tmp0 = (int)div_u64(tmp1, mcp3564_hwgain_frac[2 * i]); + + adc->scale_tbls[i][1] = tmp0; + } +} + +static int mcp3564_config(struct iio_dev *indio_dev) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; + const struct spi_device_id *dev_id; + u8 tmp_reg; + u16 tmp_u16; + enum mcp3564_ids ids; + int ret = 0; + unsigned int tmp = 0x01; + bool err = true; + + /* + * The address is set on a per-device basis by fuses in the factory, + * configured on request. If not requested, the fuses are set for 0x1. + * The device address is part of the device markings to avoid + * potential confusion. This address is coded on two bits, so four possible + * addresses are available when multiple devices are present on the same + * SPI bus with only one Chip Select line for all devices. + */ + device_property_read_u32(dev, "microchip,hw-device-address", &tmp); + + if (tmp > 3) { + dev_err_probe(dev, tmp, + "invalid device address. Must be in range 0-3.\n"); + return -EINVAL; + } + + adc->dev_addr = FIELD_GET(MCP3564_HW_ADDR_MASK, tmp); + + dev_dbg(dev, "use HW device address %i\n", adc->dev_addr); + + ret = mcp3564_read_8bits(adc, MCP3564_RESERVED_C_REG, &tmp_reg); + if (ret < 0) + return ret; + + switch (tmp_reg) { + case MCP3564_C_REG_DEFAULT: + adc->have_vref = false; + break; + case MCP3564R_C_REG_DEFAULT: + adc->have_vref = true; + break; + default: + dev_info(dev, "Unknown chip found: %d\n", tmp_reg); + err = true; + } + + if (!err) { + ret = mcp3564_read_16bits(adc, MCP3564_RESERVED_E_REG, &tmp_u16); + if (ret < 0) + return ret; + + switch (tmp_u16 & MCP3564_HW_ID_MASK) { + case MCP3461_HW_ID: + if (adc->have_vref) + ids = mcp3461r; + else + ids = mcp3461; + break; + case MCP3462_HW_ID: + if (adc->have_vref) + ids = mcp3462r; + else + ids = mcp3462; + break; + case MCP3464_HW_ID: + if (adc->have_vref) + ids = mcp3464r; + else + ids = mcp3464; + break; + case MCP3561_HW_ID: + if (adc->have_vref) + ids = mcp3561r; + else + ids = mcp3561; + break; + case MCP3562_HW_ID: + if (adc->have_vref) + ids = mcp3562r; + else + ids = mcp3562; + break; + case MCP3564_HW_ID: + if (adc->have_vref) + ids = mcp3564r; + else + ids = mcp3564; + break; + default: + dev_info(dev, "Unknown chip found: %d\n", tmp_u16); + err = true; + } + } + + if (err) { + /* + * If failed to identify the hardware based on internal registers, + * try using fallback compatible in device tree to deal with some newer part number. + */ + adc->chip_info = spi_get_device_match_data(adc->spi); + if (!adc->chip_info) { + dev_id = spi_get_device_id(adc->spi); + adc->chip_info = (const struct mcp3564_chip_info *)dev_id->driver_data; + } + + adc->have_vref = adc->chip_info->have_vref; + } else { + adc->chip_info = &mcp3564_chip_infos_tbl[ids]; + } + + dev_dbg(dev, "Found %s chip\n", adc->chip_info->name); + + adc->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(adc->vref)) { + if (PTR_ERR(adc->vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get regulator\n"); + + /* Check if chip has internal vref */ + if (!adc->have_vref) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "Unknown Vref\n"); + adc->vref = NULL; + dev_dbg(dev, "%s: Using internal Vref\n", __func__); + } else { + ret = regulator_enable(adc->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, mcp3564_disable_reg, + adc->vref); + if (ret) + return ret; + + dev_dbg(dev, "%s: Using External Vref\n", __func__); + + ret = regulator_get_voltage(adc->vref); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to read vref regulator\n"); + + adc->vref_mv = ret / MILLI; + } + + ret = mcp3564_parse_fw_children(indio_dev); + if (ret) + return ret; + + /* + * Command sequence that ensures a recovery with the desired settings + * in any cases of loss-of-power scenario (Full Chip Reset): + * - Write LOCK register to 0xA5 + * - Write IRQ register to 0x03 + * - Send "Device Full Reset" fast command + * - Wait 1ms for "Full Reset" to complete + */ + ret = mcp3564_write_8bits(adc, MCP3564_LOCK_REG, MCP3564_LOCK_WRITE_ACCESS_PASSWORD); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG, 0x03); + if (ret) + return ret; + + ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_RESET); + if (ret) + return ret; + + /* + * After Full reset wait some time to be able to fully reset the part and place + * it back in a default configuration. + * From datasheet: POR (Power On Reset Time) is ~1us + * 1ms should be enough. + */ + mdelay(1); + + /* set a gain of 1x for GAINCAL */ + ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, MCP3564_DEFAULT_GAINCAL); + if (ret) + return ret; + + adc->calib_scale = MCP3564_DEFAULT_GAINCAL; + + ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, MCP3564_DEFAULT_OFFSETCAL); + if (ret) + return ret; + + ret = mcp3564_write_24bits(adc, MCP3564_TIMER_REG, MCP3564_TIMER_DEFAULT_VALUE); + if (ret) + return ret; + + ret = mcp3564_write_24bits(adc, MCP3564_SCAN_REG, + MCP3564_SCAN_DELAY_TIME_SET(MCP3564_NO_DELAY) | + MCP3564_SCAN_CH_SEL_SET(MCP3564_SCAN_DEFAULT_VALUE)); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, MCP3564_MUX_SET(MCP3564_CH0, MCP3564_CH1)); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG, + FIELD_PREP(MCP3464_EN_FASTCMD_MASK, 1) | + FIELD_PREP(MCP3464_EN_STP_MASK, 1)); + if (ret) + return ret; + + tmp_reg = FIELD_PREP(MCP3464_CONFIG3_CONV_MODE_MASK, + MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY); + tmp_reg |= FIELD_PREP(MCP3464_CONFIG3_DATA_FORMAT_MASK, + MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT); + tmp_reg |= MCP3464_CONFIG3_EN_OFFCAL_MASK; + tmp_reg |= MCP3464_CONFIG3_EN_GAINCAL_MASK; + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG3_REG, tmp_reg); + if (ret) + return ret; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, MCP3564_BOOST_CURRENT_x1_00); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, 0x01); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, 1); + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG2_REG, tmp_reg); + if (ret) + return ret; + + adc->hwgain = 0x01; + adc->auto_zeroing_mux = true; + adc->auto_zeroing_ref = false; + adc->current_boost_mode = MCP3564_BOOST_CURRENT_x1_00; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK, MCP3564_OVERSAMPLING_RATIO_98304); + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG1_REG, tmp_reg); + if (ret) + return ret; + + adc->oversampling = MCP3564_OVERSAMPLING_RATIO_98304; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG0_ADC_MODE_MASK, MCP3564_ADC_MODE_STANDBY); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, MCP3564_CONFIG0_CS_SEL_0_0_uA); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CLK_SEL_MASK, MCP3564_CONFIG0_USE_INT_CLK); + tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT; + + if (!adc->vref) { + tmp_reg |= FIELD_PREP(MCP3456_CONFIG0_VREF_MASK, 1); + adc->vref_mv = MCP3564R_INT_VREF_MV; + } + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG0_REG, tmp_reg); + + adc->burnout_mode = MCP3564_CONFIG0_CS_SEL_0_0_uA; + + return ret; +} + +static IIO_DEVICE_ATTR(auto_zeroing_ref_enable, 0644, + mcp3564_auto_zeroing_ref_show, + mcp3564_auto_zeroing_ref_store, 0); + +static IIO_DEVICE_ATTR(auto_zeroing_mux_enable, 0644, + mcp3564_auto_zeroing_mux_show, + mcp3564_auto_zeroing_mux_store, 0); + +static struct attribute *mcp3564_attributes[] = { + &iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr, + NULL +}; + +static struct attribute *mcp3564r_attributes[] = { + &iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr, + &iio_dev_attr_auto_zeroing_ref_enable.dev_attr.attr, + NULL +}; + +static struct attribute_group mcp3564_attribute_group = { + .attrs = mcp3564_attributes, +}; + +static struct attribute_group mcp3564r_attribute_group = { + .attrs = mcp3564r_attributes, +}; + +static const struct iio_info mcp3564_info = { + .read_raw = mcp3564_read_raw, + .read_avail = mcp3564_read_avail, + .write_raw = mcp3564_write_raw, + .write_raw_get_fmt = mcp3564_write_raw_get_fmt, + .read_label = mcp3564_read_label, + .attrs = &mcp3564_attribute_group, +}; + +static const struct iio_info mcp3564r_info = { + .read_raw = mcp3564_read_raw, + .read_avail = mcp3564_read_avail, + .write_raw = mcp3564_write_raw, + .write_raw_get_fmt = mcp3564_write_raw_get_fmt, + .read_label = mcp3564_read_label, + .attrs = &mcp3564r_attribute_group, +}; + +static int mcp3564_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct mcp3564_state *adc; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) { + dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev), + "Can't allocate iio device\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->spi = spi; + + dev_dbg(&spi->dev, "%s: probe(spi = 0x%p)\n", __func__, spi); + + /* + * Do any chip specific initialization, e.g: + * read/write some registers + * enable/disable certain channels + * change the sampling rate to the requested value + */ + ret = mcp3564_config(indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Can't configure MCP356X device\n"); + + dev_dbg(&spi->dev, "%s: Vref (mV): %d\n", __func__, adc->vref_mv); + + mcp3564_fill_scale_tbls(adc); + + indio_dev->name = adc->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (!adc->vref) + indio_dev->info = &mcp3564r_info; + else + indio_dev->info = &mcp3564_info; + + mutex_init(&adc->lock); + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Can't register IIO device\n"); + + return 0; +} + +static const struct of_device_id mcp3564_dt_ids[] = { + { .compatible = "microchip,mcp3461", .data = &mcp3564_chip_infos_tbl[mcp3461] }, + { .compatible = "microchip,mcp3462", .data = &mcp3564_chip_infos_tbl[mcp3462] }, + { .compatible = "microchip,mcp3464", .data = &mcp3564_chip_infos_tbl[mcp3464] }, + { .compatible = "microchip,mcp3561", .data = &mcp3564_chip_infos_tbl[mcp3561] }, + { .compatible = "microchip,mcp3562", .data = &mcp3564_chip_infos_tbl[mcp3562] }, + { .compatible = "microchip,mcp3564", .data = &mcp3564_chip_infos_tbl[mcp3564] }, + { .compatible = "microchip,mcp3461r", .data = &mcp3564_chip_infos_tbl[mcp3461r] }, + { .compatible = "microchip,mcp3462r", .data = &mcp3564_chip_infos_tbl[mcp3462r] }, + { .compatible = "microchip,mcp3464r", .data = &mcp3564_chip_infos_tbl[mcp3464r] }, + { .compatible = "microchip,mcp3561r", .data = &mcp3564_chip_infos_tbl[mcp3561r] }, + { .compatible = "microchip,mcp3562r", .data = &mcp3564_chip_infos_tbl[mcp3562r] }, + { .compatible = "microchip,mcp3564r", .data = &mcp3564_chip_infos_tbl[mcp3564r] }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3564_dt_ids); + +static const struct spi_device_id mcp3564_id[] = { + { "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, + { "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, + { "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, + { "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, + { "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, + { "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, + { "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, + { "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, + { "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, + { "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, + { "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, + { "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp3564_id); + +static struct spi_driver mcp3564_driver = { + .driver = { + .name = "mcp3564", + .of_match_table = mcp3564_dt_ids, + }, + .probe = mcp3564_probe, + .id_table = mcp3564_id, +}; + +module_spi_driver(mcp3564_driver); + +MODULE_AUTHOR("Marius Cristea "); +MODULE_DESCRIPTION("Microchip MCP346x/MCP346xR and MCP356x/MCP346xR ADCs"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 17f961a6555ae38490cfcc39f12c0c014cbd2617 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 31 Aug 2023 20:25:02 +0200 Subject: iio: adc: ti-ads1015: Add edge trigger support The comparator generates an edge on the IRQ like and stays in the configured state until cleared. Support edge triggered IRQs as well as not all controllers do support level triggered IRQ. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20230831182502.154899-1-marex@denx.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 075c75a87544..addee45ed40a 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -1047,11 +1047,13 @@ static int ads1015_probe(struct i2c_client *client) 1 << ADS1015_CFG_COMP_LAT_SHIFT; switch (irq_trig) { + case IRQF_TRIGGER_FALLING: case IRQF_TRIGGER_LOW: cfg_comp |= ADS1015_CFG_COMP_POL_LOW << ADS1015_CFG_COMP_POL_SHIFT; break; case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: cfg_comp |= ADS1015_CFG_COMP_POL_HIGH << ADS1015_CFG_COMP_POL_SHIFT; break; -- cgit v1.2.3 From 5793ddcf1f8bf90bfe13b1757a1350d6482a4474 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 2 Sep 2023 17:26:33 +0100 Subject: iio: adc: ti-adc081c: Simplify probe() Simpilfy probe() by replacing of_device_get_match_data() and id lookup for retrieving match data by using i2c_get_match_data() by making similar OF/I2C/ACPI match tables. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230902162633.50546-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc081c.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 50c450e7a55f..6c2cb3dabbbf 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -154,7 +154,6 @@ static void adc081c_reg_disable(void *reg) static int adc081c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct iio_dev *iio; struct adc081c *adc; const struct adcxx1c_model *model; @@ -163,10 +162,7 @@ static int adc081c_probe(struct i2c_client *client) if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EOPNOTSUPP; - if (dev_fwnode(&client->dev)) - model = device_get_match_data(&client->dev); - else - model = &adcxx1c_models[id->driver_data]; + model = i2c_get_match_data(client); iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); if (!iio) @@ -207,9 +203,9 @@ static int adc081c_probe(struct i2c_client *client) } static const struct i2c_device_id adc081c_id[] = { - { "adc081c", ADC081C }, - { "adc101c", ADC101C }, - { "adc121c", ADC121C }, + { "adc081c", (kernel_ulong_t)&adcxx1c_models[ADC081C] }, + { "adc101c", (kernel_ulong_t)&adcxx1c_models[ADC101C] }, + { "adc121c", (kernel_ulong_t)&adcxx1c_models[ADC121C] }, { } }; MODULE_DEVICE_TABLE(i2c, adc081c_id); -- cgit v1.2.3 From f6b1737921dd38919a6d25826a953bd6d04c8c4b Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 2 Sep 2023 17:32:33 +0100 Subject: iio: adc: ti-ads1015: Simplify probe() Simpilfy probe() by replacing device_get_match_data() and id lookup for retrieving match data by using i2c_get_match_data(). Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230902163233.56449-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index addee45ed40a..6799ea49dbc7 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -976,16 +976,13 @@ static int ads1015_set_conv_mode(struct ads1015_data *data, int mode) static int ads1015_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); const struct ads1015_chip_data *chip; struct iio_dev *indio_dev; struct ads1015_data *data; int ret; int i; - chip = device_get_match_data(&client->dev); - if (!chip) - chip = (const struct ads1015_chip_data *)id->driver_data; + chip = i2c_get_match_data(client); if (!chip) return dev_err_probe(&client->dev, -EINVAL, "Unknown chip\n"); -- cgit v1.2.3 From 6c70012df1f39bb9bd015b6b6383f66cde95bc94 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 2 Sep 2023 18:05:29 +0100 Subject: iio: adc: ltc2497: Simplify probe() Simpilfy probe() by replacing device_get_match_data() and id lookup for retrieving match data by using i2c_get_match_data(). Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230902170529.62297-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2497.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index 5bdd40729611..6401a7727c31 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -95,7 +95,6 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, static int ltc2497_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); const struct ltc2497_chip_info *chip_info; struct iio_dev *indio_dev; struct ltc2497_driverdata *st; @@ -115,9 +114,7 @@ static int ltc2497_probe(struct i2c_client *client) st->client = client; st->common_ddata.result_and_measure = ltc2497_result_and_measure; - chip_info = device_get_match_data(dev); - if (!chip_info) - chip_info = (const struct ltc2497_chip_info *)id->driver_data; + chip_info = i2c_get_match_data(client); st->common_ddata.chip_info = chip_info; resolution = chip_info->resolution; -- cgit v1.2.3 From 7d0ba6dbf8356d20b69cfd042800d5d51ac00bd7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 3 Sep 2023 10:00:50 +0100 Subject: iio: accel: adxl345: Convert enum->pointer for data in match data table Convert enum->pointer for data in match data table, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Add struct adxl345_chip_info and replace enum->adxl345_chip_info in the match table and simplify adxl345_probe(). Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230903090051.39274-2-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345.h | 5 +++++ drivers/iio/accel/adxl345_core.c | 24 ++++++------------------ drivers/iio/accel/adxl345_i2c.c | 20 +++++++++++++++----- drivers/iio/accel/adxl345_spi.c | 20 +++++++++++++++----- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index d7e67cb08538..826e0575ae79 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -13,6 +13,11 @@ enum adxl345_device_type { ADXL375 = 2, }; +struct adxl345_chip_info { + const char *name; + unsigned int type; +}; + int adxl345_core_probe(struct device *dev, struct regmap *regmap); #endif /* _ADXL345_H_ */ diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 1919e0089c11..28f77b5de47d 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -61,9 +61,9 @@ static const int adxl345_uscale = 38300; static const int adxl375_uscale = 480000; struct adxl345_data { + const struct adxl345_chip_info *info; struct regmap *regmap; u8 data_range; - enum adxl345_device_type type; }; #define ADXL345_CHANNEL(index, axis) { \ @@ -110,7 +110,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; - switch (data->type) { + switch (data->info->type) { case ADXL345: *val2 = adxl345_uscale; break; @@ -222,25 +222,11 @@ static void adxl345_powerdown(void *regmap) int adxl345_core_probe(struct device *dev, struct regmap *regmap) { - enum adxl345_device_type type; struct adxl345_data *data; struct iio_dev *indio_dev; - const char *name; u32 regval; int ret; - type = (uintptr_t)device_get_match_data(dev); - switch (type) { - case ADXL345: - name = "adxl345"; - break; - case ADXL375: - name = "adxl375"; - break; - default: - return -EINVAL; - } - ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val); if (ret < 0) return dev_err_probe(dev, ret, "Error reading device ID\n"); @@ -255,16 +241,18 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap) data = iio_priv(indio_dev); data->regmap = regmap; - data->type = type; /* Enable full-resolution mode */ data->data_range = ADXL345_DATA_FORMAT_FULL_RES; + data->info = device_get_match_data(dev); + if (!data->info) + return -ENODEV; ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, data->data_range); if (ret < 0) return dev_err_probe(dev, ret, "Failed to set data range\n"); - indio_dev->name = name; + indio_dev->name = data->info->name; indio_dev->info = &adxl345_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl345_channels; diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index e47d12f19602..8cb6254297f7 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -30,22 +30,32 @@ static int adxl345_i2c_probe(struct i2c_client *client) return adxl345_core_probe(&client->dev, regmap); } +static const struct adxl345_chip_info adxl345_i2c_info = { + .name = "adxl345", + .type = ADXL345, +}; + +static const struct adxl345_chip_info adxl375_i2c_info = { + .name = "adxl375", + .type = ADXL375, +}; + static const struct i2c_device_id adxl345_i2c_id[] = { - { "adxl345", ADXL345 }, - { "adxl375", ADXL375 }, + { "adxl345", (kernel_ulong_t)&adxl345_i2c_info }, + { "adxl375", (kernel_ulong_t)&adxl375_i2c_info }, { } }; MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id); static const struct of_device_id adxl345_of_match[] = { - { .compatible = "adi,adxl345", .data = (const void *)ADXL345 }, - { .compatible = "adi,adxl375", .data = (const void *)ADXL375 }, + { .compatible = "adi,adxl345", .data = &adxl345_i2c_info }, + { .compatible = "adi,adxl375", .data = &adxl375_i2c_info }, { } }; MODULE_DEVICE_TABLE(of, adxl345_of_match); static const struct acpi_device_id adxl345_acpi_match[] = { - { "ADS0345", ADXL345 }, + { "ADS0345", (kernel_ulong_t)&adxl345_i2c_info }, { } }; MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match); diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index aaade5808657..ede9b9496158 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -36,22 +36,32 @@ static int adxl345_spi_probe(struct spi_device *spi) return adxl345_core_probe(&spi->dev, regmap); } +static const struct adxl345_chip_info adxl345_spi_info = { + .name = "adxl345", + .type = ADXL345, +}; + +static const struct adxl345_chip_info adxl375_spi_info = { + .name = "adxl375", + .type = ADXL375, +}; + static const struct spi_device_id adxl345_spi_id[] = { - { "adxl345", ADXL345 }, - { "adxl375", ADXL375 }, + { "adxl345", (kernel_ulong_t)&adxl345_spi_info }, + { "adxl375", (kernel_ulong_t)&adxl375_spi_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl345_spi_id); static const struct of_device_id adxl345_of_match[] = { - { .compatible = "adi,adxl345", .data = (const void *)ADXL345 }, - { .compatible = "adi,adxl375", .data = (const void *)ADXL375 }, + { .compatible = "adi,adxl345", .data = &adxl345_spi_info }, + { .compatible = "adi,adxl375", .data = &adxl375_spi_info }, { } }; MODULE_DEVICE_TABLE(of, adxl345_of_match); static const struct acpi_device_id adxl345_acpi_match[] = { - { "ADS0345", ADXL345 }, + { "ADS0345", (kernel_ulong_t)&adxl345_spi_info }, { } }; MODULE_DEVICE_TABLE(acpi, adxl345_acpi_match); -- cgit v1.2.3 From dcc3ac1381eff05e4ab546a42798dd6837b295a7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 3 Sep 2023 10:00:51 +0100 Subject: iio: accel: adxl345: Simplify adxl345_read_raw() Simplify adxl345_read_raw() by adding uscale variable to struct adxl345_chip_info. Also convert variables adxl3{4,7}5_uscale to macros and use it in OF/ACPI/ID match table. Drop enum adxl345_device_type as there is no user. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230903090051.39274-3-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345.h | 20 +++++++++++++++----- drivers/iio/accel/adxl345_core.c | 25 +------------------------ drivers/iio/accel/adxl345_i2c.c | 4 ++-- drivers/iio/accel/adxl345_spi.c | 4 ++-- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 826e0575ae79..284bd387ce69 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -8,14 +8,24 @@ #ifndef _ADXL345_H_ #define _ADXL345_H_ -enum adxl345_device_type { - ADXL345 = 1, - ADXL375 = 2, -}; +/* + * In full-resolution mode, scale factor is maintained at ~4 mg/LSB + * in all g ranges. + * + * At +/- 16g with 13-bit resolution, scale is computed as: + * (16 + 16) * 9.81 / (2^13 - 1) = 0.0383 + */ +#define ADXL345_USCALE 38300 + +/* + * The Datasheet lists a resolution of Resolution is ~49 mg per LSB. That's + * ~480mm/s**2 per LSB. + */ +#define ADXL375_USCALE 480000 struct adxl345_chip_info { const char *name; - unsigned int type; + int uscale; }; int adxl345_core_probe(struct device *dev, struct regmap *regmap); diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 28f77b5de47d..8bd30a23ed3b 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -45,21 +45,6 @@ #define ADXL345_DEVID 0xE5 -/* - * In full-resolution mode, scale factor is maintained at ~4 mg/LSB - * in all g ranges. - * - * At +/- 16g with 13-bit resolution, scale is computed as: - * (16 + 16) * 9.81 / (2^13 - 1) = 0.0383 - */ -static const int adxl345_uscale = 38300; - -/* - * The Datasheet lists a resolution of Resolution is ~49 mg per LSB. That's - * ~480mm/s**2 per LSB. - */ -static const int adxl375_uscale = 480000; - struct adxl345_data { const struct adxl345_chip_info *info; struct regmap *regmap; @@ -110,15 +95,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; - switch (data->info->type) { - case ADXL345: - *val2 = adxl345_uscale; - break; - case ADXL375: - *val2 = adxl375_uscale; - break; - } - + *val2 = data->info->uscale; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_CALIBBIAS: ret = regmap_read(data->regmap, diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index 8cb6254297f7..a3084b0a8f78 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -32,12 +32,12 @@ static int adxl345_i2c_probe(struct i2c_client *client) static const struct adxl345_chip_info adxl345_i2c_info = { .name = "adxl345", - .type = ADXL345, + .uscale = ADXL345_USCALE, }; static const struct adxl345_chip_info adxl375_i2c_info = { .name = "adxl375", - .type = ADXL375, + .uscale = ADXL375_USCALE, }; static const struct i2c_device_id adxl345_i2c_id[] = { diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index ede9b9496158..93ca349f1780 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -38,12 +38,12 @@ static int adxl345_spi_probe(struct spi_device *spi) static const struct adxl345_chip_info adxl345_spi_info = { .name = "adxl345", - .type = ADXL345, + .uscale = ADXL345_USCALE, }; static const struct adxl345_chip_info adxl375_spi_info = { .name = "adxl375", - .type = ADXL375, + .uscale = ADXL375_USCALE, }; static const struct spi_device_id adxl345_spi_id[] = { -- cgit v1.2.3 From 002d546fff4f83648903eada2b6226d314374fdb Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 3 Sep 2023 12:55:45 +0100 Subject: iio: dac: mcp4725: Replace variable 'id' from struct mcp4725_data Replace the variable 'id' from struct mcp4725_data with local variable chip_id in probe() as the id variable is not used elsewhere in the driver. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230903115548.59306-2-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4725.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index f4a3124d29f2..33a61f65bc25 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -32,7 +32,6 @@ struct mcp4725_data { struct i2c_client *client; - int id; unsigned ref_mode; bool vref_buffered; u16 dac_value; @@ -387,6 +386,7 @@ static int mcp4725_probe(struct i2c_client *client) struct mcp4725_data *data; struct iio_dev *indio_dev; struct mcp4725_platform_data *pdata, pdata_dt; + int chip_id; u8 inbuf[4]; u8 pd; u8 ref; @@ -399,9 +399,9 @@ static int mcp4725_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; if (dev_fwnode(&client->dev)) - data->id = (uintptr_t)device_get_match_data(&client->dev); + chip_id = (uintptr_t)device_get_match_data(&client->dev); else - data->id = id->driver_data; + chip_id = id->driver_data; pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -414,7 +414,7 @@ static int mcp4725_probe(struct i2c_client *client) pdata = &pdata_dt; } - if (data->id == MCP4725 && pdata->use_vref) { + if (chip_id == MCP4725 && pdata->use_vref) { dev_err(&client->dev, "external reference is unavailable on MCP4725"); return -EINVAL; @@ -455,12 +455,12 @@ static int mcp4725_probe(struct i2c_client *client) indio_dev->name = id->name; indio_dev->info = &mcp4725_info; - indio_dev->channels = &mcp472x_channel[id->driver_data]; + indio_dev->channels = &mcp472x_channel[chip_id]; indio_dev->num_channels = 1; indio_dev->modes = INDIO_DIRECT_MODE; /* read current DAC value and settings */ - err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4); + err = i2c_master_recv(client, inbuf, chip_id == MCP4725 ? 3 : 4); if (err < 0) { dev_err(&client->dev, "failed to read DAC value"); @@ -470,10 +470,10 @@ static int mcp4725_probe(struct i2c_client *client) data->powerdown = pd > 0; data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); - if (data->id == MCP4726) + if (chip_id == MCP4726) ref = (inbuf[3] >> 3) & 0x3; - if (data->id == MCP4726 && ref != data->ref_mode) { + if (chip_id == MCP4726 && ref != data->ref_mode) { dev_info(&client->dev, "voltage reference mode differs (conf: %u, eeprom: %u), setting %u", data->ref_mode, ref, data->ref_mode); -- cgit v1.2.3 From 18bdc686c1519e067dc80ec0015da52e2bd628b9 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 3 Sep 2023 12:55:46 +0100 Subject: iio: dac: mcp4725: Use i2c_get_match_data() Add struct mcp4725_chip_info with chan_spec and chip_id variable. After this simplify probe() by replacing device_get_match_data() and id lookup for retrieving match data by i2c_get_match_data() by converting enum->pointer for data in the match table. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230903115548.59306-3-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4725.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 33a61f65bc25..fe8981ec7923 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -30,6 +30,11 @@ #define MCP472X_REF_VREF_UNBUFFERED 0x02 #define MCP472X_REF_VREF_BUFFERED 0x03 +struct mcp4725_chip_info { + const struct iio_chan_spec *chan_spec; + unsigned int chip_id; +}; + struct mcp4725_data { struct i2c_client *client; unsigned ref_mode; @@ -383,10 +388,10 @@ static int mcp4725_probe_dt(struct device *dev, static int mcp4725_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct mcp4725_chip_info *info; struct mcp4725_data *data; struct iio_dev *indio_dev; struct mcp4725_platform_data *pdata, pdata_dt; - int chip_id; u8 inbuf[4]; u8 pd; u8 ref; @@ -398,10 +403,7 @@ static int mcp4725_probe(struct i2c_client *client) data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - if (dev_fwnode(&client->dev)) - chip_id = (uintptr_t)device_get_match_data(&client->dev); - else - chip_id = id->driver_data; + info = i2c_get_match_data(client); pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -414,7 +416,7 @@ static int mcp4725_probe(struct i2c_client *client) pdata = &pdata_dt; } - if (chip_id == MCP4725 && pdata->use_vref) { + if (info->chip_id == MCP4725 && pdata->use_vref) { dev_err(&client->dev, "external reference is unavailable on MCP4725"); return -EINVAL; @@ -455,12 +457,12 @@ static int mcp4725_probe(struct i2c_client *client) indio_dev->name = id->name; indio_dev->info = &mcp4725_info; - indio_dev->channels = &mcp472x_channel[chip_id]; + indio_dev->channels = info->chan_spec; indio_dev->num_channels = 1; indio_dev->modes = INDIO_DIRECT_MODE; /* read current DAC value and settings */ - err = i2c_master_recv(client, inbuf, chip_id == MCP4725 ? 3 : 4); + err = i2c_master_recv(client, inbuf, info->chip_id == MCP4725 ? 3 : 4); if (err < 0) { dev_err(&client->dev, "failed to read DAC value"); @@ -470,10 +472,10 @@ static int mcp4725_probe(struct i2c_client *client) data->powerdown = pd > 0; data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); - if (chip_id == MCP4726) + if (info->chip_id == MCP4726) ref = (inbuf[3] >> 3) & 0x3; - if (chip_id == MCP4726 && ref != data->ref_mode) { + if (info->chip_id == MCP4726 && ref != data->ref_mode) { dev_info(&client->dev, "voltage reference mode differs (conf: %u, eeprom: %u), setting %u", data->ref_mode, ref, data->ref_mode); @@ -510,9 +512,19 @@ static void mcp4725_remove(struct i2c_client *client) regulator_disable(data->vdd_reg); } +static const struct mcp4725_chip_info mcp4725 = { + .chan_spec = &mcp472x_channel[MCP4725], + .chip_id = MCP4725, +}; + +static const struct mcp4725_chip_info mcp4726 = { + .chan_spec = &mcp472x_channel[MCP4726], + .chip_id = MCP4726, +}; + static const struct i2c_device_id mcp4725_id[] = { - { "mcp4725", MCP4725 }, - { "mcp4726", MCP4726 }, + { "mcp4725", (kernel_ulong_t)&mcp4725 }, + { "mcp4726", (kernel_ulong_t)&mcp4726 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp4725_id); @@ -520,11 +532,11 @@ MODULE_DEVICE_TABLE(i2c, mcp4725_id); static const struct of_device_id mcp4725_of_match[] = { { .compatible = "microchip,mcp4725", - .data = (void *)MCP4725 + .data = &mcp4725 }, { .compatible = "microchip,mcp4726", - .data = (void *)MCP4726 + .data = &mcp4726 }, { } }; -- cgit v1.2.3 From 155da070b4388ed53f058259923f46740a51afde Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 3 Sep 2023 12:55:47 +0100 Subject: iio: dac: mcp4725: Add use_ext_ref_voltage to struct mcp4725_chip_info The MCP4725 has external voltage reference compared MCP4725 which has reference embeedded in eeprom. Add use_ext_ref_voltage variable to struct mcp4725_chip_info to handle this difference. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230903115548.59306-4-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4725.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index fe8981ec7923..9cbea6f223e4 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -33,6 +33,7 @@ struct mcp4725_chip_info { const struct iio_chan_spec *chan_spec; unsigned int chip_id; + bool use_ext_ref_voltage; }; struct mcp4725_data { @@ -416,7 +417,7 @@ static int mcp4725_probe(struct i2c_client *client) pdata = &pdata_dt; } - if (info->chip_id == MCP4725 && pdata->use_vref) { + if (info->use_ext_ref_voltage && pdata->use_vref) { dev_err(&client->dev, "external reference is unavailable on MCP4725"); return -EINVAL; @@ -472,10 +473,10 @@ static int mcp4725_probe(struct i2c_client *client) data->powerdown = pd > 0; data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); - if (info->chip_id == MCP4726) + if (!info->use_ext_ref_voltage) ref = (inbuf[3] >> 3) & 0x3; - if (info->chip_id == MCP4726 && ref != data->ref_mode) { + if (!info->use_ext_ref_voltage && ref != data->ref_mode) { dev_info(&client->dev, "voltage reference mode differs (conf: %u, eeprom: %u), setting %u", data->ref_mode, ref, data->ref_mode); @@ -515,6 +516,7 @@ static void mcp4725_remove(struct i2c_client *client) static const struct mcp4725_chip_info mcp4725 = { .chan_spec = &mcp472x_channel[MCP4725], .chip_id = MCP4725, + .use_ext_ref_voltage = true, }; static const struct mcp4725_chip_info mcp4726 = { -- cgit v1.2.3 From c377e2febd91e21782f9106e663937f0629f39f1 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 3 Sep 2023 12:55:48 +0100 Subject: iio: dac: mcp4725: Add dac_reg_offset to struct mcp4725_chip_info The MCP4725 has a register offset '3' for reading DAC value compared to '4' for MCP4726. Add dac_reg_offset variable to struct mcp4725_chip_info to handle this difference. Drop chip_id from struct mcp4725_chip_info as it is unused. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230903115548.59306-5-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4725.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 9cbea6f223e4..25bb1c0490af 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -32,7 +32,7 @@ struct mcp4725_chip_info { const struct iio_chan_spec *chan_spec; - unsigned int chip_id; + u8 dac_reg_offset; bool use_ext_ref_voltage; }; @@ -463,7 +463,7 @@ static int mcp4725_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; /* read current DAC value and settings */ - err = i2c_master_recv(client, inbuf, info->chip_id == MCP4725 ? 3 : 4); + err = i2c_master_recv(client, inbuf, info->dac_reg_offset); if (err < 0) { dev_err(&client->dev, "failed to read DAC value"); @@ -515,13 +515,13 @@ static void mcp4725_remove(struct i2c_client *client) static const struct mcp4725_chip_info mcp4725 = { .chan_spec = &mcp472x_channel[MCP4725], - .chip_id = MCP4725, + .dac_reg_offset = 3, .use_ext_ref_voltage = true, }; static const struct mcp4725_chip_info mcp4726 = { .chan_spec = &mcp472x_channel[MCP4726], - .chip_id = MCP4726, + .dac_reg_offset = 4, }; static const struct i2c_device_id mcp4725_id[] = { -- cgit v1.2.3 From 22da192f43f7d302d02644efa192ba5a05d935c9 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 3 Sep 2023 15:59:26 +0100 Subject: iio: pressure: ms5637: Use i2c_get_match_data() Simplify probe() by replacing id lookup for retrieving match data and device_get_match_data() by i2c_get_match_data(). Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230903145926.66855-1-biju.das.jz@bp.renesas.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5637.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index 9b3abffb724b..ac30d76285d1 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -144,7 +144,6 @@ static const struct iio_info ms5637_info = { static int ms5637_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); const struct ms_tp_data *data; struct ms_tp_dev *dev_data; struct iio_dev *indio_dev; @@ -159,10 +158,7 @@ static int ms5637_probe(struct i2c_client *client) return -EOPNOTSUPP; } - if (id) - data = (const struct ms_tp_data *)id->driver_data; - else - data = device_get_match_data(&client->dev); + data = i2c_get_match_data(client); if (!data) return -EINVAL; -- cgit v1.2.3 From e670bfe2736d3bd4436d160d37544e029f2aa813 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 11 Sep 2023 17:29:50 +0300 Subject: iio: addac: ad74413r: update channel function set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the datasheet switching to a new channel function implies multiple steps. All functions must be selected for a minimum of 130 μs before changing to another function. The DAC_CODEx registers are not reset by changing channel functions. Prior to changing channel functions, it is recommended to set the DAC code to 0x0000 via the DAC_CODEx registers. Set the channel function to high impedance via the CH_FUNC_SETUPx registers before transitioning to the new channel function. After the new channel function is configured, it is recommended to wait 150 μs before updating the DAC code. Even though the channel switch is done at only at probe, where a device reset is performed, that does not guarantee that the steps prior changing to new channel function should be neglected. Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20230911142950.216687-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 75216ece7ad6..7af3e4b8fe3b 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -441,12 +441,30 @@ static int ad74413r_set_channel_function(struct ad74413r_state *st, { int ret; + ret = regmap_update_bits(st->regmap, + AD74413R_REG_CH_FUNC_SETUP_X(channel), + AD74413R_CH_FUNC_SETUP_MASK, + CH_FUNC_HIGH_IMPEDANCE); + if (ret) + return ret; + + /* Set DAC code to 0 prior to changing channel function */ + ret = ad74413r_set_channel_dac_code(st, channel, 0); + if (ret) + return ret; + + /* Delay required before transition to new desired mode */ + usleep_range(130, 150); + ret = regmap_update_bits(st->regmap, AD74413R_REG_CH_FUNC_SETUP_X(channel), AD74413R_CH_FUNC_SETUP_MASK, func); if (ret) return ret; + /* Delay required before updating the new DAC code */ + usleep_range(150, 170); + if (func == CH_FUNC_CURRENT_INPUT_LOOP_POWER) ret = regmap_set_bits(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), -- cgit v1.2.3 From 9dc03309fe9ba9f9b26a37b2dd4fa2d5111a1ccf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 10 Sep 2023 07:09:03 -0700 Subject: iio: ad4310: Replace devm_clk_register() with devm_clk_hw_register() The devm_clk_register() is deprecated and devm_clk_hw_register() should be used as a replacement. Switching to the clk_hw interface also allows to use the built-in device managed version of registering the clock provider. The non-clk_hw interface does not have a device managed version. Signed-off-by: Lars-Peter Clausen Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230910140903.551081-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4130.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index 5a5dd5e87ffc..feb86fe6c422 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -1817,18 +1817,12 @@ static const struct clk_ops ad4130_int_clk_ops = { .unprepare = ad4130_int_clk_unprepare, }; -static void ad4130_clk_del_provider(void *of_node) -{ - of_clk_del_provider(of_node); -} - static int ad4130_setup_int_clk(struct ad4130_state *st) { struct device *dev = &st->spi->dev; struct device_node *of_node = dev_of_node(dev); struct clk_init_data init; const char *clk_name; - struct clk *clk; int ret; if (st->int_pin_sel == AD4130_INT_PIN_CLK || @@ -1845,15 +1839,12 @@ static int ad4130_setup_int_clk(struct ad4130_state *st) init.ops = &ad4130_int_clk_ops; st->int_clk_hw.init = &init; - clk = devm_clk_register(dev, &st->int_clk_hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - ret = of_clk_add_provider(of_node, of_clk_src_simple_get, clk); + ret = devm_clk_hw_register(dev, &st->int_clk_hw); if (ret) return ret; - return devm_add_action_or_reset(dev, ad4130_clk_del_provider, of_node); + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); } static int ad4130_setup(struct iio_dev *indio_dev) -- cgit v1.2.3 From 66fe531268f274310a052603fc298f6cc177489b Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Sat, 16 Sep 2023 07:20:31 -0400 Subject: iio: addac: stx104: Add 8254 Counter/Timer support The STX104 features an 8254 Counter/Timer chip providing three counter/timers which can be used for frequency measurement, frequency output, pulse width modulation, pulse width measurement, event count, etc. The STX104 provides a register bank selection to bank select between the 8254 Bank and the Indexed Register Array Bank; the Indexed Register Array is not utilized by this driver, so the 8254 Bank is selected unconditionally. Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20230916112031.3634-1-william.gray@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/addac/Kconfig | 1 + drivers/iio/addac/stx104.c | 61 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index 877f9124803c..b2623881f0ec 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -38,6 +38,7 @@ config STX104 select REGMAP_MMIO select GPIOLIB select GPIO_REGMAP + select I8254 help Say yes here to build support for the Apex Embedded Systems STX104 integrated analog PC/104 card. diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c index d1f7ce033b46..6946a65512ca 100644 --- a/drivers/iio/addac/stx104.c +++ b/drivers/iio/addac/stx104.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); #define STX104_ADC_STATUS (STX104_AIO_BASE + 0x8) #define STX104_ADC_CONTROL (STX104_AIO_BASE + 0x9) #define STX104_ADC_CONFIGURATION (STX104_AIO_BASE + 0x11) +#define STX104_I8254_BASE (STX104_AIO_BASE + 0x12) #define STX104_AIO_DATA_STRIDE 2 #define STX104_DAC_OFFSET(_channel) (STX104_DAC_BASE + STX104_AIO_DATA_STRIDE * (_channel)) @@ -77,6 +79,7 @@ MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); /* ADC Configuration */ #define STX104_GAIN GENMASK(1, 0) #define STX104_ADBU BIT(2) +#define STX104_RBK GENMASK(7, 4) #define STX104_BIPOLAR 0 #define STX104_GAIN_X1 0 #define STX104_GAIN_X2 1 @@ -168,6 +171,32 @@ static const struct regmap_config dio_regmap_config = { .io_port = true, }; +static const struct regmap_range pit_wr_ranges[] = { + regmap_reg_range(0x0, 0x3), +}; +static const struct regmap_range pit_rd_ranges[] = { + regmap_reg_range(0x0, 0x2), +}; +static const struct regmap_access_table pit_wr_table = { + .yes_ranges = pit_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(pit_wr_ranges), +}; +static const struct regmap_access_table pit_rd_table = { + .yes_ranges = pit_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(pit_rd_ranges), +}; + +static const struct regmap_config pit_regmap_config = { + .name = "i8254", + .reg_bits = 8, + .reg_stride = 1, + .reg_base = STX104_I8254_BASE, + .val_bits = 8, + .io_port = true, + .wr_table = &pit_wr_table, + .rd_table = &pit_rd_table, +}; + static int stx104_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { @@ -339,6 +368,21 @@ static const char *stx104_names[STX104_NGPIO] = { "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3" }; +static int bank_select_i8254(struct regmap *map) +{ + const u8 select_i8254[] = { 0x3, 0xB, 0xA }; + size_t i; + int err; + + for (i = 0; i < ARRAY_SIZE(select_i8254); i++) { + err = regmap_write_bits(map, STX104_ADC_CONFIGURATION, STX104_RBK, select_i8254[i]); + if (err) + return err; + } + + return 0; +} + static int stx104_init_hw(struct stx104_iio *const priv) { int err; @@ -361,7 +405,7 @@ static int stx104_init_hw(struct stx104_iio *const priv) if (err) return err; - return 0; + return bank_select_i8254(priv->aio_ctl_map); } static int stx104_probe(struct device *dev, unsigned int id) @@ -369,6 +413,7 @@ static int stx104_probe(struct device *dev, unsigned int id) struct iio_dev *indio_dev; struct stx104_iio *priv; struct gpio_regmap_config gpio_config; + struct i8254_regmap_config pit_config; void __iomem *stx104_base; struct regmap *aio_ctl_map; struct regmap *aio_data_map; @@ -406,6 +451,11 @@ static int stx104_probe(struct device *dev, unsigned int id) return dev_err_probe(dev, PTR_ERR(dio_map), "Unable to initialize dio register map\n"); + pit_config.map = devm_regmap_init_mmio(dev, stx104_base, &pit_regmap_config); + if (IS_ERR(pit_config.map)) + return dev_err_probe(dev, PTR_ERR(pit_config.map), + "Unable to initialize i8254 register map\n"); + priv = iio_priv(indio_dev); priv->aio_ctl_map = aio_ctl_map; priv->aio_data_map = aio_data_map; @@ -449,7 +499,13 @@ static int stx104_probe(struct device *dev, unsigned int id) .drvdata = dio_map, }; - return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); + err = PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); + if (err) + return err; + + pit_config.parent = dev; + + return devm_i8254_regmap_register(dev, &pit_config); } static struct isa_driver stx104_driver = { @@ -464,3 +520,4 @@ module_isa_driver(stx104_driver, num_stx104); MODULE_AUTHOR("William Breathitt Gray "); MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(I8254); -- cgit v1.2.3 From 377dddbe53b941fdc4babbf177c47659de68eeef Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sat, 16 Sep 2023 14:38:47 +0200 Subject: dt-bindings: iio: Add KX132-1211 accelerometer Extend the kionix,kx022a.yaml file to support the kx132-1211 device Acked-by: Krzysztof Kozlowski Acked-by: Matti Vaittinen Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/735004c624082aead6e08ae37ea4fc086b11cf86.1694867379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/kionix,kx022a.yaml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml index 986df1a6ff0a..034b69614416 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml @@ -4,19 +4,21 @@ $id: http://devicetree.org/schemas/iio/accel/kionix,kx022a.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ROHM/Kionix KX022A Accelerometer +title: ROHM/Kionix KX022A and KX132-1211 Accelerometers maintainers: - Matti Vaittinen description: | - KX022A is a 3-axis accelerometer supporting +/- 2G, 4G, 8G and 16G ranges, - output data-rates from 0.78Hz to 1600Hz and a hardware-fifo buffering. - KX022A can be accessed either via I2C or SPI. + KX022A and KX132-1211 are 3-axis accelerometers supporting +/- 2G, 4G, 8G and + 16G ranges, variable output data-rates and a hardware-fifo buffering. + KX022A and KX132-1211 can be accessed either via I2C or SPI. properties: compatible: - const: kionix,kx022a + enum: + - kionix,kx022a + - kionix,kx132-1211 reg: maxItems: 1 -- cgit v1.2.3 From cfdb1e2875321bdda0a8f0246bcf1d3cbfdd0b09 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sat, 16 Sep 2023 14:38:48 +0200 Subject: iio: accel: kionix-kx022a: Remove blank lines Remove blank lines pointed out by the checkpatch script Reviewed-by: Andy Shevchenko Reviewed-by: Matti Vaittinen Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/3489099f653491e97b13b8f19fe86635b03020c8.1694867379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kionix-kx022a.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index f164b09520c9..2f1e7da25460 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -341,7 +341,6 @@ static int kx022a_turn_on_off_unlocked(struct kx022a_data *data, bool on) dev_err(data->dev, "Turn %s fail %d\n", str_on_off(on), ret); return ret; - } static int kx022a_turn_off_lock(struct kx022a_data *data) @@ -1110,7 +1109,6 @@ int kx022a_probe_internal(struct device *dev) if (ret) return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); - ret = devm_iio_trigger_register(dev, indio_trig); if (ret) return dev_err_probe(data->dev, ret, -- cgit v1.2.3 From 8abacef3b7fb6dd7054c0b327390ae8e1087f9e5 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sat, 16 Sep 2023 14:38:49 +0200 Subject: iio: accel: kionix-kx022a: Warn on failed matches and assume compatibility Avoid error returns on a failure to match and instead just warn with assumption that we have a correct dt-binding telling us that some new device with a different ID is backwards compatible. Acked-by: Matti Vaittinen Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/b587cfec2f3350623277005f62121864bee857c7.1694867379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kionix-kx022a.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 2f1e7da25460..5a144f86c634 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -1025,10 +1025,8 @@ int kx022a_probe_internal(struct device *dev) if (ret) return dev_err_probe(dev, ret, "Failed to access sensor\n"); - if (chip_id != KX022A_ID) { - dev_err(dev, "unsupported device 0x%x\n", chip_id); - return -EINVAL; - } + if (chip_id != KX022A_ID) + dev_warn(dev, "unknown device 0x%x\n", chip_id); irq = fwnode_irq_get_byname(fwnode, "INT1"); if (irq > 0) { -- cgit v1.2.3 From 13d5398a8eebcb19e3424979658d97fbe1b21891 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sat, 16 Sep 2023 14:38:50 +0200 Subject: iio: accel: kionix-kx022a: Add an i2c_device_id table Add the missing i2c device id. Acked-by: Matti Vaittinen Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/61b43bbf35d602eac34b6d81b4d1b2d7ba39786f.1694867379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kionix-kx022a-i2c.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/accel/kionix-kx022a-i2c.c b/drivers/iio/accel/kionix-kx022a-i2c.c index b0ac78e85dad..c0d0b69680f9 100644 --- a/drivers/iio/accel/kionix-kx022a-i2c.c +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -30,6 +30,12 @@ static int kx022a_i2c_probe(struct i2c_client *i2c) return kx022a_probe_internal(dev); } +static const struct i2c_device_id kx022a_i2c_id[] = { + { .name = "kx022a" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); + static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", }, { } @@ -43,6 +49,7 @@ static struct i2c_driver kx022a_i2c_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = kx022a_i2c_probe, + .id_table = kx022a_i2c_id, }; module_i2c_driver(kx022a_i2c_driver); -- cgit v1.2.3 From e7123a4dfcd732c4d10620748588e3715f362802 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sat, 16 Sep 2023 14:38:51 +0200 Subject: iio: accel: kionix-kx022a: Refactor driver and add chip_info structure Add the chip_info structure to the driver's private data to hold all the device specific infos. Refactor the kx022a driver implementation to make it more generic and extensible. Acked-by: Matti Vaittinen Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/7a31d0cdefba15d7c791252ec8bc5db553b3996b.1694867379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kionix-kx022a-i2c.c | 13 ++-- drivers/iio/accel/kionix-kx022a-spi.c | 13 ++-- drivers/iio/accel/kionix-kx022a.c | 117 ++++++++++++++++++++++------------ drivers/iio/accel/kionix-kx022a.h | 52 ++++++++++++++- 4 files changed, 143 insertions(+), 52 deletions(-) diff --git a/drivers/iio/accel/kionix-kx022a-i2c.c b/drivers/iio/accel/kionix-kx022a-i2c.c index c0d0b69680f9..2fdc4c1ab93f 100644 --- a/drivers/iio/accel/kionix-kx022a-i2c.c +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -15,6 +15,7 @@ static int kx022a_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; + const struct kx022a_chip_info *chip_info; struct regmap *regmap; if (!i2c->irq) { @@ -22,22 +23,26 @@ static int kx022a_i2c_probe(struct i2c_client *i2c) return -EINVAL; } - regmap = devm_regmap_init_i2c(i2c, &kx022a_regmap); + chip_info = i2c_get_match_data(i2c); + if (!chip_info) + return -EINVAL; + + regmap = devm_regmap_init_i2c(i2c, chip_info->regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialize Regmap\n"); - return kx022a_probe_internal(dev); + return kx022a_probe_internal(dev, chip_info); } static const struct i2c_device_id kx022a_i2c_id[] = { - { .name = "kx022a" }, + { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); static const struct of_device_id kx022a_of_match[] = { - { .compatible = "kionix,kx022a", }, + { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a-spi.c b/drivers/iio/accel/kionix-kx022a-spi.c index f45a46899a5f..c21494ade157 100644 --- a/drivers/iio/accel/kionix-kx022a-spi.c +++ b/drivers/iio/accel/kionix-kx022a-spi.c @@ -15,6 +15,7 @@ static int kx022a_spi_probe(struct spi_device *spi) { struct device *dev = &spi->dev; + const struct kx022a_chip_info *chip_info; struct regmap *regmap; if (!spi->irq) { @@ -22,22 +23,26 @@ static int kx022a_spi_probe(struct spi_device *spi) return -EINVAL; } - regmap = devm_regmap_init_spi(spi, &kx022a_regmap); + chip_info = spi_get_device_match_data(spi); + if (!chip_info) + return -EINVAL; + + regmap = devm_regmap_init_spi(spi, chip_info->regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialize Regmap\n"); - return kx022a_probe_internal(dev); + return kx022a_probe_internal(dev, chip_info); } static const struct spi_device_id kx022a_id[] = { - { "kx022a" }, + { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, kx022a_id); static const struct of_device_id kx022a_of_match[] = { - { .compatible = "kionix,kx022a", }, + { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 5a144f86c634..448f1019e5bf 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -48,7 +48,7 @@ enum { KX022A_STATE_FIFO, }; -/* Regmap configs */ +/* kx022a Regmap configs */ static const struct regmap_range kx022a_volatile_ranges[] = { { .range_min = KX022A_REG_XHP_L, @@ -138,7 +138,7 @@ static const struct regmap_access_table kx022a_nir_regs = { .n_yes_ranges = ARRAY_SIZE(kx022a_noinc_read_ranges), }; -const struct regmap_config kx022a_regmap = { +static const struct regmap_config kx022a_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_table = &kx022a_volatile_regs, @@ -149,10 +149,10 @@ const struct regmap_config kx022a_regmap = { .max_register = KX022A_MAX_REGISTER, .cache_type = REGCACHE_RBTREE, }; -EXPORT_SYMBOL_NS_GPL(kx022a_regmap, IIO_KX022A); struct kx022a_data { struct regmap *regmap; + const struct kx022a_chip_info *chip_info; struct iio_trigger *trig; struct device *dev; struct iio_mount_matrix orientation; @@ -175,6 +175,8 @@ struct kx022a_data { struct mutex mutex; u8 watermark; + __le16 *fifo_buffer; + /* 3 x 16bit accel data + timestamp */ __le16 buffer[8] __aligned(IIO_DMA_MINALIGN); struct { @@ -208,7 +210,7 @@ static const struct iio_chan_spec_ext_info kx022a_ext_info[] = { { } }; -#define KX022A_ACCEL_CHAN(axis, index) \ +#define KX022A_ACCEL_CHAN(axis, reg, index) \ { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -220,7 +222,7 @@ static const struct iio_chan_spec_ext_info kx022a_ext_info[] = { BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .ext_info = kx022a_ext_info, \ - .address = KX022A_REG_##axis##OUT_L, \ + .address = reg, \ .scan_index = index, \ .scan_type = { \ .sign = 's', \ @@ -231,9 +233,9 @@ static const struct iio_chan_spec_ext_info kx022a_ext_info[] = { } static const struct iio_chan_spec kx022a_channels[] = { - KX022A_ACCEL_CHAN(X, 0), - KX022A_ACCEL_CHAN(Y, 1), - KX022A_ACCEL_CHAN(Z, 2), + KX022A_ACCEL_CHAN(X, KX022A_REG_XOUT_L, 0), + KX022A_ACCEL_CHAN(Y, KX022A_REG_YOUT_L, 1), + KX022A_ACCEL_CHAN(Z, KX022A_REG_ZOUT_L, 2), IIO_CHAN_SOFT_TIMESTAMP(3), }; @@ -332,10 +334,10 @@ static int kx022a_turn_on_off_unlocked(struct kx022a_data *data, bool on) int ret; if (on) - ret = regmap_set_bits(data->regmap, KX022A_REG_CNTL, + ret = regmap_set_bits(data->regmap, data->chip_info->cntl, KX022A_MASK_PC1); else - ret = regmap_clear_bits(data->regmap, KX022A_REG_CNTL, + ret = regmap_clear_bits(data->regmap, data->chip_info->cntl, KX022A_MASK_PC1); if (ret) dev_err(data->dev, "Turn %s fail %d\n", str_on_off(on), ret); @@ -402,7 +404,7 @@ static int kx022a_write_raw(struct iio_dev *idev, break; ret = regmap_update_bits(data->regmap, - KX022A_REG_ODCNTL, + data->chip_info->odcntl, KX022A_MASK_ODR, n); data->odr_ns = kx022a_odrs[n]; kx022a_turn_on_unlock(data); @@ -423,7 +425,7 @@ static int kx022a_write_raw(struct iio_dev *idev, if (ret) break; - ret = regmap_update_bits(data->regmap, KX022A_REG_CNTL, + ret = regmap_update_bits(data->regmap, data->chip_info->cntl, KX022A_MASK_GSEL, n << KX022A_GSEL_SHIFT); kx022a_turn_on_unlock(data); @@ -445,7 +447,7 @@ static int kx022a_fifo_set_wmi(struct kx022a_data *data) threshold = data->watermark; - return regmap_update_bits(data->regmap, KX022A_REG_BUF_CNTL1, + return regmap_update_bits(data->regmap, data->chip_info->buf_cntl1, KX022A_MASK_WM_TH, threshold); } @@ -488,7 +490,7 @@ static int kx022a_read_raw(struct iio_dev *idev, return ret; case IIO_CHAN_INFO_SAMP_FREQ: - ret = regmap_read(data->regmap, KX022A_REG_ODCNTL, ®val); + ret = regmap_read(data->regmap, data->chip_info->odcntl, ®val); if (ret) return ret; @@ -503,7 +505,7 @@ static int kx022a_read_raw(struct iio_dev *idev, return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: - ret = regmap_read(data->regmap, KX022A_REG_CNTL, ®val); + ret = regmap_read(data->regmap, data->chip_info->cntl, ®val); if (ret < 0) return ret; @@ -519,8 +521,7 @@ static int kx022a_set_watermark(struct iio_dev *idev, unsigned int val) { struct kx022a_data *data = iio_priv(idev); - if (val > KX022A_FIFO_LENGTH) - val = KX022A_FIFO_LENGTH; + val = min(data->chip_info->fifo_length, val); mutex_lock(&data->mutex); data->watermark = val; @@ -581,7 +582,7 @@ static int kx022a_drop_fifo_contents(struct kx022a_data *data) */ data->timestamp = 0; - return regmap_write(data->regmap, KX022A_REG_BUF_CLEAR, 0x0); + return regmap_write(data->regmap, data->chip_info->buf_clear, 0x0); } static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, @@ -589,7 +590,6 @@ static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, { struct kx022a_data *data = iio_priv(idev); struct device *dev = regmap_get_device(data->regmap); - __le16 buffer[KX022A_FIFO_LENGTH * 3]; uint64_t sample_period; int count, fifo_bytes; bool renable = false; @@ -668,13 +668,13 @@ static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, } fifo_bytes = count * KX022A_FIFO_SAMPLES_SIZE_BYTES; - ret = regmap_noinc_read(data->regmap, KX022A_REG_BUF_READ, - &buffer[0], fifo_bytes); + ret = regmap_noinc_read(data->regmap, data->chip_info->buf_read, + data->fifo_buffer, fifo_bytes); if (ret) goto renable_out; for (i = 0; i < count; i++) { - __le16 *sam = &buffer[i * 3]; + __le16 *sam = &data->fifo_buffer[i * 3]; __le16 *chs; int bit; @@ -721,10 +721,10 @@ static const struct iio_info kx022a_info = { static int kx022a_set_drdy_irq(struct kx022a_data *data, bool en) { if (en) - return regmap_set_bits(data->regmap, KX022A_REG_CNTL, + return regmap_set_bits(data->regmap, data->chip_info->cntl, KX022A_MASK_DRDY); - return regmap_clear_bits(data->regmap, KX022A_REG_CNTL, + return regmap_clear_bits(data->regmap, data->chip_info->cntl, KX022A_MASK_DRDY); } @@ -759,7 +759,7 @@ static int kx022a_fifo_disable(struct kx022a_data *data) if (ret) goto unlock_out; - ret = regmap_clear_bits(data->regmap, KX022A_REG_BUF_CNTL2, + ret = regmap_clear_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BUF_EN); if (ret) goto unlock_out; @@ -768,6 +768,8 @@ static int kx022a_fifo_disable(struct kx022a_data *data) kx022a_drop_fifo_contents(data); + kfree(data->fifo_buffer); + return kx022a_turn_on_unlock(data); unlock_out: @@ -790,6 +792,12 @@ static int kx022a_fifo_enable(struct kx022a_data *data) { int ret; + data->fifo_buffer = kmalloc_array(data->chip_info->fifo_length, + KX022A_FIFO_SAMPLES_SIZE_BYTES, + GFP_KERNEL); + if (!data->fifo_buffer) + return -ENOMEM; + ret = kx022a_turn_off_lock(data); if (ret) return ret; @@ -800,7 +808,7 @@ static int kx022a_fifo_enable(struct kx022a_data *data) goto unlock_out; /* Enable buffer */ - ret = regmap_set_bits(data->regmap, KX022A_REG_BUF_CNTL2, + ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BUF_EN); if (ret) goto unlock_out; @@ -846,7 +854,7 @@ static irqreturn_t kx022a_trigger_handler(int irq, void *p) struct kx022a_data *data = iio_priv(idev); int ret; - ret = regmap_bulk_read(data->regmap, KX022A_REG_XOUT_L, data->buffer, + ret = regmap_bulk_read(data->regmap, data->chip_info->xout_l, data->buffer, KX022A_FIFO_SAMPLES_SIZE_BYTES); if (ret < 0) goto err_read; @@ -894,7 +902,7 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) if (data->state & KX022A_STATE_FIFO) { int ok; - ok = __kx022a_fifo_flush(idev, KX022A_FIFO_LENGTH, true); + ok = __kx022a_fifo_flush(idev, data->chip_info->fifo_length, true); if (ok > 0) ret = IRQ_HANDLED; } @@ -947,7 +955,7 @@ static int kx022a_chip_init(struct kx022a_data *data) int ret, val; /* Reset the senor */ - ret = regmap_write(data->regmap, KX022A_REG_CNTL2, KX022A_MASK_SRST); + ret = regmap_write(data->regmap, data->chip_info->cntl2, KX022A_MASK_SRST); if (ret) return ret; @@ -957,7 +965,7 @@ static int kx022a_chip_init(struct kx022a_data *data) */ msleep(1); - ret = regmap_read_poll_timeout(data->regmap, KX022A_REG_CNTL2, val, + ret = regmap_read_poll_timeout(data->regmap, data->chip_info->cntl2, val, !(val & KX022A_MASK_SRST), KX022A_SOFT_RESET_WAIT_TIME_US, KX022A_SOFT_RESET_TOTAL_WAIT_TIME_US); @@ -967,14 +975,14 @@ static int kx022a_chip_init(struct kx022a_data *data) return ret; } - ret = regmap_reinit_cache(data->regmap, &kx022a_regmap); + ret = regmap_reinit_cache(data->regmap, data->chip_info->regmap_config); if (ret) { dev_err(data->dev, "Failed to reinit reg cache\n"); return ret; } /* set data res 16bit */ - ret = regmap_set_bits(data->regmap, KX022A_REG_BUF_CNTL2, + ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BRES16); if (ret) { dev_err(data->dev, "Failed to set data resolution\n"); @@ -984,7 +992,31 @@ static int kx022a_chip_init(struct kx022a_data *data) return kx022a_prepare_irq_pin(data); } -int kx022a_probe_internal(struct device *dev) +const struct kx022a_chip_info kx022a_chip_info = { + .name = "kx022-accel", + .regmap_config = &kx022a_regmap_config, + .channels = kx022a_channels, + .num_channels = ARRAY_SIZE(kx022a_channels), + .fifo_length = KX022A_FIFO_LENGTH, + .who = KX022A_REG_WHO, + .id = KX022A_ID, + .cntl = KX022A_REG_CNTL, + .cntl2 = KX022A_REG_CNTL2, + .odcntl = KX022A_REG_ODCNTL, + .buf_cntl1 = KX022A_REG_BUF_CNTL1, + .buf_cntl2 = KX022A_REG_BUF_CNTL2, + .buf_clear = KX022A_REG_BUF_CLEAR, + .buf_status1 = KX022A_REG_BUF_STATUS_1, + .buf_read = KX022A_REG_BUF_READ, + .inc1 = KX022A_REG_INC1, + .inc4 = KX022A_REG_INC4, + .inc5 = KX022A_REG_INC5, + .inc6 = KX022A_REG_INC6, + .xout_l = KX022A_REG_XOUT_L, +}; +EXPORT_SYMBOL_NS_GPL(kx022a_chip_info, IIO_KX022A); + +int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info) { static const char * const regulator_names[] = {"io-vdd", "vdd"}; struct iio_trigger *indio_trig; @@ -1011,6 +1043,7 @@ int kx022a_probe_internal(struct device *dev) return -ENOMEM; data = iio_priv(idev); + data->chip_info = chip_info; /* * VDD is the analog and digital domain voltage supply and @@ -1021,24 +1054,24 @@ int kx022a_probe_internal(struct device *dev) if (ret && ret != -ENODEV) return dev_err_probe(dev, ret, "failed to enable regulator\n"); - ret = regmap_read(regmap, KX022A_REG_WHO, &chip_id); + ret = regmap_read(regmap, chip_info->who, &chip_id); if (ret) return dev_err_probe(dev, ret, "Failed to access sensor\n"); - if (chip_id != KX022A_ID) + if (chip_id != chip_info->id) dev_warn(dev, "unknown device 0x%x\n", chip_id); irq = fwnode_irq_get_byname(fwnode, "INT1"); if (irq > 0) { - data->inc_reg = KX022A_REG_INC1; - data->ien_reg = KX022A_REG_INC4; + data->inc_reg = chip_info->inc1; + data->ien_reg = chip_info->inc4; } else { irq = fwnode_irq_get_byname(fwnode, "INT2"); if (irq < 0) return dev_err_probe(dev, irq, "No suitable IRQ\n"); - data->inc_reg = KX022A_REG_INC5; - data->ien_reg = KX022A_REG_INC6; + data->inc_reg = chip_info->inc5; + data->ien_reg = chip_info->inc6; } data->regmap = regmap; @@ -1047,9 +1080,9 @@ int kx022a_probe_internal(struct device *dev) data->odr_ns = KX022A_DEFAULT_PERIOD_NS; mutex_init(&data->mutex); - idev->channels = kx022a_channels; - idev->num_channels = ARRAY_SIZE(kx022a_channels); - idev->name = "kx022-accel"; + idev->channels = chip_info->channels; + idev->num_channels = chip_info->num_channels; + idev->name = chip_info->name; idev->info = &kx022a_info; idev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; idev->available_scan_masks = kx022a_scan_masks; diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h index 12424649d438..92f69c33a53b 100644 --- a/drivers/iio/accel/kionix-kx022a.h +++ b/drivers/iio/accel/kionix-kx022a.h @@ -76,7 +76,55 @@ struct device; -int kx022a_probe_internal(struct device *dev); -extern const struct regmap_config kx022a_regmap; +/** + * struct kx022a_chip_info - Kionix accelerometer chip specific information + * + * @name: name of the device + * @regmap_config: pointer to register map configuration + * @channels: pointer to iio_chan_spec array + * @num_channels: number of iio_chan_spec channels + * @fifo_length: number of 16-bit samples in a full buffer + * @who: WHO_AM_I register + * @id: WHO_AM_I register value + * @cntl: control register 1 + * @cntl2: control register 2 + * @odcntl: output data control register + * @buf_cntl1: buffer control register 1 + * @buf_cntl2: buffer control register 2 + * @buf_clear: buffer clear register + * @buf_status1: buffer status register 1 + * @buf_read: buffer read register + * @inc1: interrupt control register 1 + * @inc4: interrupt control register 4 + * @inc5: interrupt control register 5 + * @inc6: interrupt control register 6 + * @xout_l: x-axis output least significant byte + */ +struct kx022a_chip_info { + const char *name; + const struct regmap_config *regmap_config; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int fifo_length; + u8 who; + u8 id; + u8 cntl; + u8 cntl2; + u8 odcntl; + u8 buf_cntl1; + u8 buf_cntl2; + u8 buf_clear; + u8 buf_status1; + u8 buf_read; + u8 inc1; + u8 inc4; + u8 inc5; + u8 inc6; + u8 xout_l; +}; + +int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info); + +extern const struct kx022a_chip_info kx022a_chip_info; #endif -- cgit v1.2.3 From 774c3fabc9ff5fd10e779a4322ee4cbc36272c07 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sat, 16 Sep 2023 14:38:52 +0200 Subject: iio: accel: kionix-kx022a: Add a function to retrieve number of bytes in buffer Since Kionix accelerometers use various numbers of bits to report data, a device-specific function is required. Implement the function as a callback in the device-specific chip_info structure Reviewed-by: Matti Vaittinen Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/9c550fb28e34915d473e379f812c7753f7643bae.1694867379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kionix-kx022a.c | 28 ++++++++++++++++++---------- drivers/iio/accel/kionix-kx022a.h | 6 ++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 448f1019e5bf..cc7134b1183b 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -585,26 +585,33 @@ static int kx022a_drop_fifo_contents(struct kx022a_data *data) return regmap_write(data->regmap, data->chip_info->buf_clear, 0x0); } +static int kx022a_get_fifo_bytes_available(struct kx022a_data *data) +{ + int ret, fifo_bytes; + + ret = regmap_read(data->regmap, KX022A_REG_BUF_STATUS_1, &fifo_bytes); + if (ret) { + dev_err(data->dev, "Error reading buffer status\n"); + return ret; + } + + if (fifo_bytes == KX022A_FIFO_FULL_VALUE) + return KX022A_FIFO_MAX_BYTES; + + return fifo_bytes; +} + static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, bool irq) { struct kx022a_data *data = iio_priv(idev); - struct device *dev = regmap_get_device(data->regmap); uint64_t sample_period; int count, fifo_bytes; bool renable = false; int64_t tstamp; int ret, i; - ret = regmap_read(data->regmap, KX022A_REG_BUF_STATUS_1, &fifo_bytes); - if (ret) { - dev_err(dev, "Error reading buffer status\n"); - return ret; - } - - /* Let's not overflow if we for some reason get bogus value from i2c */ - if (fifo_bytes == KX022A_FIFO_FULL_VALUE) - fifo_bytes = KX022A_FIFO_MAX_BYTES; + fifo_bytes = data->chip_info->get_fifo_bytes_available(data); if (fifo_bytes % KX022A_FIFO_SAMPLES_SIZE_BYTES) dev_warn(data->dev, "Bad FIFO alignment. Data may be corrupt\n"); @@ -1013,6 +1020,7 @@ const struct kx022a_chip_info kx022a_chip_info = { .inc5 = KX022A_REG_INC5, .inc6 = KX022A_REG_INC6, .xout_l = KX022A_REG_XOUT_L, + .get_fifo_bytes_available = kx022a_get_fifo_bytes_available, }; EXPORT_SYMBOL_NS_GPL(kx022a_chip_info, IIO_KX022A); diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h index 92f69c33a53b..7ca48e6f2c49 100644 --- a/drivers/iio/accel/kionix-kx022a.h +++ b/drivers/iio/accel/kionix-kx022a.h @@ -76,6 +76,8 @@ struct device; +struct kx022a_data; + /** * struct kx022a_chip_info - Kionix accelerometer chip specific information * @@ -99,6 +101,9 @@ struct device; * @inc5: interrupt control register 5 * @inc6: interrupt control register 6 * @xout_l: x-axis output least significant byte + * @get_fifo_bytes_available: function pointer to get amount of acceleration + * data bytes currently stored in the sensor's FIFO + * buffer */ struct kx022a_chip_info { const char *name; @@ -121,6 +126,7 @@ struct kx022a_chip_info { u8 inc5; u8 inc6; u8 xout_l; + int (*get_fifo_bytes_available)(struct kx022a_data *); }; int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info); -- cgit v1.2.3 From e631f61f76329644dd4e88e83d6cca5c0b138949 Mon Sep 17 00:00:00 2001 From: Mehdi Djait Date: Sat, 16 Sep 2023 14:38:53 +0200 Subject: iio: accel: Add support for Kionix/ROHM KX132-1211 accelerometer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kionix KX132-1211 is a tri-axis 16-bit accelerometer that can support ranges from ±2G to ±16G, digital output through I²C/SPI. Add support for basic accelerometer features such as reading acceleration via IIO using raw reads, triggered buffer (data-ready), or the WMI IRQ. Datasheet: https://kionixfs.azureedge.net/en/document/KX132-1211-Technical-Reference-Manual-Rev-5.0.pdf Acked-by: Matti Vaittinen Reviewed-by: Andy Shevchenko Signed-off-by: Mehdi Djait Link: https://lore.kernel.org/r/389a7d6100ff2e71b1c5d60bebe662d09435996a.1694867379.git.mehdi.djait.k@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 10 ++- drivers/iio/accel/kionix-kx022a-i2c.c | 2 + drivers/iio/accel/kionix-kx022a-spi.c | 2 + drivers/iio/accel/kionix-kx022a.c | 164 ++++++++++++++++++++++++++++++++++ drivers/iio/accel/kionix-kx022a.h | 54 +++++++++++ 5 files changed, 228 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index b6b45d359f28..373257d64a7e 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -418,8 +418,9 @@ config IIO_KX022A_SPI select IIO_KX022A select REGMAP_SPI help - Enable support for the Kionix KX022A digital tri-axis - accelerometer connected to I2C interface. + Enable support for the Kionix digital tri-axis accelerometers + connected to SPI interface. Supported devices are: + KX022A, KX132-1211 config IIO_KX022A_I2C tristate "Kionix KX022A tri-axis digital accelerometer I2C interface" @@ -427,8 +428,9 @@ config IIO_KX022A_I2C select IIO_KX022A select REGMAP_I2C help - Enable support for the Kionix KX022A digital tri-axis - accelerometer connected to I2C interface. + Enable support for the Kionix digital tri-axis accelerometers + connected to I2C interface. Supported devices are: + KX022A, KX132-1211 config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" diff --git a/drivers/iio/accel/kionix-kx022a-i2c.c b/drivers/iio/accel/kionix-kx022a-i2c.c index 2fdc4c1ab93f..fc53e527cae0 100644 --- a/drivers/iio/accel/kionix-kx022a-i2c.c +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -37,12 +37,14 @@ static int kx022a_i2c_probe(struct i2c_client *i2c) static const struct i2c_device_id kx022a_i2c_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, + { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, + { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a-spi.c b/drivers/iio/accel/kionix-kx022a-spi.c index c21494ade157..e7878ba67827 100644 --- a/drivers/iio/accel/kionix-kx022a-spi.c +++ b/drivers/iio/accel/kionix-kx022a-spi.c @@ -37,12 +37,14 @@ static int kx022a_spi_probe(struct spi_device *spi) static const struct spi_device_id kx022a_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, + { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, kx022a_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, + { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index cc7134b1183b..c5b555094e60 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -150,6 +150,117 @@ static const struct regmap_config kx022a_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +/* Regmap configs kx132 */ +static const struct regmap_range kx132_volatile_ranges[] = { + { + .range_min = KX132_REG_XADP_L, + .range_max = KX132_REG_COTR, + }, { + .range_min = KX132_REG_TSCP, + .range_max = KX132_REG_INT_REL, + }, { + /* The reset bit will be cleared by sensor */ + .range_min = KX132_REG_CNTL2, + .range_max = KX132_REG_CNTL2, + }, { + .range_min = KX132_REG_CNTL5, + .range_max = KX132_REG_CNTL5, + }, { + .range_min = KX132_REG_BUF_STATUS_1, + .range_max = KX132_REG_BUF_READ, + }, +}; + +static const struct regmap_access_table kx132_volatile_regs = { + .yes_ranges = &kx132_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(kx132_volatile_ranges), +}; + +static const struct regmap_range kx132_precious_ranges[] = { + { + .range_min = KX132_REG_INT_REL, + .range_max = KX132_REG_INT_REL, + }, +}; + +static const struct regmap_access_table kx132_precious_regs = { + .yes_ranges = &kx132_precious_ranges[0], + .n_yes_ranges = ARRAY_SIZE(kx132_precious_ranges), +}; + +static const struct regmap_range kx132_read_only_ranges[] = { + { + .range_min = KX132_REG_XADP_L, + .range_max = KX132_REG_INT_REL, + }, { + .range_min = KX132_REG_BUF_STATUS_1, + .range_max = KX132_REG_BUF_STATUS_2, + }, { + .range_min = KX132_REG_BUF_READ, + .range_max = KX132_REG_BUF_READ, + }, { + /* Kionix reserved registers: should not be written */ + .range_min = 0x28, + .range_max = 0x28, + }, { + .range_min = 0x35, + .range_max = 0x36, + }, { + .range_min = 0x3c, + .range_max = 0x48, + }, { + .range_min = 0x4e, + .range_max = 0x5c, + }, { + .range_min = 0x77, + .range_max = 0x7f, + }, +}; + +static const struct regmap_access_table kx132_ro_regs = { + .no_ranges = &kx132_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(kx132_read_only_ranges), +}; + +static const struct regmap_range kx132_write_only_ranges[] = { + { + .range_min = KX132_REG_SELF_TEST, + .range_max = KX132_REG_SELF_TEST, + }, { + .range_min = KX132_REG_BUF_CLEAR, + .range_max = KX132_REG_BUF_CLEAR, + }, +}; + +static const struct regmap_access_table kx132_wo_regs = { + .no_ranges = &kx132_write_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(kx132_write_only_ranges), +}; + +static const struct regmap_range kx132_noinc_read_ranges[] = { + { + .range_min = KX132_REG_BUF_READ, + .range_max = KX132_REG_BUF_READ, + }, +}; + +static const struct regmap_access_table kx132_nir_regs = { + .yes_ranges = &kx132_noinc_read_ranges[0], + .n_yes_ranges = ARRAY_SIZE(kx132_noinc_read_ranges), +}; + +static const struct regmap_config kx132_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &kx132_volatile_regs, + .rd_table = &kx132_wo_regs, + .wr_table = &kx132_ro_regs, + .rd_noinc_table = &kx132_nir_regs, + .precious_table = &kx132_precious_regs, + .max_register = KX132_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, +}; + struct kx022a_data { struct regmap *regmap; const struct kx022a_chip_info *chip_info; @@ -239,6 +350,13 @@ static const struct iio_chan_spec kx022a_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(3), }; +static const struct iio_chan_spec kx132_channels[] = { + KX022A_ACCEL_CHAN(X, KX132_REG_XOUT_L, 0), + KX022A_ACCEL_CHAN(Y, KX132_REG_YOUT_L, 1), + KX022A_ACCEL_CHAN(Z, KX132_REG_ZOUT_L, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + /* * The sensor HW can support ODR up to 1600 Hz, which is beyond what most of the * Linux CPUs can handle without dropping samples. Also, the low power mode is @@ -601,6 +719,26 @@ static int kx022a_get_fifo_bytes_available(struct kx022a_data *data) return fifo_bytes; } +static int kx132_get_fifo_bytes_available(struct kx022a_data *data) +{ + __le16 buf_status; + int ret, fifo_bytes; + + ret = regmap_bulk_read(data->regmap, data->chip_info->buf_status1, + &buf_status, sizeof(buf_status)); + if (ret) { + dev_err(data->dev, "Error reading buffer status\n"); + return ret; + } + + fifo_bytes = le16_to_cpu(buf_status); + fifo_bytes &= data->chip_info->buf_smp_lvl_mask; + fifo_bytes = min((unsigned int)fifo_bytes, data->chip_info->fifo_length * + KX022A_FIFO_SAMPLES_SIZE_BYTES); + + return fifo_bytes; +} + static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, bool irq) { @@ -1024,6 +1162,32 @@ const struct kx022a_chip_info kx022a_chip_info = { }; EXPORT_SYMBOL_NS_GPL(kx022a_chip_info, IIO_KX022A); +const struct kx022a_chip_info kx132_chip_info = { + .name = "kx132-1211", + .regmap_config = &kx132_regmap_config, + .channels = kx132_channels, + .num_channels = ARRAY_SIZE(kx132_channels), + .fifo_length = KX132_FIFO_LENGTH, + .who = KX132_REG_WHO, + .id = KX132_ID, + .cntl = KX132_REG_CNTL, + .cntl2 = KX132_REG_CNTL2, + .odcntl = KX132_REG_ODCNTL, + .buf_cntl1 = KX132_REG_BUF_CNTL1, + .buf_cntl2 = KX132_REG_BUF_CNTL2, + .buf_clear = KX132_REG_BUF_CLEAR, + .buf_status1 = KX132_REG_BUF_STATUS_1, + .buf_smp_lvl_mask = KX132_MASK_BUF_SMP_LVL, + .buf_read = KX132_REG_BUF_READ, + .inc1 = KX132_REG_INC1, + .inc4 = KX132_REG_INC4, + .inc5 = KX132_REG_INC5, + .inc6 = KX132_REG_INC6, + .xout_l = KX132_REG_XOUT_L, + .get_fifo_bytes_available = kx132_get_fifo_bytes_available, +}; +EXPORT_SYMBOL_NS_GPL(kx132_chip_info, IIO_KX022A); + int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info) { static const char * const regulator_names[] = {"io-vdd", "vdd"}; diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h index 7ca48e6f2c49..35c548ae7eff 100644 --- a/drivers/iio/accel/kionix-kx022a.h +++ b/drivers/iio/accel/kionix-kx022a.h @@ -74,6 +74,57 @@ #define KX022A_REG_SELF_TEST 0x60 #define KX022A_MAX_REGISTER 0x60 +#define KX132_REG_WHO 0x13 +#define KX132_ID 0x3d + +#define KX132_FIFO_LENGTH 86 + +#define KX132_REG_CNTL 0x1b +#define KX132_REG_CNTL2 0x1c +#define KX132_REG_CNTL5 0x1f +#define KX132_MASK_RES BIT(6) +#define KX132_GSEL_2 0x0 +#define KX132_GSEL_4 BIT(3) +#define KX132_GSEL_8 BIT(4) +#define KX132_GSEL_16 GENMASK(4, 3) + +#define KX132_REG_INS2 0x17 +#define KX132_MASK_INS2_WMI BIT(5) + +#define KX132_REG_XADP_L 0x02 +#define KX132_REG_XOUT_L 0x08 +#define KX132_REG_YOUT_L 0x0a +#define KX132_REG_ZOUT_L 0x0c +#define KX132_REG_COTR 0x12 +#define KX132_REG_TSCP 0x14 +#define KX132_REG_INT_REL 0x1a + +#define KX132_REG_ODCNTL 0x21 + +#define KX132_REG_BTS_WUF_TH 0x4a + +#define KX132_REG_BUF_CNTL1 0x5e +#define KX132_REG_BUF_CNTL2 0x5f +#define KX132_REG_BUF_STATUS_1 0x60 +#define KX132_REG_BUF_STATUS_2 0x61 +#define KX132_MASK_BUF_SMP_LVL GENMASK(9, 0) +#define KX132_REG_BUF_CLEAR 0x62 +#define KX132_REG_BUF_READ 0x63 +#define KX132_ODR_SHIFT 3 +#define KX132_FIFO_MAX_WMI_TH 86 + +#define KX132_REG_INC1 0x22 +#define KX132_REG_INC5 0x26 +#define KX132_REG_INC6 0x27 +#define KX132_IPOL_LOW 0 +#define KX132_IPOL_HIGH KX022A_MASK_IPOL +#define KX132_ITYP_PULSE KX022A_MASK_ITYP + +#define KX132_REG_INC4 0x25 + +#define KX132_REG_SELF_TEST 0x5d +#define KX132_MAX_REGISTER 0x76 + struct device; struct kx022a_data; @@ -86,6 +137,7 @@ struct kx022a_data; * @channels: pointer to iio_chan_spec array * @num_channels: number of iio_chan_spec channels * @fifo_length: number of 16-bit samples in a full buffer + * @buf_smp_lvl_mask: buffer sample level mask * @who: WHO_AM_I register * @id: WHO_AM_I register value * @cntl: control register 1 @@ -111,6 +163,7 @@ struct kx022a_chip_info { const struct iio_chan_spec *channels; unsigned int num_channels; unsigned int fifo_length; + u16 buf_smp_lvl_mask; u8 who; u8 id; u8 cntl; @@ -132,5 +185,6 @@ struct kx022a_chip_info { int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info); extern const struct kx022a_chip_info kx022a_chip_info; +extern const struct kx022a_chip_info kx132_chip_info; #endif -- cgit v1.2.3 From 53cb25c97be2fb859166f14c862f4d09e3c1e0c9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:43 +0200 Subject: iio: accel: hid-sensor-accel-3d: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-2-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 5eac7ea19993..9b7a73a4c48a 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -422,7 +422,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_accel_3d_remove(struct platform_device *pdev) +static void hid_accel_3d_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -431,8 +431,6 @@ static int hid_accel_3d_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes); - - return 0; } static const struct platform_device_id hid_accel_3d_ids[] = { @@ -454,7 +452,7 @@ static struct platform_driver hid_accel_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_accel_3d_probe, - .remove = hid_accel_3d_remove, + .remove_new = hid_accel_3d_remove, }; module_platform_driver(hid_accel_3d_platform_driver); -- cgit v1.2.3 From 9cb1bcd4ea25b16035cb02b00b6c79d23a3a90f4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:44 +0200 Subject: iio: adc: ab8500-gpadc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-3-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ab8500-gpadc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 3b1bdd0b531d..80645fee79a4 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -1179,7 +1179,7 @@ out_dis_pm: return ret; } -static int ab8500_gpadc_remove(struct platform_device *pdev) +static void ab8500_gpadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct ab8500_gpadc *gpadc = iio_priv(indio_dev); @@ -1188,8 +1188,6 @@ static int ab8500_gpadc_remove(struct platform_device *pdev) pm_runtime_put_noidle(gpadc->dev); pm_runtime_disable(gpadc->dev); regulator_disable(gpadc->vddadc); - - return 0; } static DEFINE_RUNTIME_DEV_PM_OPS(ab8500_gpadc_pm_ops, @@ -1198,7 +1196,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ab8500_gpadc_pm_ops, static struct platform_driver ab8500_gpadc_driver = { .probe = ab8500_gpadc_probe, - .remove = ab8500_gpadc_remove, + .remove_new = ab8500_gpadc_remove, .driver = { .name = "ab8500-gpadc", .pm = pm_ptr(&ab8500_gpadc_pm_ops), -- cgit v1.2.3 From e5e92308c4691e39f57f3352ffd094ead4f4b569 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:45 +0200 Subject: iio: adc: at91-sama5d2: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-4-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index df67b63ccf69..d7fd21e7c6e2 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2486,7 +2486,7 @@ reg_disable: return ret; } -static int at91_adc_remove(struct platform_device *pdev) +static void at91_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct at91_adc_state *st = iio_priv(indio_dev); @@ -2501,8 +2501,6 @@ static int at91_adc_remove(struct platform_device *pdev) regulator_disable(st->vref); regulator_disable(st->reg); - - return 0; } static int at91_adc_suspend(struct device *dev) @@ -2627,7 +2625,7 @@ MODULE_DEVICE_TABLE(of, at91_adc_dt_match); static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, - .remove = at91_adc_remove, + .remove_new = at91_adc_remove, .driver = { .name = "at91-sama5d2_adc", .of_match_table = at91_adc_dt_match, -- cgit v1.2.3 From 3624d5fd3be2e3320b96086dc2c5e6c57255b333 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:46 +0200 Subject: iio: adc: at91: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-5-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 200c4599530b..eb501e3c86a5 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1185,7 +1185,7 @@ error_iio_device_register: return ret; } -static int at91_adc_remove(struct platform_device *pdev) +static void at91_adc_remove(struct platform_device *pdev) { struct iio_dev *idev = platform_get_drvdata(pdev); struct at91_adc_state *st = iio_priv(idev); @@ -1197,8 +1197,6 @@ static int at91_adc_remove(struct platform_device *pdev) } else { at91_ts_unregister(st); } - - return 0; } static int at91_adc_suspend(struct device *dev) @@ -1348,7 +1346,7 @@ MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, - .remove = at91_adc_remove, + .remove_new = at91_adc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = at91_adc_dt_ids, -- cgit v1.2.3 From 5ff1f754009b00a2352e17c22f224e9d77dc6fa9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:47 +0200 Subject: iio: adc: axp20x: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-6-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 75bda94dbce1..d6c51b0f48e3 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -745,7 +745,7 @@ fail_map: return ret; } -static int axp20x_remove(struct platform_device *pdev) +static void axp20x_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct axp20x_adc_iio *info = iio_priv(indio_dev); @@ -757,8 +757,6 @@ static int axp20x_remove(struct platform_device *pdev) if (info->data->adc_en2_mask) regmap_write(info->regmap, AXP20X_ADC_EN2, 0); - - return 0; } static struct platform_driver axp20x_adc_driver = { @@ -768,7 +766,7 @@ static struct platform_driver axp20x_adc_driver = { }, .id_table = axp20x_adc_id_match, .probe = axp20x_probe, - .remove = axp20x_remove, + .remove_new = axp20x_remove, }; module_platform_driver(axp20x_adc_driver); -- cgit v1.2.3 From bf7c022b58e3e62087d563b8b69ba8354bde1fe0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:48 +0200 Subject: iio: adc: bcm_iproc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20230919174931.1417681-7-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/bcm_iproc_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c index 0d6885413a7e..5bc514bd5ebc 100644 --- a/drivers/iio/adc/bcm_iproc_adc.c +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -594,7 +594,7 @@ err_adc_enable: return ret; } -static int iproc_adc_remove(struct platform_device *pdev) +static void iproc_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); @@ -602,8 +602,6 @@ static int iproc_adc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); iproc_adc_disable(indio_dev); clk_disable_unprepare(adc_priv->adc_clk); - - return 0; } static const struct of_device_id iproc_adc_of_match[] = { @@ -614,7 +612,7 @@ MODULE_DEVICE_TABLE(of, iproc_adc_of_match); static struct platform_driver iproc_adc_driver = { .probe = iproc_adc_probe, - .remove = iproc_adc_remove, + .remove_new = iproc_adc_remove, .driver = { .name = "iproc-static-adc", .of_match_table = iproc_adc_of_match, -- cgit v1.2.3 From cb299d2a561dfd6816d120deac096562d5f111b5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:49 +0200 Subject: iio: adc: dln2: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-8-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/dln2-adc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 97d162a3cba4..06cfbbabaf8d 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -691,19 +691,18 @@ unregister_event: return ret; } -static int dln2_adc_remove(struct platform_device *pdev) +static void dln2_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); iio_device_unregister(indio_dev); dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); - return 0; } static struct platform_driver dln2_adc_driver = { .driver.name = DLN2_ADC_MOD_NAME, .probe = dln2_adc_probe, - .remove = dln2_adc_remove, + .remove_new = dln2_adc_remove, }; module_platform_driver(dln2_adc_driver); -- cgit v1.2.3 From b8943902874c363ea48c101838baf4933c44836b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:50 +0200 Subject: iio: adc: ep93xx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20230919174931.1417681-9-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ep93xx_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index a35e6cead67d..971942ce4c66 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -217,15 +217,13 @@ static int ep93xx_adc_probe(struct platform_device *pdev) return ret; } -static int ep93xx_adc_remove(struct platform_device *pdev) +static void ep93xx_adc_remove(struct platform_device *pdev) { struct iio_dev *iiodev = platform_get_drvdata(pdev); struct ep93xx_adc_priv *priv = iio_priv(iiodev); iio_device_unregister(iiodev); clk_disable_unprepare(priv->clk); - - return 0; } static const struct of_device_id ep93xx_adc_of_ids[] = { @@ -240,7 +238,7 @@ static struct platform_driver ep93xx_adc_driver = { .of_match_table = ep93xx_adc_of_ids, }, .probe = ep93xx_adc_probe, - .remove = ep93xx_adc_remove, + .remove_new = ep93xx_adc_remove, }; module_platform_driver(ep93xx_adc_driver); -- cgit v1.2.3 From 5c5b7b3f9e89a522ef026fc38c53e1827a731959 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:51 +0200 Subject: iio: adc: exynos: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-10-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/exynos_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index cff1ba57fb16..eb7a2dd59517 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -946,7 +946,7 @@ err_disable_reg: return ret; } -static int exynos_adc_remove(struct platform_device *pdev) +static void exynos_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct exynos_adc *info = iio_priv(indio_dev); @@ -964,8 +964,6 @@ static int exynos_adc_remove(struct platform_device *pdev) exynos_adc_disable_clk(info); exynos_adc_unprepare_clk(info); regulator_disable(info->vdd); - - return 0; } static int exynos_adc_suspend(struct device *dev) @@ -1006,7 +1004,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, exynos_adc_suspend, static struct platform_driver exynos_adc_driver = { .probe = exynos_adc_probe, - .remove = exynos_adc_remove, + .remove_new = exynos_adc_remove, .driver = { .name = "exynos-adc", .of_match_table = exynos_adc_match, -- cgit v1.2.3 From cfac92804fd394a1b9d962825b12c989c56e4149 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:52 +0200 Subject: iio: adc: fsl-imx25-gcq: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-11-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/fsl-imx25-gcq.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index 551e83ae573c..68c813de0605 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -384,7 +384,7 @@ err_regulator_disable: return ret; } -static int mx25_gcq_remove(struct platform_device *pdev) +static void mx25_gcq_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct mx25_gcq_priv *priv = iio_priv(indio_dev); @@ -397,8 +397,6 @@ static int mx25_gcq_remove(struct platform_device *pdev) if (priv->vref[i]) regulator_disable(priv->vref[i]); } - - return 0; } static const struct of_device_id mx25_gcq_ids[] = { @@ -413,7 +411,7 @@ static struct platform_driver mx25_gcq_driver = { .of_match_table = mx25_gcq_ids, }, .probe = mx25_gcq_probe, - .remove = mx25_gcq_remove, + .remove_new = mx25_gcq_remove, }; module_platform_driver(mx25_gcq_driver); -- cgit v1.2.3 From b7962fc9a5130842786b141a8d9bbaae347d94af Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:53 +0200 Subject: iio: adc: hx711: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-12-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index f7ee856a6b8b..c80c55fb8c6c 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -580,7 +580,7 @@ error_regulator: return ret; } -static int hx711_remove(struct platform_device *pdev) +static void hx711_remove(struct platform_device *pdev) { struct hx711_data *hx711_data; struct iio_dev *indio_dev; @@ -593,8 +593,6 @@ static int hx711_remove(struct platform_device *pdev) iio_triggered_buffer_cleanup(indio_dev); regulator_disable(hx711_data->reg_avdd); - - return 0; } static const struct of_device_id of_hx711_match[] = { @@ -606,7 +604,7 @@ MODULE_DEVICE_TABLE(of, of_hx711_match); static struct platform_driver hx711_driver = { .probe = hx711_probe, - .remove = hx711_remove, + .remove_new = hx711_remove, .driver = { .name = "hx711-gpio", .of_match_table = of_hx711_match, -- cgit v1.2.3 From 7ed89f54f62f8318a65049954a8929ea98c2fe19 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:54 +0200 Subject: iio: adc: imx8qxp: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-13-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx8qxp-adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c index f5a0fc9e64c5..a180c6974d5b 100644 --- a/drivers/iio/adc/imx8qxp-adc.c +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -404,7 +404,7 @@ error_regulator_disable: return ret; } -static int imx8qxp_adc_remove(struct platform_device *pdev) +static void imx8qxp_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct imx8qxp_adc *adc = iio_priv(indio_dev); @@ -422,8 +422,6 @@ static int imx8qxp_adc_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - - return 0; } static int imx8qxp_adc_runtime_suspend(struct device *dev) @@ -489,7 +487,7 @@ MODULE_DEVICE_TABLE(of, imx8qxp_adc_match); static struct platform_driver imx8qxp_adc_driver = { .probe = imx8qxp_adc_probe, - .remove = imx8qxp_adc_remove, + .remove_new = imx8qxp_adc_remove, .driver = { .name = ADC_DRIVER_NAME, .of_match_table = imx8qxp_adc_match, -- cgit v1.2.3 From 71e79bb48f2dd8147e13d574894c6b9641d843b4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:55 +0200 Subject: iio: adc: imx93: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-14-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx93_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c index dce9ec91e4a7..9bb1e4ba1aee 100644 --- a/drivers/iio/adc/imx93_adc.c +++ b/drivers/iio/adc/imx93_adc.c @@ -392,7 +392,7 @@ error_regulator_disable: return ret; } -static int imx93_adc_remove(struct platform_device *pdev) +static void imx93_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct imx93_adc *adc = iio_priv(indio_dev); @@ -410,8 +410,6 @@ static int imx93_adc_remove(struct platform_device *pdev) free_irq(adc->irq, adc); clk_disable_unprepare(adc->ipg_clk); regulator_disable(adc->vref); - - return 0; } static int imx93_adc_runtime_suspend(struct device *dev) @@ -468,7 +466,7 @@ MODULE_DEVICE_TABLE(of, imx93_adc_match); static struct platform_driver imx93_adc_driver = { .probe = imx93_adc_probe, - .remove = imx93_adc_remove, + .remove_new = imx93_adc_remove, .driver = { .name = IMX93_ADC_DRIVER_NAME, .of_match_table = imx93_adc_match, -- cgit v1.2.3 From c0fe02aa5e69f3c09f72b943b8d0ad24c00d9a3f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:56 +0200 Subject: iio: adc: meson_saradc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-15-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 320e3e7e3d4d..a40986fb285c 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1437,15 +1437,13 @@ err: return ret; } -static int meson_sar_adc_remove(struct platform_device *pdev) +static void meson_sar_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); iio_device_unregister(indio_dev); meson_sar_adc_hw_disable(indio_dev); - - return 0; } static int meson_sar_adc_suspend(struct device *dev) @@ -1480,7 +1478,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops, static struct platform_driver meson_sar_adc_driver = { .probe = meson_sar_adc_probe, - .remove = meson_sar_adc_remove, + .remove_new = meson_sar_adc_remove, .driver = { .name = "meson-saradc", .of_match_table = meson_sar_adc_of_match, -- cgit v1.2.3 From 9e7e402055bd9d611f1779df1dd2758668cefb3a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:57 +0200 Subject: iio: adc: mp2629: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-16-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mp2629_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c index 88e947f300cf..7c66c2cd5be2 100644 --- a/drivers/iio/adc/mp2629_adc.c +++ b/drivers/iio/adc/mp2629_adc.c @@ -171,7 +171,7 @@ fail_disable: return ret; } -static int mp2629_adc_remove(struct platform_device *pdev) +static void mp2629_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct mp2629_adc *info = iio_priv(indio_dev); @@ -184,8 +184,6 @@ static int mp2629_adc_remove(struct platform_device *pdev) MP2629_ADC_CONTINUOUS, 0); regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, MP2629_ADC_START, 0); - - return 0; } static const struct of_device_id mp2629_adc_of_match[] = { @@ -200,7 +198,7 @@ static struct platform_driver mp2629_adc_driver = { .of_match_table = mp2629_adc_of_match, }, .probe = mp2629_adc_probe, - .remove = mp2629_adc_remove, + .remove_new = mp2629_adc_remove, }; module_platform_driver(mp2629_adc_driver); -- cgit v1.2.3 From a72e156f53095b1893a9a133744e454879992f76 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:58 +0200 Subject: iio: adc: mxs-lradc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-17-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mxs-lradc-adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c index a50f39143d3e..2e60c10ee4ff 100644 --- a/drivers/iio/adc/mxs-lradc-adc.c +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -807,7 +807,7 @@ err_trig: return ret; } -static int mxs_lradc_adc_remove(struct platform_device *pdev) +static void mxs_lradc_adc_remove(struct platform_device *pdev) { struct iio_dev *iio = platform_get_drvdata(pdev); struct mxs_lradc_adc *adc = iio_priv(iio); @@ -816,8 +816,6 @@ static int mxs_lradc_adc_remove(struct platform_device *pdev) mxs_lradc_adc_hw_stop(adc); iio_triggered_buffer_cleanup(iio); mxs_lradc_adc_trigger_remove(iio); - - return 0; } static struct platform_driver mxs_lradc_adc_driver = { @@ -825,7 +823,7 @@ static struct platform_driver mxs_lradc_adc_driver = { .name = "mxs-lradc-adc", }, .probe = mxs_lradc_adc_probe, - .remove = mxs_lradc_adc_remove, + .remove_new = mxs_lradc_adc_remove, }; module_platform_driver(mxs_lradc_adc_driver); -- cgit v1.2.3 From 5253a5cc7709688b9a000f7928bfaa3366d0af98 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:48:59 +0200 Subject: iio: adc: npcm: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-18-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/npcm_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index 3d9207c160eb..3a55465951e7 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -320,7 +320,7 @@ err_disable_clk: return ret; } -static int npcm_adc_remove(struct platform_device *pdev) +static void npcm_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct npcm_adc *info = iio_priv(indio_dev); @@ -333,13 +333,11 @@ static int npcm_adc_remove(struct platform_device *pdev) if (!IS_ERR(info->vref)) regulator_disable(info->vref); clk_disable_unprepare(info->adc_clk); - - return 0; } static struct platform_driver npcm_adc_driver = { .probe = npcm_adc_probe, - .remove = npcm_adc_remove, + .remove_new = npcm_adc_remove, .driver = { .name = "npcm_adc", .of_match_table = npcm_adc_match, -- cgit v1.2.3 From 0de6e194459168c804816abb08e9d955da73b40c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:00 +0200 Subject: iio: adc: qcom-pm8xxx-xoadc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230919174931.1417681-19-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 64a3aeb6261c..01c5586df56d 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -957,7 +957,7 @@ out_disable_vref: return ret; } -static int pm8xxx_xoadc_remove(struct platform_device *pdev) +static void pm8xxx_xoadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct pm8xxx_xoadc *adc = iio_priv(indio_dev); @@ -965,8 +965,6 @@ static int pm8xxx_xoadc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); regulator_disable(adc->vref); - - return 0; } static const struct xoadc_variant pm8018_variant = { @@ -1019,7 +1017,7 @@ static struct platform_driver pm8xxx_xoadc_driver = { .of_match_table = pm8xxx_xoadc_id_table, }, .probe = pm8xxx_xoadc_probe, - .remove = pm8xxx_xoadc_remove, + .remove_new = pm8xxx_xoadc_remove, }; module_platform_driver(pm8xxx_xoadc_driver); -- cgit v1.2.3 From e785bace1a77a31c3853f01735e651c9c2b43b03 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:01 +0200 Subject: iio: adc: rcar-gyroadc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-20-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rcar-gyroadc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index b8972f673c9d..d524f2e8e927 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -559,7 +559,7 @@ err_clk_if_enable: return ret; } -static int rcar_gyroadc_remove(struct platform_device *pdev) +static void rcar_gyroadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct rcar_gyroadc *priv = iio_priv(indio_dev); @@ -573,8 +573,6 @@ static int rcar_gyroadc_remove(struct platform_device *pdev) pm_runtime_set_suspended(dev); clk_disable_unprepare(priv->clk); rcar_gyroadc_deinit_supplies(indio_dev); - - return 0; } static int rcar_gyroadc_suspend(struct device *dev) @@ -603,7 +601,7 @@ static const struct dev_pm_ops rcar_gyroadc_pm_ops = { static struct platform_driver rcar_gyroadc_driver = { .probe = rcar_gyroadc_probe, - .remove = rcar_gyroadc_remove, + .remove_new = rcar_gyroadc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = rcar_gyroadc_match, -- cgit v1.2.3 From eedcd8c7327f27924048ebdf2532ee7004c668e4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:02 +0200 Subject: iio: adc: stm32-adc-core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-21-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index bbd5bdd732f0..c19506b0aac8 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -819,7 +819,7 @@ err_pm_stop: return ret; } -static int stm32_adc_remove(struct platform_device *pdev) +static void stm32_adc_remove(struct platform_device *pdev) { struct stm32_adc_common *common = platform_get_drvdata(pdev); struct stm32_adc_priv *priv = to_stm32_adc_priv(common); @@ -831,8 +831,6 @@ static int stm32_adc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - - return 0; } static int stm32_adc_core_runtime_suspend(struct device *dev) @@ -913,7 +911,7 @@ MODULE_DEVICE_TABLE(of, stm32_adc_of_match); static struct platform_driver stm32_adc_driver = { .probe = stm32_adc_probe, - .remove = stm32_adc_remove, + .remove_new = stm32_adc_remove, .driver = { .name = "stm32-adc-core", .of_match_table = stm32_adc_of_match, -- cgit v1.2.3 From da8431b4fd160ac9f13c8ca2bef04d89ff559a67 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:03 +0200 Subject: iio: adc: stm32-adc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-22-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index f7613efb870d..25a912805439 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2513,7 +2513,7 @@ err_dma_disable: return ret; } -static int stm32_adc_remove(struct platform_device *pdev) +static void stm32_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct stm32_adc *adc = iio_priv(indio_dev); @@ -2532,8 +2532,6 @@ static int stm32_adc_remove(struct platform_device *pdev) adc->rx_buf, adc->rx_dma_buf); dma_release_channel(adc->dma_chan); } - - return 0; } static int stm32_adc_suspend(struct device *dev) @@ -2659,7 +2657,7 @@ MODULE_DEVICE_TABLE(of, stm32_adc_of_match); static struct platform_driver stm32_adc_driver = { .probe = stm32_adc_probe, - .remove = stm32_adc_remove, + .remove_new = stm32_adc_remove, .driver = { .name = "stm32-adc", .of_match_table = stm32_adc_of_match, -- cgit v1.2.3 From cd918e75b42e24bac86d1b33118b936cff3bb3bd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:04 +0200 Subject: iio: adc: stm32-dfsdm-adc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-23-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index b5cc43d12b6f..ca08ae3108b2 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -1620,7 +1620,7 @@ err_cleanup: return ret; } -static int stm32_dfsdm_adc_remove(struct platform_device *pdev) +static void stm32_dfsdm_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); @@ -1629,8 +1629,6 @@ static int stm32_dfsdm_adc_remove(struct platform_device *pdev) of_platform_depopulate(&pdev->dev); iio_device_unregister(indio_dev); stm32_dfsdm_dma_release(indio_dev); - - return 0; } static int stm32_dfsdm_adc_suspend(struct device *dev) @@ -1677,7 +1675,7 @@ static struct platform_driver stm32_dfsdm_adc_driver = { .pm = pm_sleep_ptr(&stm32_dfsdm_adc_pm_ops), }, .probe = stm32_dfsdm_adc_probe, - .remove = stm32_dfsdm_adc_remove, + .remove_new = stm32_dfsdm_adc_remove, }; module_platform_driver(stm32_dfsdm_adc_driver); -- cgit v1.2.3 From 259fbaecb7b56c202ff4fae9a4ad98df3de28b22 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:05 +0200 Subject: iio: adc: stm32-dfsdm-core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-24-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index 0f6ebb3061a0..a05d978b8cb8 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -436,7 +436,7 @@ pm_put: return ret; } -static int stm32_dfsdm_core_remove(struct platform_device *pdev) +static void stm32_dfsdm_core_remove(struct platform_device *pdev) { struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev); @@ -446,8 +446,6 @@ static int stm32_dfsdm_core_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); stm32_dfsdm_clk_disable_unprepare(dfsdm); - - return 0; } static int stm32_dfsdm_core_suspend(struct device *dev) @@ -508,7 +506,7 @@ static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = { static struct platform_driver stm32_dfsdm_driver = { .probe = stm32_dfsdm_probe, - .remove = stm32_dfsdm_core_remove, + .remove_new = stm32_dfsdm_core_remove, .driver = { .name = "stm32-dfsdm", .of_match_table = stm32_dfsdm_of_match, -- cgit v1.2.3 From 794c760fd3da939db6fdd903d190a68838b86aa3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:06 +0200 Subject: iio: adc: sun4i-gpadc-iio: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Jernej Skrabec Link: https://lore.kernel.org/r/20230919174931.1417681-25-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/sun4i-gpadc-iio.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 25bba96367a8..100ecced5fc1 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -669,7 +669,7 @@ err_map: return ret; } -static int sun4i_gpadc_remove(struct platform_device *pdev) +static void sun4i_gpadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct sun4i_gpadc_iio *info = iio_priv(indio_dev); @@ -678,12 +678,10 @@ static int sun4i_gpadc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); if (!IS_ENABLED(CONFIG_THERMAL_OF)) - return 0; + return; if (!info->no_irq) iio_map_array_unregister(indio_dev); - - return 0; } static const struct platform_device_id sun4i_gpadc_id[] = { @@ -702,7 +700,7 @@ static struct platform_driver sun4i_gpadc_driver = { }, .id_table = sun4i_gpadc_id, .probe = sun4i_gpadc_probe, - .remove = sun4i_gpadc_remove, + .remove_new = sun4i_gpadc_remove, }; MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_id); -- cgit v1.2.3 From e4bd0e6040e93f4895371abd4fca7291a5c5e9ef Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:07 +0200 Subject: iio: adc: ti_am335x_adc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-26-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti_am335x_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 8db7a01cb5fb..c755e8cd5220 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -681,7 +681,7 @@ err_dma: return err; } -static int tiadc_remove(struct platform_device *pdev) +static void tiadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct tiadc_device *adc_dev = iio_priv(indio_dev); @@ -697,8 +697,6 @@ static int tiadc_remove(struct platform_device *pdev) step_en = get_adc_step_mask(adc_dev); am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en); - - return 0; } static int tiadc_suspend(struct device *dev) @@ -747,7 +745,7 @@ static struct platform_driver tiadc_driver = { .of_match_table = ti_adc_dt_ids, }, .probe = tiadc_probe, - .remove = tiadc_remove, + .remove_new = tiadc_remove, }; module_platform_driver(tiadc_driver); -- cgit v1.2.3 From 30ff88e96b5e9ff1d190bbce6caa80a2265d9a43 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:08 +0200 Subject: iio: adc: twl4030-madc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-27-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl4030-madc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index c279c4f2c9b7..4a247ca25a44 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -892,7 +892,7 @@ err_current_generator: return ret; } -static int twl4030_madc_remove(struct platform_device *pdev) +static void twl4030_madc_remove(struct platform_device *pdev) { struct iio_dev *iio_dev = platform_get_drvdata(pdev); struct twl4030_madc_data *madc = iio_priv(iio_dev); @@ -903,8 +903,6 @@ static int twl4030_madc_remove(struct platform_device *pdev) twl4030_madc_set_power(madc, 0); regulator_disable(madc->usb3v1); - - return 0; } #ifdef CONFIG_OF @@ -917,7 +915,7 @@ MODULE_DEVICE_TABLE(of, twl_madc_of_match); static struct platform_driver twl4030_madc_driver = { .probe = twl4030_madc_probe, - .remove = twl4030_madc_remove, + .remove_new = twl4030_madc_remove, .driver = { .name = "twl4030_madc", .of_match_table = of_match_ptr(twl_madc_of_match), -- cgit v1.2.3 From d355c20e710edf9242c5e5ef5fad42ed79786046 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:09 +0200 Subject: iio: adc: twl6030-gpadc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-28-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl6030-gpadc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 32873fb5f367..224e9cb5e147 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -968,14 +968,12 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) return iio_device_register(indio_dev); } -static int twl6030_gpadc_remove(struct platform_device *pdev) +static void twl6030_gpadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); twl6030_gpadc_disable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); iio_device_unregister(indio_dev); - - return 0; } static int twl6030_gpadc_suspend(struct device *pdev) @@ -1007,7 +1005,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend, static struct platform_driver twl6030_gpadc_driver = { .probe = twl6030_gpadc_probe, - .remove = twl6030_gpadc_remove, + .remove_new = twl6030_gpadc_remove, .driver = { .name = DRIVER_NAME, .pm = pm_sleep_ptr(&twl6030_gpadc_pm_ops), -- cgit v1.2.3 From aec6dbe61f2b01b572cc85643eaf0095b8c66bbf Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:10 +0200 Subject: iio: adc: vf610_adc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-29-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/vf610_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index ae31aafd2653..e4548df3f8fb 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -916,7 +916,7 @@ error_adc_clk_enable: return ret; } -static int vf610_adc_remove(struct platform_device *pdev) +static void vf610_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct vf610_adc *info = iio_priv(indio_dev); @@ -925,8 +925,6 @@ static int vf610_adc_remove(struct platform_device *pdev) iio_triggered_buffer_cleanup(indio_dev); regulator_disable(info->vref); clk_disable_unprepare(info->clk); - - return 0; } static int vf610_adc_suspend(struct device *dev) @@ -974,7 +972,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, static struct platform_driver vf610_adc_driver = { .probe = vf610_adc_probe, - .remove = vf610_adc_remove, + .remove_new = vf610_adc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = vf610_adc_match, -- cgit v1.2.3 From 3de9b0729567c873571090287baf45957efd5ba9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:11 +0200 Subject: iio: dac: dpot-dac: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-30-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/dpot-dac.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index 83ce9489259c..7332064d0852 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -226,15 +226,13 @@ disable_reg: return ret; } -static int dpot_dac_remove(struct platform_device *pdev) +static void dpot_dac_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct dpot_dac *dac = iio_priv(indio_dev); iio_device_unregister(indio_dev); regulator_disable(dac->vref); - - return 0; } static const struct of_device_id dpot_dac_match[] = { @@ -245,7 +243,7 @@ MODULE_DEVICE_TABLE(of, dpot_dac_match); static struct platform_driver dpot_dac_driver = { .probe = dpot_dac_probe, - .remove = dpot_dac_remove, + .remove_new = dpot_dac_remove, .driver = { .name = "iio-dpot-dac", .of_match_table = dpot_dac_match, -- cgit v1.2.3 From b4785a25a969bc53773c6abb89f3592b7fd4fe9e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:12 +0200 Subject: iio: dac: lpc18xx_dac: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-31-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/lpc18xx_dac.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index 60467c6f2c6e..b3aa4443a6a4 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -165,7 +165,7 @@ dis_reg: return ret; } -static int lpc18xx_dac_remove(struct platform_device *pdev) +static void lpc18xx_dac_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct lpc18xx_dac *dac = iio_priv(indio_dev); @@ -175,8 +175,6 @@ static int lpc18xx_dac_remove(struct platform_device *pdev) writel(0, dac->base + LPC18XX_DAC_CTRL); clk_disable_unprepare(dac->clk); regulator_disable(dac->vref); - - return 0; } static const struct of_device_id lpc18xx_dac_match[] = { @@ -187,7 +185,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_dac_match); static struct platform_driver lpc18xx_dac_driver = { .probe = lpc18xx_dac_probe, - .remove = lpc18xx_dac_remove, + .remove_new = lpc18xx_dac_remove, .driver = { .name = "lpc18xx-dac", .of_match_table = lpc18xx_dac_match, -- cgit v1.2.3 From b5821e60388428226cc1e7cfe0543f7e190fa4fc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:13 +0200 Subject: iio: dac: stm32-dac-core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-32-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/stm32-dac-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 83bf184e3adc..15abe048729e 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -183,7 +183,7 @@ err_pm_stop: return ret; } -static int stm32_dac_remove(struct platform_device *pdev) +static void stm32_dac_remove(struct platform_device *pdev) { pm_runtime_get_sync(&pdev->dev); of_platform_depopulate(&pdev->dev); @@ -191,8 +191,6 @@ static int stm32_dac_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - - return 0; } static int stm32_dac_core_resume(struct device *dev) @@ -249,7 +247,7 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match); static struct platform_driver stm32_dac_driver = { .probe = stm32_dac_probe, - .remove = stm32_dac_remove, + .remove_new = stm32_dac_remove, .driver = { .name = "stm32-dac-core", .of_match_table = stm32_dac_of_match, -- cgit v1.2.3 From 85a32a0ae2212ccbf8551993e494d68dbfc059ef Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:14 +0200 Subject: iio: dac: stm32-dac: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-33-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/stm32-dac.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index 3cab28c7ee3b..5a722f307e7e 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -362,7 +362,7 @@ err_pm_put: return ret; } -static int stm32_dac_remove(struct platform_device *pdev) +static void stm32_dac_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -371,8 +371,6 @@ static int stm32_dac_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - - return 0; } static int stm32_dac_suspend(struct device *dev) @@ -400,7 +398,7 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match); static struct platform_driver stm32_dac_driver = { .probe = stm32_dac_probe, - .remove = stm32_dac_remove, + .remove_new = stm32_dac_remove, .driver = { .name = "stm32-dac", .of_match_table = stm32_dac_of_match, -- cgit v1.2.3 From cf22fc5566ccf1b23c4a5489912e6e406a0377fd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:15 +0200 Subject: iio: dac: vf610: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-34-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/vf610_dac.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c index fc182250c622..de73bc5a1c93 100644 --- a/drivers/iio/dac/vf610_dac.c +++ b/drivers/iio/dac/vf610_dac.c @@ -231,7 +231,7 @@ error_iio_device_register: return ret; } -static int vf610_dac_remove(struct platform_device *pdev) +static void vf610_dac_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct vf610_dac *info = iio_priv(indio_dev); @@ -239,8 +239,6 @@ static int vf610_dac_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); vf610_dac_exit(info); clk_disable_unprepare(info->clk); - - return 0; } static int vf610_dac_suspend(struct device *dev) @@ -274,7 +272,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, static struct platform_driver vf610_dac_driver = { .probe = vf610_dac_probe, - .remove = vf610_dac_remove, + .remove_new = vf610_dac_remove, .driver = { .name = "vf610-dac", .of_match_table = vf610_dac_match, -- cgit v1.2.3 From 3a8799735b406919b28f0539a1234fe8a895bd88 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:16 +0200 Subject: iio: gyro: hid-sensor-gyro-3d: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-35-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/hid-sensor-gyro-3d.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 698c50da1f10..59a38bf9459b 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -359,7 +359,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_gyro_3d_remove(struct platform_device *pdev) +static void hid_gyro_3d_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -368,8 +368,6 @@ static int hid_gyro_3d_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes); - - return 0; } static const struct platform_device_id hid_gyro_3d_ids[] = { @@ -388,7 +386,7 @@ static struct platform_driver hid_gyro_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_gyro_3d_probe, - .remove = hid_gyro_3d_remove, + .remove_new = hid_gyro_3d_remove, }; module_platform_driver(hid_gyro_3d_platform_driver); -- cgit v1.2.3 From b06ab8c8ac9da9d03cec61678fe04404450ee76a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:17 +0200 Subject: iio: humidity: hid-sensor-humidity: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-36-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hid-sensor-humidity.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index fa0fe404a70a..bf6d2636a85e 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -260,7 +260,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_humidity_remove(struct platform_device *pdev) +static void hid_humidity_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -269,8 +269,6 @@ static int hid_humidity_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY); hid_sensor_remove_trigger(indio_dev, &humid_st->common_attributes); - - return 0; } static const struct platform_device_id hid_humidity_ids[] = { @@ -289,7 +287,7 @@ static struct platform_driver hid_humidity_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_humidity_probe, - .remove = hid_humidity_remove, + .remove_new = hid_humidity_remove, }; module_platform_driver(hid_humidity_platform_driver); -- cgit v1.2.3 From 3e1e6787dc4b42c09a23cfe4e898b01f5a07b559 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:18 +0200 Subject: iio: light: cm3605: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-37-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm3605.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index e7f0b81b7f5a..22a63a89f289 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -266,7 +266,7 @@ out_disable_vdd: return ret; } -static int cm3605_remove(struct platform_device *pdev) +static void cm3605_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct cm3605 *cm3605 = iio_priv(indio_dev); @@ -276,8 +276,6 @@ static int cm3605_remove(struct platform_device *pdev) gpiod_set_value_cansleep(cm3605->aset, 0); iio_device_unregister(indio_dev); regulator_disable(cm3605->vdd); - - return 0; } static int cm3605_pm_suspend(struct device *dev) @@ -320,7 +318,7 @@ static struct platform_driver cm3605_driver = { .pm = pm_sleep_ptr(&cm3605_dev_pm_ops), }, .probe = cm3605_probe, - .remove = cm3605_remove, + .remove_new = cm3605_remove, }; module_platform_driver(cm3605_driver); -- cgit v1.2.3 From b428adb4113b20f123bf467a909332824048b741 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:19 +0200 Subject: iio: light: hid-sensor-als: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-38-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index eb1aedad7edc..cb76841ec0ce 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -347,7 +347,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_als_remove(struct platform_device *pdev) +static void hid_als_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -356,8 +356,6 @@ static int hid_als_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes); - - return 0; } static const struct platform_device_id hid_als_ids[] = { @@ -380,7 +378,7 @@ static struct platform_driver hid_als_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_als_probe, - .remove = hid_als_remove, + .remove_new = hid_als_remove, }; module_platform_driver(hid_als_platform_driver); -- cgit v1.2.3 From c707ccc7005475764ee0f2438055e2592c3d5138 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:20 +0200 Subject: iio: light: hid-sensor-prox: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-39-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-prox.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index a47591e1bad9..26c481d2998c 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -313,7 +313,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_prox_remove(struct platform_device *pdev) +static void hid_prox_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -322,8 +322,6 @@ static int hid_prox_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes); - - return 0; } static const struct platform_device_id hid_prox_ids[] = { @@ -346,7 +344,7 @@ static struct platform_driver hid_prox_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_prox_probe, - .remove = hid_prox_remove, + .remove_new = hid_prox_remove, }; module_platform_driver(hid_prox_platform_driver); -- cgit v1.2.3 From 4235ac7e6f9650ab07bbcd7c33c46a2a70f6ca56 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:21 +0200 Subject: iio: light: lm3533-als: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-40-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/light/lm3533-als.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 827bc25269e9..7800f7fa51b7 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -895,7 +895,7 @@ err_free_irq: return ret; } -static int lm3533_als_remove(struct platform_device *pdev) +static void lm3533_als_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct lm3533_als *als = iio_priv(indio_dev); @@ -905,8 +905,6 @@ static int lm3533_als_remove(struct platform_device *pdev) lm3533_als_disable(als); if (als->irq) free_irq(als->irq, indio_dev); - - return 0; } static struct platform_driver lm3533_als_driver = { @@ -914,7 +912,7 @@ static struct platform_driver lm3533_als_driver = { .name = "lm3533-als", }, .probe = lm3533_als_probe, - .remove = lm3533_als_remove, + .remove_new = lm3533_als_remove, }; module_platform_driver(lm3533_als_driver); -- cgit v1.2.3 From 622adde5e54de35eaca7ff065ef2970b8495fffd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:22 +0200 Subject: iio: magnetometer: hid-sensor-magn-3d: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-41-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index e85a3a8eea90..5c795a430d09 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -547,7 +547,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_magn_3d_remove(struct platform_device *pdev) +static void hid_magn_3d_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -556,8 +556,6 @@ static int hid_magn_3d_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes); - - return 0; } static const struct platform_device_id hid_magn_3d_ids[] = { @@ -576,7 +574,7 @@ static struct platform_driver hid_magn_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_magn_3d_probe, - .remove = hid_magn_3d_remove, + .remove_new = hid_magn_3d_remove, }; module_platform_driver(hid_magn_3d_platform_driver); -- cgit v1.2.3 From 44b25cf7195e953a8f56d3928e62133cd2ef790e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:23 +0200 Subject: iio: orientation: hid-sensor-incl-3d: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-42-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-incl-3d.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index ba5b581d5b25..8943d5c78bc0 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -383,7 +383,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_incl_3d_remove(struct platform_device *pdev) +static void hid_incl_3d_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -392,8 +392,6 @@ static int hid_incl_3d_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes); - - return 0; } static const struct platform_device_id hid_incl_3d_ids[] = { @@ -412,7 +410,7 @@ static struct platform_driver hid_incl_3d_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_incl_3d_probe, - .remove = hid_incl_3d_remove, + .remove_new = hid_incl_3d_remove, }; module_platform_driver(hid_incl_3d_platform_driver); -- cgit v1.2.3 From d93837b8d4331af9d5d270e36d2dcb7273c24eee Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:24 +0200 Subject: iio: orientation: hid-sensor-rotation: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-43-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index a033699910e8..5e8cadd5177a 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -327,7 +327,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_dev_rot_remove(struct platform_device *pdev) +static void hid_dev_rot_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -336,8 +336,6 @@ static int hid_dev_rot_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, hsdev->usage); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes); - - return 0; } static const struct platform_device_id hid_dev_rot_ids[] = { @@ -364,7 +362,7 @@ static struct platform_driver hid_dev_rot_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_dev_rot_probe, - .remove = hid_dev_rot_remove, + .remove_new = hid_dev_rot_remove, }; module_platform_driver(hid_dev_rot_platform_driver); -- cgit v1.2.3 From 94f2dab24ee87b4322a7c9e60959391717368e5e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:25 +0200 Subject: iio: position: hid-sensor-custom-intel-hinge: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-44-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/position/hid-sensor-custom-intel-hinge.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index 07c30d217255..76e173850a35 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -342,7 +342,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_hinge_remove(struct platform_device *pdev) +static void hid_hinge_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -351,8 +351,6 @@ static int hid_hinge_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); sensor_hub_remove_callback(hsdev, hsdev->usage); hid_sensor_remove_trigger(indio_dev, &st->common_attributes); - - return 0; } static const struct platform_device_id hid_hinge_ids[] = { @@ -371,7 +369,7 @@ static struct platform_driver hid_hinge_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_hinge_probe, - .remove = hid_hinge_remove, + .remove_new = hid_hinge_remove, }; module_platform_driver(hid_hinge_platform_driver); -- cgit v1.2.3 From 23c81c1098ae4569aecca4d2040ee9d144e804f7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:26 +0200 Subject: iio: pressure: hid-sensor: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-45-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/hid-sensor-press.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index a9215eb32d70..956045e2db29 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -323,7 +323,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_press_remove(struct platform_device *pdev) +static void hid_press_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -332,8 +332,6 @@ static int hid_press_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); iio_device_unregister(indio_dev); hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes); - - return 0; } static const struct platform_device_id hid_press_ids[] = { @@ -352,7 +350,7 @@ static struct platform_driver hid_press_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_press_probe, - .remove = hid_press_remove, + .remove_new = hid_press_remove, }; module_platform_driver(hid_press_platform_driver); -- cgit v1.2.3 From 2df694f710d29d6c9d10f09cd368d2f544c7ba4c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:27 +0200 Subject: iio: proximity: cros_ec_mkbp: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20230919174931.1417681-46-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/cros_ec_mkbp_proximity.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/cros_ec_mkbp_proximity.c b/drivers/iio/proximity/cros_ec_mkbp_proximity.c index 571ea1812246..4df506bb8b38 100644 --- a/drivers/iio/proximity/cros_ec_mkbp_proximity.c +++ b/drivers/iio/proximity/cros_ec_mkbp_proximity.c @@ -239,15 +239,13 @@ static int cros_ec_mkbp_proximity_probe(struct platform_device *pdev) return 0; } -static int cros_ec_mkbp_proximity_remove(struct platform_device *pdev) +static void cros_ec_mkbp_proximity_remove(struct platform_device *pdev) { struct cros_ec_mkbp_proximity_data *data = platform_get_drvdata(pdev); struct cros_ec_device *ec = data->ec; blocking_notifier_chain_unregister(&ec->event_notifier, &data->notifier); - - return 0; } static const struct of_device_id cros_ec_mkbp_proximity_of_match[] = { @@ -263,7 +261,7 @@ static struct platform_driver cros_ec_mkbp_proximity_driver = { .pm = pm_sleep_ptr(&cros_ec_mkbp_proximity_pm_ops), }, .probe = cros_ec_mkbp_proximity_probe, - .remove = cros_ec_mkbp_proximity_remove, + .remove_new = cros_ec_mkbp_proximity_remove, }; module_platform_driver(cros_ec_mkbp_proximity_driver); -- cgit v1.2.3 From 4abfc97db98be23f33c5b003d13bbb02c8fde679 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:28 +0200 Subject: iio: proximity: srf04: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-47-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/srf04.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index faf2f806ce80..86c57672fc7e 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -344,7 +344,7 @@ static int srf04_probe(struct platform_device *pdev) return ret; } -static int srf04_remove(struct platform_device *pdev) +static void srf04_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct srf04_data *data = iio_priv(indio_dev); @@ -355,8 +355,6 @@ static int srf04_remove(struct platform_device *pdev) pm_runtime_disable(data->dev); pm_runtime_set_suspended(data->dev); } - - return 0; } static int srf04_pm_runtime_suspend(struct device *dev) @@ -391,7 +389,7 @@ static const struct dev_pm_ops srf04_pm_ops = { static struct platform_driver srf04_driver = { .probe = srf04_probe, - .remove = srf04_remove, + .remove_new = srf04_remove, .driver = { .name = "srf04-gpio", .of_match_table = of_srf04_match, -- cgit v1.2.3 From f55891730682f3e2d7401de6f52eddb1338107cb Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:29 +0200 Subject: iio: temperature: hid-sensor: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-48-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/hid-sensor-temperature.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index d40f235af1d4..0143fd478933 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -257,7 +257,7 @@ error_remove_trigger: } /* Function to deinitialize the processing for usage id */ -static int hid_temperature_remove(struct platform_device *pdev) +static void hid_temperature_remove(struct platform_device *pdev) { struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -265,8 +265,6 @@ static int hid_temperature_remove(struct platform_device *pdev) sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE); hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes); - - return 0; } static const struct platform_device_id hid_temperature_ids[] = { @@ -285,7 +283,7 @@ static struct platform_driver hid_temperature_platform_driver = { .pm = &hid_sensor_pm_ops, }, .probe = hid_temperature_probe, - .remove = hid_temperature_remove, + .remove_new = hid_temperature_remove, }; module_platform_driver(hid_temperature_platform_driver); -- cgit v1.2.3 From 548eb81d7bfcf48370f4a82483f6ed0f1378b719 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:30 +0200 Subject: iio: trigger: iio-trig-interrupt: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-49-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/iio-trig-interrupt.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 5f49cd105fae..dec256bfbd73 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -81,7 +81,7 @@ error_ret: return ret; } -static int iio_interrupt_trigger_remove(struct platform_device *pdev) +static void iio_interrupt_trigger_remove(struct platform_device *pdev) { struct iio_trigger *trig; struct iio_interrupt_trigger_info *trig_info; @@ -92,13 +92,11 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev) free_irq(trig_info->irq, trig); kfree(trig_info); iio_trigger_free(trig); - - return 0; } static struct platform_driver iio_interrupt_trigger_driver = { .probe = iio_interrupt_trigger_probe, - .remove = iio_interrupt_trigger_remove, + .remove_new = iio_interrupt_trigger_remove, .driver = { .name = "iio_interrupt_trigger", }, -- cgit v1.2.3 From e377df03082b36f748894904c990617682b8cec6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 19 Sep 2023 19:49:31 +0200 Subject: iio: trigger: stm32-timer: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919174931.1417681-50-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/stm32-timer-trigger.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 3643c4afae67..d76444030a28 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -809,7 +809,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) return 0; } -static int stm32_timer_trigger_remove(struct platform_device *pdev) +static void stm32_timer_trigger_remove(struct platform_device *pdev) { struct stm32_timer_trigger *priv = platform_get_drvdata(pdev); u32 val; @@ -824,8 +824,6 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev) if (priv->enabled) clk_disable(priv->clk); - - return 0; } static int stm32_timer_trigger_suspend(struct device *dev) @@ -904,7 +902,7 @@ MODULE_DEVICE_TABLE(of, stm32_trig_of_match); static struct platform_driver stm32_timer_trigger_driver = { .probe = stm32_timer_trigger_probe, - .remove = stm32_timer_trigger_remove, + .remove_new = stm32_timer_trigger_remove, .driver = { .name = "stm32-timer-trigger", .of_match_table = stm32_trig_of_match, -- cgit v1.2.3 From 2da980fd5dcb5345a1c072fd29a04130631913c0 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 21 Sep 2023 21:24:20 +0200 Subject: dt-bindings: iio: adc: ti,ads1015: Document optional interrupt line The ADS1015 can have optional IRQ line connected, document it in the DT bindings. Signed-off-by: Marek Vasut Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20230921192420.70643-1-marex@denx.de Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml index e004659099c1..d605999ffe28 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml @@ -23,6 +23,9 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + "#address-cells": const: 1 -- cgit v1.2.3 From 57d3909596f9941c7d0777004994438df1b5ba0a Mon Sep 17 00:00:00 2001 From: Andy Shen Shen Date: Thu, 21 Sep 2023 11:14:44 +0800 Subject: iio: adc: palmas_gpadc: Drop duplicated the in comment. In line 460 of the palmas_gpadc.c file, fix kernel comment errors. Signed-off-by: Andy Shen Shen Link: https://lore.kernel.org/r/20230921031444.63594-1-shengaoya@inspur.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index e202ea18af10..203cbbc70719 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -457,7 +457,7 @@ static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, * * The gain error include both gain error, as specified in the datasheet, and * the gain error drift. These paramenters vary depending on device and whether - * the the channel is calibrated (trimmed) or not. + * the channel is calibrated (trimmed) or not. */ static int palmas_gpadc_threshold_with_tolerance(int val, const int INL, const int gain_error, -- cgit v1.2.3 From 286d528bf0fae9f334fba857825b9701df1675b2 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:46 +0530 Subject: iio: hid-sensor-als: Use channel index to support more hub attributes Sensor hub attributes can be extended to support more channels. Repeat the reading for the two existing channels and store them separately. It still operates in the same manner as before where there was just one entry. So in order to support more sensor hub attributes for ALS use channel index to get specific sensor hub attributes. Signed-off-by: Basavaraj Natikar Acked-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230919081054.2050714-2-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index eb1aedad7edc..efb1f8862b28 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -24,7 +24,7 @@ enum { struct als_state { struct hid_sensor_hub_callbacks callbacks; struct hid_sensor_common common_attributes; - struct hid_sensor_hub_attribute_info als_illum; + struct hid_sensor_hub_attribute_info als[CHANNEL_SCAN_INDEX_MAX]; struct { u32 illum[CHANNEL_SCAN_INDEX_MAX]; u64 timestamp __aligned(8); @@ -99,8 +99,8 @@ static int als_read_raw(struct iio_dev *indio_dev, switch (chan->scan_index) { case CHANNEL_SCAN_INDEX_INTENSITY: case CHANNEL_SCAN_INDEX_ILLUM: - report_id = als_state->als_illum.report_id; - min = als_state->als_illum.logical_minimum; + report_id = als_state->als[chan->scan_index].report_id; + min = als_state->als[chan->scan_index].logical_minimum; address = HID_USAGE_SENSOR_LIGHT_ILLUM; break; default: @@ -242,22 +242,24 @@ static int als_parse_report(struct platform_device *pdev, struct als_state *st) { int ret; + int i; + + for (i = 0; i <= CHANNEL_SCAN_INDEX_ILLUM; ++i) { + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_LIGHT_ILLUM, + &st->als[i]); + if (ret < 0) + return ret; + als_adjust_channel_bit_mask(channels, i, st->als[i].size); + + dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index, + st->als[i].report_id); + } - ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, - usage_id, - HID_USAGE_SENSOR_LIGHT_ILLUM, - &st->als_illum); - if (ret < 0) - return ret; - als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_INTENSITY, - st->als_illum.size); - als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM, - st->als_illum.size); - - dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index, - st->als_illum.report_id); - - st->scale_precision = hid_sensor_format_scale(usage_id, &st->als_illum, + st->scale_precision = hid_sensor_format_scale(usage_id, + &st->als[CHANNEL_SCAN_INDEX_INTENSITY], &st->scale_pre_decml, &st->scale_post_decml); return ret; -- cgit v1.2.3 From 42f311751102ef4884ae7352d7e85bd97280d2d3 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:47 +0530 Subject: iio: Add channel type light color temperature In most cases, ambient color sensors also support light color temperature, which is measured in kelvin. Thus, add channel type light color temperature. Signed-off-by: Basavaraj Natikar Link: https://lore.kernel.org/r/20230919081054.2050714-3-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 2 ++ 4 files changed, 11 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index a2854dc9a839..4cf7ed9ca57b 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2179,3 +2179,10 @@ Contact: linux-iio@vger.kernel.org Description: Number of conditions that must occur, during a running period, before an event is generated. + +What: /sys/bus/iio/devices/iio:deviceX/in_colortemp_raw +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Represents light color temperature, which measures light color + temperature in Kelvin. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index d752e9c0499b..cba942cadf97 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -90,6 +90,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_POSITIONRELATIVE] = "positionrelative", [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", + [IIO_COLORTEMP] = "colortemp", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index c79f2f046a0b..08c20e540c13 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -47,6 +47,7 @@ enum iio_chan_type { IIO_POSITIONRELATIVE, IIO_PHASE, IIO_MASSCONCENTRATION, + IIO_COLORTEMP, }; enum iio_modifier { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 0a5c2bb60030..a63741e43ddf 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -59,6 +59,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_POSITIONRELATIVE] = "positionrelative", [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", + [IIO_COLORTEMP] = "colortemp", }; static const char * const iio_ev_type_text[] = { @@ -173,6 +174,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_POSITIONRELATIVE: case IIO_PHASE: case IIO_MASSCONCENTRATION: + case IIO_COLORTEMP: break; default: return false; -- cgit v1.2.3 From 5f05285df691b1e82108eead7165feae238c95ef Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:48 +0530 Subject: iio: hid-sensor-als: Add light color temperature support In most cases, ambient color sensors also support light color temperature. As a result, add support of light color temperature. Signed-off-by: Basavaraj Natikar Acked-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230919081054.2050714-4-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 37 +++++++++++++++++++++++++++++++++++-- include/linux/hid-sensor-ids.h | 1 + 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index efb1f8862b28..16a3f1941c27 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -14,8 +14,9 @@ #include "../common/hid-sensors/hid-sensor-trigger.h" enum { - CHANNEL_SCAN_INDEX_INTENSITY = 0, - CHANNEL_SCAN_INDEX_ILLUM = 1, + CHANNEL_SCAN_INDEX_INTENSITY, + CHANNEL_SCAN_INDEX_ILLUM, + CHANNEL_SCAN_INDEX_COLOR_TEMP, CHANNEL_SCAN_INDEX_MAX }; @@ -65,6 +66,16 @@ static const struct iio_chan_spec als_channels[] = { BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_ILLUM, }, + { + .type = IIO_COLORTEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), + .scan_index = CHANNEL_SCAN_INDEX_COLOR_TEMP, + }, IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) }; @@ -103,6 +114,11 @@ static int als_read_raw(struct iio_dev *indio_dev, min = als_state->als[chan->scan_index].logical_minimum; address = HID_USAGE_SENSOR_LIGHT_ILLUM; break; + case CHANNEL_SCAN_INDEX_COLOR_TEMP: + report_id = als_state->als[chan->scan_index].report_id; + min = als_state->als[chan->scan_index].logical_minimum; + address = HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE; + break; default: report_id = -1; break; @@ -223,6 +239,10 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data; ret = 0; break; + case HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE: + als_state->scan.illum[CHANNEL_SCAN_INDEX_COLOR_TEMP] = sample_data; + ret = 0; + break; case HID_USAGE_SENSOR_TIME_TIMESTAMP: als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes, *(s64 *)raw_data); @@ -258,6 +278,19 @@ static int als_parse_report(struct platform_device *pdev, st->als[i].report_id); } + ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE, + &st->als[CHANNEL_SCAN_INDEX_COLOR_TEMP]); + if (ret < 0) + return ret; + als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_COLOR_TEMP, + st->als[CHANNEL_SCAN_INDEX_COLOR_TEMP].size); + + dev_dbg(&pdev->dev, "als %x:%x\n", + st->als[CHANNEL_SCAN_INDEX_COLOR_TEMP].index, + st->als[CHANNEL_SCAN_INDEX_COLOR_TEMP].report_id); + st->scale_precision = hid_sensor_format_scale(usage_id, &st->als[CHANNEL_SCAN_INDEX_INTENSITY], &st->scale_pre_decml, &st->scale_post_decml); diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 13b1e65fbdcc..8af4fb3e0254 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -21,6 +21,7 @@ #define HID_USAGE_SENSOR_ALS 0x200041 #define HID_USAGE_SENSOR_DATA_LIGHT 0x2004d0 #define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1 +#define HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE 0x2004d2 /* PROX (200011) */ #define HID_USAGE_SENSOR_PROX 0x200011 -- cgit v1.2.3 From 908fee511ced79537b78db60f9b9bdb2f537679a Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:49 +0530 Subject: HID: amd_sfh: Add support for light color temperature In most cases, ambient color sensors also support light color temperature. As a result, add support of light color temperature. Signed-off-by: Basavaraj Natikar Acked-by: Jiri Kosina Link: https://lore.kernel.org/r/20230919081054.2050714-5-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c | 4 ++++ drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h | 1 + drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c index 8716a05950c8..b7e732ec4806 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c @@ -257,6 +257,10 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id, else als_input.illuminance_value = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER; + + if (sensor_idx == ACS_IDX) + als_input.light_color_temp = sensor_virt_addr[1]; + report_size = sizeof(als_input); memcpy(input_report, &als_input, sizeof(als_input)); break; diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h index ebd55675eb62..a7fc50deca4d 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h @@ -99,6 +99,7 @@ struct als_input_report { struct common_input_property common_property; /* values specific to this sensor */ int illuminance_value; + int light_color_temp; } __packed; struct hpd_feature_report { diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h index 697f2791ea9c..26e994e54ded 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h @@ -641,6 +641,13 @@ static const u8 als_report_descriptor[] = { 0x75, 32, /* HID report size(32) */ 0x95, 1, /* HID report count (1) */ 0X81, 0x02, /* HID Input (Data_Arr_Abs) */ +0x0A, 0xD2, 0x04, /* HID usage sensor data light temperature */ +0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */ +0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */ +0x55, 0x0, /* HID unit exponent(0x0) */ +0x75, 32, /* HID report size(32) */ +0x95, 1, /* HID report count (1) */ +0X81, 0x02, /* HID Input (Data_Arr_Abs) */ 0xC0 /* HID end collection */ }; -- cgit v1.2.3 From 82cdcdf227f3965cce3ac94bc2e473e9c898f402 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:50 +0530 Subject: HID: amd_sfh: Add support for SFH1.1 light color temperature In most cases, ambient color sensors also support light color temperature. As a result, add support of light color temperature for SFH1.1. Signed-off-by: Basavaraj Natikar Acked-by: Jiri Kosina Link: https://lore.kernel.org/r/20230919081054.2050714-6-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c | 6 ++++++ drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c index 06bdcf072d10..f100aaafa167 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c @@ -188,6 +188,7 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, struct sfh_mag_data mag_data; struct sfh_als_data als_data; struct hpd_status hpdstatus; + struct sfh_base_info binfo; void __iomem *sensoraddr; u8 report_size = 0; @@ -235,6 +236,11 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data)); get_common_inputs(&als_input.common_property, report_id); als_input.illuminance_value = float_to_int(als_data.lux); + + memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info)); + if (binfo.sbase.s_prop[ALS_IDX].sf.feat & 0x2) + als_input.light_color_temp = als_data.light_color_temp; + report_size = sizeof(als_input); memcpy(input_report, &als_input, sizeof(als_input)); break; diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h index 9d31d5b510eb..6f6f5db150c3 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h @@ -88,6 +88,16 @@ struct sfh_sensor_list { }; }; +struct sfh_sensor_prop { + union { + u32 sprop; + struct { + u32 elist : 16; + u32 feat : 16; + } sf; + }; +}; + struct sfh_base_info { union { u32 sfh_base[24]; @@ -95,6 +105,8 @@ struct sfh_base_info { struct sfh_platform_info plat_info; struct sfh_firmware_info fw_info; struct sfh_sensor_list s_list; + u32 rsvd; + struct sfh_sensor_prop s_prop[16]; } sbase; }; }; @@ -134,6 +146,7 @@ struct sfh_mag_data { struct sfh_als_data { struct sfh_common_data commondata; u32 lux; + u32 light_color_temp; }; struct hpd_status { -- cgit v1.2.3 From 06790d4c69d1851573baa4459e5bf6ac986cb0eb Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:51 +0530 Subject: iio: Add channel type for chromaticity In most cases, ambient color sensors also support the x and y light colors, which represent the coordinates on the CIE 1931 chromaticity diagram. Thus, add channel type for chromaticity. Signed-off-by: Basavaraj Natikar Link: https://lore.kernel.org/r/20230919081054.2050714-7-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 2 ++ 4 files changed, 12 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 4cf7ed9ca57b..0c9389ad3709 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2186,3 +2186,11 @@ Contact: linux-iio@vger.kernel.org Description: Represents light color temperature, which measures light color temperature in Kelvin. + +What: /sys/bus/iio/devices/iio:deviceX/in_chromaticity_x_raw +What: /sys/bus/iio/devices/iio:deviceX/in_chromaticity_y_raw +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + The x and y light color coordinate on the CIE 1931 chromaticity + diagram. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index cba942cadf97..6dc4d2b296bb 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -91,6 +91,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", [IIO_COLORTEMP] = "colortemp", + [IIO_CHROMATICITY] = "chromaticity", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 08c20e540c13..4832c611c027 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -48,6 +48,7 @@ enum iio_chan_type { IIO_PHASE, IIO_MASSCONCENTRATION, IIO_COLORTEMP, + IIO_CHROMATICITY, }; enum iio_modifier { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index a63741e43ddf..a28a86f2bd5b 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -60,6 +60,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_PHASE] = "phase", [IIO_MASSCONCENTRATION] = "massconcentration", [IIO_COLORTEMP] = "colortemp", + [IIO_CHROMATICITY] = "chromaticity", }; static const char * const iio_ev_type_text[] = { @@ -175,6 +176,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_PHASE: case IIO_MASSCONCENTRATION: case IIO_COLORTEMP: + case IIO_CHROMATICITY: break; default: return false; -- cgit v1.2.3 From ee3710f39f9d0ae5137a866138d005fe1ad18132 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:52 +0530 Subject: iio: hid-sensor-als: Add light chromaticity support In most cases, ambient color sensors also support the x and y light colors, which represent the coordinates on the CIE 1931 chromaticity diagram. Thus, add light chromaticity x and y. Signed-off-by: Basavaraj Natikar Acked-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230919081054.2050714-8-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 63 ++++++++++++++++++++++++++++++++++++++ include/linux/hid-sensor-ids.h | 3 ++ 2 files changed, 66 insertions(+) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 16a3f1941c27..c9d114ff080a 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -17,6 +17,8 @@ enum { CHANNEL_SCAN_INDEX_INTENSITY, CHANNEL_SCAN_INDEX_ILLUM, CHANNEL_SCAN_INDEX_COLOR_TEMP, + CHANNEL_SCAN_INDEX_CHROMATICITY_X, + CHANNEL_SCAN_INDEX_CHROMATICITY_Y, CHANNEL_SCAN_INDEX_MAX }; @@ -76,6 +78,30 @@ static const struct iio_chan_spec als_channels[] = { BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_COLOR_TEMP, }, + { + .type = IIO_CHROMATICITY, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), + .scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_X, + }, + { + .type = IIO_CHROMATICITY, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), + .scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_Y, + }, IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) }; @@ -119,6 +145,16 @@ static int als_read_raw(struct iio_dev *indio_dev, min = als_state->als[chan->scan_index].logical_minimum; address = HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE; break; + case CHANNEL_SCAN_INDEX_CHROMATICITY_X: + report_id = als_state->als[chan->scan_index].report_id; + min = als_state->als[chan->scan_index].logical_minimum; + address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X; + break; + case CHANNEL_SCAN_INDEX_CHROMATICITY_Y: + report_id = als_state->als[chan->scan_index].report_id; + min = als_state->als[chan->scan_index].logical_minimum; + address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y; + break; default: report_id = -1; break; @@ -243,6 +279,14 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, als_state->scan.illum[CHANNEL_SCAN_INDEX_COLOR_TEMP] = sample_data; ret = 0; break; + case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X: + als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_X] = sample_data; + ret = 0; + break; + case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y: + als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_Y] = sample_data; + ret = 0; + break; case HID_USAGE_SENSOR_TIME_TIMESTAMP: als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes, *(s64 *)raw_data); @@ -291,6 +335,25 @@ static int als_parse_report(struct platform_device *pdev, st->als[CHANNEL_SCAN_INDEX_COLOR_TEMP].index, st->als[CHANNEL_SCAN_INDEX_COLOR_TEMP].report_id); + for (i = 0; i < 2; i++) { + int next_scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_X + i; + + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, usage_id, + HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X + i, + &st->als[next_scan_index]); + if (ret < 0) + return ret; + + als_adjust_channel_bit_mask(channels, + CHANNEL_SCAN_INDEX_CHROMATICITY_X + i, + st->als[next_scan_index].size); + + dev_dbg(&pdev->dev, "als %x:%x\n", + st->als[next_scan_index].index, + st->als[next_scan_index].report_id); + } + st->scale_precision = hid_sensor_format_scale(usage_id, &st->als[CHANNEL_SCAN_INDEX_INTENSITY], &st->scale_pre_decml, &st->scale_post_decml); diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 8af4fb3e0254..6730ee900ee1 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -22,6 +22,9 @@ #define HID_USAGE_SENSOR_DATA_LIGHT 0x2004d0 #define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1 #define HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE 0x2004d2 +#define HID_USAGE_SENSOR_LIGHT_CHROMATICITY 0x2004d3 +#define HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X 0x2004d4 +#define HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y 0x2004d5 /* PROX (200011) */ #define HID_USAGE_SENSOR_PROX 0x200011 -- cgit v1.2.3 From 3244d44a7c5e6961d79eafa44370ff13c909d670 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:53 +0530 Subject: HID: amd_sfh: Add light chromaticity support In most cases, ambient color sensors also support the x and y light colors, which represent the coordinates on the CIE 1931 chromaticity diagram. Thus, add light chromaticity x and y. Signed-off-by: Basavaraj Natikar Acked-by: Jiri Kosina Link: https://lore.kernel.org/r/20230919081054.2050714-9-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c | 5 ++++- drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h | 2 ++ .../amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c index b7e732ec4806..ef1f9be8b893 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c @@ -258,8 +258,11 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id, als_input.illuminance_value = (int)sensor_virt_addr[0] / AMD_SFH_FW_MULTIPLIER; - if (sensor_idx == ACS_IDX) + if (sensor_idx == ACS_IDX) { als_input.light_color_temp = sensor_virt_addr[1]; + als_input.chromaticity_x_value = sensor_virt_addr[2]; + als_input.chromaticity_y_value = sensor_virt_addr[3]; + } report_size = sizeof(als_input); memcpy(input_report, &als_input, sizeof(als_input)); diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h index a7fc50deca4d..882434b1501f 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h @@ -100,6 +100,8 @@ struct als_input_report { /* values specific to this sensor */ int illuminance_value; int light_color_temp; + int chromaticity_x_value; + int chromaticity_y_value; } __packed; struct hpd_feature_report { diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h index 26e994e54ded..67ec2d6a417d 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h @@ -648,6 +648,20 @@ static const u8 als_report_descriptor[] = { 0x75, 32, /* HID report size(32) */ 0x95, 1, /* HID report count (1) */ 0X81, 0x02, /* HID Input (Data_Arr_Abs) */ +0x0A, 0xD4, 0x04, /* HID usage sensor data light chromaticity_x */ +0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */ +0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */ +0x55, 0x0, /* HID unit exponent(0x0) */ +0x75, 32, /* HID report size(32) */ +0x95, 1, /* HID report count(1) */ +0X81, 0x02, /* HID Input (Data_Var_Abs) */ +0x0A, 0xD5, 0x04, /* HID usage sensor data light chromaticity_y */ +0x17, 0x00, 0x00, 0x01, 0x80, /* HID logical Min_32 */ +0x27, 0xFF, 0xFF, 0xFF, 0x7F, /* HID logical Max_32 */ +0x55, 0x0, /* HID unit exponent(0x0) */ +0x75, 32, /* HID report size(32) */ +0x95, 1, /* HID report count (1) */ +0X81, 0x02, /* HID Input (Data_Var_Abs) */ 0xC0 /* HID end collection */ }; -- cgit v1.2.3 From c6c97210e27237438689611980f8a7b87bcc79bf Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Tue, 19 Sep 2023 13:40:54 +0530 Subject: HID: amd_sfh: Add light chromaticity for SFH1.1 In most cases, ambient color sensors also support the x and y light colors, which represent the coordinates on the CIE 1931 chromaticity diagram. Thus, add light chromaticity x and y for SFH1.1. Signed-off-by: Basavaraj Natikar Acked-by: Jiri Kosina Link: https://lore.kernel.org/r/20230919081054.2050714-10-Basavaraj.Natikar@amd.com Signed-off-by: Jonathan Cameron --- drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c | 5 ++++- drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c index f100aaafa167..8a037de08e92 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c @@ -238,8 +238,11 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, als_input.illuminance_value = float_to_int(als_data.lux); memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info)); - if (binfo.sbase.s_prop[ALS_IDX].sf.feat & 0x2) + if (binfo.sbase.s_prop[ALS_IDX].sf.feat & 0x2) { als_input.light_color_temp = als_data.light_color_temp; + als_input.chromaticity_x_value = float_to_int(als_data.chromaticity_x); + als_input.chromaticity_y_value = float_to_int(als_data.chromaticity_y); + } report_size = sizeof(als_input); memcpy(input_report, &als_input, sizeof(als_input)); diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h index 6f6f5db150c3..656c3e95ef8c 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h @@ -147,6 +147,8 @@ struct sfh_als_data { struct sfh_common_data commondata; u32 lux; u32 light_color_temp; + u32 chromaticity_x; + u32 chromaticity_y; }; struct hpd_status { -- cgit v1.2.3 From efea15e3c65d96bac17a4d8104e3fff7c07cc910 Mon Sep 17 00:00:00 2001 From: Marius Cristea Date: Mon, 18 Sep 2023 10:56:33 +0300 Subject: iio: adc: MCP3564: fix the static checker warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The patch 33ec3e5fc1ea: "iio: adc: adding support for MCP3564 ADC" from Aug 29, 2023 (linux-next), leads to the following Smatch static checker warning:         drivers/iio/adc/mcp3564.c:1426 mcp3564_probe()         warn: address of NULL pointer 'indio_dev' drivers/iio/adc/mcp3564.c 1421 struct iio_dev *indio_dev; 1422 struct mcp3564_state *adc; 1423 1424 indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); 1425 if (!indio_dev) { --> 1426 dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev), ^^^^^^^^^^^^^^^ Fixes: 33ec3e5fc1ea (iio: adc: adding support for MCP3564 ADC) Signed-off-by: Marius Cristea Link: https://lore.kernel.org/r/20230918075633.1884-1-marius.cristea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3564.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index 64145f4ae55c..9ede1a5d5d7b 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -1422,11 +1422,8 @@ static int mcp3564_probe(struct spi_device *spi) struct mcp3564_state *adc; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); - if (!indio_dev) { - dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev), - "Can't allocate iio device\n"); + if (!indio_dev) return -ENOMEM; - } adc = iio_priv(indio_dev); adc->spi = spi; -- cgit v1.2.3 From c78a96ab0f52a12b344882241019dd9070aa988c Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 21 Sep 2023 09:43:44 -0500 Subject: staging: iio: resolver: ad2s1210: fix ad2s1210_show_fault When reading the fault attribute, an empty string was printed if the fault register value was non-zero. This is fixed by checking that the return value is less than zero instead of not zero. Also always print two hex digits while we are touching this line. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230921144400.62380-4-dlechner@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 06de5823eb8e..7fb2d9e3196f 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -393,7 +393,7 @@ static ssize_t ad2s1210_show_fault(struct device *dev, ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); mutex_unlock(&st->lock); - return ret ? ret : sprintf(buf, "0x%x\n", ret); + return ret < 0 ? ret : sprintf(buf, "0x%02x\n", ret); } static ssize_t ad2s1210_clear_fault(struct device *dev, -- cgit v1.2.3 From 5e99f692d4e32e3250ab18d511894ca797407aec Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 21 Sep 2023 09:43:45 -0500 Subject: staging: iio: resolver: ad2s1210: fix not restoring sample gpio in channel read In theory, this code path should not be reachable because of the previous switch statement. But just in case we should make sure we are restoring the SAMPLE gpio to its original state before returning in addition to releasing the mutex lock. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230921144400.62380-5-dlechner@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 7fb2d9e3196f..f695ca0547e4 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -510,8 +510,8 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, ret = IIO_VAL_INT; break; default: - mutex_unlock(&st->lock); - return -EINVAL; + ret = -EINVAL; + break; } error_ret: -- cgit v1.2.3 From 7fe2d05cee46b1c4d9f1efaeab08cc31a0dfff60 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:07 -0500 Subject: staging: iio: resolver: ad2s1210: fix use before initialization This fixes a use before initialization in ad2s1210_probe(). The ad2s1210_setup_gpios() function uses st->sdev but it was being called before this field was initialized. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-2-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index f695ca0547e4..3f08b59f4e19 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -658,9 +658,6 @@ static int ad2s1210_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - ret = ad2s1210_setup_gpios(st); - if (ret < 0) - return ret; spi_set_drvdata(spi, indio_dev); @@ -671,6 +668,10 @@ static int ad2s1210_probe(struct spi_device *spi) st->resolution = 12; st->fexcit = AD2S1210_DEF_EXCIT; + ret = ad2s1210_setup_gpios(st); + if (ret < 0) + return ret; + indio_dev->info = &ad2s1210_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad2s1210_channels; -- cgit v1.2.3 From 9829ebacea804ad4b5d1a74a8d94a1f843505546 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:08 -0500 Subject: staging: iio: resolver: ad2s1210: remove call to spi_setup() This removes the call to spi_setup() in the ad2s1210 driver. Setting MODE_3 was incorrect. It should be MODE_1 but we can let the device tree select this and avoid the need to call spi_setup(). Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-3-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 3f08b59f4e19..8fde08887f7f 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -683,8 +683,6 @@ static int ad2s1210_probe(struct spi_device *spi) return ret; st->fclkin = spi->max_speed_hz; - spi->mode = SPI_MODE_3; - spi_setup(spi); ad2s1210_initial(st); return 0; -- cgit v1.2.3 From 3c1f41d5f320a001507257ca3504716e8f8bc00c Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:09 -0500 Subject: staging: iio: resolver: ad2s1210: check return of ad2s1210_initial() This adds a check to the return value of ad2s1210_initial() since it can fail. The call is also moved before devm_iio_device_register() so that we don't have to unregister the device if it fails. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-4-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 8fde08887f7f..b5e071d7c5fd 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -672,6 +672,10 @@ static int ad2s1210_probe(struct spi_device *spi) if (ret < 0) return ret; + ret = ad2s1210_initial(st); + if (ret < 0) + return ret; + indio_dev->info = &ad2s1210_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad2s1210_channels; @@ -683,7 +687,6 @@ static int ad2s1210_probe(struct spi_device *spi) return ret; st->fclkin = spi->max_speed_hz; - ad2s1210_initial(st); return 0; } -- cgit v1.2.3 From f434eac79bee6e44d7d46113391e55d9157cbc65 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:10 -0500 Subject: staging: iio: resolver: ad2s1210: remove spi_set_drvdata() Since we never call spi_get_drvdata(), we can remove spi_set_drvdata(). Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-5-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index b5e071d7c5fd..28015322f562 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -659,8 +659,6 @@ static int ad2s1210_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - mutex_init(&st->lock); st->sdev = spi; st->hysteresis = true; -- cgit v1.2.3 From acbfaee17014811d96d9094ce2fb723384dfc009 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:11 -0500 Subject: staging: iio: resolver: ad2s1210: sort imports There are quite a few imports and we will be adding more so it will make it easier to read if they are sorted. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-6-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 28015322f562..832f86bf15e5 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -4,16 +4,16 @@ * * Copyright (c) 2010-2010 Analog Devices Inc. */ -#include -#include +#include #include +#include +#include +#include #include -#include #include +#include #include -#include -#include -#include +#include #include #include -- cgit v1.2.3 From 1b6eba71cae261d504d5826897a22a9a45f200d2 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:12 -0500 Subject: staging: iio: resolver: ad2s1210: always use 16-bit value for raw read This removes the special handling for resolutions lower than 16 bits. This will allow us to use a fixed scale independent of the resolution. A new sample field is added to store the raw data instead of reusing the config mode rx buffer so that we don't have to do a cast or worry about unaligned access. Also, for the record, according to the datasheet, the logic for the special handling based on hysteresis that was removed was incorrect. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-7-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 832f86bf15e5..f9774dff2df4 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -95,7 +95,11 @@ struct ad2s1210_state { bool hysteresis; u8 resolution; enum ad2s1210_mode mode; - u8 rx[2] __aligned(IIO_DMA_MINALIGN); + /** For reading raw sample value via SPI. */ + __be16 sample __aligned(IIO_DMA_MINALIGN); + /** SPI transmit buffer. */ + u8 rx[2]; + /** SPI receive buffer. */ u8 tx[2]; }; @@ -464,10 +468,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, long m) { struct ad2s1210_state *st = iio_priv(indio_dev); - u16 negative; int ret = 0; - u16 pos; - s16 vel; mutex_lock(&st->lock); gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); @@ -487,26 +488,17 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, } if (ret < 0) goto error_ret; - ret = spi_read(st->sdev, st->rx, 2); + ret = spi_read(st->sdev, &st->sample, 2); if (ret < 0) goto error_ret; switch (chan->type) { case IIO_ANGL: - pos = be16_to_cpup((__be16 *)st->rx); - if (st->hysteresis) - pos >>= 16 - st->resolution; - *val = pos; + *val = be16_to_cpu(st->sample); ret = IIO_VAL_INT; break; case IIO_ANGL_VEL: - vel = be16_to_cpup((__be16 *)st->rx); - vel >>= 16 - st->resolution; - if (vel & 0x8000) { - negative = (0xffff >> st->resolution) << st->resolution; - vel |= negative; - } - *val = vel; + *val = (s16)be16_to_cpu(st->sample); ret = IIO_VAL_INT; break; default: -- cgit v1.2.3 From 4623b414e68bb57ce30fe286ca932202fc438a16 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:13 -0500 Subject: staging: iio: resolver: ad2s1210: implement IIO_CHAN_INFO_SCALE This adds an implementation of IIO_CHAN_INFO_SCALE to the ad2s1210 resolver driver. This allows userspace to get the scale factor for the raw values. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-8-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 53 ++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index f9774dff2df4..a710598a64f0 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -461,13 +461,10 @@ error_ret: return ret < 0 ? ret : len; } -static int ad2s1210_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) +static int ad2s1210_single_conversion(struct ad2s1210_state *st, + struct iio_chan_spec const *chan, + int *val) { - struct ad2s1210_state *st = iio_priv(indio_dev); int ret = 0; mutex_lock(&st->lock); @@ -514,6 +511,44 @@ error_ret: return ret; } +static const int ad2s1210_velocity_scale[] = { + 17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */ + 42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */ + 85445659, /* 8.192MHz / (2*pi * 500 / 2^15) */ + 341782638, /* 8.192MHz / (2*pi * 125 / 2^15) */ +}; + +static int ad2s1210_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return ad2s1210_single_conversion(st, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL: + /* approx 0.3 arc min converted to radians */ + *val = 0; + *val2 = 95874; + return IIO_VAL_INT_PLUS_NANO; + case IIO_ANGL_VEL: + *val = st->fclkin; + *val2 = ad2s1210_velocity_scale[st->resolution]; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + static IIO_DEVICE_ATTR(fclkin, 0644, ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0); static IIO_DEVICE_ATTR(fexcit, 0644, @@ -552,12 +587,14 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .type = IIO_ANGL, .indexed = 1, .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), }, { .type = IIO_ANGL_VEL, .indexed = 1, .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), } }; -- cgit v1.2.3 From 68d319571b8fee7b4135b118ad4733b4469ccaf0 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:14 -0500 Subject: staging: iio: resolver: ad2s1210: use devicetree to get CLKIN rate This removes the fclkin sysfs attribute and replaces it with getting the CLKIN clock rate using the clk subsystem (i.e. from the devicetree). CLKIN comes from an external oscillator that is connected directly to the AD2S1210 chip, so users of the sysfs attributes should not need to be concerned with this. The fclkin field (the datasheet name) is renamed to clkin_hz to be more obvious that it is a frequency in Hz. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-9-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/Kconfig | 1 + drivers/staging/iio/resolver/ad2s1210.c | 81 ++++++++++++--------------------- 2 files changed, 30 insertions(+), 52 deletions(-) diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig index 6d1e2622e0b0..bebb35822c9e 100644 --- a/drivers/staging/iio/resolver/Kconfig +++ b/drivers/staging/iio/resolver/Kconfig @@ -7,6 +7,7 @@ menu "Resolver to digital converters" config AD2S1210 tristate "Analog Devices ad2s1210 driver" depends on SPI + depends on COMMON_CLK depends on GPIOLIB || COMPILE_TEST help Say yes here to build support for Analog Devices spi resolver diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index a710598a64f0..c8723b6f3a3b 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -3,7 +3,9 @@ * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210 * * Copyright (c) 2010-2010 Analog Devices Inc. + * Copyright (c) 2023 BayLibre, SAS */ +#include #include #include #include @@ -90,7 +92,8 @@ struct ad2s1210_state { struct mutex lock; struct spi_device *sdev; struct gpio_desc *gpios[5]; - unsigned int fclkin; + /** The external oscillator frequency in Hz. */ + unsigned long clkin_hz; unsigned int fexcit; bool hysteresis; u8 resolution; @@ -165,7 +168,7 @@ int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) int ret; unsigned char fcw; - fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin); + fcw = (unsigned char)(st->fexcit * (1 << 15) / st->clkin_hz); if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) { dev_err(&st->sdev->dev, "ad2s1210: FCW out of range\n"); return -ERANGE; @@ -201,45 +204,6 @@ static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) return ad2s1210_config_write(st, 0x0); } -static ssize_t ad2s1210_show_fclkin(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - - return sprintf(buf, "%u\n", st->fclkin); -} - -static ssize_t ad2s1210_store_fclkin(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int fclkin; - int ret; - - ret = kstrtouint(buf, 10, &fclkin); - if (ret) - return ret; - if (fclkin < AD2S1210_MIN_CLKIN || fclkin > AD2S1210_MAX_CLKIN) { - dev_err(dev, "ad2s1210: fclkin out of range\n"); - return -EINVAL; - } - - mutex_lock(&st->lock); - st->fclkin = fclkin; - - ret = ad2s1210_update_frequency_control_word(st); - if (ret < 0) - goto error_ret; - ret = ad2s1210_soft_reset(st); -error_ret: - mutex_unlock(&st->lock); - - return ret < 0 ? ret : len; -} - static ssize_t ad2s1210_show_fexcit(struct device *dev, struct device_attribute *attr, char *buf) @@ -537,7 +501,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, *val2 = 95874; return IIO_VAL_INT_PLUS_NANO; case IIO_ANGL_VEL: - *val = st->fclkin; + *val = st->clkin_hz; *val2 = ad2s1210_velocity_scale[st->resolution]; return IIO_VAL_FRACTIONAL; default: @@ -549,8 +513,6 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, } } -static IIO_DEVICE_ATTR(fclkin, 0644, - ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0); static IIO_DEVICE_ATTR(fexcit, 0644, ad2s1210_show_fexcit, ad2s1210_store_fexcit, 0); static IIO_DEVICE_ATTR(control, 0644, @@ -599,7 +561,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = { }; static struct attribute *ad2s1210_attributes[] = { - &iio_dev_attr_fclkin.dev_attr.attr, &iio_dev_attr_fexcit.dev_attr.attr, &iio_dev_attr_control.dev_attr.attr, &iio_dev_attr_bits.dev_attr.attr, @@ -657,6 +618,24 @@ static const struct iio_info ad2s1210_info = { .attrs = &ad2s1210_attribute_group, }; +static int ad2s1210_setup_clocks(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + struct clk *clk; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); + + st->clkin_hz = clk_get_rate(clk); + if (st->clkin_hz < AD2S1210_MIN_CLKIN || st->clkin_hz > AD2S1210_MAX_CLKIN) + return dev_err_probe(dev, -EINVAL, + "clock frequency out of range: %lu\n", + st->clkin_hz); + + return 0; +} + static int ad2s1210_setup_gpios(struct ad2s1210_state *st) { struct spi_device *spi = st->sdev; @@ -695,6 +674,10 @@ static int ad2s1210_probe(struct spi_device *spi) st->resolution = 12; st->fexcit = AD2S1210_DEF_EXCIT; + ret = ad2s1210_setup_clocks(st); + if (ret < 0) + return ret; + ret = ad2s1210_setup_gpios(st); if (ret < 0) return ret; @@ -709,13 +692,7 @@ static int ad2s1210_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); indio_dev->name = spi_get_device_id(spi)->name; - ret = devm_iio_device_register(&spi->dev, indio_dev); - if (ret) - return ret; - - st->fclkin = spi->max_speed_hz; - - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ad2s1210_of_match[] = { -- cgit v1.2.3 From b3689e14415a874630f0894d8c3ac7ea01603d57 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:15 -0500 Subject: staging: iio: resolver: ad2s1210: use regmap for config registers This makes use of the regmap API to read and write the configuration registers. This simplifies code quite a bit and makes it safer (previously, it was easy to write a bad value to the config registers which causes the chip to lock up and need to be reset). This chip has multiple modes of operation. In normal mode, we do not use regmap since there is no addressing - data is just bitshifted out during the SPI read. In config mode, we use regmap since it requires writing the address (with read/write flag) before reading and writing. We don't use the lock provided by the regmap because we need to also synchronize with the normal mode SPI reads and with the various GPIOs. There is also a quirk when reading registers (other than the fault register). If the address/data bit is set in the value read, then it indicates there is a configuration parity error and the data is not valid. Previously, this was checked in a few places, but not consistently. Now, we always check it in the regmap read function. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-10-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 259 +++++++++++++++++++------------- 1 file changed, 155 insertions(+), 104 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index c8723b6f3a3b..0663a51d04ad 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -5,6 +5,8 @@ * Copyright (c) 2010-2010 Analog Devices Inc. * Copyright (c) 2023 BayLibre, SAS */ +#include +#include #include #include #include @@ -12,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,21 +25,17 @@ #define DRV_NAME "ad2s1210" -#define AD2S1210_DEF_CONTROL 0x7E - -#define AD2S1210_MSB_IS_HIGH 0x80 -#define AD2S1210_MSB_IS_LOW 0x7F -#define AD2S1210_PHASE_LOCK_RANGE_44 0x20 -#define AD2S1210_ENABLE_HYSTERESIS 0x10 -#define AD2S1210_SET_ENRES1 0x08 -#define AD2S1210_SET_ENRES0 0x04 -#define AD2S1210_SET_RES1 0x02 -#define AD2S1210_SET_RES0 0x01 - -#define AD2S1210_SET_RESOLUTION (AD2S1210_SET_RES1 | AD2S1210_SET_RES0) - -#define AD2S1210_REG_POSITION 0x80 -#define AD2S1210_REG_VELOCITY 0x82 +/* control register flags */ +#define AD2S1210_ADDRESS_DATA BIT(7) +#define AD2S1210_PHASE_LOCK_RANGE_44 BIT(5) +#define AD2S1210_ENABLE_HYSTERESIS BIT(4) +#define AD2S1210_SET_ENRES GENMASK(3, 2) +#define AD2S1210_SET_RES GENMASK(1, 0) + +#define AD2S1210_REG_POSITION_MSB 0x80 +#define AD2S1210_REG_POSITION_LSB 0x81 +#define AD2S1210_REG_VELOCITY_MSB 0x82 +#define AD2S1210_REG_VELOCITY_LSB 0x83 #define AD2S1210_REG_LOS_THRD 0x88 #define AD2S1210_REG_DOS_OVR_THRD 0x89 #define AD2S1210_REG_DOS_MIS_THRD 0x8A @@ -92,6 +91,8 @@ struct ad2s1210_state { struct mutex lock; struct spi_device *sdev; struct gpio_desc *gpios[5]; + /** Used to access config registers. */ + struct regmap *regmap; /** The external oscillator frequency in Hz. */ unsigned long clkin_hz; unsigned int fexcit; @@ -120,24 +121,51 @@ static inline void ad2s1210_set_mode(enum ad2s1210_mode mode, st->mode = mode; } -/* write 1 bytes (address or data) to the chip */ -static int ad2s1210_config_write(struct ad2s1210_state *st, u8 data) +/* + * Writes the given data to the given register address. + * + * If the mode is configurable, the device will first be placed in + * configuration mode. + */ +static int ad2s1210_regmap_reg_write(void *context, unsigned int reg, + unsigned int val) { - int ret; + struct ad2s1210_state *st = context; + struct spi_transfer xfers[] = { + { + .len = 1, + .rx_buf = &st->rx[0], + .tx_buf = &st->tx[0], + .cs_change = 1, + }, { + .len = 1, + .rx_buf = &st->rx[1], + .tx_buf = &st->tx[1], + }, + }; + + /* values can only be 7 bits, the MSB indicates an address */ + if (val & ~0x7F) + return -EINVAL; + + st->tx[0] = reg; + st->tx[1] = val; ad2s1210_set_mode(MOD_CONFIG, st); - st->tx[0] = data; - ret = spi_write(st->sdev, st->tx, 1); - if (ret < 0) - return ret; - return 0; + return spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); } -/* read value from one of the registers */ -static int ad2s1210_config_read(struct ad2s1210_state *st, - unsigned char address) +/* + * Reads value from one of the registers. + * + * If the mode is configurable, the device will first be placed in + * configuration mode. + */ +static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, + unsigned int *val) { + struct ad2s1210_state *st = context; struct spi_transfer xfers[] = { { .len = 1, @@ -150,22 +178,36 @@ static int ad2s1210_config_read(struct ad2s1210_state *st, .tx_buf = &st->tx[1], }, }; - int ret = 0; + int ret; ad2s1210_set_mode(MOD_CONFIG, st); - st->tx[0] = address | AD2S1210_MSB_IS_HIGH; + st->tx[0] = reg; + /* + * Must be valid register address here otherwise this could write data. + * It doesn't matter which one. + */ st->tx[1] = AD2S1210_REG_FAULT; - ret = spi_sync_transfer(st->sdev, xfers, 2); + + ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); if (ret < 0) return ret; - return st->rx[1]; + /* + * If the D7 bit is set on any read/write register, it indicates a + * parity error. The fault register is read-only and the D7 bit means + * something else there. + */ + if (reg != AD2S1210_REG_FAULT && st->rx[1] & AD2S1210_ADDRESS_DATA) + return -EBADMSG; + + *val = st->rx[1]; + + return 0; } static inline int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) { - int ret; unsigned char fcw; fcw = (unsigned char)(st->fexcit * (1 << 15) / st->clkin_hz); @@ -174,11 +216,7 @@ int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) return -ERANGE; } - ret = ad2s1210_config_write(st, AD2S1210_REG_EXCIT_FREQ); - if (ret < 0) - return ret; - - return ad2s1210_config_write(st, fcw); + return regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw); } static const int ad2s1210_res_pins[4][2] = { @@ -195,13 +233,7 @@ static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st) static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) { - int ret; - - ret = ad2s1210_config_write(st, AD2S1210_REG_SOFT_RESET); - if (ret < 0) - return ret; - - return ad2s1210_config_write(st, 0x0); + return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); } static ssize_t ad2s1210_show_fexcit(struct device *dev, @@ -246,12 +278,13 @@ static ssize_t ad2s1210_show_control(struct device *dev, char *buf) { struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int value; int ret; mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); + ret = regmap_read(st->regmap, AD2S1210_REG_CONTROL, &value); mutex_unlock(&st->lock); - return ret < 0 ? ret : sprintf(buf, "0x%x\n", ret); + return ret < 0 ? ret : sprintf(buf, "0x%x\n", value); } static ssize_t ad2s1210_store_control(struct device *dev, @@ -268,25 +301,13 @@ static ssize_t ad2s1210_store_control(struct device *dev, return -EINVAL; mutex_lock(&st->lock); - ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - data = udata & AD2S1210_MSB_IS_LOW; - ret = ad2s1210_config_write(st, data); + data = udata & ~AD2S1210_ADDRESS_DATA; + ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); if (ret < 0) goto error_ret; - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - if (ret & AD2S1210_MSB_IS_HIGH) { - ret = -EIO; - dev_err(dev, - "ad2s1210: write control register fail\n"); - goto error_ret; - } st->resolution = - ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; + ad2s1210_resolution_value[data & AD2S1210_SET_RES]; ad2s1210_set_resolution_pin(st); ret = len; st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS); @@ -319,30 +340,17 @@ static ssize_t ad2s1210_store_resolution(struct device *dev, dev_err(dev, "ad2s1210: resolution out of range\n"); return -EINVAL; } + + data = (udata - 10) >> 1; + mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - data = ret; - data &= ~AD2S1210_SET_RESOLUTION; - data |= (udata - 10) >> 1; - ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); + ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_SET_RES, data); if (ret < 0) goto error_ret; - data = ret; - if (data & AD2S1210_MSB_IS_HIGH) { - ret = -EIO; - dev_err(dev, "ad2s1210: setting resolution fail\n"); - goto error_ret; - } + st->resolution = - ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; + ad2s1210_resolution_value[data & AD2S1210_SET_RES]; ad2s1210_set_resolution_pin(st); ret = len; error_ret: @@ -355,13 +363,14 @@ static ssize_t ad2s1210_show_fault(struct device *dev, struct device_attribute *attr, char *buf) { struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int value; int ret; mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &value); mutex_unlock(&st->lock); - return ret < 0 ? ret : sprintf(buf, "0x%02x\n", ret); + return ret < 0 ? ret : sprintf(buf, "0x%02x\n", value); } static ssize_t ad2s1210_clear_fault(struct device *dev, @@ -370,6 +379,7 @@ static ssize_t ad2s1210_clear_fault(struct device *dev, size_t len) { struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int value; int ret; mutex_lock(&st->lock); @@ -377,7 +387,7 @@ static ssize_t ad2s1210_clear_fault(struct device *dev, /* delay (2 * tck + 20) nano seconds */ udelay(1); gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1); - ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &value); if (ret < 0) goto error_ret; gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); @@ -394,13 +404,14 @@ static ssize_t ad2s1210_show_reg(struct device *dev, { struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + unsigned int value; int ret; mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, iattr->address); + ret = regmap_read(st->regmap, iattr->address, &value); mutex_unlock(&st->lock); - return ret < 0 ? ret : sprintf(buf, "%d\n", ret); + return ret < 0 ? ret : sprintf(buf, "%d\n", value); } static ssize_t ad2s1210_store_reg(struct device *dev, @@ -415,12 +426,9 @@ static ssize_t ad2s1210_store_reg(struct device *dev, ret = kstrtou8(buf, 10, &data); if (ret) return -EINVAL; + mutex_lock(&st->lock); - ret = ad2s1210_config_write(st, iattr->address); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); -error_ret: + ret = regmap_write(st->regmap, iattr->address, data); mutex_unlock(&st->lock); return ret < 0 ? ret : len; } @@ -587,22 +595,15 @@ static int ad2s1210_initial(struct ad2s1210_state *st) mutex_lock(&st->lock); ad2s1210_set_resolution_pin(st); - ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - data = AD2S1210_DEF_CONTROL & ~(AD2S1210_SET_RESOLUTION); - data |= (st->resolution - 10) >> 1; - ret = ad2s1210_config_write(st, data); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; + /* Use default config register value plus resolution from devicetree. */ + data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1); + data |= FIELD_PREP(AD2S1210_ENABLE_HYSTERESIS, 1); + data |= FIELD_PREP(AD2S1210_SET_ENRES, 0x3); + data |= FIELD_PREP(AD2S1210_SET_RES, (st->resolution - 10) >> 1); - if (ret & AD2S1210_MSB_IS_HIGH) { - ret = -EIO; + ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); + if (ret < 0) goto error_ret; - } ret = ad2s1210_update_frequency_control_word(st); if (ret < 0) @@ -656,6 +657,52 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st) return 0; } +static const struct regmap_range ad2s1210_regmap_readable_ranges[] = { + regmap_reg_range(AD2S1210_REG_POSITION_MSB, AD2S1210_REG_VELOCITY_LSB), + regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), + regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), + regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), +}; + +static const struct regmap_access_table ad2s1210_regmap_rd_table = { + .yes_ranges = ad2s1210_regmap_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_readable_ranges), +}; + +static const struct regmap_range ad2s1210_regmap_writeable_ranges[] = { + regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), + regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), + regmap_reg_range(AD2S1210_REG_SOFT_RESET, AD2S1210_REG_SOFT_RESET), + regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), +}; + +static const struct regmap_access_table ad2s1210_regmap_wr_table = { + .yes_ranges = ad2s1210_regmap_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_writeable_ranges), +}; + +static int ad2s1210_setup_regmap(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .disable_locking = true, + .reg_read = ad2s1210_regmap_reg_read, + .reg_write = ad2s1210_regmap_reg_write, + .rd_table = &ad2s1210_regmap_rd_table, + .wr_table = &ad2s1210_regmap_wr_table, + .can_sleep = true, + }; + + st->regmap = devm_regmap_init(dev, NULL, st, &config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "failed to allocate register map\n"); + + return 0; +} + static int ad2s1210_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -682,6 +729,10 @@ static int ad2s1210_probe(struct spi_device *spi) if (ret < 0) return ret; + ret = ad2s1210_setup_regmap(st); + if (ret < 0) + return ret; + ret = ad2s1210_initial(st); if (ret < 0) return ret; -- cgit v1.2.3 From 569dc8054e702f504bd8d07e2c4e315b2097d746 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:16 -0500 Subject: staging: iio: resolver: ad2s1210: add debugfs reg access This add an implementation of debugfs_reg_access for the AD2S1210 driver. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-11-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 0663a51d04ad..31415fbb6384 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -614,9 +614,29 @@ error_ret: return ret; } +static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + if (readval) + ret = regmap_read(st->regmap, reg, readval); + else + ret = regmap_write(st->regmap, reg, writeval); + + mutex_unlock(&st->lock); + + return ret; +} + static const struct iio_info ad2s1210_info = { .read_raw = ad2s1210_read_raw, .attrs = &ad2s1210_attribute_group, + .debugfs_reg_access = &ad2s1210_debugfs_reg_access, }; static int ad2s1210_setup_clocks(struct ad2s1210_state *st) -- cgit v1.2.3 From 8ab5bf1a2264e380635913bc9ac72d1ab55088c0 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:17 -0500 Subject: staging: iio: resolver: ad2s1210: remove config attribute This removes the config register sysfs attribute. Writing to the config register directly can be dangerous and userspace should not need to have to know the register layout. This register can still be accessed though debugfs if needed. We can add new attributes to set specific flags in the config register in the future if needed (e.g. `enable_hysterisis` and `phase_lock_range`). Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-12-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 47 --------------------------------- 1 file changed, 47 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 31415fbb6384..2b9377447f6a 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -273,50 +273,6 @@ error_ret: return ret < 0 ? ret : len; } -static ssize_t ad2s1210_show_control(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int value; - int ret; - - mutex_lock(&st->lock); - ret = regmap_read(st->regmap, AD2S1210_REG_CONTROL, &value); - mutex_unlock(&st->lock); - return ret < 0 ? ret : sprintf(buf, "0x%x\n", value); -} - -static ssize_t ad2s1210_store_control(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned char udata; - unsigned char data; - int ret; - - ret = kstrtou8(buf, 16, &udata); - if (ret) - return -EINVAL; - - mutex_lock(&st->lock); - data = udata & ~AD2S1210_ADDRESS_DATA; - ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); - if (ret < 0) - goto error_ret; - - st->resolution = - ad2s1210_resolution_value[data & AD2S1210_SET_RES]; - ad2s1210_set_resolution_pin(st); - ret = len; - st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS); - -error_ret: - mutex_unlock(&st->lock); - return ret; -} - static ssize_t ad2s1210_show_resolution(struct device *dev, struct device_attribute *attr, char *buf) @@ -523,8 +479,6 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, static IIO_DEVICE_ATTR(fexcit, 0644, ad2s1210_show_fexcit, ad2s1210_store_fexcit, 0); -static IIO_DEVICE_ATTR(control, 0644, - ad2s1210_show_control, ad2s1210_store_control, 0); static IIO_DEVICE_ATTR(bits, 0644, ad2s1210_show_resolution, ad2s1210_store_resolution, 0); static IIO_DEVICE_ATTR(fault, 0644, @@ -570,7 +524,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = { static struct attribute *ad2s1210_attributes[] = { &iio_dev_attr_fexcit.dev_attr.attr, - &iio_dev_attr_control.dev_attr.attr, &iio_dev_attr_bits.dev_attr.attr, &iio_dev_attr_fault.dev_attr.attr, &iio_dev_attr_los_thrd.dev_attr.attr, -- cgit v1.2.3 From 168115f989311867b48292f4f056eb2f96cef67b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:18 -0500 Subject: staging: iio: resolver: ad2s1210: rework gpios - Remove "adi," prefix from gpio names. - Sample gpio is now expected to be active low. - Convert A0 and A1 gpios to "mode-gpios" gpio array. - Convert RES0 and RES1 gpios to "resolution-gpios" gpio array. - Remove extraneous lookup tables. - Remove unused mode field from state struct. - Swap argument order of ad2s1210_set_mode() while we are touching this. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-13-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 164 +++++++++++++++++--------------- 1 file changed, 85 insertions(+), 79 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 2b9377447f6a..5744f1673cba 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -58,39 +58,21 @@ #define AD2S1210_DEF_EXCIT 10000 enum ad2s1210_mode { - MOD_POS = 0, - MOD_VEL, - MOD_CONFIG, - MOD_RESERVED, + MOD_POS = 0b00, + MOD_VEL = 0b01, + MOD_RESERVED = 0b10, + MOD_CONFIG = 0b11, }; -enum ad2s1210_gpios { - AD2S1210_SAMPLE, - AD2S1210_A0, - AD2S1210_A1, - AD2S1210_RES0, - AD2S1210_RES1, -}; - -struct ad2s1210_gpio { - const char *name; - unsigned long flags; -}; - -static const struct ad2s1210_gpio gpios[] = { - [AD2S1210_SAMPLE] = { .name = "adi,sample", .flags = GPIOD_OUT_LOW }, - [AD2S1210_A0] = { .name = "adi,a0", .flags = GPIOD_OUT_LOW }, - [AD2S1210_A1] = { .name = "adi,a1", .flags = GPIOD_OUT_LOW }, - [AD2S1210_RES0] = { .name = "adi,res0", .flags = GPIOD_OUT_LOW }, - [AD2S1210_RES1] = { .name = "adi,res1", .flags = GPIOD_OUT_LOW }, -}; - -static const unsigned int ad2s1210_resolution_value[] = { 10, 12, 14, 16 }; - struct ad2s1210_state { struct mutex lock; struct spi_device *sdev; - struct gpio_desc *gpios[5]; + /** GPIO pin connected to SAMPLE line. */ + struct gpio_desc *sample_gpio; + /** GPIO pins connected to A0 and A1 lines. */ + struct gpio_descs *mode_gpios; + /** GPIO pins connected to RES0 and RES1 lines. */ + struct gpio_descs *resolution_gpios; /** Used to access config registers. */ struct regmap *regmap; /** The external oscillator frequency in Hz. */ @@ -98,7 +80,6 @@ struct ad2s1210_state { unsigned int fexcit; bool hysteresis; u8 resolution; - enum ad2s1210_mode mode; /** For reading raw sample value via SPI. */ __be16 sample __aligned(IIO_DMA_MINALIGN); /** SPI transmit buffer. */ @@ -107,18 +88,15 @@ struct ad2s1210_state { u8 tx[2]; }; -static const int ad2s1210_mode_vals[4][2] = { - [MOD_POS] = { 0, 0 }, - [MOD_VEL] = { 0, 1 }, - [MOD_CONFIG] = { 1, 1 }, -}; - -static inline void ad2s1210_set_mode(enum ad2s1210_mode mode, - struct ad2s1210_state *st) +static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode) { - gpiod_set_value(st->gpios[AD2S1210_A0], ad2s1210_mode_vals[mode][0]); - gpiod_set_value(st->gpios[AD2S1210_A1], ad2s1210_mode_vals[mode][1]); - st->mode = mode; + struct gpio_descs *gpios = st->mode_gpios; + DECLARE_BITMAP(bitmap, 2); + + bitmap[0] = mode; + + return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, + bitmap); } /* @@ -143,6 +121,7 @@ static int ad2s1210_regmap_reg_write(void *context, unsigned int reg, .tx_buf = &st->tx[1], }, }; + int ret; /* values can only be 7 bits, the MSB indicates an address */ if (val & ~0x7F) @@ -151,7 +130,9 @@ static int ad2s1210_regmap_reg_write(void *context, unsigned int reg, st->tx[0] = reg; st->tx[1] = val; - ad2s1210_set_mode(MOD_CONFIG, st); + ret = ad2s1210_set_mode(st, MOD_CONFIG); + if (ret < 0) + return ret; return spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); } @@ -180,7 +161,10 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, }; int ret; - ad2s1210_set_mode(MOD_CONFIG, st); + ret = ad2s1210_set_mode(st, MOD_CONFIG); + if (ret < 0) + return ret; + st->tx[0] = reg; /* * Must be valid register address here otherwise this could write data. @@ -219,16 +203,16 @@ int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) return regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw); } -static const int ad2s1210_res_pins[4][2] = { - { 0, 0 }, {0, 1}, {1, 0}, {1, 1} -}; - -static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st) +static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st, + u8 resolution) { - gpiod_set_value(st->gpios[AD2S1210_RES0], - ad2s1210_res_pins[(st->resolution - 10) / 2][0]); - gpiod_set_value(st->gpios[AD2S1210_RES1], - ad2s1210_res_pins[(st->resolution - 10) / 2][1]); + struct gpio_descs *gpios = st->resolution_gpios; + DECLARE_BITMAP(bitmap, 2); + + bitmap[0] = (resolution - 10) >> 1; + + return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, + bitmap); } static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) @@ -305,10 +289,13 @@ static ssize_t ad2s1210_store_resolution(struct device *dev, if (ret < 0) goto error_ret; - st->resolution = - ad2s1210_resolution_value[data & AD2S1210_SET_RES]; - ad2s1210_set_resolution_pin(st); + ret = ad2s1210_set_resolution_gpios(st, udata); + if (ret < 0) + goto error_ret; + + st->resolution = udata; ret = len; + error_ret: mutex_unlock(&st->lock); return ret; @@ -339,15 +326,19 @@ static ssize_t ad2s1210_clear_fault(struct device *dev, int ret; mutex_lock(&st->lock); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); + + gpiod_set_value(st->sample_gpio, 1); /* delay (2 * tck + 20) nano seconds */ udelay(1); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1); + gpiod_set_value(st->sample_gpio, 0); + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &value); if (ret < 0) goto error_ret; - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1); + + gpiod_set_value(st->sample_gpio, 1); + gpiod_set_value(st->sample_gpio, 0); + error_ret: mutex_unlock(&st->lock); @@ -393,19 +384,19 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st, struct iio_chan_spec const *chan, int *val) { - int ret = 0; + int ret; mutex_lock(&st->lock); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); + gpiod_set_value(st->sample_gpio, 1); /* delay (6 * tck + 20) nano seconds */ udelay(1); switch (chan->type) { case IIO_ANGL: - ad2s1210_set_mode(MOD_POS, st); + ret = ad2s1210_set_mode(st, MOD_POS); break; case IIO_ANGL_VEL: - ad2s1210_set_mode(MOD_VEL, st); + ret = ad2s1210_set_mode(st, MOD_VEL); break; default: ret = -EINVAL; @@ -432,7 +423,7 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st, } error_ret: - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1); + gpiod_set_value(st->sample_gpio, 0); /* delay (2 * tck + 20) nano seconds */ udelay(1); mutex_unlock(&st->lock); @@ -546,7 +537,9 @@ static int ad2s1210_initial(struct ad2s1210_state *st) int ret; mutex_lock(&st->lock); - ad2s1210_set_resolution_pin(st); + ret = ad2s1210_set_resolution_gpios(st, st->resolution); + if (ret < 0) + goto error_ret; /* Use default config register value plus resolution from devicetree. */ data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1); @@ -612,20 +605,34 @@ static int ad2s1210_setup_clocks(struct ad2s1210_state *st) static int ad2s1210_setup_gpios(struct ad2s1210_state *st) { - struct spi_device *spi = st->sdev; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(gpios); i++) { - st->gpios[i] = devm_gpiod_get(&spi->dev, gpios[i].name, - gpios[i].flags); - if (IS_ERR(st->gpios[i])) { - ret = PTR_ERR(st->gpios[i]); - dev_err(&spi->dev, - "ad2s1210: failed to request %s GPIO: %d\n", - gpios[i].name, ret); - return ret; - } - } + struct device *dev = &st->sdev->dev; + + /* should not be sampling on startup */ + st->sample_gpio = devm_gpiod_get(dev, "sample", GPIOD_OUT_LOW); + if (IS_ERR(st->sample_gpio)) + return dev_err_probe(dev, PTR_ERR(st->sample_gpio), + "failed to request sample GPIO\n"); + + /* both pins high means that we start in config mode */ + st->mode_gpios = devm_gpiod_get_array(dev, "mode", GPIOD_OUT_HIGH); + if (IS_ERR(st->mode_gpios)) + return dev_err_probe(dev, PTR_ERR(st->mode_gpios), + "failed to request mode GPIOs\n"); + + if (st->mode_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, + "requires exactly 2 mode-gpios\n"); + + /* both pins high means that we start with 16-bit resolution */ + st->resolution_gpios = devm_gpiod_get_array(dev, "resolution", + GPIOD_OUT_HIGH); + if (IS_ERR(st->resolution_gpios)) + return dev_err_probe(dev, PTR_ERR(st->resolution_gpios), + "failed to request resolution GPIOs\n"); + + if (st->resolution_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, + "requires exactly 2 resolution-gpios\n"); return 0; } @@ -690,7 +697,6 @@ static int ad2s1210_probe(struct spi_device *spi) mutex_init(&st->lock); st->sdev = spi; st->hysteresis = true; - st->mode = MOD_CONFIG; st->resolution = 12; st->fexcit = AD2S1210_DEF_EXCIT; -- cgit v1.2.3 From de69623c7cc7b960ee5bec134707bf6567199fc9 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:20 -0500 Subject: staging: iio: resolver: ad2s1210: refactor setting excitation frequency This combines the ad2s1210_update_frequency_control_word() and ad2s1210_soft_reset() functions into a single function since they both have to be called together. (The software reset does not reset any configuration registers, it only updates the excitation output and resets the tracking loop.) Also clean up a few things while touching this: - move AD2S1210_DEF_EXCIT macro with similar macros - remove unnecessary dev_err() calls Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-15-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 66 +++++++++++++++++---------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 5744f1673cba..2b728863a3ff 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -51,12 +51,11 @@ #define AD2S1210_MIN_CLKIN 6144000 #define AD2S1210_MAX_CLKIN 10240000 #define AD2S1210_MIN_EXCIT 2000 +#define AD2S1210_DEF_EXCIT 10000 #define AD2S1210_MAX_EXCIT 20000 #define AD2S1210_MIN_FCW 0x4 #define AD2S1210_MAX_FCW 0x50 -#define AD2S1210_DEF_EXCIT 10000 - enum ad2s1210_mode { MOD_POS = 0b00, MOD_VEL = 0b01, @@ -189,18 +188,32 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, return 0; } -static inline -int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) +/* + * Sets the excitation frequency and performs software reset. + * + * Must be called with lock held. + */ +static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, + u16 fexcit) { - unsigned char fcw; + int ret; + u8 fcw; - fcw = (unsigned char)(st->fexcit * (1 << 15) / st->clkin_hz); - if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) { - dev_err(&st->sdev->dev, "ad2s1210: FCW out of range\n"); + fcw = fexcit * (1 << 15) / st->clkin_hz; + if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) return -ERANGE; - } - return regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw); + ret = regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw); + if (ret < 0) + return ret; + + st->fexcit = fexcit; + + /* + * Software reset reinitializes the excitation frequency output. + * It does not reset any of the configuration registers. + */ + return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); } static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st, @@ -215,11 +228,6 @@ static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st, bitmap); } -static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) -{ - return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); -} - static ssize_t ad2s1210_show_fexcit(struct device *dev, struct device_attribute *attr, char *buf) @@ -234,27 +242,24 @@ static ssize_t ad2s1210_store_fexcit(struct device *dev, const char *buf, size_t len) { struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int fexcit; + u16 fexcit; int ret; - ret = kstrtouint(buf, 10, &fexcit); - if (ret < 0) - return ret; - if (fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) { - dev_err(dev, - "ad2s1210: excitation frequency out of range\n"); + ret = kstrtou16(buf, 10, &fexcit); + if (ret < 0 || fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) return -EINVAL; - } + mutex_lock(&st->lock); - st->fexcit = fexcit; - ret = ad2s1210_update_frequency_control_word(st); + ret = ad2s1210_reinit_excitation_frequency(st, fexcit); if (ret < 0) goto error_ret; - ret = ad2s1210_soft_reset(st); + + ret = len; + error_ret: mutex_unlock(&st->lock); - return ret < 0 ? ret : len; + return ret; } static ssize_t ad2s1210_show_resolution(struct device *dev, @@ -551,10 +556,8 @@ static int ad2s1210_initial(struct ad2s1210_state *st) if (ret < 0) goto error_ret; - ret = ad2s1210_update_frequency_control_word(st); - if (ret < 0) - goto error_ret; - ret = ad2s1210_soft_reset(st); + ret = ad2s1210_reinit_excitation_frequency(st, AD2S1210_DEF_EXCIT); + error_ret: mutex_unlock(&st->lock); return ret; @@ -698,7 +701,6 @@ static int ad2s1210_probe(struct spi_device *spi) st->sdev = spi; st->hysteresis = true; st->resolution = 12; - st->fexcit = AD2S1210_DEF_EXCIT; ret = ad2s1210_setup_clocks(st); if (ret < 0) -- cgit v1.2.3 From 500d7640f63d557c397f74b7d5bf8c81377b50b3 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:21 -0500 Subject: staging: iio: resolver: ad2s1210: read excitation frequency from control register This modifies the ad2s1210_show_fexcit() function to read the excitation frequency from the control register. This way we don't have to keep track of the value and don't risk returning a stale value. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-16-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 2b728863a3ff..67d8af0dd7ae 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -76,7 +76,6 @@ struct ad2s1210_state { struct regmap *regmap; /** The external oscillator frequency in Hz. */ unsigned long clkin_hz; - unsigned int fexcit; bool hysteresis; u8 resolution; /** For reading raw sample value via SPI. */ @@ -207,8 +206,6 @@ static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, if (ret < 0) return ret; - st->fexcit = fexcit; - /* * Software reset reinitializes the excitation frequency output. * It does not reset any of the configuration registers. @@ -233,8 +230,22 @@ static ssize_t ad2s1210_show_fexcit(struct device *dev, char *buf) { struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int value; + u16 fexcit; + int ret; - return sprintf(buf, "%u\n", st->fexcit); + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, &value); + if (ret < 0) + goto error_ret; + + fexcit = value * st->clkin_hz / (1 << 15); + + ret = sprintf(buf, "%u\n", fexcit); + +error_ret: + mutex_unlock(&st->lock); + return ret; } static ssize_t ad2s1210_store_fexcit(struct device *dev, -- cgit v1.2.3 From d2d1ecc67c48caf479319c4e1ab3d7896f826429 Mon Sep 17 00:00:00 2001 From: Ramona Gradinariu Date: Tue, 26 Sep 2023 11:57:21 +0300 Subject: iio: imu: adis16475: Add buffer padding after temp channel The temperature channel has 16-bit storage size. We need to perform the padding to have the buffer elements naturally aligned in case the temperature channel is enabled and there are any 32-bit storage size channels enabled which have a scan index higher than the temperature channel scan index. Fixes: 8f6bc87d67c0 ("iio: imu: adis16475.c: Add delta angle and delta velocity channels") Signed-off-by: Ramona Gradinariu Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230926085721.645687-2-ramona.gradinariu@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16475.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 00e4e09cdafb..9af07fec0d89 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -1197,6 +1197,16 @@ static irqreturn_t adis16475_trigger_handler(int irq, void *p) switch (bit) { case ADIS16475_SCAN_TEMP: st->data[i++] = buffer[offset]; + /* + * The temperature channel has 16-bit storage size. + * We need to perform the padding to have the buffer + * elements naturally aligned in case there are any + * 32-bit storage size channels enabled which have a + * scan index higher than the temperature channel scan + * index. + */ + if (*indio_dev->active_scan_mask & GENMASK(ADIS16475_SCAN_DELTVEL_Z, ADIS16475_SCAN_DELTANG_X)) + st->data[i++] = 0; break; case ADIS16475_SCAN_DELTANG_X ... ADIS16475_SCAN_DELTVEL_Z: buff_offset = ADIS16475_SCAN_DELTANG_X; -- cgit v1.2.3 From 67142d6f1e2fd97c035a42612b5b28809dc5f6ea Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 26 Sep 2023 13:01:49 +0300 Subject: MAINTAINERS: fix Analog Devices website link The http://ez.analog.com/community/linux-device-drivers link is broken. Update website link to the new available one: https://ez.analog.com/linux-software-drivers Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20230926100149.4611-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 337a4244ee30..0782484cceb8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1119,7 +1119,7 @@ ANALOG DEVICES INC AD4130 DRIVER M: Cosmin Tanislav L: linux-iio@vger.kernel.org S: Supported -W: http://ez.analog.com/community/linux-device-drivers +W: https://ez.analog.com/linux-software-drivers F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c @@ -1152,7 +1152,7 @@ ANALOG DEVICES INC AD74115 DRIVER M: Cosmin Tanislav L: linux-iio@vger.kernel.org S: Supported -W: http://ez.analog.com/community/linux-device-drivers +W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml F: drivers/iio/addac/ad74115.c @@ -12820,7 +12820,7 @@ MAX31827 TEMPERATURE SWITCH DRIVER M: Daniel Matyas L: linux-hwmon@vger.kernel.org S: Supported -W: http://ez.analog.com/community/linux-device-drivers +W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/hwmon/adi,max31827.yaml F: Documentation/hwmon/max31827.rst F: drivers/hwmon/max31827.c -- cgit v1.2.3 From 14114c8a747e12c169550d1806f8f87345480448 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Wed, 27 Sep 2023 19:32:43 +0200 Subject: dt-bindings: iio: imu: mpu6050: Add level shifter Add a level shifter flag as found in ancient platform data struct: level_shifter: 0: VLogic, 1: VDD Signed-off-by: Andreas Kemnade Acked-by: Rob Herring Acked-by: Jean-Baptiste Maneyrol Link: https://lore.kernel.org/r/20230927173245.2151083-2-andreas@kemnade.info Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml index 1db6952ddca5..297b8a1a7ffb 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml @@ -48,6 +48,11 @@ properties: mount-matrix: true + invensense,level-shifter: + type: boolean + description: | + From ancient platform data struct: false: VLogic, true: VDD + i2c-gate: $ref: /schemas/i2c/i2c-controller.yaml unevaluatedProperties: false -- cgit v1.2.3 From c48fb9f956081d663bc8cfe3df32c59d2e869bea Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Wed, 27 Sep 2023 19:32:44 +0200 Subject: iio: imu: mpu6050: add level shifter flag Some boards fail in magnetometer probe if level shifter flag is not set, definition was found in the vendor Linux kernel v3.0. Signed-off-by: Andreas Kemnade Reviewed-by: Andy Shevchenko Acked-by: Jean-Baptiste Maneyrol Link: https://lore.kernel.org/r/20230927173245.2151083-3-andreas@kemnade.info Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c | 13 +++++++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 3 +++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 1 + 3 files changed, 17 insertions(+) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c index 7327e5723f96..8a7f2911905a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c @@ -71,6 +71,19 @@ int inv_mpu_aux_init(const struct inv_mpu6050_state *st) unsigned int val; int ret; + /* + * Code based on the vendor Linux kernel v3.0, + * the exact meaning is unknown. + */ + if (st->chip_type == INV_MPU9150) { + unsigned int mask = BIT(7); + + val = st->level_shifter ? mask : 0; + ret = regmap_update_bits(st->map, 0x1, mask, val); + if (ret) + return ret; + } + /* configure i2c master */ val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ | INV_MPU6050_BIT_WAIT_FOR_ES; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 29f906c884bd..3fbeef1a7018 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -1495,6 +1496,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, st->irq = irq; st->map = regmap; + st->level_shifter = device_property_read_bool(dev, + "invensense,level-shifter"); pdata = dev_get_platdata(dev); if (!pdata) { result = iio_read_mount_matrix(dev, &st->orientation); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 95f548235de7..5950e2419ebb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -203,6 +203,7 @@ struct inv_mpu6050_state { s32 magn_raw_to_gauss[3]; struct iio_mount_matrix magn_orient; unsigned int suspended_sensors; + bool level_shifter; u8 *data; }; -- cgit v1.2.3 From 8a76356e7db02ec7b1913db06605e70294d94672 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 27 Sep 2023 11:27:41 +0300 Subject: iio: improve doc for available_scan_mask The available_scan_mask is an array of bitmaps representing the channels which can be simultaneously enabled by the driver. In many cases, the hardware can offer more channels than what the user is interested in obtaining. In such cases, it may be preferred that only a subset of channels are enabled, and the driver reads only a subset of the channels from the hardware. Some devices can't support all channel combinations. For example, the BM1390 pressure sensor must always read the pressure data in order to acknowledge the watermark IRQ, while reading temperature can be omitted. So, the available scan masks would be 'pressure and temperature' and 'pressure only'. When IIO searches for the scan mask it asks the driver to use, it will pick the first suitable one from the 'available_scan_mask' array. Hence, ordering the masks in the array makes a difference. We should 'prefer' reading just the pressure from the hardware (as it is a cheaper operation than reading both pressure and temperature) over reading both pressure and temperature. Hence, we should set the 'only pressure' as the first scan mask in available_scan_mask array. If we set the 'pressure and temperature' as first in the array, then the 'only temperature' will never get used as 'pressure and temperature' can always serve the user's needs. Add (minimal) kerneldoc to the 'available_scan_mask' to hint the user that the ordering of masks matters. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/4e43bf0186df5c8a56b470318b4827605f9cad6c.1695727471.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 202e55b0a28b..7bfa1b9bc8a2 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -556,7 +556,9 @@ struct iio_buffer_setup_ops { * and owner * @buffer: [DRIVER] any buffer present * @scan_bytes: [INTERN] num bytes captured to be fed to buffer demux - * @available_scan_masks: [DRIVER] optional array of allowed bitmasks + * @available_scan_masks: [DRIVER] optional array of allowed bitmasks. Sort the + * array in order of preference, the most preferred + * masks first. * @masklength: [INTERN] the length of the mask established from * channels * @active_scan_mask: [INTERN] union of all scan masks requested by buffers -- cgit v1.2.3 From 62593189b66ac6b5b69d2cf5b90728cc65a8fd68 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 27 Sep 2023 11:27:57 +0300 Subject: dt-bindings: Add ROHM BM1390 pressure sensor BM1390GLV-Z is a pressure sensor which performs internal temperature compensation for the MEMS. Pressure range is from 300 hPa to 1300 hPa and sample averaging and IIR filtering is built in sensor. Temperature measurement is also supported. Add dt-bindings for the sensor. Signed-off-by: Matti Vaittinen Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/08796e6b28d2c67933b1a1a5eca0f43da322a597.1695727471.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/pressure/rohm,bm1390.yaml | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/pressure/rohm,bm1390.yaml diff --git a/Documentation/devicetree/bindings/iio/pressure/rohm,bm1390.yaml b/Documentation/devicetree/bindings/iio/pressure/rohm,bm1390.yaml new file mode 100644 index 000000000000..7c4ca6322bf8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/pressure/rohm,bm1390.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/pressure/rohm,bm1390.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BM1390 pressure sensor + +maintainers: + - Matti Vaittinen + +description: + BM1390GLV-Z is a pressure sensor which performs internal temperature + compensation for the MEMS. Pressure range is from 300 hPa to 1300 hPa + and sample averaging and IIR filtering is built in. Temperature + measurement is also supported. + +properties: + compatible: + const: rohm,bm1390glv-z + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + pressure-sensor@5d { + compatible = "rohm,bm1390glv-z"; + reg = <0x5d>; + + interrupt-parent = <&gpio1>; + interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vdd>; + }; + }; -- cgit v1.2.3 From 81ca5979b6ed9a2b49cf9e253a494e0777b0edf9 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 27 Sep 2023 11:28:13 +0300 Subject: iio: pressure: Support ROHM BU1390 Support for the ROHM BM1390 pressure sensor. The BM1390GLV-Z can measure pressures ranging from 300 hPa to 1300 hPa with configurable measurement averaging and internal FIFO. The sensor does also provide temperature measurements. Sensor does also contain IIR filter implemented in HW. The data-sheet says the IIR filter can be configured to be "weak", "middle" or "strong". Some RMS noise figures are provided in data sheet but no accurate maths for the filter configurations is provided. Hence, the IIR filter configuration is not supported by this driver and the filter is configured to the "middle" setting (at least not for now). The FIFO measurement mode is only measuring the pressure and not the temperature. The driver measures temperature when FIFO is flushed and simply uses the same measured temperature value to all reported temperatures. This should not be a problem when temperature is not changing very rapidly (several degrees C / second) but allows users to get the temperature measurements from sensor without any additional logic. This driver allows the sensor to be used in two muitually exclusive ways, 1. With trigger (data-ready IRQ). In this case the FIFO is not used as we get data ready for each collected sample. Instead, for each data-ready IRQ we read the sample from sensor and push it to the IIO buffer. 2. With hardware FIFO and watermark IRQ. In this case the data-ready is not used but we enable watermark IRQ. At each watermark IRQ we go and read all samples in FIFO and push them to the IIO buffer. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/4f23211e3cf248f9f48ddb2a752387bb00e9c2c4.1695727471.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 9 + drivers/iio/pressure/Makefile | 1 + drivers/iio/pressure/rohm-bm1390.c | 934 +++++++++++++++++++++++++++++++++++++ 3 files changed, 944 insertions(+) create mode 100644 drivers/iio/pressure/rohm-bm1390.c diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 7b4c2af32852..95efa32e4289 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -16,6 +16,15 @@ config ABP060MG To compile this driver as a module, choose M here: the module will be called abp060mg. +config ROHM_BM1390 + tristate "ROHM BM1390GLV-Z pressure sensor driver" + depends on I2C + help + Support for the ROHM BM1390 pressure sensor. The BM1390GLV-Z + can measure pressures ranging from 300 hPa to 1300 hPa with + configurable measurement averaging and internal FIFO. The + sensor does also provide temperature measurements. + config BMP280 tristate "Bosch Sensortec BMP180/BMP280/BMP380/BMP580 pressure sensor driver" depends on (I2C || SPI_MASTER) diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index c90f77210e94..436aec7e65f3 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ABP060MG) += abp060mg.o +obj-$(CONFIG_ROHM_BM1390) += rohm-bm1390.o obj-$(CONFIG_BMP280) += bmp280.o bmp280-objs := bmp280-core.o bmp280-regmap.o obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c new file mode 100644 index 000000000000..ccaa07a569c9 --- /dev/null +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BM1390 ROHM pressure sensor + * + * Copyright (c) 2023, ROHM Semiconductor. + * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/pressure/bm1390glv-z-e.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BM1390_REG_MANUFACT_ID 0x0f +#define BM1390_REG_PART_ID 0x10 +#define BM1390_REG_POWER 0x12 +#define BM1390_MASK_POWER BIT(0) +#define BM1390_POWER_ON BM1390_MASK_POWER +#define BM1390_POWER_OFF 0x00 +#define BM1390_REG_RESET 0x13 +#define BM1390_MASK_RESET BIT(0) +#define BM1390_RESET_RELEASE BM1390_MASK_RESET +#define BM1390_RESET 0x00 +#define BM1390_REG_MODE_CTRL 0x14 +#define BM1390_MASK_MEAS_MODE GENMASK(1, 0) +#define BM1390_MASK_DRDY_EN BIT(4) +#define BM1390_MASK_WMI_EN BIT(2) +#define BM1390_MASK_AVE_NUM GENMASK(7, 5) + +/* + * Data-sheet states that when the IIR is used, the AVE_NUM must be set to + * value 110b + */ +#define BM1390_IIR_AVE_NUM 0x06 +#define BM1390_REG_FIFO_CTRL 0x15 +#define BM1390_MASK_IIR_MODE GENMASK(1, 0) +#define BM1390_IIR_MODE_OFF 0x0 +#define BM1390_IIR_MODE_WEAK 0x1 +#define BM1390_IIR_MODE_MID 0x2 +#define BM1390_IIR_MODE_STRONG 0x3 + +#define BM1390_MASK_FIFO_LEN BIT(6) +#define BM1390_MASK_FIFO_EN BIT(7) +#define BM1390_WMI_MIN 2 +#define BM1390_WMI_MAX 3 + +#define BM1390_REG_FIFO_LVL 0x18 +#define BM1390_MASK_FIFO_LVL GENMASK(2, 0) +#define BM1390_REG_STATUS 0x19 +#define BM1390_REG_PRESSURE_BASE 0x1a +#define BM1390_REG_TEMP_HI 0x1d +#define BM1390_REG_TEMP_LO 0x1e +#define BM1390_MAX_REGISTER BM1390_REG_TEMP_LO + +#define BM1390_ID 0x34 + +/* Regmap configs */ +static const struct regmap_range bm1390_volatile_ranges[] = { + { + .range_min = BM1390_REG_STATUS, + .range_max = BM1390_REG_STATUS, + }, + { + .range_min = BM1390_REG_FIFO_LVL, + .range_max = BM1390_REG_TEMP_LO, + }, +}; + +static const struct regmap_access_table bm1390_volatile_regs = { + .yes_ranges = &bm1390_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bm1390_volatile_ranges), +}; + +static const struct regmap_range bm1390_precious_ranges[] = { + { + .range_min = BM1390_REG_STATUS, + .range_max = BM1390_REG_STATUS, + }, +}; + +static const struct regmap_access_table bm1390_precious_regs = { + .yes_ranges = &bm1390_precious_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bm1390_precious_ranges), +}; + +static const struct regmap_range bm1390_read_only_ranges[] = { + { + .range_min = BM1390_REG_MANUFACT_ID, + .range_max = BM1390_REG_PART_ID, + }, { + .range_min = BM1390_REG_FIFO_LVL, + .range_max = BM1390_REG_TEMP_LO, + }, +}; + +static const struct regmap_access_table bm1390_ro_regs = { + .no_ranges = &bm1390_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(bm1390_read_only_ranges), +}; + +static const struct regmap_range bm1390_noinc_read_ranges[] = { + { + .range_min = BM1390_REG_PRESSURE_BASE, + .range_max = BM1390_REG_TEMP_LO, + }, +}; + +static const struct regmap_access_table bm1390_nir_regs = { + .yes_ranges = &bm1390_noinc_read_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bm1390_noinc_read_ranges), +}; + +static const struct regmap_config bm1390_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &bm1390_volatile_regs, + .wr_table = &bm1390_ro_regs, + .rd_noinc_table = &bm1390_nir_regs, + .precious_table = &bm1390_precious_regs, + .max_register = BM1390_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, + .disable_locking = true, +}; + +enum { + BM1390_STATE_SAMPLE, + BM1390_STATE_FIFO, +}; + +struct bm1390_data_buf { + u32 pressure; + __be16 temp; + s64 ts __aligned(8); +}; + +/* BM1390 has FIFO for 4 pressure samples */ +#define BM1390_FIFO_LENGTH 4 + +struct bm1390_data { + s64 timestamp, old_timestamp; + struct iio_trigger *trig; + struct regmap *regmap; + struct device *dev; + struct bm1390_data_buf buf; + int irq; + unsigned int state; + bool trigger_enabled; + u8 watermark; + + /* Prevent accessing sensor during FIFO read sequence */ + struct mutex mutex; +}; + +enum { + BM1390_CHAN_PRESSURE, + BM1390_CHAN_TEMP, +}; + +static const struct iio_chan_spec bm1390_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + /* + * When IIR is used, we must fix amount of averaged samples. + * Thus we don't allow setting oversampling ratio. + */ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = BM1390_CHAN_PRESSURE, + .scan_type = { + .sign = 'u', + .realbits = 22, + .storagebits = 32, + .endianness = IIO_LE, + }, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = BM1390_CHAN_TEMP, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +/* + * We can't skip reading the pressure because the watermark IRQ is acked + * only when the pressure data is read from the FIFO. + */ +static const unsigned long bm1390_scan_masks[] = { + BIT(BM1390_CHAN_PRESSURE), + BIT(BM1390_CHAN_PRESSURE) | BIT(BM1390_CHAN_TEMP), + 0 +}; + +static int bm1390_read_temp(struct bm1390_data *data, int *temp) +{ + __be16 temp_raw; + int ret; + + ret = regmap_bulk_read(data->regmap, BM1390_REG_TEMP_HI, &temp_raw, + sizeof(temp_raw)); + if (ret) + return ret; + + *temp = be16_to_cpu(temp_raw); + + return 0; +} + +static int bm1390_pressure_read(struct bm1390_data *data, u32 *pressure) +{ + /* Pressure data is in 3 8-bit registers */ + u8 raw[3]; + int ret; + + ret = regmap_bulk_read(data->regmap, BM1390_REG_PRESSURE_BASE, + raw, sizeof(raw)); + if (ret < 0) + return ret; + + *pressure = (u32)(raw[2] >> 2 | raw[1] << 6 | raw[0] << 14); + + return 0; +} + + /* The enum values map directly to register bits */ +enum bm1390_meas_mode { + BM1390_MEAS_MODE_STOP = 0x0, + BM1390_MEAS_MODE_1SHOT = 0x1, + BM1390_MEAS_MODE_CONTINUOUS = 0x2, +}; + +static int bm1390_meas_set(struct bm1390_data *data, enum bm1390_meas_mode mode) +{ + return regmap_update_bits(data->regmap, BM1390_REG_MODE_CTRL, + BM1390_MASK_MEAS_MODE, mode); +} + +/* + * If the trigger is not used we just wait until the measurement has + * completed. The data-sheet says maximum measurement cycle (regardless + * the AVE_NUM) is 200 mS so let's just sleep at least that long. If speed + * is needed the trigger should be used. + */ +#define BM1390_MAX_MEAS_TIME_MS 205 + +static int bm1390_read_data(struct bm1390_data *data, + struct iio_chan_spec const *chan, int *val, int *val2) +{ + int ret, warn; + + mutex_lock(&data->mutex); + /* + * We use 'continuous mode' even for raw read because according to the + * data-sheet an one-shot mode can't be used with IIR filter. + */ + ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); + if (ret) + goto unlock_out; + + switch (chan->type) { + case IIO_PRESSURE: + msleep(BM1390_MAX_MEAS_TIME_MS); + ret = bm1390_pressure_read(data, val); + break; + case IIO_TEMP: + msleep(BM1390_MAX_MEAS_TIME_MS); + ret = bm1390_read_temp(data, val); + break; + default: + ret = -EINVAL; + } + warn = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); + if (warn) + dev_warn(data->dev, "Failed to stop measurement (%d)\n", warn); +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bm1390_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bm1390_data *data = iio_priv(idev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + *val = 31; + *val2 = 250000; + + return IIO_VAL_INT_PLUS_MICRO; + } else if (chan->type == IIO_PRESSURE) { + /* + * pressure in hPa is register value divided by 2048. + * This means kPa is 1/20480 times the register value, + */ + *val = 1; + *val2 = 2048; + + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + ret = bm1390_read_data(data, chan, val, val2); + iio_device_release_direct_mode(idev); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int __bm1390_fifo_flush(struct iio_dev *idev, unsigned int samples, + s64 timestamp) +{ + /* BM1390_FIFO_LENGTH is small so we shouldn't run out of stack */ + struct bm1390_data_buf buffer[BM1390_FIFO_LENGTH]; + struct bm1390_data *data = iio_priv(idev); + int smp_lvl, ret, i, warn, dummy; + u64 sample_period; + __be16 temp = 0; + + ret = regmap_read(data->regmap, BM1390_REG_FIFO_LVL, &smp_lvl); + if (ret) + return ret; + + smp_lvl = FIELD_GET(BM1390_MASK_FIFO_LVL, smp_lvl); + if (!smp_lvl) + return 0; + + if (smp_lvl > BM1390_FIFO_LENGTH) { + /* + * The fifo holds maximum of 4 samples so valid values + * should be 0, 1, 2, 3, 4 - rest are probably bit errors + * in I2C line. Don't overflow if this happens. + */ + dev_err(data->dev, "bad FIFO level %d\n", smp_lvl); + smp_lvl = BM1390_FIFO_LENGTH; + } + + sample_period = timestamp - data->old_timestamp; + do_div(sample_period, smp_lvl); + + if (samples && smp_lvl > samples) + smp_lvl = samples; + + + /* + * After some testing it appears that the temperature is not readable + * until the FIFO access has been done after the WMI. Thus, we need + * to read the all pressure values to memory and read the temperature + * only after that. + */ + for (i = 0; i < smp_lvl; i++) { + /* + * When we start reading data from the FIFO the sensor goes to + * special FIFO reading mode. If any other register is accessed + * during the FIFO read, samples can be dropped. Prevent access + * until FIFO_LVL is read. We have mutex locked and we do also + * go performing reading of FIFO_LVL even if this read fails. + */ + if (test_bit(BM1390_CHAN_PRESSURE, idev->active_scan_mask)) { + ret = bm1390_pressure_read(data, &buffer[i].pressure); + if (ret) + break; + } + + /* + * Old timestamp is either the previous sample IRQ time, + * previous flush-time or, if this was first sample, the enable + * time. When we add a sample period to that we should get the + * best approximation of the time-stamp we are handling. + * + * Idea is to always keep the "old_timestamp" matching the + * timestamp which we are currently handling. + */ + data->old_timestamp += sample_period; + buffer[i].ts = data->old_timestamp; + } + /* Reading the FIFO_LVL closes the FIFO access sequence */ + warn = regmap_read(data->regmap, BM1390_REG_FIFO_LVL, &dummy); + if (warn) + dev_warn(data->dev, "Closing FIFO sequence failed\n"); + + if (ret) + return ret; + + if (test_bit(BM1390_CHAN_TEMP, idev->active_scan_mask)) { + ret = regmap_bulk_read(data->regmap, BM1390_REG_TEMP_HI, &temp, + sizeof(temp)); + if (ret) + return ret; + } + + if (ret) + return ret; + + for (i = 0; i < smp_lvl; i++) { + buffer[i].temp = temp; + iio_push_to_buffers(idev, &buffer[i]); + } + + return smp_lvl; +} + +static int bm1390_fifo_flush(struct iio_dev *idev, unsigned int samples) +{ + struct bm1390_data *data = iio_priv(idev); + s64 timestamp; + int ret; + + /* + * If fifo_flush is being called from IRQ handler we know the stored + * timestamp is fairly accurate for the last stored sample. If we are + * called as a result of a read operation from userspace and hence + * before the watermark interrupt was triggered, take a timestamp + * now. We can fall anywhere in between two samples so the error in this + * case is at most one sample period. + * We need to have the IRQ disabled or we risk of messing-up + * the timestamps. If we are ran from IRQ, then the + * IRQF_ONESHOT has us covered - but if we are ran by the + * user-space read we need to disable the IRQ to be on a safe + * side. We do this usng synchronous disable so that if the + * IRQ thread is being ran on other CPU we wait for it to be + * finished. + */ + + timestamp = iio_get_time_ns(idev); + mutex_lock(&data->mutex); + ret = __bm1390_fifo_flush(idev, samples, timestamp); + mutex_unlock(&data->mutex); + + return ret; +} + +static int bm1390_set_watermark(struct iio_dev *idev, unsigned int val) +{ + struct bm1390_data *data = iio_priv(idev); + + if (val < BM1390_WMI_MIN || val > BM1390_WMI_MAX) + return -EINVAL; + + mutex_lock(&data->mutex); + data->watermark = val; + mutex_unlock(&data->mutex); + + return 0; +} + +static const struct iio_info bm1390_noirq_info = { + .read_raw = &bm1390_read_raw, +}; + +static const struct iio_info bm1390_info = { + .read_raw = &bm1390_read_raw, + .hwfifo_set_watermark = bm1390_set_watermark, + .hwfifo_flush_to_buffer = bm1390_fifo_flush, +}; + +static int bm1390_chip_init(struct bm1390_data *data) +{ + int ret; + + ret = regmap_write_bits(data->regmap, BM1390_REG_POWER, + BM1390_MASK_POWER, BM1390_POWER_ON); + if (ret) + return ret; + + msleep(1); + + ret = regmap_write_bits(data->regmap, BM1390_REG_RESET, + BM1390_MASK_RESET, BM1390_RESET); + if (ret) + return ret; + + msleep(1); + + ret = regmap_write_bits(data->regmap, BM1390_REG_RESET, + BM1390_MASK_RESET, BM1390_RESET_RELEASE); + if (ret) + return ret; + + msleep(1); + + ret = regmap_reinit_cache(data->regmap, &bm1390_regmap); + if (ret) { + dev_err(data->dev, "Failed to reinit reg cache\n"); + return ret; + } + + /* + * Default to use IIR filter in "middle" mode. Also the AVE_NUM must + * be fixed when IIR is in use. + */ + ret = regmap_update_bits(data->regmap, BM1390_REG_MODE_CTRL, + BM1390_MASK_AVE_NUM, BM1390_IIR_AVE_NUM); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, BM1390_REG_FIFO_CTRL, + BM1390_MASK_IIR_MODE, BM1390_IIR_MODE_MID); +} + +static int bm1390_fifo_set_wmi(struct bm1390_data *data) +{ + u8 regval; + + regval = FIELD_PREP(BM1390_MASK_FIFO_LEN, + data->watermark - BM1390_WMI_MIN); + + return regmap_update_bits(data->regmap, BM1390_REG_FIFO_CTRL, + BM1390_MASK_FIFO_LEN, regval); +} + +static int bm1390_fifo_enable(struct iio_dev *idev) +{ + struct bm1390_data *data = iio_priv(idev); + int ret; + + /* We can't do buffered stuff without IRQ as we never get WMI */ + if (data->irq <= 0) + return -EINVAL; + + mutex_lock(&data->mutex); + if (data->trigger_enabled) { + ret = -EBUSY; + goto unlock_out; + } + + /* Update watermark to HW */ + ret = bm1390_fifo_set_wmi(data); + if (ret) + goto unlock_out; + + /* Enable WMI_IRQ */ + ret = regmap_set_bits(data->regmap, BM1390_REG_MODE_CTRL, + BM1390_MASK_WMI_EN); + if (ret) + goto unlock_out; + + /* Enable FIFO */ + ret = regmap_set_bits(data->regmap, BM1390_REG_FIFO_CTRL, + BM1390_MASK_FIFO_EN); + if (ret) + goto unlock_out; + + data->state = BM1390_STATE_FIFO; + + data->old_timestamp = iio_get_time_ns(idev); + ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bm1390_fifo_disable(struct iio_dev *idev) +{ + struct bm1390_data *data = iio_priv(idev); + int ret; + + msleep(1); + + mutex_lock(&data->mutex); + ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); + if (ret) + goto unlock_out; + + /* Disable FIFO */ + ret = regmap_clear_bits(data->regmap, BM1390_REG_FIFO_CTRL, + BM1390_MASK_FIFO_EN); + if (ret) + goto unlock_out; + + data->state = BM1390_STATE_SAMPLE; + + /* Disable WMI_IRQ */ + ret = regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL, + BM1390_MASK_WMI_EN); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bm1390_buffer_postenable(struct iio_dev *idev) +{ + /* + * If we use data-ready trigger, then the IRQ masks should be handled by + * trigger enable and the hardware buffer is not used but we just update + * results to the IIO FIFO when data-ready triggers. + */ + if (iio_device_get_current_mode(idev) == INDIO_BUFFER_TRIGGERED) + return 0; + + return bm1390_fifo_enable(idev); +} + +static int bm1390_buffer_predisable(struct iio_dev *idev) +{ + if (iio_device_get_current_mode(idev) == INDIO_BUFFER_TRIGGERED) + return 0; + + return bm1390_fifo_disable(idev); +} + +static const struct iio_buffer_setup_ops bm1390_buffer_ops = { + .postenable = bm1390_buffer_postenable, + .predisable = bm1390_buffer_predisable, +}; + +static irqreturn_t bm1390_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct bm1390_data *data = iio_priv(idev); + int ret, status; + + /* DRDY is acked by reading status reg */ + ret = regmap_read(data->regmap, BM1390_REG_STATUS, &status); + if (ret || !status) + return IRQ_NONE; + + dev_dbg(data->dev, "DRDY trig status 0x%x\n", status); + + if (test_bit(BM1390_CHAN_PRESSURE, idev->active_scan_mask)) { + ret = bm1390_pressure_read(data, &data->buf.pressure); + if (ret) { + dev_warn(data->dev, "sample read failed %d\n", ret); + return IRQ_NONE; + } + } + + if (test_bit(BM1390_CHAN_TEMP, idev->active_scan_mask)) { + ret = regmap_bulk_read(data->regmap, BM1390_REG_TEMP_HI, + &data->buf.temp, sizeof(data->buf.temp)); + if (ret) { + dev_warn(data->dev, "temp read failed %d\n", ret); + return IRQ_HANDLED; + } + } + + iio_push_to_buffers_with_timestamp(idev, &data->buf, data->timestamp); + iio_trigger_notify_done(idev->trig); + + return IRQ_HANDLED; +} + +/* Get timestamps and wake the thread if we need to read data */ +static irqreturn_t bm1390_irq_handler(int irq, void *private) +{ + struct iio_dev *idev = private; + struct bm1390_data *data = iio_priv(idev); + + data->timestamp = iio_get_time_ns(idev); + + if (data->state == BM1390_STATE_FIFO || data->trigger_enabled) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + +static irqreturn_t bm1390_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *idev = private; + struct bm1390_data *data = iio_priv(idev); + int ret = IRQ_NONE; + + mutex_lock(&data->mutex); + + if (data->trigger_enabled) { + iio_trigger_poll_nested(data->trig); + ret = IRQ_HANDLED; + } else if (data->state == BM1390_STATE_FIFO) { + int ok; + + ok = __bm1390_fifo_flush(idev, BM1390_FIFO_LENGTH, + data->timestamp); + if (ok > 0) + ret = IRQ_HANDLED; + } + + mutex_unlock(&data->mutex); + + return ret; +} + +static int bm1390_set_drdy_irq(struct bm1390_data *data, bool en) +{ + if (en) + return regmap_set_bits(data->regmap, BM1390_REG_MODE_CTRL, + BM1390_MASK_DRDY_EN); + return regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL, + BM1390_MASK_DRDY_EN); +} + +static int bm1390_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct bm1390_data *data = iio_trigger_get_drvdata(trig); + int ret = 0; + + mutex_lock(&data->mutex); + + if (data->trigger_enabled == state) + goto unlock_out; + + if (data->state == BM1390_STATE_FIFO) { + dev_warn(data->dev, "Can't set trigger when FIFO enabled\n"); + ret = -EBUSY; + goto unlock_out; + } + + data->trigger_enabled = state; + + if (state) { + ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); + if (ret) + goto unlock_out; + } else { + int dummy; + + ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); + if (ret) + goto unlock_out; + + /* + * We need to read the status register in order to ACK the + * data-ready which may have been generated just before we + * disabled the measurement. + */ + ret = regmap_read(data->regmap, BM1390_REG_STATUS, &dummy); + if (ret) + dev_warn(data->dev, "status read failed\n"); + } + + ret = bm1390_set_drdy_irq(data, state); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_trigger_ops bm1390_trigger_ops = { + .set_trigger_state = bm1390_trigger_set_state, +}; + +static int bm1390_setup_buffer(struct bm1390_data *data, struct iio_dev *idev) +{ + int ret; + + ret = devm_iio_triggered_buffer_setup(data->dev, idev, + &iio_pollfunc_store_time, + &bm1390_trigger_handler, + &bm1390_buffer_ops); + + if (ret) + return dev_err_probe(data->dev, ret, + "iio_triggered_buffer_setup FAIL\n"); + + idev->available_scan_masks = bm1390_scan_masks; + + return 0; +} + +static int bm1390_setup_trigger(struct bm1390_data *data, struct iio_dev *idev, + int irq) +{ + struct iio_trigger *itrig; + char *name; + int ret; + + itrig = devm_iio_trigger_alloc(data->dev, "%sdata-rdy-dev%d", idev->name, + iio_device_id(idev)); + if (!itrig) + return -ENOMEM; + + data->trig = itrig; + + itrig->ops = &bm1390_trigger_ops; + iio_trigger_set_drvdata(itrig, data); + + name = devm_kasprintf(data->dev, GFP_KERNEL, "%s-bm1390", + dev_name(data->dev)); + if (name == NULL) + return -ENOMEM; + + ret = devm_request_threaded_irq(data->dev, irq, bm1390_irq_handler, + &bm1390_irq_thread_handler, + IRQF_ONESHOT, name, idev); + if (ret) + return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + + + ret = devm_iio_trigger_register(data->dev, itrig); + if (ret) + return dev_err_probe(data->dev, ret, + "Trigger registration failed\n"); + + return 0; +} + +static int bm1390_probe(struct i2c_client *i2c) +{ + struct bm1390_data *data; + struct regmap *regmap; + struct iio_dev *idev; + struct device *dev; + unsigned int part_id; + int ret; + + dev = &i2c->dev; + + regmap = devm_regmap_init_i2c(i2c, &bm1390_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); + + ret = regmap_read(regmap, BM1390_REG_PART_ID, &part_id); + if (ret) + return dev_err_probe(dev, ret, "Failed to access sensor\n"); + + if (part_id != BM1390_ID) + dev_warn(dev, "unknown device 0x%x\n", part_id); + + idev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!idev) + return -ENOMEM; + + data = iio_priv(idev); + data->regmap = regmap; + data->dev = dev; + data->irq = i2c->irq; + /* + * For now we just allow BM1390_WMI_MIN to BM1390_WMI_MAX and + * discard every other configuration when triggered mode is not used. + */ + data->watermark = BM1390_WMI_MAX; + mutex_init(&data->mutex); + + idev->channels = bm1390_channels; + idev->num_channels = ARRAY_SIZE(bm1390_channels); + idev->name = "bm1390"; + idev->modes = INDIO_DIRECT_MODE; + + ret = bm1390_chip_init(data); + if (ret) + return dev_err_probe(dev, ret, "sensor init failed\n"); + + ret = bm1390_setup_buffer(data, idev); + if (ret) + return ret; + + /* No trigger if we don't have IRQ for data-ready and WMI */ + if (i2c->irq > 0) { + idev->info = &bm1390_info; + idev->modes |= INDIO_BUFFER_SOFTWARE; + ret = bm1390_setup_trigger(data, idev, i2c->irq); + if (ret) + return ret; + } else { + idev->info = &bm1390_noirq_info; + } + + ret = devm_iio_device_register(dev, idev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Unable to register iio device\n"); + + return 0; +} + +static const struct of_device_id bm1390_of_match[] = { + { .compatible = "rohm,bm1390glv-z" }, + {} +}; +MODULE_DEVICE_TABLE(of, bm1390_of_match); + +static const struct i2c_device_id bm1390_id[] = { + { "bm1390glv-z", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, bm1390_id); + +static struct i2c_driver bm1390_driver = { + .driver = { + .name = "bm1390", + .of_match_table = bm1390_of_match, + /* + * Probing explicitly requires a few millisecond of sleep. + * Enabling the VDD regulator may include ramp up rates. + */ + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = bm1390_probe, + .id_table = bm1390_id, +}; +module_i2c_driver(bm1390_driver); + +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("Driver for ROHM BM1390 pressure sensor"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3b4e0e9677551806766a1ffd19a4a365c10f43d9 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 27 Sep 2023 11:28:29 +0300 Subject: MAINTAINERS: Add ROHM BM1390 Add myself as a maintainer for ROHM BM1390 pressure sensor driver. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/fb19d3027ac19663789e18d4dc972a5dac0fde74.1695727471.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0782484cceb8..4e07c032d06a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18570,6 +18570,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/bh1750.yaml F: drivers/iio/light/bh1750.c +ROHM BM1390 PRESSURE SENSOR DRIVER +M: Matti Vaittinen +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/pressure/rohm-bm1390.c + ROHM BU270xx LIGHT SENSOR DRIVERs M: Matti Vaittinen L: linux-iio@vger.kernel.org -- cgit v1.2.3 From 874bbd1219c70b0d14881214c9768a0e4598fad5 Mon Sep 17 00:00:00 2001 From: Alisa-Dariana Roman Date: Mon, 25 Sep 2023 00:51:46 +0300 Subject: iio: adc: ad7192: Use bitfield access macros Include bitfield.h and update driver to use bitfield access macros GENMASK, FIELD_PREP and FIELD_GET. Remove old macros in favor of using FIELD_PREP and masks. Change %d to %ld to match the type of FIELD_GET(). Signed-off-by: Alisa-Dariana Roman Link: https://lore.kernel.org/r/20230924215148.102491-2-alisadariana@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 73 ++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 69d1103b9508..6fe3e7b7a606 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -43,7 +44,7 @@ #define AD7192_COMM_WEN BIT(7) /* Write Enable */ #define AD7192_COMM_WRITE 0 /* Write Operation */ #define AD7192_COMM_READ BIT(6) /* Read Operation */ -#define AD7192_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ +#define AD7192_COMM_ADDR_MASK GENMASK(5, 3) /* Register Address Mask */ #define AD7192_COMM_CREAD BIT(2) /* Continuous Read of Data Register */ /* Status Register Bit Designations (AD7192_REG_STAT) */ @@ -56,17 +57,16 @@ #define AD7192_STAT_CH1 BIT(0) /* Channel 1 */ /* Mode Register Bit Designations (AD7192_REG_MODE) */ -#define AD7192_MODE_SEL(x) (((x) & 0x7) << 21) /* Operation Mode Select */ -#define AD7192_MODE_SEL_MASK (0x7 << 21) /* Operation Mode Select Mask */ -#define AD7192_MODE_STA(x) (((x) & 0x1) << 20) /* Status Register transmission */ +#define AD7192_MODE_SEL_MASK GENMASK(23, 21) /* Operation Mode Select Mask */ #define AD7192_MODE_STA_MASK BIT(20) /* Status Register transmission Mask */ -#define AD7192_MODE_CLKSRC(x) (((x) & 0x3) << 18) /* Clock Source Select */ +#define AD7192_MODE_CLKSRC_MASK GENMASK(19, 18) /* Clock Source Select Mask */ #define AD7192_MODE_SINC3 BIT(15) /* SINC3 Filter Select */ #define AD7192_MODE_ENPAR BIT(13) /* Parity Enable */ #define AD7192_MODE_CLKDIV BIT(12) /* Clock divide by 2 (AD7190/2 only)*/ #define AD7192_MODE_SCYCLE BIT(11) /* Single cycle conversion */ #define AD7192_MODE_REJ60 BIT(10) /* 50/60Hz notch filter */ -#define AD7192_MODE_RATE(x) ((x) & 0x3FF) /* Filter Update Rate Select */ + /* Filter Update Rate Select Mask */ +#define AD7192_MODE_RATE_MASK GENMASK(9, 0) /* Mode Register: AD7192_MODE_SEL options */ #define AD7192_MODE_CONT 0 /* Continuous Conversion Mode */ @@ -92,13 +92,12 @@ #define AD7192_CONF_CHOP BIT(23) /* CHOP enable */ #define AD7192_CONF_ACX BIT(22) /* AC excitation enable(AD7195 only) */ #define AD7192_CONF_REFSEL BIT(20) /* REFIN1/REFIN2 Reference Select */ -#define AD7192_CONF_CHAN(x) ((x) << 8) /* Channel select */ -#define AD7192_CONF_CHAN_MASK (0x7FF << 8) /* Channel select mask */ +#define AD7192_CONF_CHAN_MASK GENMASK(18, 8) /* Channel select mask */ #define AD7192_CONF_BURN BIT(7) /* Burnout current enable */ #define AD7192_CONF_REFDET BIT(6) /* Reference detect enable */ #define AD7192_CONF_BUF BIT(4) /* Buffered Mode Enable */ #define AD7192_CONF_UNIPOLAR BIT(3) /* Unipolar/Bipolar Enable */ -#define AD7192_CONF_GAIN(x) ((x) & 0x7) /* Gain Select */ +#define AD7192_CONF_GAIN_MASK GENMASK(2, 0) /* Gain Select */ #define AD7192_CH_AIN1P_AIN2M BIT(0) /* AIN1(+) - AIN2(-) */ #define AD7192_CH_AIN3P_AIN4M BIT(1) /* AIN3(+) - AIN4(-) */ @@ -130,7 +129,7 @@ #define CHIPID_AD7192 0x0 #define CHIPID_AD7193 0x2 #define CHIPID_AD7195 0x6 -#define AD7192_ID_MASK 0x0F +#define AD7192_ID_MASK GENMASK(3, 0) /* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */ #define AD7192_GPOCON_BPDSW BIT(6) /* Bridge power-down switch enable */ @@ -272,7 +271,7 @@ static int ad7192_set_channel(struct ad_sigma_delta *sd, unsigned int channel) struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); st->conf &= ~AD7192_CONF_CHAN_MASK; - st->conf |= AD7192_CONF_CHAN(channel); + st->conf |= FIELD_PREP(AD7192_CONF_CHAN_MASK, channel); return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); } @@ -283,7 +282,7 @@ static int ad7192_set_mode(struct ad_sigma_delta *sd, struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); st->mode &= ~AD7192_MODE_SEL_MASK; - st->mode |= AD7192_MODE_SEL(mode); + st->mode |= FIELD_PREP(AD7192_MODE_SEL_MASK, mode); return ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); } @@ -295,7 +294,7 @@ static int ad7192_append_status(struct ad_sigma_delta *sd, bool append) int ret; mode &= ~AD7192_MODE_STA_MASK; - mode |= AD7192_MODE_STA(append); + mode |= FIELD_PREP(AD7192_MODE_STA_MASK, append); ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, mode); if (ret < 0) @@ -399,17 +398,17 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) if (ret) return ret; - id &= AD7192_ID_MASK; + id = FIELD_GET(AD7192_ID_MASK, id); if (id != st->chip_info->chip_id) dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n", id, st->chip_info->chip_id); - st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) | - AD7192_MODE_CLKSRC(st->clock_sel) | - AD7192_MODE_RATE(480); + st->mode = FIELD_PREP(AD7192_MODE_SEL_MASK, AD7192_MODE_IDLE) | + FIELD_PREP(AD7192_MODE_CLKSRC_MASK, st->clock_sel) | + FIELD_PREP(AD7192_MODE_RATE_MASK, 480); - st->conf = AD7192_CONF_GAIN(0); + st->conf = FIELD_PREP(AD7192_CONF_GAIN_MASK, 0); rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable"); if (rej60_en) @@ -455,7 +454,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { scale_uv = ((u64)st->int_vref_mv * 100000000) >> (indio_dev->channels[0].scan_type.realbits - - ((st->conf & AD7192_CONF_UNIPOLAR) ? 0 : 1)); + !FIELD_GET(AD7192_CONF_UNIPOLAR, st->conf)); scale_uv >>= i; st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; @@ -472,7 +471,7 @@ static ssize_t ad7192_show_ac_excitation(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7192_state *st = iio_priv(indio_dev); - return sysfs_emit(buf, "%d\n", !!(st->conf & AD7192_CONF_ACX)); + return sysfs_emit(buf, "%ld\n", FIELD_GET(AD7192_CONF_ACX, st->conf)); } static ssize_t ad7192_show_bridge_switch(struct device *dev, @@ -482,7 +481,8 @@ static ssize_t ad7192_show_bridge_switch(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7192_state *st = iio_priv(indio_dev); - return sysfs_emit(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW)); + return sysfs_emit(buf, "%ld\n", + FIELD_GET(AD7192_GPOCON_BPDSW, st->gpocon)); } static ssize_t ad7192_set(struct device *dev, @@ -537,14 +537,14 @@ static void ad7192_get_available_filter_freq(struct ad7192_state *st, /* Formulas for filter at page 25 of the datasheet */ fadc = DIV_ROUND_CLOSEST(st->fclk, - AD7192_SYNC4_FILTER * AD7192_MODE_RATE(st->mode)); + AD7192_SYNC4_FILTER * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); freq[0] = DIV_ROUND_CLOSEST(fadc * 240, 1024); fadc = DIV_ROUND_CLOSEST(st->fclk, - AD7192_SYNC3_FILTER * AD7192_MODE_RATE(st->mode)); + AD7192_SYNC3_FILTER * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); freq[1] = DIV_ROUND_CLOSEST(fadc * 240, 1024); - fadc = DIV_ROUND_CLOSEST(st->fclk, AD7192_MODE_RATE(st->mode)); + fadc = DIV_ROUND_CLOSEST(st->fclk, FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); freq[2] = DIV_ROUND_CLOSEST(fadc * 230, 1024); freq[3] = DIV_ROUND_CLOSEST(fadc * 272, 1024); } @@ -665,11 +665,11 @@ static int ad7192_get_3db_filter_freq(struct ad7192_state *st) unsigned int fadc; fadc = DIV_ROUND_CLOSEST(st->fclk, - st->f_order * AD7192_MODE_RATE(st->mode)); + st->f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); - if (st->conf & AD7192_CONF_CHOP) + if (FIELD_GET(AD7192_CONF_CHOP, st->conf)) return DIV_ROUND_CLOSEST(fadc * 240, 1024); - if (st->mode & AD7192_MODE_SINC3) + if (FIELD_GET(AD7192_MODE_SINC3, st->mode)) return DIV_ROUND_CLOSEST(fadc * 272, 1024); else return DIV_ROUND_CLOSEST(fadc * 230, 1024); @@ -682,7 +682,8 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, long m) { struct ad7192_state *st = iio_priv(indio_dev); - bool unipolar = !!(st->conf & AD7192_CONF_UNIPOLAR); + bool unipolar = FIELD_GET(AD7192_CONF_UNIPOLAR, st->conf); + u8 gain = FIELD_GET(AD7192_CONF_GAIN_MASK, st->conf); switch (m) { case IIO_CHAN_INFO_RAW: @@ -691,8 +692,8 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_VOLTAGE: mutex_lock(&st->lock); - *val = st->scale_avail[AD7192_CONF_GAIN(st->conf)][0]; - *val2 = st->scale_avail[AD7192_CONF_GAIN(st->conf)][1]; + *val = st->scale_avail[gain][0]; + *val2 = st->scale_avail[gain][1]; mutex_unlock(&st->lock); return IIO_VAL_INT_PLUS_NANO; case IIO_TEMP: @@ -713,7 +714,7 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: *val = st->fclk / - (st->f_order * 1024 * AD7192_MODE_RATE(st->mode)); + (st->f_order * 1024 * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: *val = ad7192_get_3db_filter_freq(st); @@ -746,8 +747,8 @@ static int ad7192_write_raw(struct iio_dev *indio_dev, if (val2 == st->scale_avail[i][1]) { ret = 0; tmp = st->conf; - st->conf &= ~AD7192_CONF_GAIN(-1); - st->conf |= AD7192_CONF_GAIN(i); + st->conf &= ~AD7192_CONF_GAIN_MASK; + st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i); if (tmp == st->conf) break; ad_sd_write_reg(&st->sd, AD7192_REG_CONF, @@ -769,8 +770,8 @@ static int ad7192_write_raw(struct iio_dev *indio_dev, break; } - st->mode &= ~AD7192_MODE_RATE(-1); - st->mode |= AD7192_MODE_RATE(div); + st->mode &= ~AD7192_MODE_RATE_MASK; + st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div); ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); break; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: @@ -830,7 +831,7 @@ static int ad7192_update_scan_mode(struct iio_dev *indio_dev, const unsigned lon conf &= ~AD7192_CONF_CHAN_MASK; for_each_set_bit(i, scan_mask, 8) - conf |= AD7192_CONF_CHAN(i); + conf |= FIELD_PREP(AD7192_CONF_CHAN_MASK, i); ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, conf); if (ret < 0) -- cgit v1.2.3 From e49075c7e9420b11b8e1aa8434e8790282df4a39 Mon Sep 17 00:00:00 2001 From: Alisa-Dariana Roman Date: Mon, 25 Sep 2023 00:51:47 +0300 Subject: iio: adc: ad7192: Improve f_order computation Instead of using the f_order member of ad7192_state, a function that computes the f_order coefficient makes more sense. This coefficient is a function of the sinc filter and chop filter states. Remove f_order member of ad7192_state structure. Instead use ad7192_compute_f_order function to compute the f_order coefficient according to the sinc filter and chop filter states passed as parameters. Add ad7192_get_f_order function that returns the current f_order coefficient of the device. Add ad7192_compute_f_adc function that computes the f_adc value according to the sinc filter and chop filter states passed as parameters. Add ad7192_get_f_adc function that returns the current f_adc value of the device. Signed-off-by: Alisa-Dariana Roman Link: https://lore.kernel.org/r/20230924215148.102491-3-alisadariana@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 62 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 6fe3e7b7a606..e0be394ccb34 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -179,7 +179,6 @@ struct ad7192_state { struct clk *mclk; u16 int_vref_mv; u32 fclk; - u32 f_order; u32 mode; u32 conf; u32 scale_avail[8][2]; @@ -419,7 +418,6 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) st->conf |= AD7192_CONF_REFSEL; st->conf &= ~AD7192_CONF_CHOP; - st->f_order = AD7192_NO_SYNC_FILTER; buf_en = of_property_read_bool(np, "adi,buffer-enable"); if (buf_en) @@ -530,22 +528,60 @@ static ssize_t ad7192_set(struct device *dev, return ret ? ret : len; } +static int ad7192_compute_f_order(bool sinc3_en, bool chop_en) +{ + if (!chop_en) + return 1; + + if (sinc3_en) + return AD7192_SYNC3_FILTER; + + return AD7192_SYNC4_FILTER; +} + +static int ad7192_get_f_order(struct ad7192_state *st) +{ + bool sinc3_en, chop_en; + + sinc3_en = FIELD_GET(AD7192_MODE_SINC3, st->mode); + chop_en = FIELD_GET(AD7192_CONF_CHOP, st->conf); + + return ad7192_compute_f_order(sinc3_en, chop_en); +} + +static int ad7192_compute_f_adc(struct ad7192_state *st, bool sinc3_en, + bool chop_en) +{ + unsigned int f_order = ad7192_compute_f_order(sinc3_en, chop_en); + + return DIV_ROUND_CLOSEST(st->fclk, + f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); +} + +static int ad7192_get_f_adc(struct ad7192_state *st) +{ + unsigned int f_order = ad7192_get_f_order(st); + + return DIV_ROUND_CLOSEST(st->fclk, + f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); +} + static void ad7192_get_available_filter_freq(struct ad7192_state *st, int *freq) { unsigned int fadc; /* Formulas for filter at page 25 of the datasheet */ - fadc = DIV_ROUND_CLOSEST(st->fclk, - AD7192_SYNC4_FILTER * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); + fadc = ad7192_compute_f_adc(st, false, true); freq[0] = DIV_ROUND_CLOSEST(fadc * 240, 1024); - fadc = DIV_ROUND_CLOSEST(st->fclk, - AD7192_SYNC3_FILTER * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); + fadc = ad7192_compute_f_adc(st, true, true); freq[1] = DIV_ROUND_CLOSEST(fadc * 240, 1024); - fadc = DIV_ROUND_CLOSEST(st->fclk, FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); + fadc = ad7192_compute_f_adc(st, false, false); freq[2] = DIV_ROUND_CLOSEST(fadc * 230, 1024); + + fadc = ad7192_compute_f_adc(st, true, false); freq[3] = DIV_ROUND_CLOSEST(fadc * 272, 1024); } @@ -628,25 +664,21 @@ static int ad7192_set_3db_filter_freq(struct ad7192_state *st, switch (idx) { case 0: - st->f_order = AD7192_SYNC4_FILTER; st->mode &= ~AD7192_MODE_SINC3; st->conf |= AD7192_CONF_CHOP; break; case 1: - st->f_order = AD7192_SYNC3_FILTER; st->mode |= AD7192_MODE_SINC3; st->conf |= AD7192_CONF_CHOP; break; case 2: - st->f_order = AD7192_NO_SYNC_FILTER; st->mode &= ~AD7192_MODE_SINC3; st->conf &= ~AD7192_CONF_CHOP; break; case 3: - st->f_order = AD7192_NO_SYNC_FILTER; st->mode |= AD7192_MODE_SINC3; st->conf &= ~AD7192_CONF_CHOP; @@ -664,8 +696,7 @@ static int ad7192_get_3db_filter_freq(struct ad7192_state *st) { unsigned int fadc; - fadc = DIV_ROUND_CLOSEST(st->fclk, - st->f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); + fadc = ad7192_get_f_adc(st); if (FIELD_GET(AD7192_CONF_CHOP, st->conf)) return DIV_ROUND_CLOSEST(fadc * 240, 1024); @@ -713,8 +744,7 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, *val -= 273 * ad7192_get_temp_scale(unipolar); return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: - *val = st->fclk / - (st->f_order * 1024 * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); + *val = DIV_ROUND_CLOSEST(ad7192_get_f_adc(st), 1024); return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: *val = ad7192_get_3db_filter_freq(st); @@ -764,7 +794,7 @@ static int ad7192_write_raw(struct iio_dev *indio_dev, break; } - div = st->fclk / (val * st->f_order * 1024); + div = st->fclk / (val * ad7192_get_f_order(st) * 1024); if (div < 1 || div > 1023) { ret = -EINVAL; break; -- cgit v1.2.3 From a68ad2062fb2fb227a022b0392398bb53fd75012 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Thu, 28 Sep 2023 11:45:21 +0300 Subject: dt-bindings: iio: Add KX132ACR-LBZ accelerometer ROHM KX132ACR-LBZ is an accelerometer for industrial applications. It has a subset of KX022A functionalities, dropping support for tap, free fall and tilt detection engines. Also, the register interface is an exact subset of what is found on KX022A. Extend the kionix,kx022a.yaml file to support the KX132ACR-LBZ device Signed-off-by: Matti Vaittinen Acked-by: Conor Dooley Link: https://lore.kernel.org/r/5c9e03ffad5e6e5970d6e71fb02eab4b652e109f.1695879676.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml index 034b69614416..66ea894dbe55 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml @@ -4,21 +4,23 @@ $id: http://devicetree.org/schemas/iio/accel/kionix,kx022a.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ROHM/Kionix KX022A and KX132-1211 Accelerometers +title: ROHM/Kionix KX022A, KX132-1211 and KX132ACR-LBZ Accelerometers maintainers: - Matti Vaittinen description: | - KX022A and KX132-1211 are 3-axis accelerometers supporting +/- 2G, 4G, 8G and - 16G ranges, variable output data-rates and a hardware-fifo buffering. - KX022A and KX132-1211 can be accessed either via I2C or SPI. + KX022A, KX132ACR-LBZ and KX132-1211 are 3-axis accelerometers supporting + +/- 2G, 4G, 8G and 16G ranges, variable output data-rates and a + hardware-fifo buffering. These accelerometers can be accessed either + via I2C or SPI. properties: compatible: enum: - kionix,kx022a - kionix,kx132-1211 + - rohm,kx132acr-lbz reg: maxItems: 1 -- cgit v1.2.3 From 1c8af63782583ee9d3a95e971a26570ae3f25af6 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Thu, 28 Sep 2023 11:45:39 +0300 Subject: iio: kx022a: Support ROHM KX132ACR-LBZ dt-bindings: iio: Add KX132ACR-LBZ accelerometer ROHM KX132ACR-LBZ is an accelerometer for industrial applications. It has a subset of KX022A functionalities, dropping support for tap, free fall and tilt detection engines. Also, the register interface is an exact subset of what is found on KX022A (except the WHO_AM_I register value). Add support for KX132ACR-LBZ. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/272065b9f35b817aff86b7760ff1aaaaaaa969f5.1695879676.git.mazziesaccount@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 4 ++-- drivers/iio/accel/kionix-kx022a-i2c.c | 4 +++- drivers/iio/accel/kionix-kx022a-spi.c | 4 +++- drivers/iio/accel/kionix-kx022a.c | 34 +++++++++++++++++++++++++++++++++- drivers/iio/accel/kionix-kx022a.h | 2 ++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 373257d64a7e..f113dae59048 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -420,7 +420,7 @@ config IIO_KX022A_SPI help Enable support for the Kionix digital tri-axis accelerometers connected to SPI interface. Supported devices are: - KX022A, KX132-1211 + KX022A, KX132-1211, KX132ACR-LBZ config IIO_KX022A_I2C tristate "Kionix KX022A tri-axis digital accelerometer I2C interface" @@ -430,7 +430,7 @@ config IIO_KX022A_I2C help Enable support for the Kionix digital tri-axis accelerometers connected to I2C interface. Supported devices are: - KX022A, KX132-1211 + KX022A, KX132-1211, KX132ACR-LBZ config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" diff --git a/drivers/iio/accel/kionix-kx022a-i2c.c b/drivers/iio/accel/kionix-kx022a-i2c.c index fc53e527cae0..8a1d4fc28ddd 100644 --- a/drivers/iio/accel/kionix-kx022a-i2c.c +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2022 ROHM Semiconductors * - * ROHM/KIONIX KX022A accelerometer driver + * ROHM/KIONIX accelerometer driver */ #include @@ -38,6 +38,7 @@ static int kx022a_i2c_probe(struct i2c_client *i2c) static const struct i2c_device_id kx022a_i2c_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, + { .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); @@ -45,6 +46,7 @@ MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, + { .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a-spi.c b/drivers/iio/accel/kionix-kx022a-spi.c index e7878ba67827..f798b964d0b5 100644 --- a/drivers/iio/accel/kionix-kx022a-spi.c +++ b/drivers/iio/accel/kionix-kx022a-spi.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2022 ROHM Semiconductors * - * ROHM/KIONIX KX022A accelerometer driver + * ROHM/KIONIX accelerometer driver */ #include @@ -38,6 +38,7 @@ static int kx022a_spi_probe(struct spi_device *spi) static const struct spi_device_id kx022a_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, + { .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, kx022a_id); @@ -45,6 +46,7 @@ MODULE_DEVICE_TABLE(spi, kx022a_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, + { .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index c5b555094e60..60864be3a667 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2022 ROHM Semiconductors * - * ROHM/KIONIX KX022A accelerometer driver + * ROHM/KIONIX accelerometer driver */ #include @@ -1188,6 +1188,38 @@ const struct kx022a_chip_info kx132_chip_info = { }; EXPORT_SYMBOL_NS_GPL(kx132_chip_info, IIO_KX022A); +/* + * Despite the naming, KX132ACR-LBZ is not similar to KX132-1211 but it is + * exact subset of KX022A. KX132ACR-LBZ is meant to be used for industrial + * applications and the tap/double tap, free fall and tilt engines were + * removed. Rest of the registers and functionalities (excluding the ID + * register) are exact match to what is found in KX022. + */ +const struct kx022a_chip_info kx132acr_chip_info = { + .name = "kx132acr-lbz", + .regmap_config = &kx022a_regmap_config, + .channels = kx022a_channels, + .num_channels = ARRAY_SIZE(kx022a_channels), + .fifo_length = KX022A_FIFO_LENGTH, + .who = KX022A_REG_WHO, + .id = KX132ACR_LBZ_ID, + .cntl = KX022A_REG_CNTL, + .cntl2 = KX022A_REG_CNTL2, + .odcntl = KX022A_REG_ODCNTL, + .buf_cntl1 = KX022A_REG_BUF_CNTL1, + .buf_cntl2 = KX022A_REG_BUF_CNTL2, + .buf_clear = KX022A_REG_BUF_CLEAR, + .buf_status1 = KX022A_REG_BUF_STATUS_1, + .buf_read = KX022A_REG_BUF_READ, + .inc1 = KX022A_REG_INC1, + .inc4 = KX022A_REG_INC4, + .inc5 = KX022A_REG_INC5, + .inc6 = KX022A_REG_INC6, + .xout_l = KX022A_REG_XOUT_L, + .get_fifo_bytes_available = kx022a_get_fifo_bytes_available, +}; +EXPORT_SYMBOL_NS_GPL(kx132acr_chip_info, IIO_KX022A); + int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info) { static const char * const regulator_names[] = {"io-vdd", "vdd"}; diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h index 35c548ae7eff..7060438ad88c 100644 --- a/drivers/iio/accel/kionix-kx022a.h +++ b/drivers/iio/accel/kionix-kx022a.h @@ -13,6 +13,7 @@ #define KX022A_REG_WHO 0x0f #define KX022A_ID 0xc8 +#define KX132ACR_LBZ_ID 0xd8 #define KX022A_REG_CNTL2 0x19 #define KX022A_MASK_SRST BIT(7) @@ -186,5 +187,6 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi extern const struct kx022a_chip_info kx022a_chip_info; extern const struct kx022a_chip_info kx132_chip_info; +extern const struct kx022a_chip_info kx132acr_chip_info; #endif -- cgit v1.2.3 From d27425d5d8b1cd930a31b92fb426cdbcbf876b10 Mon Sep 17 00:00:00 2001 From: George Stark Date: Fri, 21 Jul 2023 13:23:09 +0300 Subject: iio: adc: meson: improve error logging at probe stage Add log messages for errors that may occur at the probe stage Signed-off-by: George Stark Link: https://lore.kernel.org/r/20230721102413.255726-3-gnstark@sberdevices.ru Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index a40986fb285c..950ff13e6dde 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1045,8 +1045,10 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) u32 regval; ret = meson_sar_adc_lock(indio_dev); - if (ret) + if (ret) { + dev_err(dev, "failed to lock adc\n"); goto err_lock; + } ret = regulator_enable(priv->vref); if (ret < 0) { @@ -1354,15 +1356,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev) priv->regmap = devm_regmap_init_mmio(dev, base, priv->param->regmap_config); if (IS_ERR(priv->regmap)) - return PTR_ERR(priv->regmap); + return dev_err_probe(dev, PTR_ERR(priv->regmap), "failed to init regmap\n"); irq = irq_of_parse_and_map(dev->of_node, 0); if (!irq) - return -EINVAL; + return dev_err_probe(dev, -EINVAL, "failed to get irq\n"); ret = devm_request_irq(dev, irq, meson_sar_adc_irq, IRQF_SHARED, dev_name(dev), indio_dev); if (ret) - return ret; + return dev_err_probe(dev, ret, "failed to request irq\n"); priv->clkin = devm_clk_get(dev, "clkin"); if (IS_ERR(priv->clkin)) @@ -1384,7 +1386,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) if (!priv->adc_clk) { ret = meson_sar_adc_clk_init(indio_dev, base); if (ret) - return ret; + return dev_err_probe(dev, ret, "failed to init internal clk\n"); } priv->vref = devm_regulator_get(dev, "vref"); @@ -1426,8 +1428,10 @@ static int meson_sar_adc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, indio_dev); ret = iio_device_register(indio_dev); - if (ret) + if (ret) { + dev_err_probe(dev, ret, "failed to register iio device\n"); goto err_hw; + } return 0; -- cgit v1.2.3 From 1731a0c492c806ba3678b6c1aa3081a77ca1abb3 Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Thu, 21 Sep 2023 04:54:00 +0000 Subject: iio: adc: stm32-adc: Replace deprecated strncpy() with strscpy() strncpy() is deprecated for use on NUL-terminated destination strings [1]. We should prefer more robust and less ambiguous string interfaces. We expect adc->chan_name[val] to be NUL-terminated based on ch_name's use within functions that expect NUL-terminated strings like strncmp and printf-likes: | if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) { | /* Check internal channel availability */ | switch (i) { | case STM32_ADC_INT_CH_VDDCORE: | if (!adc->cfg->regs->or_vddcore.reg) | dev_warn(&indio_dev->dev, | "%s channel not available\n", ch_name); ... There is no evidence that NUL-padding is needed either. Considering the above, a suitable replacement is strscpy() [2] due to the fact that it guarantees NUL-termination on the destination buffer without unnecessarily NUL-padding. If, for any reason, NUL-padding _is_ required we should go for `strscpy_pad`. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1] Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2] Link: https://github.com/KSPP/linux/issues/90 Cc: linux-hardening@vger.kernel.org Signed-off-by: Justin Stitt Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20230921-strncpy-drivers-iio-adc-stm32-adc-c-v1-1-c50eca098597@google.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 25a912805439..b5d3c9cea5c4 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2209,7 +2209,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, ret = -EINVAL; goto err; } - strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); + strscpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); ret = stm32_adc_populate_int_ch(indio_dev, name, val); if (ret == -ENOENT) continue; -- cgit v1.2.3 From 1c61848f53180e18e725290991da453fc59c80a4 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 29 Sep 2023 12:23:06 -0500 Subject: dt-bindings: iio: resolver: add devicetree bindings for ad2s1210 This adds new DeviceTree bindings for the Analog Devices, Inc. AD2S1210 resolver-to-digital converter. Co-developed-by: Apelete Seketeli Signed-off-by: Apelete Seketeli Signed-off-by: David Lechner Acked-by: Michael Hennerich Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230929-ad2s1210-mainline-v3-1-fa4364281745@baylibre.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/resolver/adi,ad2s1210.yaml | 177 +++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml diff --git a/Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml b/Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml new file mode 100644 index 000000000000..8980b3cd8337 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/resolver/adi,ad2s1210.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD2S1210 Resolver-to-Digital Converter + +maintainers: + - Michael Hennerich + +description: | + The AD2S1210 is a complete 10-bit to 16-bit resolution tracking + resolver-to-digital converter, integrating an on-board programmable + sinusoidal oscillator that provides sine wave excitation for + resolvers. + + The AD2S1210 allows the user to read the angular position or the + angular velocity data directly from the parallel outputs or through + the serial interface. + + The mode of operation of the communication channel (parallel or serial) is + selected by the A0 and A1 input pins. In normal mode, data is latched by + toggling the SAMPLE line and can then be read directly. In configuration mode, + data is read or written using a register access scheme (address byte with + read/write flag and data byte). + + A1 A0 Result + 0 0 Normal mode - position output + 0 1 Normal mode - velocity output + 1 0 Reserved + 1 1 Configuration mode + + In normal mode, the resolution of the digital output is selected using + the RES0 and RES1 input pins. In configuration mode, the resolution is + selected by setting the RES0 and RES1 bits in the control register. + + RES1 RES0 Resolution (Bits) + 0 0 10 + 0 1 12 + 1 0 14 + 1 1 16 + + Note on SPI connections: The CS line on the AD2S1210 should hard-wired to + logic low and the WR/FSYNC line on the AD2S1210 should be connected to the + SPI CSn output of the SPI controller. + + Datasheet: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad2s1210.pdf + +properties: + compatible: + const: adi,ad2s1210 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 25000000 + + spi-cpha: true + + avdd-supply: + description: + A 4.75 to 5.25 V regulator that powers the Analog Supply Voltage (AVDD) + pin. + + dvdd-supply: + description: + A 4.75 to 5.25 V regulator that powers the Digital Supply Voltage (DVDD) + pin. + + vdrive-supply: + description: + A 2.3 to 5.25 V regulator that powers the Logic Power Supply Input + (VDrive) pin. + + clocks: + maxItems: 1 + description: External oscillator clock (CLKIN). + + reset-gpios: + description: + GPIO connected to the /RESET pin. As the line needs to be low for the + reset to be active, it should be configured as GPIO_ACTIVE_LOW. + maxItems: 1 + + sample-gpios: + description: + GPIO connected to the /SAMPLE pin. As the line needs to be low to trigger + a sample, it should be configured as GPIO_ACTIVE_LOW. + maxItems: 1 + + mode-gpios: + description: + GPIO lines connected to the A0 and A1 pins. These pins select the data + transfer mode. + minItems: 2 + maxItems: 2 + + resolution-gpios: + description: + GPIO lines connected to the RES0 and RES1 pins. These pins select the + resolution of the digital output. If omitted, it is assumed that the + RES0 and RES1 pins are hard-wired to match the assigned-resolution-bits + property. + minItems: 2 + maxItems: 2 + + fault-gpios: + description: + GPIO lines connected to the LOT and DOS pins. These pins combined indicate + the type of fault present, if any. As these pins a pulled low to indicate + a fault condition, they should be configured as GPIO_ACTIVE_LOW. + minItems: 2 + maxItems: 2 + + adi,fixed-mode: + description: + This is used to indicate the selected mode if A0 and A1 are hard-wired + instead of connected to GPIOS (i.e. mode-gpios is omitted). + $ref: /schemas/types.yaml#/definitions/string + enum: [config, velocity, position] + + assigned-resolution-bits: + description: + Resolution of the digital output required by the application. This + determines the precision of the angle and/or the maximum speed that can + be measured. If resolution-gpios is omitted, it is assumed that RES0 and + RES1 are hard-wired to match this value. + enum: [10, 12, 14, 16] + +required: + - compatible + - reg + - spi-cpha + - avdd-supply + - dvdd-supply + - vdrive-supply + - clocks + - sample-gpios + - assigned-resolution-bits + +oneOf: + - required: + - mode-gpios + - required: + - adi,fixed-mode + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + resolver@0 { + compatible = "adi,ad2s1210"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpha; + avdd-supply = <&avdd_regulator>; + dvdd-supply = <&dvdd_regulator>; + vdrive-supply = <&vdrive_regulator>; + clocks = <&ext_osc>; + sample-gpios = <&gpio0 90 GPIO_ACTIVE_LOW>; + mode-gpios = <&gpio0 86 0>, <&gpio0 87 0>; + resolution-gpios = <&gpio0 88 0>, <&gpio0 89 0>; + assigned-resolution-bits = <16>; + }; + }; -- cgit v1.2.3 From 848f68c760ab1e14a9046ea6e45e3304ab9fa50b Mon Sep 17 00:00:00 2001 From: André Apitzsch Date: Sun, 1 Oct 2023 18:09:56 +0200 Subject: iio: magnetometer: ak8975: Fix 'Unexpected device' error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicity specify array indices to fix mapping between asahi_compass_chipset and ak_def_array. While at it, remove unneeded AKXXXX. Fixes: 4f9ea93afde1 ("iio: magnetometer: ak8975: Convert enum->pointer for data in the match tables") Signed-off-by: André Apitzsch Reviewed-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231001-ak_magnetometer-v1-1-09bf3b8798a3@apitzsch.eu Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 8cfceb007936..dd466c5fa621 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -204,7 +204,6 @@ static long ak09912_raw_to_gauss(u16 data) /* Compatible Asahi Kasei Compass parts */ enum asahi_compass_chipset { - AKXXXX = 0, AK8975, AK8963, AK09911, @@ -248,7 +247,7 @@ struct ak_def { }; static const struct ak_def ak_def_array[] = { - { + [AK8975] = { .type = AK8975, .raw_to_gauss = ak8975_raw_to_gauss, .range = 4096, @@ -273,7 +272,7 @@ static const struct ak_def ak_def_array[] = { AK8975_REG_HYL, AK8975_REG_HZL}, }, - { + [AK8963] = { .type = AK8963, .raw_to_gauss = ak8963_09911_raw_to_gauss, .range = 8190, @@ -298,7 +297,7 @@ static const struct ak_def ak_def_array[] = { AK8975_REG_HYL, AK8975_REG_HZL}, }, - { + [AK09911] = { .type = AK09911, .raw_to_gauss = ak8963_09911_raw_to_gauss, .range = 8192, @@ -323,7 +322,7 @@ static const struct ak_def ak_def_array[] = { AK09912_REG_HYL, AK09912_REG_HZL}, }, - { + [AK09912] = { .type = AK09912, .raw_to_gauss = ak09912_raw_to_gauss, .range = 32752, @@ -348,7 +347,7 @@ static const struct ak_def ak_def_array[] = { AK09912_REG_HYL, AK09912_REG_HZL}, }, - { + [AK09916] = { .type = AK09916, .raw_to_gauss = ak09912_raw_to_gauss, .range = 32752, -- cgit v1.2.3 From 2d3dff577dd0ea8fe9637a13822f7603c4a881c8 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 3 Oct 2023 12:57:47 +0300 Subject: tools: iio: iio_generic_buffer ensure alignment The iio_generic_buffer can return garbage values when the total size of scan data is not a multiple of the largest element in the scan. This can be demonstrated by reading a scan, consisting, for example of one 4-byte and one 2-byte element, where the 4-byte element is first in the buffer. The IIO generic buffer code does not take into account the last two padding bytes that are needed to ensure that the 4-byte data for next scan is correctly aligned. Add the padding bytes required to align the next sample with the scan size. Signed-off-by: Matti Vaittinen Fixes: e58537ccce73 ("staging: iio: update example application.") Link: https://lore.kernel.org/r/ZRvlm4ktNLu+qmlf@dc78bmyyyyyyyyyyyyydt-3.rev.dnainternet.fi Signed-off-by: Jonathan Cameron --- tools/iio/iio_generic_buffer.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index 44bbf80f0cfd..0d0a7a19d6f9 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -54,9 +54,12 @@ enum autochan { static unsigned int size_from_channelarray(struct iio_channel_info *channels, int num_channels) { unsigned int bytes = 0; - int i = 0; + int i = 0, max = 0; + unsigned int misalignment; while (i < num_channels) { + if (channels[i].bytes > max) + max = channels[i].bytes; if (bytes % channels[i].bytes == 0) channels[i].location = bytes; else @@ -66,6 +69,14 @@ static unsigned int size_from_channelarray(struct iio_channel_info *channels, in bytes = channels[i].location + channels[i].bytes; i++; } + /* + * We want the data in next sample to also be properly aligned so + * we'll add padding at the end if needed. Adding padding only + * works for channel data which size is 2^n bytes. + */ + misalignment = bytes % max; + if (misalignment) + bytes += max - misalignment; return bytes; } -- cgit v1.2.3 From 4d7c16d08d248952c116f2eb9b7b5abc43a19688 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 4 Oct 2023 18:39:28 +0200 Subject: iio: accel: mxc4005: allow module autoloading via OF compatible Add OF device table with compatible strings to allow automatic module loading. Signed-off-by: Luca Ceresoli Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231004-mxc4005-device-tree-support-v1-2-e7c0faea72e4@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mxc4005.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 75d142bc14b4..82e8d0b39049 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -476,6 +476,13 @@ static const struct acpi_device_id mxc4005_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match); +static const struct of_device_id mxc4005_of_match[] = { + { .compatible = "memsic,mxc4005", }, + { .compatible = "memsic,mxc6655", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mxc4005_of_match); + static const struct i2c_device_id mxc4005_id[] = { {"mxc4005", 0}, {"mxc6655", 0}, @@ -487,6 +494,7 @@ static struct i2c_driver mxc4005_driver = { .driver = { .name = MXC4005_DRV_NAME, .acpi_match_table = ACPI_PTR(mxc4005_acpi_match), + .of_match_table = mxc4005_of_match, }, .probe = mxc4005_probe, .id_table = mxc4005_id, -- cgit v1.2.3 From fca63709310267d942fa4991e65636ab42d51ed3 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 10 Oct 2023 09:46:00 +0100 Subject: Revert "dt-bindings: iio: magnetometer: asahi-kasei,ak8975: Drop deprecated enums" Reverted as Rob Herring is tracking undocumented compatibles and as per discussion in the thread, we can not remove this deprecated compatibles from the driver without potential/likely regressions. So keep it around as it's not that painful anyway. This reverts commit 711fb79a1ea8e79dc600f25d9f8c1ac25870b4de. Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml index ee77558e9800..9790f75fc669 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml @@ -18,6 +18,13 @@ properties: - asahi-kasei,ak09911 - asahi-kasei,ak09912 - asahi-kasei,ak09916 + - enum: + - ak8975 + - ak8963 + - ak09911 + - ak09912 + - ak09916 + deprecated: true reg: maxItems: 1 -- cgit v1.2.3 From 564cfb28409a265f272f0e7c73ce211827b36204 Mon Sep 17 00:00:00 2001 From: Alisa-Dariana Roman Date: Tue, 10 Oct 2023 15:49:24 +0300 Subject: iio: adc: ad7192: Organize chip info Move all chip specific data into chip_info structure. Signed-off-by: Alisa-Dariana Roman Link: https://lore.kernel.org/r/20231010124927.143343-2-alisadariana@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index e0be394ccb34..d72a190c5109 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -171,6 +171,9 @@ enum { struct ad7192_chip_info { unsigned int chip_id; const char *name; + const struct iio_chan_spec *channels; + u8 num_channels; + const struct iio_info *info; }; struct ad7192_state { @@ -964,39 +967,33 @@ static const struct ad7192_chip_info ad7192_chip_info_tbl[] = { [ID_AD7190] = { .chip_id = CHIPID_AD7190, .name = "ad7190", + .channels = ad7192_channels, + .num_channels = ARRAY_SIZE(ad7192_channels), + .info = &ad7192_info, }, [ID_AD7192] = { .chip_id = CHIPID_AD7192, .name = "ad7192", + .channels = ad7192_channels, + .num_channels = ARRAY_SIZE(ad7192_channels), + .info = &ad7192_info, }, [ID_AD7193] = { .chip_id = CHIPID_AD7193, .name = "ad7193", + .channels = ad7193_channels, + .num_channels = ARRAY_SIZE(ad7193_channels), + .info = &ad7192_info, }, [ID_AD7195] = { .chip_id = CHIPID_AD7195, .name = "ad7195", + .channels = ad7192_channels, + .num_channels = ARRAY_SIZE(ad7192_channels), + .info = &ad7195_info, }, }; -static int ad7192_channels_config(struct iio_dev *indio_dev) -{ - struct ad7192_state *st = iio_priv(indio_dev); - - switch (st->chip_info->chip_id) { - case CHIPID_AD7193: - indio_dev->channels = ad7193_channels; - indio_dev->num_channels = ARRAY_SIZE(ad7193_channels); - break; - default: - indio_dev->channels = ad7192_channels; - indio_dev->num_channels = ARRAY_SIZE(ad7192_channels); - break; - } - - return 0; -} - static void ad7192_reg_disable(void *reg) { regulator_disable(reg); @@ -1051,15 +1048,9 @@ static int ad7192_probe(struct spi_device *spi) st->chip_info = (void *)spi_get_device_id(spi)->driver_data; indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; - - ret = ad7192_channels_config(indio_dev); - if (ret < 0) - return ret; - - if (st->chip_info->chip_id == CHIPID_AD7195) - indio_dev->info = &ad7195_info; - else - indio_dev->info = &ad7192_info; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->info; ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info); if (ret) -- cgit v1.2.3 From 15f3b48799f7ebcc49b22211f0fcb58a16874474 Mon Sep 17 00:00:00 2001 From: Alisa-Dariana Roman Date: Tue, 10 Oct 2023 15:49:25 +0300 Subject: iio: adc: ad7192: Remove unused member Remove extend_name from channel macro since it is not used anywhere. Signed-off-by: Alisa-Dariana Roman Link: https://lore.kernel.org/r/20231010124927.143343-3-alisadariana@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index d72a190c5109..fe47ef43b3d7 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -895,8 +895,8 @@ static const struct iio_info ad7195_info = { .update_scan_mode = ad7192_update_scan_mode, }; -#define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _extend_name, \ - _type, _mask_type_av, _ext_info) \ +#define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _type, \ + _mask_type_av, _ext_info) \ { \ .type = (_type), \ .differential = ((_channel2) == -1 ? 0 : 1), \ @@ -904,7 +904,6 @@ static const struct iio_info ad7195_info = { .channel = (_channel1), \ .channel2 = (_channel2), \ .address = (_address), \ - .extend_name = (_extend_name), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ @@ -922,16 +921,15 @@ static const struct iio_info ad7195_info = { } #define AD719x_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \ - __AD719x_CHANNEL(_si, _channel1, _channel2, _address, NULL, \ - IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE), \ - ad7192_calibsys_ext_info) + __AD719x_CHANNEL(_si, _channel1, _channel2, _address, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) #define AD719x_CHANNEL(_si, _channel1, _address) \ - __AD719x_CHANNEL(_si, _channel1, -1, _address, NULL, IIO_VOLTAGE, \ + __AD719x_CHANNEL(_si, _channel1, -1, _address, IIO_VOLTAGE, \ BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) #define AD719x_TEMP_CHANNEL(_si, _address) \ - __AD719x_CHANNEL(_si, 0, -1, _address, NULL, IIO_TEMP, 0, NULL) + __AD719x_CHANNEL(_si, 0, -1, _address, IIO_TEMP, 0, NULL) static const struct iio_chan_spec ad7192_channels[] = { AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M), -- cgit v1.2.3 From db7fe1f610b39b398076f6c3fcb33378c4cb58ef Mon Sep 17 00:00:00 2001 From: Alisa-Dariana Roman Date: Tue, 10 Oct 2023 15:49:26 +0300 Subject: iio: adc: ad7192: Add fast settling support Add fast settling mode support for AD7193. Add two new device specific attributes: oversampling_ratio and oversampling_ratio_available. For AD7193 the user can set the average factor by writing to oversampling_ratio. The possible values are exposed when reading oversampling_ratio_available. Signed-off-by: Alisa-Dariana Roman Link: https://lore.kernel.org/r/20231010124927.143343-4-alisadariana@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 107 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index fe47ef43b3d7..954093ee0fbd 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -60,6 +60,8 @@ #define AD7192_MODE_SEL_MASK GENMASK(23, 21) /* Operation Mode Select Mask */ #define AD7192_MODE_STA_MASK BIT(20) /* Status Register transmission Mask */ #define AD7192_MODE_CLKSRC_MASK GENMASK(19, 18) /* Clock Source Select Mask */ +#define AD7192_MODE_AVG_MASK GENMASK(17, 16) + /* Fast Settling Filter Average Select Mask (AD7193 only) */ #define AD7192_MODE_SINC3 BIT(15) /* SINC3 Filter Select */ #define AD7192_MODE_ENPAR BIT(13) /* Parity Enable */ #define AD7192_MODE_CLKDIV BIT(12) /* Clock divide by 2 (AD7190/2 only)*/ @@ -185,6 +187,7 @@ struct ad7192_state { u32 mode; u32 conf; u32 scale_avail[8][2]; + u32 oversampling_ratio_avail[4]; u8 gpocon; u8 clock_sel; struct mutex lock; /* protect sensor state */ @@ -462,6 +465,11 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) st->scale_avail[i][0] = scale_uv; } + st->oversampling_ratio_avail[0] = 1; + st->oversampling_ratio_avail[1] = 2; + st->oversampling_ratio_avail[2] = 8; + st->oversampling_ratio_avail[3] = 16; + return 0; } @@ -531,15 +539,21 @@ static ssize_t ad7192_set(struct device *dev, return ret ? ret : len; } -static int ad7192_compute_f_order(bool sinc3_en, bool chop_en) +static int ad7192_compute_f_order(struct ad7192_state *st, bool sinc3_en, bool chop_en) { - if (!chop_en) + u8 avg_factor_selected, oversampling_ratio; + + avg_factor_selected = FIELD_GET(AD7192_MODE_AVG_MASK, st->mode); + + if (!avg_factor_selected && !chop_en) return 1; + oversampling_ratio = st->oversampling_ratio_avail[avg_factor_selected]; + if (sinc3_en) - return AD7192_SYNC3_FILTER; + return AD7192_SYNC3_FILTER + oversampling_ratio - 1; - return AD7192_SYNC4_FILTER; + return AD7192_SYNC4_FILTER + oversampling_ratio - 1; } static int ad7192_get_f_order(struct ad7192_state *st) @@ -549,13 +563,13 @@ static int ad7192_get_f_order(struct ad7192_state *st) sinc3_en = FIELD_GET(AD7192_MODE_SINC3, st->mode); chop_en = FIELD_GET(AD7192_CONF_CHOP, st->conf); - return ad7192_compute_f_order(sinc3_en, chop_en); + return ad7192_compute_f_order(st, sinc3_en, chop_en); } static int ad7192_compute_f_adc(struct ad7192_state *st, bool sinc3_en, bool chop_en) { - unsigned int f_order = ad7192_compute_f_order(sinc3_en, chop_en); + unsigned int f_order = ad7192_compute_f_order(st, sinc3_en, chop_en); return DIV_ROUND_CLOSEST(st->fclk, f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); @@ -753,6 +767,9 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, *val = ad7192_get_3db_filter_freq(st); *val2 = 1000; return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling_ratio_avail[FIELD_GET(AD7192_MODE_AVG_MASK, st->mode)]; + return IIO_VAL_INT; } return -EINVAL; @@ -810,6 +827,23 @@ static int ad7192_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: ret = ad7192_set_3db_filter_freq(st, val, val2 / 1000); break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = -EINVAL; + mutex_lock(&st->lock); + for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++) + if (val == st->oversampling_ratio_avail[i]) { + ret = 0; + tmp = st->mode; + st->mode &= ~AD7192_MODE_AVG_MASK; + st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i); + if (tmp == st->mode) + break; + ad_sd_write_reg(&st->sd, AD7192_REG_MODE, + 3, st->mode); + break; + } + mutex_unlock(&st->lock); + break; default: ret = -EINVAL; } @@ -830,6 +864,8 @@ static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; default: return -EINVAL; } @@ -849,6 +885,12 @@ static int ad7192_read_avail(struct iio_dev *indio_dev, /* Values are stored in a 2D matrix */ *length = ARRAY_SIZE(st->scale_avail) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = (int *)st->oversampling_ratio_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(st->oversampling_ratio_avail); + return IIO_AVAIL_LIST; } @@ -896,7 +938,7 @@ static const struct iio_info ad7195_info = { }; #define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _type, \ - _mask_type_av, _ext_info) \ + _mask_all, _mask_type_av, _mask_all_av, _ext_info) \ { \ .type = (_type), \ .differential = ((_channel2) == -1 ? 0 : 1), \ @@ -908,8 +950,10 @@ static const struct iio_info ad7195_info = { BIT(IIO_CHAN_INFO_OFFSET), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + (_mask_all), \ .info_mask_shared_by_type_available = (_mask_type_av), \ + .info_mask_shared_by_all_available = (_mask_all_av), \ .ext_info = (_ext_info), \ .scan_index = (_si), \ .scan_type = { \ @@ -921,15 +965,26 @@ static const struct iio_info ad7195_info = { } #define AD719x_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \ - __AD719x_CHANNEL(_si, _channel1, _channel2, _address, IIO_VOLTAGE, \ - BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) + __AD719x_CHANNEL(_si, _channel1, _channel2, _address, IIO_VOLTAGE, 0, \ + BIT(IIO_CHAN_INFO_SCALE), 0, ad7192_calibsys_ext_info) #define AD719x_CHANNEL(_si, _channel1, _address) \ - __AD719x_CHANNEL(_si, _channel1, -1, _address, IIO_VOLTAGE, \ - BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) + __AD719x_CHANNEL(_si, _channel1, -1, _address, IIO_VOLTAGE, 0, \ + BIT(IIO_CHAN_INFO_SCALE), 0, ad7192_calibsys_ext_info) #define AD719x_TEMP_CHANNEL(_si, _address) \ - __AD719x_CHANNEL(_si, 0, -1, _address, IIO_TEMP, 0, NULL) + __AD719x_CHANNEL(_si, 0, -1, _address, IIO_TEMP, 0, 0, 0, NULL) + +#define AD7193_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \ + __AD719x_CHANNEL(_si, _channel1, _channel2, _address, \ + IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + ad7192_calibsys_ext_info) + +#define AD7193_CHANNEL(_si, _channel1, _address) \ + AD7193_DIFF_CHANNEL(_si, _channel1, -1, _address) static const struct iio_chan_spec ad7192_channels[] = { AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M), @@ -944,20 +999,20 @@ static const struct iio_chan_spec ad7192_channels[] = { }; static const struct iio_chan_spec ad7193_channels[] = { - AD719x_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M), - AD719x_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M), - AD719x_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M), - AD719x_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M), + AD7193_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M), + AD7193_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M), + AD7193_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M), + AD7193_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M), AD719x_TEMP_CHANNEL(4, AD7193_CH_TEMP), - AD719x_DIFF_CHANNEL(5, 2, 2, AD7193_CH_AIN2P_AIN2M), - AD719x_CHANNEL(6, 1, AD7193_CH_AIN1), - AD719x_CHANNEL(7, 2, AD7193_CH_AIN2), - AD719x_CHANNEL(8, 3, AD7193_CH_AIN3), - AD719x_CHANNEL(9, 4, AD7193_CH_AIN4), - AD719x_CHANNEL(10, 5, AD7193_CH_AIN5), - AD719x_CHANNEL(11, 6, AD7193_CH_AIN6), - AD719x_CHANNEL(12, 7, AD7193_CH_AIN7), - AD719x_CHANNEL(13, 8, AD7193_CH_AIN8), + AD7193_DIFF_CHANNEL(5, 2, 2, AD7193_CH_AIN2P_AIN2M), + AD7193_CHANNEL(6, 1, AD7193_CH_AIN1), + AD7193_CHANNEL(7, 2, AD7193_CH_AIN2), + AD7193_CHANNEL(8, 3, AD7193_CH_AIN3), + AD7193_CHANNEL(9, 4, AD7193_CH_AIN4), + AD7193_CHANNEL(10, 5, AD7193_CH_AIN5), + AD7193_CHANNEL(11, 6, AD7193_CH_AIN6), + AD7193_CHANNEL(12, 7, AD7193_CH_AIN7), + AD7193_CHANNEL(13, 8, AD7193_CH_AIN8), IIO_CHAN_SOFT_TIMESTAMP(14), }; -- cgit v1.2.3 From 6eb14ffa1873d756b9141837720ff70f07d849f1 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 10 Oct 2023 14:19:40 +0200 Subject: iio: adc: mt6577_auxadc: Fix kernel panic on suspend Commit a2d518fbe376 ("iio: adc: mt6577_auxadc: Simplify with device managed function") simplified the driver with devm hooks, but wrongly states that the platform_set_drvdata(), platform_get_drvdata() are unused after the simplification: the driver data is infact used in .suspend() and .resume() PM callbacks, currently producing a kernel panic. Reintroduce the call to platform_set_drvdata() in the probe function Fixes: a2d518fbe376 ("iio: adc: mt6577_auxadc: Simplify with device managed function") Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Markus Schneider-Pargmann Reviewed-by: Alexandre Mergnat Link: https://lore.kernel.org/r/20231010121940.159696-1-angelogioacchino.delregno@collabora.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6577_auxadc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c index 370b84c2d0ba..3343b54e8e44 100644 --- a/drivers/iio/adc/mt6577_auxadc.c +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -293,6 +293,7 @@ static int mt6577_auxadc_probe(struct platform_device *pdev) mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, MT6577_AUXADC_PDN_EN, 0); mdelay(MT6577_AUXADC_POWER_READY_MS); + platform_set_drvdata(pdev, indio_dev); ret = devm_add_action_or_reset(&pdev->dev, mt6577_power_off, adc_dev); if (ret) -- cgit v1.2.3 From d97d11c70f25726fc21b2b2f1a26e16ac96a8ccf Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:18 -0500 Subject: staging: iio: resolver: ad2s1210: do not use fault register for dummy read When reading registers on the AD2S1210 chip, we have to supply a "dummy" address for the second SPI tx byte so that we don't accidentally write to a register. This register will be read and the value discarded on the next regmap read or write call. Reading the fault register has a side-effect of clearing the faults so we should not use this register for the dummy read. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-1-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 67d8af0dd7ae..8fbde9517fe9 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -166,9 +166,10 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, st->tx[0] = reg; /* * Must be valid register address here otherwise this could write data. - * It doesn't matter which one. + * It doesn't matter which one as long as reading doesn't have side- + * effects. */ - st->tx[1] = AD2S1210_REG_FAULT; + st->tx[1] = AD2S1210_REG_CONTROL; ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); if (ret < 0) -- cgit v1.2.3 From bae023765199bef243b49c0f8860a3290a5c9f6d Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:19 -0500 Subject: staging: iio: resolver: ad2s1210: implement hysteresis as channel attr The AD2S1210 resolver has a hysteresis feature that can be used to prevent flicker in the LSB of the position register. This can be either enabled or disabled. Disabling hysteresis is useful for increasing precision by oversampling. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-2-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 91 +++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 8fbde9517fe9..af063eb25e9c 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -76,7 +76,8 @@ struct ad2s1210_state { struct regmap *regmap; /** The external oscillator frequency in Hz. */ unsigned long clkin_hz; - bool hysteresis; + /** Available raw hysteresis values based on resolution. */ + int hysteresis_available[2]; u8 resolution; /** For reading raw sample value via SPI. */ __be16 sample __aligned(IIO_DMA_MINALIGN); @@ -311,6 +312,7 @@ static ssize_t ad2s1210_store_resolution(struct device *dev, goto error_ret; st->resolution = udata; + st->hysteresis_available[1] = 1 << (16 - st->resolution); ret = len; error_ret: @@ -447,6 +449,35 @@ error_ret: return ret; } +static int ad2s1210_get_hysteresis(struct ad2s1210_state *st, int *val) +{ + int ret; + + mutex_lock(&st->lock); + ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_ENABLE_HYSTERESIS); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + *val = ret << (16 - st->resolution); + return IIO_VAL_INT; +} + +static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_ENABLE_HYSTERESIS, + val ? AD2S1210_ENABLE_HYSTERESIS : 0); + mutex_unlock(&st->lock); + + return ret; +} + static const int ad2s1210_velocity_scale[] = { 17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */ 42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */ @@ -479,7 +510,55 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + return ad2s1210_get_hysteresis(st, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad2s1210_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + *vals = st->hysteresis_available; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(st->hysteresis_available); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} +static int ad2s1210_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + return ad2s1210_set_hysteresis(st, val); + default: + return -EINVAL; + } default: return -EINVAL; } @@ -520,7 +599,10 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .indexed = 1, .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_HYSTERESIS), }, { .type = IIO_ANGL_VEL, .indexed = 1, @@ -596,6 +678,8 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, static const struct iio_info ad2s1210_info = { .read_raw = ad2s1210_read_raw, + .read_avail = ad2s1210_read_avail, + .write_raw = ad2s1210_write_raw, .attrs = &ad2s1210_attribute_group, .debugfs_reg_access = &ad2s1210_debugfs_reg_access, }; @@ -711,8 +795,9 @@ static int ad2s1210_probe(struct spi_device *spi) mutex_init(&st->lock); st->sdev = spi; - st->hysteresis = true; st->resolution = 12; + st->hysteresis_available[0] = 0; + st->hysteresis_available[1] = 1 << (16 - st->resolution); ret = ad2s1210_setup_clocks(st); if (ret < 0) -- cgit v1.2.3 From 7dde2719fb3892ad69da4b1c5790bac5ea958a09 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:20 -0500 Subject: staging: iio: resolver: ad2s1210: convert fexcit to channel attribute The ad2s1210 driver has a device-specific attribute `fexcit` for setting the frequency of the excitation output. This converts it to a channel in order to use standard IIO ABI. The excitation frequency is an analog output that generates a sine wave. Only the frequency is configurable. According to the datasheet, the specified range of the excitation frequency is from 2 kHz to 20 kHz and can be set in increments of 250 Hz. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-3-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 123 ++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 52 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index af063eb25e9c..0c7772725330 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -227,54 +227,6 @@ static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st, bitmap); } -static ssize_t ad2s1210_show_fexcit(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int value; - u16 fexcit; - int ret; - - mutex_lock(&st->lock); - ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, &value); - if (ret < 0) - goto error_ret; - - fexcit = value * st->clkin_hz / (1 << 15); - - ret = sprintf(buf, "%u\n", fexcit); - -error_ret: - mutex_unlock(&st->lock); - return ret; -} - -static ssize_t ad2s1210_store_fexcit(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - u16 fexcit; - int ret; - - ret = kstrtou16(buf, 10, &fexcit); - if (ret < 0 || fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) - return -EINVAL; - - mutex_lock(&st->lock); - ret = ad2s1210_reinit_excitation_frequency(st, fexcit); - if (ret < 0) - goto error_ret; - - ret = len; - -error_ret: - mutex_unlock(&st->lock); - - return ret; -} - static ssize_t ad2s1210_show_resolution(struct device *dev, struct device_attribute *attr, char *buf) @@ -478,6 +430,38 @@ static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val) return ret; } +static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val) +{ + unsigned int reg_val; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, ®_val); + if (ret < 0) + goto error_ret; + + *val = reg_val * st->clkin_hz / (1 << 15); + ret = IIO_VAL_INT; + +error_ret: + mutex_unlock(&st->lock); + return ret; +} + +static int ad2s1210_set_excitation_frequency(struct ad2s1210_state *st, int val) +{ + int ret; + + if (val < AD2S1210_MIN_EXCIT || val > AD2S1210_MAX_EXCIT) + return -EINVAL; + + mutex_lock(&st->lock); + ret = ad2s1210_reinit_excitation_frequency(st, val); + mutex_unlock(&st->lock); + + return ret; +} + static const int ad2s1210_velocity_scale[] = { 17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */ 42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */ @@ -510,6 +494,13 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->type) { + case IIO_ALTVOLTAGE: + return ad2s1210_get_excitation_frequency(st, val); + default: + return -EINVAL; + } case IIO_CHAN_INFO_HYSTERESIS: switch (chan->type) { case IIO_ANGL: @@ -527,9 +518,24 @@ static int ad2s1210_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + static const int excitation_frequency_available[] = { + AD2S1210_MIN_EXCIT, + 250, /* step */ + AD2S1210_MAX_EXCIT, + }; + struct ad2s1210_state *st = iio_priv(indio_dev); switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->type) { + case IIO_ALTVOLTAGE: + *type = IIO_VAL_INT; + *vals = excitation_frequency_available; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } case IIO_CHAN_INFO_HYSTERESIS: switch (chan->type) { case IIO_ANGL: @@ -552,6 +558,13 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, struct ad2s1210_state *st = iio_priv(indio_dev); switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->type) { + case IIO_ALTVOLTAGE: + return ad2s1210_set_excitation_frequency(st, val); + default: + return -EINVAL; + } case IIO_CHAN_INFO_HYSTERESIS: switch (chan->type) { case IIO_ANGL: @@ -564,8 +577,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, } } -static IIO_DEVICE_ATTR(fexcit, 0644, - ad2s1210_show_fexcit, ad2s1210_store_fexcit, 0); static IIO_DEVICE_ATTR(bits, 0644, ad2s1210_show_resolution, ad2s1210_store_resolution, 0); static IIO_DEVICE_ATTR(fault, 0644, @@ -609,11 +620,19 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - } + }, { + /* excitation frequency output */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 0, + .output = 1, + .scan_index = -1, + .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_FREQUENCY), + }, }; static struct attribute *ad2s1210_attributes[] = { - &iio_dev_attr_fexcit.dev_attr.attr, &iio_dev_attr_bits.dev_attr.attr, &iio_dev_attr_fault.dev_attr.attr, &iio_dev_attr_los_thrd.dev_attr.attr, -- cgit v1.2.3 From 3e7d173aef28d05a8db2941d78498f67746b27bc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:21 -0500 Subject: staging: iio: resolver: ad2s1210: convert resolution to devicetree property Selecting the resolution was implemented as the `bits` sysfs attribute. However, the selection of the resolution depends on how the hardware is wired and the specific application, so this is rather a job for devicetree to describe. A new devicetree property `assigned-resolution-bits` to specify the resolution required for each chip is added and the `bits` sysfs attribute is removed. Since the resolution is now supplied by a devicetree property, the resolution-gpios are now optional and we can allow for the case where the resolution pins on the AD2S1210 are hard-wired instead of requiring them to be connected to gpios. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-4-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 150 +++++++++++++++----------------- 1 file changed, 71 insertions(+), 79 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 0c7772725330..66ef35fbb6fe 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -63,6 +63,13 @@ enum ad2s1210_mode { MOD_CONFIG = 0b11, }; +enum ad2s1210_resolution { + AD2S1210_RES_10 = 0b00, + AD2S1210_RES_12 = 0b01, + AD2S1210_RES_14 = 0b10, + AD2S1210_RES_16 = 0b11, +}; + struct ad2s1210_state { struct mutex lock; struct spi_device *sdev; @@ -70,15 +77,14 @@ struct ad2s1210_state { struct gpio_desc *sample_gpio; /** GPIO pins connected to A0 and A1 lines. */ struct gpio_descs *mode_gpios; - /** GPIO pins connected to RES0 and RES1 lines. */ - struct gpio_descs *resolution_gpios; /** Used to access config registers. */ struct regmap *regmap; /** The external oscillator frequency in Hz. */ unsigned long clkin_hz; /** Available raw hysteresis values based on resolution. */ int hysteresis_available[2]; - u8 resolution; + /** The selected resolution */ + enum ad2s1210_resolution resolution; /** For reading raw sample value via SPI. */ __be16 sample __aligned(IIO_DMA_MINALIGN); /** SPI transmit buffer. */ @@ -215,63 +221,6 @@ static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); } -static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st, - u8 resolution) -{ - struct gpio_descs *gpios = st->resolution_gpios; - DECLARE_BITMAP(bitmap, 2); - - bitmap[0] = (resolution - 10) >> 1; - - return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, - bitmap); -} - -static ssize_t ad2s1210_show_resolution(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - - return sprintf(buf, "%d\n", st->resolution); -} - -static ssize_t ad2s1210_store_resolution(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned char data; - unsigned char udata; - int ret; - - ret = kstrtou8(buf, 10, &udata); - if (ret || udata < 10 || udata > 16) { - dev_err(dev, "ad2s1210: resolution out of range\n"); - return -EINVAL; - } - - data = (udata - 10) >> 1; - - mutex_lock(&st->lock); - ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, - AD2S1210_SET_RES, data); - if (ret < 0) - goto error_ret; - - ret = ad2s1210_set_resolution_gpios(st, udata); - if (ret < 0) - goto error_ret; - - st->resolution = udata; - st->hysteresis_available[1] = 1 << (16 - st->resolution); - ret = len; - -error_ret: - mutex_unlock(&st->lock); - return ret; -} - /* read the fault register since last sample */ static ssize_t ad2s1210_show_fault(struct device *dev, struct device_attribute *attr, char *buf) @@ -413,7 +362,7 @@ static int ad2s1210_get_hysteresis(struct ad2s1210_state *st, int *val) if (ret < 0) return ret; - *val = ret << (16 - st->resolution); + *val = ret << (2 * (AD2S1210_RES_16 - st->resolution)); return IIO_VAL_INT; } @@ -577,8 +526,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, } } -static IIO_DEVICE_ATTR(bits, 0644, - ad2s1210_show_resolution, ad2s1210_store_resolution, 0); static IIO_DEVICE_ATTR(fault, 0644, ad2s1210_show_fault, ad2s1210_clear_fault, 0); @@ -633,7 +580,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = { }; static struct attribute *ad2s1210_attributes[] = { - &iio_dev_attr_bits.dev_attr.attr, &iio_dev_attr_fault.dev_attr.attr, &iio_dev_attr_los_thrd.dev_attr.attr, &iio_dev_attr_dos_ovr_thrd.dev_attr.attr, @@ -655,15 +601,12 @@ static int ad2s1210_initial(struct ad2s1210_state *st) int ret; mutex_lock(&st->lock); - ret = ad2s1210_set_resolution_gpios(st, st->resolution); - if (ret < 0) - goto error_ret; /* Use default config register value plus resolution from devicetree. */ data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1); data |= FIELD_PREP(AD2S1210_ENABLE_HYSTERESIS, 1); data |= FIELD_PREP(AD2S1210_SET_ENRES, 0x3); - data |= FIELD_PREP(AD2S1210_SET_RES, (st->resolution - 10) >> 1); + data |= FIELD_PREP(AD2S1210_SET_RES, st->resolution); ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); if (ret < 0) @@ -703,6 +646,35 @@ static const struct iio_info ad2s1210_info = { .debugfs_reg_access = &ad2s1210_debugfs_reg_access, }; +static int ad2s1210_setup_properties(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + u32 val; + int ret; + + ret = device_property_read_u32(dev, "assigned-resolution-bits", &val); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to read assigned-resolution-bits property\n"); + + if (val < 10 || val > 16) + return dev_err_probe(dev, -EINVAL, + "resolution out of range: %u\n", val); + + st->resolution = (val - 10) >> 1; + /* + * These are values that correlate to the hysteresis bit in the Control + * register. 0 = disabled, 1 = enabled. When enabled, the actual + * hysteresis is +/- 1 LSB of the raw position value. Which bit is the + * LSB depends on the specified resolution. + */ + st->hysteresis_available[0] = 0; + st->hysteresis_available[1] = 1 << (2 * (AD2S1210_RES_16 - + st->resolution)); + + return 0; +} + static int ad2s1210_setup_clocks(struct ad2s1210_state *st) { struct device *dev = &st->sdev->dev; @@ -724,6 +696,9 @@ static int ad2s1210_setup_clocks(struct ad2s1210_state *st) static int ad2s1210_setup_gpios(struct ad2s1210_state *st) { struct device *dev = &st->sdev->dev; + struct gpio_descs *resolution_gpios; + DECLARE_BITMAP(bitmap, 2); + int ret; /* should not be sampling on startup */ st->sample_gpio = devm_gpiod_get(dev, "sample", GPIOD_OUT_LOW); @@ -741,16 +716,32 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st) return dev_err_probe(dev, -EINVAL, "requires exactly 2 mode-gpios\n"); - /* both pins high means that we start with 16-bit resolution */ - st->resolution_gpios = devm_gpiod_get_array(dev, "resolution", - GPIOD_OUT_HIGH); - if (IS_ERR(st->resolution_gpios)) - return dev_err_probe(dev, PTR_ERR(st->resolution_gpios), + /* + * If resolution gpios are provided, they get set to the required + * resolution, otherwise it is assumed the RES0 and RES1 pins are + * hard-wired to match the resolution indicated in the devicetree. + */ + resolution_gpios = devm_gpiod_get_array_optional(dev, "resolution", + GPIOD_ASIS); + if (IS_ERR(resolution_gpios)) + return dev_err_probe(dev, PTR_ERR(resolution_gpios), "failed to request resolution GPIOs\n"); - if (st->resolution_gpios->ndescs != 2) - return dev_err_probe(dev, -EINVAL, - "requires exactly 2 resolution-gpios\n"); + if (resolution_gpios) { + if (resolution_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, + "requires exactly 2 resolution-gpios\n"); + + bitmap[0] = st->resolution; + + ret = gpiod_set_array_value(resolution_gpios->ndescs, + resolution_gpios->desc, + resolution_gpios->info, + bitmap); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to set resolution gpios\n"); + } return 0; } @@ -814,9 +805,10 @@ static int ad2s1210_probe(struct spi_device *spi) mutex_init(&st->lock); st->sdev = spi; - st->resolution = 12; - st->hysteresis_available[0] = 0; - st->hysteresis_available[1] = 1 << (16 - st->resolution); + + ret = ad2s1210_setup_properties(st); + if (ret < 0) + return ret; ret = ad2s1210_setup_clocks(st); if (ret < 0) -- cgit v1.2.3 From b3335cd557fb886cd58ac6f9d0b283bbab120411 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:22 -0500 Subject: staging: iio: resolver: ad2s1210: add phase lock range support The AD2S1210 chip has a phase lock range feature that allows selecting the allowable phase difference between the excitation output and the sine and cosine inputs. This can be set to either 44 degrees (default) or 360 degrees. This patch adds a new phase channel with a phase0_mag_rising event that can be used to configure the phase lock range. Actually emitting the event will be added in a subsequent patch. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-5-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 125 ++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 66ef35fbb6fe..83f6ac890dbc 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -56,6 +56,13 @@ #define AD2S1210_MIN_FCW 0x4 #define AD2S1210_MAX_FCW 0x50 +/* 44 degrees ~= 0.767945 radians */ +#define PHASE_44_DEG_TO_RAD_INT 0 +#define PHASE_44_DEG_TO_RAD_MICRO 767945 +/* 360 degrees ~= 6.283185 radians */ +#define PHASE_360_DEG_TO_RAD_INT 6 +#define PHASE_360_DEG_TO_RAD_MICRO 283185 + enum ad2s1210_mode { MOD_POS = 0b00, MOD_VEL = 0b01, @@ -379,6 +386,54 @@ static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val) return ret; } +static int ad2s1210_get_phase_lock_range(struct ad2s1210_state *st, + int *val, int *val2) +{ + int ret; + + mutex_lock(&st->lock); + ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_PHASE_LOCK_RANGE_44); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + if (ret) { + /* 44 degrees as radians */ + *val = PHASE_44_DEG_TO_RAD_INT; + *val2 = PHASE_44_DEG_TO_RAD_MICRO; + } else { + /* 360 degrees as radians */ + *val = PHASE_360_DEG_TO_RAD_INT; + *val2 = PHASE_360_DEG_TO_RAD_MICRO; + } + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad2s1210_set_phase_lock_range(struct ad2s1210_state *st, + int val, int val2) +{ + int deg, ret; + + /* convert radians to degrees - only two allowable values */ + if (val == PHASE_44_DEG_TO_RAD_INT && val2 == PHASE_44_DEG_TO_RAD_MICRO) + deg = 44; + else if (val == PHASE_360_DEG_TO_RAD_INT && + val2 == PHASE_360_DEG_TO_RAD_MICRO) + deg = 360; + else + return -EINVAL; + + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_PHASE_LOCK_RANGE_44, + deg == 44 ? AD2S1210_PHASE_LOCK_RANGE_44 : 0); + mutex_unlock(&st->lock); + return ret; +} + static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val) { unsigned int reg_val; @@ -551,6 +606,16 @@ static IIO_DEVICE_ATTR(lot_low_thrd, 0644, ad2s1210_show_reg, ad2s1210_store_reg, AD2S1210_REG_LOT_LOW_THRD); +static const struct iio_event_spec ad2s1210_phase_event_spec[] = { + { + /* Phase error fault. */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + /* Phase lock range. */ + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + static const struct iio_chan_spec ad2s1210_channels[] = { { .type = IIO_ANGL, @@ -567,6 +632,14 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + }, { + /* used to configure phase lock range and get phase lock error */ + .type = IIO_PHASE, + .indexed = 1, + .channel = 0, + .scan_index = -1, + .event_spec = ad2s1210_phase_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_phase_event_spec), }, { /* excitation frequency output */ .type = IIO_ALTVOLTAGE, @@ -595,6 +668,21 @@ static const struct attribute_group ad2s1210_attribute_group = { .attrs = ad2s1210_attributes, }; +static IIO_CONST_ATTR(in_phase0_mag_rising_value_available, + __stringify(PHASE_44_DEG_TO_RAD_INT) "." + __stringify(PHASE_44_DEG_TO_RAD_MICRO) " " + __stringify(PHASE_360_DEG_TO_RAD_INT) "." + __stringify(PHASE_360_DEG_TO_RAD_MICRO)); + +static struct attribute *ad2s1210_event_attributes[] = { + &iio_const_attr_in_phase0_mag_rising_value_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad2s1210_event_attribute_group = { + .attrs = ad2s1210_event_attributes, +}; + static int ad2s1210_initial(struct ad2s1210_state *st) { unsigned char data; @@ -619,6 +707,40 @@ error_ret: return ret; } +static int ad2s1210_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_PHASE: + return ad2s1210_get_phase_lock_range(st, val, val2); + default: + return -EINVAL; + } +} + +static int ad2s1210_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_PHASE: + return ad2s1210_set_phase_lock_range(st, val, val2); + default: + return -EINVAL; + } +} + static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) @@ -639,10 +761,13 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, } static const struct iio_info ad2s1210_info = { + .event_attrs = &ad2s1210_event_attribute_group, .read_raw = ad2s1210_read_raw, .read_avail = ad2s1210_read_avail, .write_raw = ad2s1210_write_raw, .attrs = &ad2s1210_attribute_group, + .read_event_value = ad2s1210_read_event_value, + .write_event_value = ad2s1210_write_event_value, .debugfs_reg_access = &ad2s1210_debugfs_reg_access, }; -- cgit v1.2.3 From 128b9389db0ed66ab0a108a1b439cabf2537032f Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:23 -0500 Subject: staging: iio: resolver: ad2s1210: add triggered buffer support This adds support for triggered buffers to the AD2S1210 resolver driver. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-6-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 83 ++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 83f6ac890dbc..4d651a2d0f38 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -20,8 +20,11 @@ #include #include +#include #include #include +#include +#include #define DRV_NAME "ad2s1210" @@ -94,6 +97,12 @@ struct ad2s1210_state { enum ad2s1210_resolution resolution; /** For reading raw sample value via SPI. */ __be16 sample __aligned(IIO_DMA_MINALIGN); + /** Scan buffer */ + struct { + __be16 chan[2]; + /* Ensure timestamp is naturally aligned. */ + s64 timestamp __aligned(8); + } scan; /** SPI transmit buffer. */ u8 rx[2]; /** SPI receive buffer. */ @@ -621,6 +630,13 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .type = IIO_ANGL, .indexed = 1, .channel = 0, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_HYSTERESIS), @@ -630,9 +646,18 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .type = IIO_ANGL_VEL, .indexed = 1, .channel = 0, + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - }, { + }, + IIO_CHAN_SOFT_TIMESTAMP(2), + { /* used to configure phase lock range and get phase lock error */ .type = IIO_PHASE, .indexed = 1, @@ -760,6 +785,55 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, return ret; } +static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad2s1210_state *st = iio_priv(indio_dev); + size_t chan = 0; + int ret; + + mutex_lock(&st->lock); + + memset(&st->scan, 0, sizeof(st->scan)); + gpiod_set_value(st->sample_gpio, 1); + + if (test_bit(0, indio_dev->active_scan_mask)) { + ret = ad2s1210_set_mode(st, MOD_POS); + if (ret < 0) + goto error_ret; + + /* REVIST: we can read 3 bytes here and also get fault flags */ + ret = spi_read(st->sdev, st->rx, 2); + if (ret < 0) + goto error_ret; + + memcpy(&st->scan.chan[chan++], st->rx, 2); + } + + if (test_bit(1, indio_dev->active_scan_mask)) { + ret = ad2s1210_set_mode(st, MOD_VEL); + if (ret < 0) + goto error_ret; + + /* REVIST: we can read 3 bytes here and also get fault flags */ + ret = spi_read(st->sdev, st->rx, 2); + if (ret < 0) + goto error_ret; + + memcpy(&st->scan.chan[chan++], st->rx, 2); + } + + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); + +error_ret: + gpiod_set_value(st->sample_gpio, 0); + mutex_unlock(&st->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const struct iio_info ad2s1210_info = { .event_attrs = &ad2s1210_event_attribute_group, .read_raw = ad2s1210_read_raw, @@ -957,6 +1031,13 @@ static int ad2s1210_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); indio_dev->name = spi_get_device_id(spi)->name; + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &ad2s1210_trigger_handler, NULL); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "iio triggered buffer setup failed\n"); + return devm_iio_device_register(&spi->dev, indio_dev); } -- cgit v1.2.3 From 235e4b988046d29097523703bb5e5e3ba77259ca Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:24 -0500 Subject: staging: iio: resolver: ad2s1210: convert LOT threshold attrs to event attrs The AD2S1210 monitors the internal error signal (difference between estimated angle and measured angle) to determine a loss of position tracking (LOT) condition. When the error value exceeds a threshold, a fault is triggered. This threshold is user-configurable. This patch converts the custom lot_high_thrd and lot_low_thrd attributes in the ad2s1210 driver to standard event attributes. This will allow tooling to be able to expose these in a generic way. Since the low threshold determines the hysteresis, it requires some special handling to expose the difference between the high and low register values as the hysteresis instead of exposing the low register value directly. The attributes also return the values in radians now as required by the ABI. Actually emitting the fault event will be done in a later patch. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-7-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 191 ++++++++++++++++++++++++++++++-- 1 file changed, 183 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 4d651a2d0f38..12437f697f79 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -443,6 +443,123 @@ static int ad2s1210_set_phase_lock_range(struct ad2s1210_state *st, return ret; } +/* map resolution to microradians/LSB for LOT registers */ +static const int ad2s1210_lot_threshold_urad_per_lsb[] = { + 6184, /* 10-bit: ~0.35 deg/LSB, 45 deg max */ + 2473, /* 12-bit: ~0.14 deg/LSB, 18 deg max */ + 1237, /* 14-bit: ~0.07 deg/LSB, 9 deg max */ + 1237, /* 16-bit: same as 14-bit */ +}; + +static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st, + int *val, int *val2) +{ + unsigned int reg_val; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + *val = 0; + *val2 = reg_val * ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad2s1210_set_lot_high_threshold(struct ad2s1210_state *st, + int val, int val2) +{ + unsigned int high_reg_val, low_reg_val, hysteresis; + int ret; + + /* all valid values are between 0 and pi/4 radians */ + if (val != 0) + return -EINVAL; + + mutex_lock(&st->lock); + /* + * We need to read both high and low registers first so we can preserve + * the hysteresis. + */ + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); + if (ret < 0) + goto error_ret; + + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); + if (ret < 0) + goto error_ret; + + hysteresis = high_reg_val - low_reg_val; + high_reg_val = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + low_reg_val = high_reg_val - hysteresis; + + ret = regmap_write(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, high_reg_val); + if (ret < 0) + goto error_ret; + + ret = regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, low_reg_val); + +error_ret: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad2s1210_get_lot_low_threshold(struct ad2s1210_state *st, + int *val, int *val2) +{ + unsigned int high_reg_val, low_reg_val; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); + if (ret < 0) + goto error_ret; + + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); + +error_ret: + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + /* sysfs value is hysteresis rather than actual low value */ + *val = 0; + *val2 = (high_reg_val - low_reg_val) * + ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad2s1210_set_lot_low_threshold(struct ad2s1210_state *st, + int val, int val2) +{ + unsigned int reg_val, hysteresis; + int ret; + + /* all valid values are between 0 and pi/4 radians */ + if (val != 0) + return -EINVAL; + + hysteresis = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); + if (ret < 0) + goto error_ret; + + ret = regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, + reg_val - hysteresis); + +error_ret: + mutex_unlock(&st->lock); + + return ret; +} + static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val) { unsigned int reg_val; @@ -608,12 +725,19 @@ static IIO_DEVICE_ATTR(dos_rst_max_thrd, 0644, static IIO_DEVICE_ATTR(dos_rst_min_thrd, 0644, ad2s1210_show_reg, ad2s1210_store_reg, AD2S1210_REG_DOS_RST_MIN_THRD); -static IIO_DEVICE_ATTR(lot_high_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_LOT_HIGH_THRD); -static IIO_DEVICE_ATTR(lot_low_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_LOT_LOW_THRD); + +static const struct iio_event_spec ad2s1210_position_event_spec[] = { + { + /* Tracking error exceeds LOT threshold fault. */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = + /* Loss of tracking high threshold. */ + BIT(IIO_EV_INFO_VALUE) | + /* Loss of tracking low threshold. */ + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; static const struct iio_event_spec ad2s1210_phase_event_spec[] = { { @@ -657,6 +781,15 @@ static const struct iio_chan_spec ad2s1210_channels[] = { BIT(IIO_CHAN_INFO_SCALE), }, IIO_CHAN_SOFT_TIMESTAMP(2), + { + /* used to configure LOT thresholds and get tracking error */ + .type = IIO_ANGL, + .indexed = 1, + .channel = 1, + .scan_index = -1, + .event_spec = ad2s1210_position_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_position_event_spec), + }, { /* used to configure phase lock range and get phase lock error */ .type = IIO_PHASE, @@ -684,8 +817,6 @@ static struct attribute *ad2s1210_attributes[] = { &iio_dev_attr_dos_mis_thrd.dev_attr.attr, &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, - &iio_dev_attr_lot_high_thrd.dev_attr.attr, - &iio_dev_attr_lot_low_thrd.dev_attr.attr, NULL, }; @@ -693,14 +824,40 @@ static const struct attribute_group ad2s1210_attribute_group = { .attrs = ad2s1210_attributes, }; +static ssize_t +in_angl1_thresh_rising_value_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + int step = ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + + return sysfs_emit(buf, "[0 0.%06d 0.%06d]\n", step, step * 0x7F); +} + +static ssize_t +in_angl1_thresh_rising_hysteresis_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + int step = ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + + return sysfs_emit(buf, "[0 0.%06d 0.%06d]\n", step, step * 0x7F); +} + static IIO_CONST_ATTR(in_phase0_mag_rising_value_available, __stringify(PHASE_44_DEG_TO_RAD_INT) "." __stringify(PHASE_44_DEG_TO_RAD_MICRO) " " __stringify(PHASE_360_DEG_TO_RAD_INT) "." __stringify(PHASE_360_DEG_TO_RAD_MICRO)); +static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0); +static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0); static struct attribute *ad2s1210_event_attributes[] = { &iio_const_attr_in_phase0_mag_rising_value_available.dev_attr.attr, + &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr, + &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr, NULL, }; @@ -742,6 +899,15 @@ static int ad2s1210_read_event_value(struct iio_dev *indio_dev, struct ad2s1210_state *st = iio_priv(indio_dev); switch (chan->type) { + case IIO_ANGL: + switch (info) { + case IIO_EV_INFO_VALUE: + return ad2s1210_get_lot_high_threshold(st, val, val2); + case IIO_EV_INFO_HYSTERESIS: + return ad2s1210_get_lot_low_threshold(st, val, val2); + default: + return -EINVAL; + } case IIO_PHASE: return ad2s1210_get_phase_lock_range(st, val, val2); default: @@ -759,6 +925,15 @@ static int ad2s1210_write_event_value(struct iio_dev *indio_dev, struct ad2s1210_state *st = iio_priv(indio_dev); switch (chan->type) { + case IIO_ANGL: + switch (info) { + case IIO_EV_INFO_VALUE: + return ad2s1210_set_lot_high_threshold(st, val, val2); + case IIO_EV_INFO_HYSTERESIS: + return ad2s1210_set_lot_low_threshold(st, val, val2); + default: + return -EINVAL; + } case IIO_PHASE: return ad2s1210_set_phase_lock_range(st, val, val2); default: -- cgit v1.2.3 From af8b284f23a600a86bb51a591d27e85d5f861b93 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:25 -0500 Subject: staging: iio: resolver: ad2s1210: convert LOS threshold to event attr The AD2S1210 has a programmable threshold for the loss of signal (LOS) fault. This fault is triggered when either the sine or cosine input falls below the threshold voltage. This patch converts the custom device LOS threshold attribute to an event falling edge threshold attribute on a new monitor signal channel. The monitor signal is an internal signal that combines the amplitudes of the sine and cosine inputs as well as the current angle and position output. This signal is used to detect faults in the input signals. The attribute now uses millivolts instead of the raw register value in accordance with the IIO ABI. Emitting the event will be implemented in a later patch. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-8-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 76 +++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 12437f697f79..d52aed30ca66 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -66,6 +66,11 @@ #define PHASE_360_DEG_TO_RAD_INT 6 #define PHASE_360_DEG_TO_RAD_MICRO 283185 +/* Threshold voltage registers have 1 LSB == 38 mV */ +#define THRESHOLD_MILLIVOLT_PER_LSB 38 +/* max voltage for threshold registers is 0x7F * 38 mV */ +#define THRESHOLD_RANGE_STR "[0 38 4826]" + enum ad2s1210_mode { MOD_POS = 0b00, MOD_VEL = 0b01, @@ -451,6 +456,38 @@ static const int ad2s1210_lot_threshold_urad_per_lsb[] = { 1237, /* 16-bit: same as 14-bit */ }; +static int ad2s1210_get_voltage_threshold(struct ad2s1210_state *st, + unsigned int reg, int *val) +{ + unsigned int reg_val; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, reg, ®_val); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + *val = reg_val * THRESHOLD_MILLIVOLT_PER_LSB; + return IIO_VAL_INT; +} + +static int ad2s1210_set_voltage_threshold(struct ad2s1210_state *st, + unsigned int reg, int val) +{ + unsigned int reg_val; + int ret; + + reg_val = val / THRESHOLD_MILLIVOLT_PER_LSB; + + mutex_lock(&st->lock); + ret = regmap_write(st->regmap, reg, reg_val); + mutex_unlock(&st->lock); + + return ret; +} + static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st, int *val, int *val2) { @@ -710,9 +747,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, static IIO_DEVICE_ATTR(fault, 0644, ad2s1210_show_fault, ad2s1210_clear_fault, 0); -static IIO_DEVICE_ATTR(los_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_LOS_THRD); static IIO_DEVICE_ATTR(dos_ovr_thrd, 0644, ad2s1210_show_reg, ad2s1210_store_reg, AD2S1210_REG_DOS_OVR_THRD); @@ -749,6 +783,16 @@ static const struct iio_event_spec ad2s1210_phase_event_spec[] = { }, }; +static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { + { + /* Sine/cosine below LOS threshold fault. */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + /* Loss of signal threshold. */ + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + static const struct iio_chan_spec ad2s1210_channels[] = { { .type = IIO_ANGL, @@ -807,12 +851,19 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .scan_index = -1, .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY), .info_mask_separate_available = BIT(IIO_CHAN_INFO_FREQUENCY), + }, { + /* monitor signal */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 0, + .scan_index = -1, + .event_spec = ad2s1210_monitor_signal_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_monitor_signal_event_spec), }, }; static struct attribute *ad2s1210_attributes[] = { &iio_dev_attr_fault.dev_attr.attr, - &iio_dev_attr_los_thrd.dev_attr.attr, &iio_dev_attr_dos_ovr_thrd.dev_attr.attr, &iio_dev_attr_dos_mis_thrd.dev_attr.attr, &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, @@ -851,11 +902,14 @@ static IIO_CONST_ATTR(in_phase0_mag_rising_value_available, __stringify(PHASE_44_DEG_TO_RAD_MICRO) " " __stringify(PHASE_360_DEG_TO_RAD_INT) "." __stringify(PHASE_360_DEG_TO_RAD_MICRO)); +static IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, + THRESHOLD_RANGE_STR); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0); static struct attribute *ad2s1210_event_attributes[] = { &iio_const_attr_in_phase0_mag_rising_value_available.dev_attr.attr, + &iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr, NULL, @@ -908,6 +962,13 @@ static int ad2s1210_read_event_value(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_ALTVOLTAGE: + if (chan->output) + return -EINVAL; + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) + return ad2s1210_get_voltage_threshold(st, + AD2S1210_REG_LOS_THRD, val); + return -EINVAL; case IIO_PHASE: return ad2s1210_get_phase_lock_range(st, val, val2); default: @@ -934,6 +995,13 @@ static int ad2s1210_write_event_value(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_ALTVOLTAGE: + if (chan->output) + return -EINVAL; + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) + return ad2s1210_set_voltage_threshold(st, + AD2S1210_REG_LOS_THRD, val); + return -EINVAL; case IIO_PHASE: return ad2s1210_set_phase_lock_range(st, val, val2); default: -- cgit v1.2.3 From 6c5cc2b4a01e8e10d38bad17720ec55ae60a5299 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:26 -0500 Subject: staging: iio: resolver: ad2s1210: convert DOS overrange threshold to event attr The AD2S1210 has a programmable threshold for the degradation of signal (DOS) overrange fault. This fault is triggered when either the sine or cosine input rises above the threshold voltage. This patch converts the custom device DOS overrange threshold attribute to an event rising edge threshold attribute on the monitor signal channel. The attribute now uses millivolts instead of the raw register value in accordance with the IIO ABI. Emitting the event will be implemented in a later patch. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-9-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index d52aed30ca66..3c224bbeae17 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -747,9 +747,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, static IIO_DEVICE_ATTR(fault, 0644, ad2s1210_show_fault, ad2s1210_clear_fault, 0); -static IIO_DEVICE_ATTR(dos_ovr_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_OVR_THRD); static IIO_DEVICE_ATTR(dos_mis_thrd, 0644, ad2s1210_show_reg, ad2s1210_store_reg, AD2S1210_REG_DOS_MIS_THRD); @@ -791,6 +788,13 @@ static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { /* Loss of signal threshold. */ .mask_separate = BIT(IIO_EV_INFO_VALUE), }, + { + /* Sine/cosine DOS overrange fault.*/ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + /* Degredation of signal overrange threshold. */ + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, }; static const struct iio_chan_spec ad2s1210_channels[] = { @@ -864,7 +868,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = { static struct attribute *ad2s1210_attributes[] = { &iio_dev_attr_fault.dev_attr.attr, - &iio_dev_attr_dos_ovr_thrd.dev_attr.attr, &iio_dev_attr_dos_mis_thrd.dev_attr.attr, &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, @@ -904,12 +907,15 @@ static IIO_CONST_ATTR(in_phase0_mag_rising_value_available, __stringify(PHASE_360_DEG_TO_RAD_MICRO)); static IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, THRESHOLD_RANGE_STR); +static IIO_CONST_ATTR(in_altvoltage0_thresh_rising_value_available, + THRESHOLD_RANGE_STR); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0); static struct attribute *ad2s1210_event_attributes[] = { &iio_const_attr_in_phase0_mag_rising_value_available.dev_attr.attr, &iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr, + &iio_const_attr_in_altvoltage0_thresh_rising_value_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr, NULL, @@ -968,6 +974,9 @@ static int ad2s1210_read_event_value(struct iio_dev *indio_dev, if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) return ad2s1210_get_voltage_threshold(st, AD2S1210_REG_LOS_THRD, val); + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) + return ad2s1210_get_voltage_threshold(st, + AD2S1210_REG_DOS_OVR_THRD, val); return -EINVAL; case IIO_PHASE: return ad2s1210_get_phase_lock_range(st, val, val2); @@ -1001,6 +1010,9 @@ static int ad2s1210_write_event_value(struct iio_dev *indio_dev, if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) return ad2s1210_set_voltage_threshold(st, AD2S1210_REG_LOS_THRD, val); + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) + return ad2s1210_set_voltage_threshold(st, + AD2S1210_REG_DOS_OVR_THRD, val); return -EINVAL; case IIO_PHASE: return ad2s1210_set_phase_lock_range(st, val, val2); -- cgit v1.2.3 From e025cb2c684827ffde62f99dd7c911540136b347 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:27 -0500 Subject: staging: iio: resolver: ad2s1210: convert DOS mismatch threshold to event attr The AD2S1210 has a programmable threshold for the degradation of signal (DOS) mismatch fault. This fault is triggered when the difference in voltage between the sine and cosine inputs exceeds the threshold. In other words, when the magnitude of sine and cosine inputs are equal, the AC component of the monitor signal is zero and when the magnitudes of the sine and cosine inputs are not equal, the AC component of the monitor signal is the difference between the sine and cosine inputs. So the fault occurs when the magnitude of the AC component of the monitor signal exceeds the DOS mismatch threshold voltage. This patch converts the custom device DOS mismatch threshold attribute to an event magnitude attribute on the monitor signal channel. The attribute now uses millivolts instead of the raw register value in accordance with the IIO ABI. Emitting the event will be implemented in a later patch. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-10-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 3c224bbeae17..870c4a9a6214 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -747,9 +747,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, static IIO_DEVICE_ATTR(fault, 0644, ad2s1210_show_fault, ad2s1210_clear_fault, 0); -static IIO_DEVICE_ATTR(dos_mis_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_MIS_THRD); static IIO_DEVICE_ATTR(dos_rst_max_thrd, 0644, ad2s1210_show_reg, ad2s1210_store_reg, AD2S1210_REG_DOS_RST_MAX_THRD); @@ -795,6 +792,12 @@ static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { /* Degredation of signal overrange threshold. */ .mask_separate = BIT(IIO_EV_INFO_VALUE), }, + { + /* Sine/cosine DOS mismatch fault.*/ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, }; static const struct iio_chan_spec ad2s1210_channels[] = { @@ -868,7 +871,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = { static struct attribute *ad2s1210_attributes[] = { &iio_dev_attr_fault.dev_attr.attr, - &iio_dev_attr_dos_mis_thrd.dev_attr.attr, &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, NULL, @@ -909,6 +911,8 @@ static IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, THRESHOLD_RANGE_STR); static IIO_CONST_ATTR(in_altvoltage0_thresh_rising_value_available, THRESHOLD_RANGE_STR); +static IIO_CONST_ATTR(in_altvoltage0_mag_rising_value_available, + THRESHOLD_RANGE_STR); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0); @@ -916,6 +920,7 @@ static struct attribute *ad2s1210_event_attributes[] = { &iio_const_attr_in_phase0_mag_rising_value_available.dev_attr.attr, &iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr, &iio_const_attr_in_altvoltage0_thresh_rising_value_available.dev_attr.attr, + &iio_const_attr_in_altvoltage0_mag_rising_value_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr, NULL, @@ -977,6 +982,9 @@ static int ad2s1210_read_event_value(struct iio_dev *indio_dev, if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) return ad2s1210_get_voltage_threshold(st, AD2S1210_REG_DOS_OVR_THRD, val); + if (type == IIO_EV_TYPE_MAG) + return ad2s1210_get_voltage_threshold(st, + AD2S1210_REG_DOS_MIS_THRD, val); return -EINVAL; case IIO_PHASE: return ad2s1210_get_phase_lock_range(st, val, val2); @@ -1013,6 +1021,9 @@ static int ad2s1210_write_event_value(struct iio_dev *indio_dev, if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) return ad2s1210_set_voltage_threshold(st, AD2S1210_REG_DOS_OVR_THRD, val); + if (type == IIO_EV_TYPE_MAG) + return ad2s1210_set_voltage_threshold(st, + AD2S1210_REG_DOS_MIS_THRD, val); return -EINVAL; case IIO_PHASE: return ad2s1210_set_phase_lock_range(st, val, val2); -- cgit v1.2.3 From 86a333c59806bc170156209e9a52c6393d00c652 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:28 -0500 Subject: staging: iio: resolver: ad2s1210: rename DOS reset min/max attrs The AD2S1210 has a programmable threshold for the degradation of signal (DOS) mismatch fault. This fault is triggered when the difference in amplitude between the sine and cosine inputs exceeds the threshold. The DOS reset min/max registers on the chip provide initial values for internal tracking of the min/max of the monitor signal after the fault register is cleared. This patch converts the custom device DOS reset min/max threshold attributes custom event attributes on the monitor signal channel. The attributes now use millivolts instead of the raw register value in accordance with the IIO ABI. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-11-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- .../Documentation/sysfs-bus-iio-resolver-ad2s1210 | 27 ++++++ drivers/staging/iio/resolver/ad2s1210.c | 99 ++++++++++++---------- 2 files changed, 82 insertions(+), 44 deletions(-) create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 b/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 new file mode 100644 index 000000000000..f92c79342b93 --- /dev/null +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 @@ -0,0 +1,27 @@ +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_max +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Reset Maximum + Threshold value in millivolts. Writing sets the value. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_max_available +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the allowable voltage range for + in_altvoltage0_mag_rising_reset_max. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_min +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Reset Minimum + Threshold value in millivolts. Writing sets the value. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_min_available +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the allowable voltage range for + in_altvoltage0_mag_rising_reset_min. diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 870c4a9a6214..9fac806c2a5f 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -286,41 +286,6 @@ error_ret: return ret < 0 ? ret : len; } -static ssize_t ad2s1210_show_reg(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - struct iio_dev_attr *iattr = to_iio_dev_attr(attr); - unsigned int value; - int ret; - - mutex_lock(&st->lock); - ret = regmap_read(st->regmap, iattr->address, &value); - mutex_unlock(&st->lock); - - return ret < 0 ? ret : sprintf(buf, "%d\n", value); -} - -static ssize_t ad2s1210_store_reg(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned char data; - int ret; - struct iio_dev_attr *iattr = to_iio_dev_attr(attr); - - ret = kstrtou8(buf, 10, &data); - if (ret) - return -EINVAL; - - mutex_lock(&st->lock); - ret = regmap_write(st->regmap, iattr->address, data); - mutex_unlock(&st->lock); - return ret < 0 ? ret : len; -} - static int ad2s1210_single_conversion(struct ad2s1210_state *st, struct iio_chan_spec const *chan, int *val) @@ -747,13 +712,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, static IIO_DEVICE_ATTR(fault, 0644, ad2s1210_show_fault, ad2s1210_clear_fault, 0); -static IIO_DEVICE_ATTR(dos_rst_max_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_RST_MAX_THRD); -static IIO_DEVICE_ATTR(dos_rst_min_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_RST_MIN_THRD); - static const struct iio_event_spec ad2s1210_position_event_spec[] = { { /* Tracking error exceeds LOT threshold fault. */ @@ -871,8 +829,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = { static struct attribute *ad2s1210_attributes[] = { &iio_dev_attr_fault.dev_attr.attr, - &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, - &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, NULL, }; @@ -880,6 +836,49 @@ static const struct attribute_group ad2s1210_attribute_group = { .attrs = ad2s1210_attributes, }; +static ssize_t event_attr_voltage_reg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + unsigned int value; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, iattr->address, &value); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", value * THRESHOLD_MILLIVOLT_PER_LSB); +} + +static ssize_t event_attr_voltage_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + u16 data; + int ret; + + ret = kstrtou16(buf, 10, &data); + if (ret) + return -EINVAL; + + mutex_lock(&st->lock); + ret = regmap_write(st->regmap, iattr->address, + data / THRESHOLD_MILLIVOLT_PER_LSB); + mutex_unlock(&st->lock); + + if (ret < 0) + return ret; + + return len; +} + static ssize_t in_angl1_thresh_rising_value_available_show(struct device *dev, struct device_attribute *attr, @@ -913,6 +912,14 @@ static IIO_CONST_ATTR(in_altvoltage0_thresh_rising_value_available, THRESHOLD_RANGE_STR); static IIO_CONST_ATTR(in_altvoltage0_mag_rising_value_available, THRESHOLD_RANGE_STR); +static IIO_DEVICE_ATTR(in_altvoltage0_mag_rising_reset_max, 0644, + event_attr_voltage_reg_show, event_attr_voltage_reg_store, + AD2S1210_REG_DOS_RST_MAX_THRD); +static IIO_CONST_ATTR(in_altvoltage0_mag_rising_reset_max_available, THRESHOLD_RANGE_STR); +static IIO_DEVICE_ATTR(in_altvoltage0_mag_rising_reset_min, 0644, + event_attr_voltage_reg_show, event_attr_voltage_reg_store, + AD2S1210_REG_DOS_RST_MIN_THRD); +static IIO_CONST_ATTR(in_altvoltage0_mag_rising_reset_min_available, THRESHOLD_RANGE_STR); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0); static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0); @@ -921,6 +928,10 @@ static struct attribute *ad2s1210_event_attributes[] = { &iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr, &iio_const_attr_in_altvoltage0_thresh_rising_value_available.dev_attr.attr, &iio_const_attr_in_altvoltage0_mag_rising_value_available.dev_attr.attr, + &iio_dev_attr_in_altvoltage0_mag_rising_reset_max.dev_attr.attr, + &iio_const_attr_in_altvoltage0_mag_rising_reset_max_available.dev_attr.attr, + &iio_dev_attr_in_altvoltage0_mag_rising_reset_min.dev_attr.attr, + &iio_const_attr_in_altvoltage0_mag_rising_reset_min_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr, &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr, NULL, -- cgit v1.2.3 From 5987279373446e97206a7078b2229446ba871ea0 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:29 -0500 Subject: iio: event: add optional event label support This adds a new optional field to struct iio_info to allow drivers to specify a label for the event. This is useful for cases where there are many events or the event attribute name is not descriptive enough or where an event doesn't have any other attributes. The implementation is based on the existing label support for channels. So either all events of a device have a label attribute or none do. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-12-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-event.c | 55 ++++++++++++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 8 ++++++ 2 files changed, 63 insertions(+) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 19f7a91157ee..910c1f14abd5 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -355,6 +355,21 @@ static ssize_t iio_ev_value_store(struct device *dev, return len; } +static ssize_t iio_ev_label_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + if (indio_dev->info->read_event_label) + return indio_dev->info->read_event_label(indio_dev, + this_attr->c, iio_ev_attr_type(this_attr), + iio_ev_attr_dir(this_attr), buf); + + return -EINVAL; +} + static int iio_device_add_event(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int spec_index, enum iio_event_type type, enum iio_event_direction dir, @@ -411,6 +426,41 @@ static int iio_device_add_event(struct iio_dev *indio_dev, return attrcount; } +static int iio_device_add_event_label(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int spec_index, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + char *postfix; + int ret; + + if (!indio_dev->info->read_event_label) + return 0; + + if (dir != IIO_EV_DIR_NONE) + postfix = kasprintf(GFP_KERNEL, "%s_%s_label", + iio_ev_type_text[type], + iio_ev_dir_text[dir]); + else + postfix = kasprintf(GFP_KERNEL, "%s_label", + iio_ev_type_text[type]); + if (postfix == NULL) + return -ENOMEM; + + ret = __iio_add_chan_devattr(postfix, chan, &iio_ev_label_show, NULL, + spec_index, IIO_SEPARATE, &indio_dev->dev, NULL, + &iio_dev_opaque->event_interface->dev_attr_list); + + kfree(postfix); + + if (ret < 0) + return ret; + + return 1; +} + static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, struct iio_chan_spec const *chan) { @@ -448,6 +498,11 @@ static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, if (ret < 0) return ret; attrcount += ret; + + ret = iio_device_add_event_label(indio_dev, chan, i, type, dir); + if (ret < 0) + return ret; + attrcount += ret; } ret = attrcount; return ret; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 7bfa1b9bc8a2..d0ce3b71106a 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -427,6 +427,8 @@ struct iio_trigger; /* forward declaration */ * @write_event_config: set if the event is enabled. * @read_event_value: read a configuration value associated with the event. * @write_event_value: write a configuration value for the event. + * @read_event_label: function to request label name for a specified label, + * for better event identification. * @validate_trigger: function to validate the trigger when the * current trigger gets changed. * @update_scan_mode: function to configure device and scan buffer when @@ -511,6 +513,12 @@ struct iio_info { enum iio_event_direction dir, enum iio_event_info info, int val, int val2); + int (*read_event_label)(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum iio_event_type type, + enum iio_event_direction dir, + char *label); + int (*validate_trigger)(struct iio_dev *indio_dev, struct iio_trigger *trig); int (*update_scan_mode)(struct iio_dev *indio_dev, -- cgit v1.2.3 From a5101e91e94d4f12e402fb4e8b46caa0213ea03a Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:30 -0500 Subject: staging: iio: resolver: ad2s1210: implement fault events When reading the position and velocity on the AD2S1210, there is also a 3rd byte following the two data bytes that contains the fault flag bits. This patch adds support for reading this byte and generating events when faults occur. The faults are mapped to various channels and event type in order to have a unique event for each fault. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-13-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 15 +++ drivers/staging/iio/resolver/ad2s1210.c | 211 +++++++++++++++++++++++++++++--- 2 files changed, 212 insertions(+), 14 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 8e13642bbe23..19cde14f3869 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2239,3 +2239,18 @@ Contact: linux-iio@vger.kernel.org Description: The x and y light color coordinate on the CIE 1931 chromaticity diagram. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltageY_mag_either_label +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltageY_mag_rising_label +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltageY_thresh_falling_label +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltageY_thresh_rising_label +What: /sys/bus/iio/devices/iio:deviceX/events/in_anglvelY_mag_rising_label +What: /sys/bus/iio/devices/iio:deviceX/events/in_anglY_thresh_rising_label +What: /sys/bus/iio/devices/iio:deviceX/events/in_phaseY_mag_rising_label +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Optional symbolic label to a device channel event. + If a label is defined for this event add that to the event + specific attributes. This is useful for userspace to be able to + better identify an individual event. diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 9fac806c2a5f..f3f26b1438c0 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,16 @@ #define AD2S1210_SET_ENRES GENMASK(3, 2) #define AD2S1210_SET_RES GENMASK(1, 0) +/* fault register flags */ +#define AD2S1210_FAULT_CLIP BIT(7) +#define AD2S1210_FAULT_LOS BIT(6) +#define AD2S1210_FAULT_DOS_OVR BIT(5) +#define AD2S1210_FAULT_DOS_MIS BIT(4) +#define AD2S1210_FAULT_LOT BIT(3) +#define AD2S1210_FAULT_VELOCITY BIT(2) +#define AD2S1210_FAULT_PHASE BIT(1) +#define AD2S1210_FAULT_CONFIG_PARITY BIT(0) + #define AD2S1210_REG_POSITION_MSB 0x80 #define AD2S1210_REG_POSITION_LSB 0x81 #define AD2S1210_REG_VELOCITY_MSB 0x82 @@ -71,6 +82,8 @@ /* max voltage for threshold registers is 0x7F * 38 mV */ #define THRESHOLD_RANGE_STR "[0 38 4826]" +#define FAULT_ONESHOT(bit, new, old) (new & bit && !(old & bit)) + enum ad2s1210_mode { MOD_POS = 0b00, MOD_VEL = 0b01, @@ -100,8 +113,13 @@ struct ad2s1210_state { int hysteresis_available[2]; /** The selected resolution */ enum ad2s1210_resolution resolution; + /** Copy of fault register from the previous read. */ + u8 prev_fault_flags; /** For reading raw sample value via SPI. */ - __be16 sample __aligned(IIO_DMA_MINALIGN); + struct { + __be16 raw; + u8 fault; + } sample __aligned(IIO_DMA_MINALIGN); /** Scan buffer */ struct { __be16 chan[2]; @@ -160,7 +178,15 @@ static int ad2s1210_regmap_reg_write(void *context, unsigned int reg, if (ret < 0) return ret; - return spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); + ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); + if (ret < 0) + return ret; + + /* soft reset also clears the fault register */ + if (reg == AD2S1210_REG_SOFT_RESET) + st->prev_fault_flags = 0; + + return 0; } /* @@ -203,6 +229,10 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, if (ret < 0) return ret; + /* reading the fault register also clears it */ + if (reg == AD2S1210_REG_FAULT) + st->prev_fault_flags = 0; + /* * If the D7 bit is set on any read/write register, it indicates a * parity error. The fault register is read-only and the D7 bit means @@ -286,14 +316,101 @@ error_ret: return ret < 0 ? ret : len; } -static int ad2s1210_single_conversion(struct ad2s1210_state *st, +static void ad2s1210_push_events(struct iio_dev *indio_dev, + u8 flags, s64 timestamp) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + /* Sine/cosine inputs clipped */ + if (FAULT_ONESHOT(AD2S1210_FAULT_CLIP, flags, st->prev_fault_flags)) { + /* + * The chip does not differentiate between fault on sine vs. + * cosine channel so we just send an event on both channels. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 1, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_EITHER), + timestamp); + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 2, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_EITHER), + timestamp); + } + + /* Sine/cosine inputs below LOS threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_LOS, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + + /* Sine/cosine inputs exceed DOS overrange threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_OVR, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + /* Sine/cosine inputs exceed DOS mismatch threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_MIS, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + timestamp); + + /* Tracking error exceeds LOT threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_LOT, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ANGL, 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + /* Velocity exceeds maximum tracking rate */ + if (FAULT_ONESHOT(AD2S1210_FAULT_VELOCITY, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ANGL_VEL, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + /* Phase error exceeds phase lock range */ + if (FAULT_ONESHOT(AD2S1210_FAULT_PHASE, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PHASE, 0, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + timestamp); + + /* Configuration parity error */ + if (FAULT_ONESHOT(AD2S1210_FAULT_CONFIG_PARITY, flags, + st->prev_fault_flags)) + /* + * Userspace should also get notified of this via error return + * when trying to write to any attribute that writes a register. + */ + dev_err_ratelimited(&indio_dev->dev, + "Configuration parity error\n"); + + st->prev_fault_flags = flags; +} + +static int ad2s1210_single_conversion(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { + struct ad2s1210_state *st = iio_priv(indio_dev); + s64 timestamp; int ret; mutex_lock(&st->lock); gpiod_set_value(st->sample_gpio, 1); + timestamp = iio_get_time_ns(indio_dev); /* delay (6 * tck + 20) nano seconds */ udelay(1); @@ -310,17 +427,17 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st, } if (ret < 0) goto error_ret; - ret = spi_read(st->sdev, &st->sample, 2); + ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) goto error_ret; switch (chan->type) { case IIO_ANGL: - *val = be16_to_cpu(st->sample); + *val = be16_to_cpu(st->sample.raw); ret = IIO_VAL_INT; break; case IIO_ANGL_VEL: - *val = (s16)be16_to_cpu(st->sample); + *val = (s16)be16_to_cpu(st->sample.raw); ret = IIO_VAL_INT; break; default: @@ -328,6 +445,8 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st, break; } + ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); + error_ret: gpiod_set_value(st->sample_gpio, 0); /* delay (2 * tck + 20) nano seconds */ @@ -611,7 +730,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - return ad2s1210_single_conversion(st, chan, val); + return ad2s1210_single_conversion(indio_dev, chan, val); case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL: @@ -725,6 +844,14 @@ static const struct iio_event_spec ad2s1210_position_event_spec[] = { }, }; +static const struct iio_event_spec ad2s1210_velocity_event_spec[] = { + { + /* Velocity exceeds maximum tracking rate fault. */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + }, +}; + static const struct iio_event_spec ad2s1210_phase_event_spec[] = { { /* Phase error fault. */ @@ -758,6 +885,14 @@ static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { }, }; +static const struct iio_event_spec ad2s1210_sin_cos_event_spec[] = { + { + /* Sine/cosine clipping fault. */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_EITHER, + }, +}; + static const struct iio_chan_spec ad2s1210_channels[] = { { .type = IIO_ANGL, @@ -788,6 +923,8 @@ static const struct iio_chan_spec ad2s1210_channels[] = { }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .event_spec = ad2s1210_velocity_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_velocity_event_spec), }, IIO_CHAN_SOFT_TIMESTAMP(2), { @@ -824,6 +961,22 @@ static const struct iio_chan_spec ad2s1210_channels[] = { .scan_index = -1, .event_spec = ad2s1210_monitor_signal_event_spec, .num_event_specs = ARRAY_SIZE(ad2s1210_monitor_signal_event_spec), + }, { + /* sine input */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 1, + .scan_index = -1, + .event_spec = ad2s1210_sin_cos_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec), + }, { + /* cosine input */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 2, + .scan_index = -1, + .event_spec = ad2s1210_sin_cos_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec), }, }; @@ -943,7 +1096,7 @@ static const struct attribute_group ad2s1210_event_attribute_group = { static int ad2s1210_initial(struct ad2s1210_state *st) { - unsigned char data; + unsigned int data; int ret; mutex_lock(&st->lock); @@ -1043,6 +1196,36 @@ static int ad2s1210_write_event_value(struct iio_dev *indio_dev, } } +static int ad2s1210_read_event_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum iio_event_type type, + enum iio_event_direction dir, + char *label) +{ + if (chan->type == IIO_ANGL) + return sprintf(label, "LOT\n"); + if (chan->type == IIO_ANGL_VEL) + return sprintf(label, "max tracking rate\n"); + if (chan->type == IIO_PHASE) + return sprintf(label, "phase lock\n"); + if (chan->type == IIO_ALTVOLTAGE) { + if (chan->channel == 0) { + if (type == IIO_EV_TYPE_THRESH && + dir == IIO_EV_DIR_FALLING) + return sprintf(label, "LOS\n"); + if (type == IIO_EV_TYPE_THRESH && + dir == IIO_EV_DIR_RISING) + return sprintf(label, "DOS overrange\n"); + if (type == IIO_EV_TYPE_MAG) + return sprintf(label, "DOS mismatch\n"); + } + if (chan->channel == 1 || chan->channel == 2) + return sprintf(label, "clipped\n"); + } + + return -EINVAL; +} + static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) @@ -1080,12 +1263,11 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) if (ret < 0) goto error_ret; - /* REVIST: we can read 3 bytes here and also get fault flags */ - ret = spi_read(st->sdev, st->rx, 2); + ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) goto error_ret; - memcpy(&st->scan.chan[chan++], st->rx, 2); + memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); } if (test_bit(1, indio_dev->active_scan_mask)) { @@ -1093,14 +1275,14 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) if (ret < 0) goto error_ret; - /* REVIST: we can read 3 bytes here and also get fault flags */ - ret = spi_read(st->sdev, st->rx, 2); + ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) goto error_ret; - memcpy(&st->scan.chan[chan++], st->rx, 2); + memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); } + ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); error_ret: @@ -1119,6 +1301,7 @@ static const struct iio_info ad2s1210_info = { .attrs = &ad2s1210_attribute_group, .read_event_value = ad2s1210_read_event_value, .write_event_value = ad2s1210_write_event_value, + .read_event_label = ad2s1210_read_event_label, .debugfs_reg_access = &ad2s1210_debugfs_reg_access, }; -- cgit v1.2.3 From 1638b6d4f7c9515a6c26b2401ab3624fcef20916 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:31 -0500 Subject: staging: iio: resolver: ad2s1210: add register/fault support summary The ad2s1210 driver shoe-horns the register and fault support into IIO events. The mapping between the registers/faults and the events is not obvious. To save users from having to read the entire driver to figure out how to use it, add a summary of the register/fault support to the top of the file. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-14-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index f3f26b1438c0..b843a1f5b45a 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -4,7 +4,47 @@ * * Copyright (c) 2010-2010 Analog Devices Inc. * Copyright (c) 2023 BayLibre, SAS + * + * Device register to IIO ABI mapping: + * + * Register | Addr | IIO ABI (sysfs) + * ----------------------------|------|------------------------------------------- + * DOS Overrange Threshold | 0x89 | events/in_altvoltage0_thresh_rising_value + * DOS Mismatch Threshold | 0x8A | events/in_altvoltage0_mag_rising_value + * DOS Reset Maximum Threshold | 0x8B | events/in_altvoltage0_mag_rising_reset_max + * DOS Reset Minimum Threshold | 0x8C | events/in_altvoltage0_mag_rising_reset_min + * LOT High Threshold | 0x8D | events/in_angl1_thresh_rising_value + * LOT Low Threshold [1] | 0x8E | events/in_angl1_thresh_rising_hysteresis + * Excitation Frequency | 0x91 | out_altvoltage0_frequency + * Control | 0x92 | *as bit fields* + * Phase lock range | D5 | events/in_phase0_mag_rising_value + * Hysteresis | D4 | in_angl0_hysteresis + * Encoder resolution | D3:2 | *not implemented* + * Resolution | D1:0 | *device tree: assigned-resolution-bits* + * Soft Reset | 0xF0 | [2] + * Fault | 0xFF | *not implemented* + * + * [1]: The value written to the LOT low register is high value minus the + * hysteresis. + * [2]: Soft reset is performed when `out_altvoltage0_frequency` is written. + * + * Fault to event mapping: + * + * Fault | | Channel | Type | Direction + * ----------------------------------------|----|--------------------------------- + * Sine/cosine inputs clipped [3] | D7 | altvoltage1 | mag | either + * Sine/cosine inputs below LOS | D6 | altvoltage0 | thresh | falling + * Sine/cosine inputs exceed DOS overrange | D5 | altvoltage0 | thresh | rising + * Sine/cosine inputs exceed DOS mismatch | D4 | altvoltage0 | mag | rising + * Tracking error exceeds LOT | D3 | angl1 | thresh | rising + * Velocity exceeds maximum tracking rate | D2 | anglvel0 | mag | rising + * Phase error exceeds phase lock range | D1 | phase0 | mag | rising + * Configuration parity error | D0 | *writes to kernel log* + * + * [3]: The chip does not differentiate between fault on sine vs. cosine so + * there will also be an event on the altvoltage2 channel. */ + #include #include #include -- cgit v1.2.3 From 40efeec7600949c2f6319c11d8a7f294a1c5e590 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:32 -0500 Subject: staging: iio: resolver: ad2s1210: add label attribute support The ad2s1210 resolver driver has quite a few channels, mostly for internal signals for event support. This makes it difficult to know which channel is which. This patch adds a label attribute to the channels to make it easier to identify them. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-15-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index b843a1f5b45a..2d7b9bfeabd3 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -1158,6 +1158,34 @@ error_ret: return ret; } +static int ad2s1210_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->type == IIO_ANGL) { + if (chan->channel == 0) + return sprintf(label, "position\n"); + if (chan->channel == 1) + return sprintf(label, "tracking error\n"); + } + if (chan->type == IIO_ANGL_VEL) + return sprintf(label, "velocity\n"); + if (chan->type == IIO_PHASE) + return sprintf(label, "synthetic reference\n"); + if (chan->type == IIO_ALTVOLTAGE) { + if (chan->output) + return sprintf(label, "excitation\n"); + if (chan->channel == 0) + return sprintf(label, "monitor signal\n"); + if (chan->channel == 1) + return sprintf(label, "cosine\n"); + if (chan->channel == 2) + return sprintf(label, "sine\n"); + } + + return -EINVAL; +} + static int ad2s1210_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, @@ -1338,6 +1366,7 @@ static const struct iio_info ad2s1210_info = { .read_raw = ad2s1210_read_raw, .read_avail = ad2s1210_read_avail, .write_raw = ad2s1210_write_raw, + .read_label = ad2s1210_read_label, .attrs = &ad2s1210_attribute_group, .read_event_value = ad2s1210_read_event_value, .write_event_value = ad2s1210_write_event_value, -- cgit v1.2.3 From 73aa842baf877cc8c8da819f4ea927307dd8b6e4 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 5 Oct 2023 19:50:33 -0500 Subject: staging: iio: resolver: ad2s1210: remove fault attribute Faults have been converted to events and we are now polling the fault register each time we read a sample, so we no longer need the fault attribute. This attribute was not suitable for promotion out of staging anyway since it was returning multiple values in a single attribute. The fault clearing feature should not be needed unless we need to support the fault output pins on the chip which is not currently supported. So we can add this feature back in if we need it later. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231005-ad2s1210-mainline-v4-16-ec00746840fc@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 57 --------------------------------- 1 file changed, 57 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 2d7b9bfeabd3..e0bea3c68664 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -312,50 +312,6 @@ static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); } -/* read the fault register since last sample */ -static ssize_t ad2s1210_show_fault(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int value; - int ret; - - mutex_lock(&st->lock); - ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &value); - mutex_unlock(&st->lock); - - return ret < 0 ? ret : sprintf(buf, "0x%02x\n", value); -} - -static ssize_t ad2s1210_clear_fault(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int value; - int ret; - - mutex_lock(&st->lock); - - gpiod_set_value(st->sample_gpio, 1); - /* delay (2 * tck + 20) nano seconds */ - udelay(1); - gpiod_set_value(st->sample_gpio, 0); - - ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &value); - if (ret < 0) - goto error_ret; - - gpiod_set_value(st->sample_gpio, 1); - gpiod_set_value(st->sample_gpio, 0); - -error_ret: - mutex_unlock(&st->lock); - - return ret < 0 ? ret : len; -} - static void ad2s1210_push_events(struct iio_dev *indio_dev, u8 flags, s64 timestamp) { @@ -868,9 +824,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev, } } -static IIO_DEVICE_ATTR(fault, 0644, - ad2s1210_show_fault, ad2s1210_clear_fault, 0); - static const struct iio_event_spec ad2s1210_position_event_spec[] = { { /* Tracking error exceeds LOT threshold fault. */ @@ -1020,15 +973,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = { }, }; -static struct attribute *ad2s1210_attributes[] = { - &iio_dev_attr_fault.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad2s1210_attribute_group = { - .attrs = ad2s1210_attributes, -}; - static ssize_t event_attr_voltage_reg_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1367,7 +1311,6 @@ static const struct iio_info ad2s1210_info = { .read_avail = ad2s1210_read_avail, .write_raw = ad2s1210_write_raw, .read_label = ad2s1210_read_label, - .attrs = &ad2s1210_attribute_group, .read_event_value = ad2s1210_read_event_value, .write_event_value = ad2s1210_write_event_value, .read_event_label = ad2s1210_read_event_label, -- cgit v1.2.3 From 27ffa2216acfab3527064ec13666f35d70f76045 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 10 Oct 2023 16:12:33 -0500 Subject: staging: iio: resolver: ad2s1210: refactor sample toggle This refactors the sample line toggle in the ad2s1210 resolver driver to a separate function. The sample has some timing requirements, so this ensures that it is always done the same way, both in the existing call sites and any future usage. Previously, the sample line was kept on for the duration of the read, but this is not necessary. Data is latched in on the rising edge and after the specified delay the state of the sample line does not matter. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231010-ad2s1210-mainline-v5-1-35a0f6ffa04a@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index e0bea3c68664..59c273a4b6a9 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -286,6 +286,26 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, return 0; } +/* + * Toggles the SAMPLE line on the AD2S1210 to latch in the current position, + * velocity, and faults. + * + * Must be called with lock held. + */ +static void ad2s1210_toggle_sample_line(struct ad2s1210_state *st) +{ + /* + * Datasheet specifies minimum hold time t16 = 2 * tck + 20 ns. So the + * longest time needed is when CLKIN is 6.144 MHz, in which case t16 + * ~= 350 ns. The same delay is also needed before re-asserting the + * SAMPLE line. + */ + gpiod_set_value(st->sample_gpio, 1); + ndelay(350); + gpiod_set_value(st->sample_gpio, 0); + ndelay(350); +} + /* * Sets the excitation frequency and performs software reset. * @@ -405,10 +425,8 @@ static int ad2s1210_single_conversion(struct iio_dev *indio_dev, int ret; mutex_lock(&st->lock); - gpiod_set_value(st->sample_gpio, 1); + ad2s1210_toggle_sample_line(st); timestamp = iio_get_time_ns(indio_dev); - /* delay (6 * tck + 20) nano seconds */ - udelay(1); switch (chan->type) { case IIO_ANGL: @@ -444,9 +462,6 @@ static int ad2s1210_single_conversion(struct iio_dev *indio_dev, ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); error_ret: - gpiod_set_value(st->sample_gpio, 0); - /* delay (2 * tck + 20) nano seconds */ - udelay(1); mutex_unlock(&st->lock); return ret; } @@ -1268,7 +1283,7 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) mutex_lock(&st->lock); memset(&st->scan, 0, sizeof(st->scan)); - gpiod_set_value(st->sample_gpio, 1); + ad2s1210_toggle_sample_line(st); if (test_bit(0, indio_dev->active_scan_mask)) { ret = ad2s1210_set_mode(st, MOD_POS); @@ -1298,7 +1313,6 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); error_ret: - gpiod_set_value(st->sample_gpio, 0); mutex_unlock(&st->lock); iio_trigger_notify_done(indio_dev->trig); -- cgit v1.2.3 From 4efa877ac942d63c335820bf52d262e76901bea9 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 10 Oct 2023 16:12:34 -0500 Subject: staging: iio: resolver: ad2s1210: clear faults after soft reset When a software reset is performed on the AD2S1210 to make the selected excitation frequency take effect, it always triggers faults on the input signals because the output signal is interrupted momentarily. So we need to clear the faults after the software reset to avoid triggering fault events the next time a sample is read. The datasheet specifies a time t[track] in Table 27 that specifies the settle time in milliseconds after a reset depending on the selected resolution. This is used in the driver to add an appropriate delay. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231010-ad2s1210-mainline-v5-2-35a0f6ffa04a@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index 59c273a4b6a9..cf4018434447 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -314,6 +314,9 @@ static void ad2s1210_toggle_sample_line(struct ad2s1210_state *st) static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, u16 fexcit) { + /* Map resolution to settle time in milliseconds. */ + static const int track_time_ms[] = { 10, 20, 25, 60 }; + unsigned int ignored; int ret; u8 fcw; @@ -329,7 +332,27 @@ static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, * Software reset reinitializes the excitation frequency output. * It does not reset any of the configuration registers. */ - return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); + ret = regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); + if (ret < 0) + return ret; + + /* + * Soft reset always triggers some faults due the change in the output + * signal so clear the faults too. We need to delay for some time + * (what datasheet calls t[track]) to allow things to settle before + * clearing the faults. + */ + msleep(track_time_ms[st->resolution] * 8192000 / st->clkin_hz); + + /* Reading the fault register clears the faults. */ + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &ignored); + if (ret < 0) + return ret; + + /* Have to toggle sample line to get fault output pins to reset. */ + ad2s1210_toggle_sample_line(st); + + return 0; } static void ad2s1210_push_events(struct iio_dev *indio_dev, -- cgit v1.2.3 From 169dc2adafecc19ecf53a101398edd7f3ff03529 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 10 Oct 2023 16:12:35 -0500 Subject: staging: iio: resolver: ad2s1210: simplify code with guard(mutex) We can simplify the code and get rid of most of the gotos by using guard(mutex) from cleanup.h. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231010-ad2s1210-mainline-v5-3-35a0f6ffa04a@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/resolver/ad2s1210.c | 153 ++++++++++---------------------- 1 file changed, 49 insertions(+), 104 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index cf4018434447..bd4a90c222b5 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -447,7 +448,8 @@ static int ad2s1210_single_conversion(struct iio_dev *indio_dev, s64 timestamp; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ad2s1210_toggle_sample_line(st); timestamp = iio_get_time_ns(indio_dev); @@ -459,14 +461,13 @@ static int ad2s1210_single_conversion(struct iio_dev *indio_dev, ret = ad2s1210_set_mode(st, MOD_VEL); break; default: - ret = -EINVAL; - break; + return -EINVAL; } if (ret < 0) - goto error_ret; + return ret; ret = spi_read(st->sdev, &st->sample, 3); if (ret < 0) - goto error_ret; + return ret; switch (chan->type) { case IIO_ANGL: @@ -478,14 +479,11 @@ static int ad2s1210_single_conversion(struct iio_dev *indio_dev, ret = IIO_VAL_INT; break; default: - ret = -EINVAL; - break; + return -EINVAL; } ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); -error_ret: - mutex_unlock(&st->lock); return ret; } @@ -493,11 +491,9 @@ static int ad2s1210_get_hysteresis(struct ad2s1210_state *st, int *val) { int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, AD2S1210_ENABLE_HYSTERESIS); - mutex_unlock(&st->lock); - if (ret < 0) return ret; @@ -507,15 +503,10 @@ static int ad2s1210_get_hysteresis(struct ad2s1210_state *st, int *val) static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val) { - int ret; - - mutex_lock(&st->lock); - ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, - AD2S1210_ENABLE_HYSTERESIS, - val ? AD2S1210_ENABLE_HYSTERESIS : 0); - mutex_unlock(&st->lock); - - return ret; + guard(mutex)(&st->lock); + return regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_ENABLE_HYSTERESIS, + val ? AD2S1210_ENABLE_HYSTERESIS : 0); } static int ad2s1210_get_phase_lock_range(struct ad2s1210_state *st, @@ -523,11 +514,9 @@ static int ad2s1210_get_phase_lock_range(struct ad2s1210_state *st, { int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, AD2S1210_PHASE_LOCK_RANGE_44); - mutex_unlock(&st->lock); - if (ret < 0) return ret; @@ -547,7 +536,7 @@ static int ad2s1210_get_phase_lock_range(struct ad2s1210_state *st, static int ad2s1210_set_phase_lock_range(struct ad2s1210_state *st, int val, int val2) { - int deg, ret; + int deg; /* convert radians to degrees - only two allowable values */ if (val == PHASE_44_DEG_TO_RAD_INT && val2 == PHASE_44_DEG_TO_RAD_MICRO) @@ -558,12 +547,10 @@ static int ad2s1210_set_phase_lock_range(struct ad2s1210_state *st, else return -EINVAL; - mutex_lock(&st->lock); - ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, - AD2S1210_PHASE_LOCK_RANGE_44, - deg == 44 ? AD2S1210_PHASE_LOCK_RANGE_44 : 0); - mutex_unlock(&st->lock); - return ret; + guard(mutex)(&st->lock); + return regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_PHASE_LOCK_RANGE_44, + deg == 44 ? AD2S1210_PHASE_LOCK_RANGE_44 : 0); } /* map resolution to microradians/LSB for LOT registers */ @@ -580,10 +567,8 @@ static int ad2s1210_get_voltage_threshold(struct ad2s1210_state *st, unsigned int reg_val; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = regmap_read(st->regmap, reg, ®_val); - mutex_unlock(&st->lock); - if (ret < 0) return ret; @@ -595,15 +580,11 @@ static int ad2s1210_set_voltage_threshold(struct ad2s1210_state *st, unsigned int reg, int val) { unsigned int reg_val; - int ret; reg_val = val / THRESHOLD_MILLIVOLT_PER_LSB; - mutex_lock(&st->lock); - ret = regmap_write(st->regmap, reg, reg_val); - mutex_unlock(&st->lock); - - return ret; + guard(mutex)(&st->lock); + return regmap_write(st->regmap, reg, reg_val); } static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st, @@ -612,10 +593,8 @@ static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st, unsigned int reg_val; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); - mutex_unlock(&st->lock); - if (ret < 0) return ret; @@ -634,18 +613,18 @@ static int ad2s1210_set_lot_high_threshold(struct ad2s1210_state *st, if (val != 0) return -EINVAL; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); /* * We need to read both high and low registers first so we can preserve * the hysteresis. */ ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); if (ret < 0) - goto error_ret; + return ret; ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); if (ret < 0) - goto error_ret; + return ret; hysteresis = high_reg_val - low_reg_val; high_reg_val = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; @@ -653,14 +632,9 @@ static int ad2s1210_set_lot_high_threshold(struct ad2s1210_state *st, ret = regmap_write(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, high_reg_val); if (ret < 0) - goto error_ret; - - ret = regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, low_reg_val); - -error_ret: - mutex_unlock(&st->lock); + return ret; - return ret; + return regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, low_reg_val); } static int ad2s1210_get_lot_low_threshold(struct ad2s1210_state *st, @@ -669,16 +643,13 @@ static int ad2s1210_get_lot_low_threshold(struct ad2s1210_state *st, unsigned int high_reg_val, low_reg_val; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); if (ret < 0) - goto error_ret; + return ret; ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); - -error_ret: - mutex_unlock(&st->lock); - if (ret < 0) return ret; @@ -701,18 +672,14 @@ static int ad2s1210_set_lot_low_threshold(struct ad2s1210_state *st, hysteresis = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); if (ret < 0) - goto error_ret; + return ret; - ret = regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, + return regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, reg_val - hysteresis); - -error_ret: - mutex_unlock(&st->lock); - - return ret; } static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val) @@ -720,31 +687,23 @@ static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val unsigned int reg_val; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, ®_val); if (ret < 0) - goto error_ret; + return ret; *val = reg_val * st->clkin_hz / (1 << 15); - ret = IIO_VAL_INT; - -error_ret: - mutex_unlock(&st->lock); - return ret; + return IIO_VAL_INT; } static int ad2s1210_set_excitation_frequency(struct ad2s1210_state *st, int val) { - int ret; - if (val < AD2S1210_MIN_EXCIT || val > AD2S1210_MAX_EXCIT) return -EINVAL; - mutex_lock(&st->lock); - ret = ad2s1210_reinit_excitation_frequency(st, val); - mutex_unlock(&st->lock); - - return ret; + guard(mutex)(&st->lock); + return ad2s1210_reinit_excitation_frequency(st, val); } static const int ad2s1210_velocity_scale[] = { @@ -1020,10 +979,8 @@ static ssize_t event_attr_voltage_reg_show(struct device *dev, unsigned int value; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = regmap_read(st->regmap, iattr->address, &value); - mutex_unlock(&st->lock); - if (ret < 0) return ret; @@ -1043,11 +1000,9 @@ static ssize_t event_attr_voltage_reg_store(struct device *dev, if (ret) return -EINVAL; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = regmap_write(st->regmap, iattr->address, data / THRESHOLD_MILLIVOLT_PER_LSB); - mutex_unlock(&st->lock); - if (ret < 0) return ret; @@ -1121,7 +1076,7 @@ static int ad2s1210_initial(struct ad2s1210_state *st) unsigned int data; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); /* Use default config register value plus resolution from devicetree. */ data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1); @@ -1131,13 +1086,9 @@ static int ad2s1210_initial(struct ad2s1210_state *st) ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); if (ret < 0) - goto error_ret; - - ret = ad2s1210_reinit_excitation_frequency(st, AD2S1210_DEF_EXCIT); + return ret; -error_ret: - mutex_unlock(&st->lock); - return ret; + return ad2s1210_reinit_excitation_frequency(st, AD2S1210_DEF_EXCIT); } static int ad2s1210_read_label(struct iio_dev *indio_dev, @@ -1281,18 +1232,13 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int *readval) { struct ad2s1210_state *st = iio_priv(indio_dev); - int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); if (readval) - ret = regmap_read(st->regmap, reg, readval); - else - ret = regmap_write(st->regmap, reg, writeval); - - mutex_unlock(&st->lock); + return regmap_read(st->regmap, reg, readval); - return ret; + return regmap_write(st->regmap, reg, writeval); } static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) @@ -1303,7 +1249,7 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) size_t chan = 0; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); memset(&st->scan, 0, sizeof(st->scan)); ad2s1210_toggle_sample_line(st); @@ -1336,7 +1282,6 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); error_ret: - mutex_unlock(&st->lock); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; -- cgit v1.2.3 From 291e45eeeb901b1d22b74ac5ea48cab3c407c705 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 10 Oct 2023 16:12:36 -0500 Subject: iio: resolver: ad2s1210: move out of staging This moves the ad2s1210 resolver driver out of staging. The driver has been fixed up and is ready to graduate. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231010-ad2s1210-mainline-v5-4-35a0f6ffa04a@baylibre.com Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-resolver-ad2s1210 | 27 + drivers/iio/resolver/Kconfig | 13 + drivers/iio/resolver/Makefile | 1 + drivers/iio/resolver/ad2s1210.c | 1522 ++++++++++++++++++++ .../Documentation/sysfs-bus-iio-resolver-ad2s1210 | 27 - drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/resolver/Kconfig | 19 - drivers/staging/iio/resolver/Makefile | 6 - drivers/staging/iio/resolver/ad2s1210.c | 1522 -------------------- 10 files changed, 1563 insertions(+), 1576 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 create mode 100644 drivers/iio/resolver/ad2s1210.c delete mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 delete mode 100644 drivers/staging/iio/resolver/Kconfig delete mode 100644 drivers/staging/iio/resolver/Makefile delete mode 100644 drivers/staging/iio/resolver/ad2s1210.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 b/Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 new file mode 100644 index 000000000000..f92c79342b93 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 @@ -0,0 +1,27 @@ +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_max +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Reset Maximum + Threshold value in millivolts. Writing sets the value. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_max_available +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the allowable voltage range for + in_altvoltage0_mag_rising_reset_max. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_min +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Reset Minimum + Threshold value in millivolts. Writing sets the value. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_min_available +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the allowable voltage range for + in_altvoltage0_mag_rising_reset_min. diff --git a/drivers/iio/resolver/Kconfig b/drivers/iio/resolver/Kconfig index 47dbfead9b31..424529d36080 100644 --- a/drivers/iio/resolver/Kconfig +++ b/drivers/iio/resolver/Kconfig @@ -25,4 +25,17 @@ config AD2S1200 To compile this driver as a module, choose M here: the module will be called ad2s1200. + +config AD2S1210 + tristate "Analog Devices ad2s1210 driver" + depends on SPI + depends on COMMON_CLK + depends on GPIOLIB || COMPILE_TEST + help + Say yes here to build support for Analog Devices spi resolver + to digital converters, ad2s1210, provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called ad2s1210. + endmenu diff --git a/drivers/iio/resolver/Makefile b/drivers/iio/resolver/Makefile index fa558138ce45..7f6c876c35ae 100644 --- a/drivers/iio/resolver/Makefile +++ b/drivers/iio/resolver/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_AD2S90) += ad2s90.o obj-$(CONFIG_AD2S1200) += ad2s1200.o +obj-$(CONFIG_AD2S1210) += ad2s1210.o diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c new file mode 100644 index 000000000000..bd4a90c222b5 --- /dev/null +++ b/drivers/iio/resolver/ad2s1210.c @@ -0,0 +1,1522 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * Copyright (c) 2023 BayLibre, SAS + * + * Device register to IIO ABI mapping: + * + * Register | Addr | IIO ABI (sysfs) + * ----------------------------|------|------------------------------------------- + * DOS Overrange Threshold | 0x89 | events/in_altvoltage0_thresh_rising_value + * DOS Mismatch Threshold | 0x8A | events/in_altvoltage0_mag_rising_value + * DOS Reset Maximum Threshold | 0x8B | events/in_altvoltage0_mag_rising_reset_max + * DOS Reset Minimum Threshold | 0x8C | events/in_altvoltage0_mag_rising_reset_min + * LOT High Threshold | 0x8D | events/in_angl1_thresh_rising_value + * LOT Low Threshold [1] | 0x8E | events/in_angl1_thresh_rising_hysteresis + * Excitation Frequency | 0x91 | out_altvoltage0_frequency + * Control | 0x92 | *as bit fields* + * Phase lock range | D5 | events/in_phase0_mag_rising_value + * Hysteresis | D4 | in_angl0_hysteresis + * Encoder resolution | D3:2 | *not implemented* + * Resolution | D1:0 | *device tree: assigned-resolution-bits* + * Soft Reset | 0xF0 | [2] + * Fault | 0xFF | *not implemented* + * + * [1]: The value written to the LOT low register is high value minus the + * hysteresis. + * [2]: Soft reset is performed when `out_altvoltage0_frequency` is written. + * + * Fault to event mapping: + * + * Fault | | Channel | Type | Direction + * ----------------------------------------|----|--------------------------------- + * Sine/cosine inputs clipped [3] | D7 | altvoltage1 | mag | either + * Sine/cosine inputs below LOS | D6 | altvoltage0 | thresh | falling + * Sine/cosine inputs exceed DOS overrange | D5 | altvoltage0 | thresh | rising + * Sine/cosine inputs exceed DOS mismatch | D4 | altvoltage0 | mag | rising + * Tracking error exceeds LOT | D3 | angl1 | thresh | rising + * Velocity exceeds maximum tracking rate | D2 | anglvel0 | mag | rising + * Phase error exceeds phase lock range | D1 | phase0 | mag | rising + * Configuration parity error | D0 | *writes to kernel log* + * + * [3]: The chip does not differentiate between fault on sine vs. cosine so + * there will also be an event on the altvoltage2 channel. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "ad2s1210" + +/* control register flags */ +#define AD2S1210_ADDRESS_DATA BIT(7) +#define AD2S1210_PHASE_LOCK_RANGE_44 BIT(5) +#define AD2S1210_ENABLE_HYSTERESIS BIT(4) +#define AD2S1210_SET_ENRES GENMASK(3, 2) +#define AD2S1210_SET_RES GENMASK(1, 0) + +/* fault register flags */ +#define AD2S1210_FAULT_CLIP BIT(7) +#define AD2S1210_FAULT_LOS BIT(6) +#define AD2S1210_FAULT_DOS_OVR BIT(5) +#define AD2S1210_FAULT_DOS_MIS BIT(4) +#define AD2S1210_FAULT_LOT BIT(3) +#define AD2S1210_FAULT_VELOCITY BIT(2) +#define AD2S1210_FAULT_PHASE BIT(1) +#define AD2S1210_FAULT_CONFIG_PARITY BIT(0) + +#define AD2S1210_REG_POSITION_MSB 0x80 +#define AD2S1210_REG_POSITION_LSB 0x81 +#define AD2S1210_REG_VELOCITY_MSB 0x82 +#define AD2S1210_REG_VELOCITY_LSB 0x83 +#define AD2S1210_REG_LOS_THRD 0x88 +#define AD2S1210_REG_DOS_OVR_THRD 0x89 +#define AD2S1210_REG_DOS_MIS_THRD 0x8A +#define AD2S1210_REG_DOS_RST_MAX_THRD 0x8B +#define AD2S1210_REG_DOS_RST_MIN_THRD 0x8C +#define AD2S1210_REG_LOT_HIGH_THRD 0x8D +#define AD2S1210_REG_LOT_LOW_THRD 0x8E +#define AD2S1210_REG_EXCIT_FREQ 0x91 +#define AD2S1210_REG_CONTROL 0x92 +#define AD2S1210_REG_SOFT_RESET 0xF0 +#define AD2S1210_REG_FAULT 0xFF + +#define AD2S1210_MIN_CLKIN 6144000 +#define AD2S1210_MAX_CLKIN 10240000 +#define AD2S1210_MIN_EXCIT 2000 +#define AD2S1210_DEF_EXCIT 10000 +#define AD2S1210_MAX_EXCIT 20000 +#define AD2S1210_MIN_FCW 0x4 +#define AD2S1210_MAX_FCW 0x50 + +/* 44 degrees ~= 0.767945 radians */ +#define PHASE_44_DEG_TO_RAD_INT 0 +#define PHASE_44_DEG_TO_RAD_MICRO 767945 +/* 360 degrees ~= 6.283185 radians */ +#define PHASE_360_DEG_TO_RAD_INT 6 +#define PHASE_360_DEG_TO_RAD_MICRO 283185 + +/* Threshold voltage registers have 1 LSB == 38 mV */ +#define THRESHOLD_MILLIVOLT_PER_LSB 38 +/* max voltage for threshold registers is 0x7F * 38 mV */ +#define THRESHOLD_RANGE_STR "[0 38 4826]" + +#define FAULT_ONESHOT(bit, new, old) (new & bit && !(old & bit)) + +enum ad2s1210_mode { + MOD_POS = 0b00, + MOD_VEL = 0b01, + MOD_RESERVED = 0b10, + MOD_CONFIG = 0b11, +}; + +enum ad2s1210_resolution { + AD2S1210_RES_10 = 0b00, + AD2S1210_RES_12 = 0b01, + AD2S1210_RES_14 = 0b10, + AD2S1210_RES_16 = 0b11, +}; + +struct ad2s1210_state { + struct mutex lock; + struct spi_device *sdev; + /** GPIO pin connected to SAMPLE line. */ + struct gpio_desc *sample_gpio; + /** GPIO pins connected to A0 and A1 lines. */ + struct gpio_descs *mode_gpios; + /** Used to access config registers. */ + struct regmap *regmap; + /** The external oscillator frequency in Hz. */ + unsigned long clkin_hz; + /** Available raw hysteresis values based on resolution. */ + int hysteresis_available[2]; + /** The selected resolution */ + enum ad2s1210_resolution resolution; + /** Copy of fault register from the previous read. */ + u8 prev_fault_flags; + /** For reading raw sample value via SPI. */ + struct { + __be16 raw; + u8 fault; + } sample __aligned(IIO_DMA_MINALIGN); + /** Scan buffer */ + struct { + __be16 chan[2]; + /* Ensure timestamp is naturally aligned. */ + s64 timestamp __aligned(8); + } scan; + /** SPI transmit buffer. */ + u8 rx[2]; + /** SPI receive buffer. */ + u8 tx[2]; +}; + +static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode) +{ + struct gpio_descs *gpios = st->mode_gpios; + DECLARE_BITMAP(bitmap, 2); + + bitmap[0] = mode; + + return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, + bitmap); +} + +/* + * Writes the given data to the given register address. + * + * If the mode is configurable, the device will first be placed in + * configuration mode. + */ +static int ad2s1210_regmap_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct ad2s1210_state *st = context; + struct spi_transfer xfers[] = { + { + .len = 1, + .rx_buf = &st->rx[0], + .tx_buf = &st->tx[0], + .cs_change = 1, + }, { + .len = 1, + .rx_buf = &st->rx[1], + .tx_buf = &st->tx[1], + }, + }; + int ret; + + /* values can only be 7 bits, the MSB indicates an address */ + if (val & ~0x7F) + return -EINVAL; + + st->tx[0] = reg; + st->tx[1] = val; + + ret = ad2s1210_set_mode(st, MOD_CONFIG); + if (ret < 0) + return ret; + + ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); + if (ret < 0) + return ret; + + /* soft reset also clears the fault register */ + if (reg == AD2S1210_REG_SOFT_RESET) + st->prev_fault_flags = 0; + + return 0; +} + +/* + * Reads value from one of the registers. + * + * If the mode is configurable, the device will first be placed in + * configuration mode. + */ +static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct ad2s1210_state *st = context; + struct spi_transfer xfers[] = { + { + .len = 1, + .rx_buf = &st->rx[0], + .tx_buf = &st->tx[0], + .cs_change = 1, + }, { + .len = 1, + .rx_buf = &st->rx[1], + .tx_buf = &st->tx[1], + }, + }; + int ret; + + ret = ad2s1210_set_mode(st, MOD_CONFIG); + if (ret < 0) + return ret; + + st->tx[0] = reg; + /* + * Must be valid register address here otherwise this could write data. + * It doesn't matter which one as long as reading doesn't have side- + * effects. + */ + st->tx[1] = AD2S1210_REG_CONTROL; + + ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); + if (ret < 0) + return ret; + + /* reading the fault register also clears it */ + if (reg == AD2S1210_REG_FAULT) + st->prev_fault_flags = 0; + + /* + * If the D7 bit is set on any read/write register, it indicates a + * parity error. The fault register is read-only and the D7 bit means + * something else there. + */ + if (reg != AD2S1210_REG_FAULT && st->rx[1] & AD2S1210_ADDRESS_DATA) + return -EBADMSG; + + *val = st->rx[1]; + + return 0; +} + +/* + * Toggles the SAMPLE line on the AD2S1210 to latch in the current position, + * velocity, and faults. + * + * Must be called with lock held. + */ +static void ad2s1210_toggle_sample_line(struct ad2s1210_state *st) +{ + /* + * Datasheet specifies minimum hold time t16 = 2 * tck + 20 ns. So the + * longest time needed is when CLKIN is 6.144 MHz, in which case t16 + * ~= 350 ns. The same delay is also needed before re-asserting the + * SAMPLE line. + */ + gpiod_set_value(st->sample_gpio, 1); + ndelay(350); + gpiod_set_value(st->sample_gpio, 0); + ndelay(350); +} + +/* + * Sets the excitation frequency and performs software reset. + * + * Must be called with lock held. + */ +static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, + u16 fexcit) +{ + /* Map resolution to settle time in milliseconds. */ + static const int track_time_ms[] = { 10, 20, 25, 60 }; + unsigned int ignored; + int ret; + u8 fcw; + + fcw = fexcit * (1 << 15) / st->clkin_hz; + if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) + return -ERANGE; + + ret = regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw); + if (ret < 0) + return ret; + + /* + * Software reset reinitializes the excitation frequency output. + * It does not reset any of the configuration registers. + */ + ret = regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); + if (ret < 0) + return ret; + + /* + * Soft reset always triggers some faults due the change in the output + * signal so clear the faults too. We need to delay for some time + * (what datasheet calls t[track]) to allow things to settle before + * clearing the faults. + */ + msleep(track_time_ms[st->resolution] * 8192000 / st->clkin_hz); + + /* Reading the fault register clears the faults. */ + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &ignored); + if (ret < 0) + return ret; + + /* Have to toggle sample line to get fault output pins to reset. */ + ad2s1210_toggle_sample_line(st); + + return 0; +} + +static void ad2s1210_push_events(struct iio_dev *indio_dev, + u8 flags, s64 timestamp) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + /* Sine/cosine inputs clipped */ + if (FAULT_ONESHOT(AD2S1210_FAULT_CLIP, flags, st->prev_fault_flags)) { + /* + * The chip does not differentiate between fault on sine vs. + * cosine channel so we just send an event on both channels. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 1, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_EITHER), + timestamp); + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 2, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_EITHER), + timestamp); + } + + /* Sine/cosine inputs below LOS threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_LOS, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + + /* Sine/cosine inputs exceed DOS overrange threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_OVR, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + /* Sine/cosine inputs exceed DOS mismatch threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_MIS, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + timestamp); + + /* Tracking error exceeds LOT threshold */ + if (FAULT_ONESHOT(AD2S1210_FAULT_LOT, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ANGL, 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + /* Velocity exceeds maximum tracking rate */ + if (FAULT_ONESHOT(AD2S1210_FAULT_VELOCITY, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_ANGL_VEL, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + /* Phase error exceeds phase lock range */ + if (FAULT_ONESHOT(AD2S1210_FAULT_PHASE, flags, st->prev_fault_flags)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PHASE, 0, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + timestamp); + + /* Configuration parity error */ + if (FAULT_ONESHOT(AD2S1210_FAULT_CONFIG_PARITY, flags, + st->prev_fault_flags)) + /* + * Userspace should also get notified of this via error return + * when trying to write to any attribute that writes a register. + */ + dev_err_ratelimited(&indio_dev->dev, + "Configuration parity error\n"); + + st->prev_fault_flags = flags; +} + +static int ad2s1210_single_conversion(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + s64 timestamp; + int ret; + + guard(mutex)(&st->lock); + + ad2s1210_toggle_sample_line(st); + timestamp = iio_get_time_ns(indio_dev); + + switch (chan->type) { + case IIO_ANGL: + ret = ad2s1210_set_mode(st, MOD_POS); + break; + case IIO_ANGL_VEL: + ret = ad2s1210_set_mode(st, MOD_VEL); + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + ret = spi_read(st->sdev, &st->sample, 3); + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_ANGL: + *val = be16_to_cpu(st->sample.raw); + ret = IIO_VAL_INT; + break; + case IIO_ANGL_VEL: + *val = (s16)be16_to_cpu(st->sample.raw); + ret = IIO_VAL_INT; + break; + default: + return -EINVAL; + } + + ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); + + return ret; +} + +static int ad2s1210_get_hysteresis(struct ad2s1210_state *st, int *val) +{ + int ret; + + guard(mutex)(&st->lock); + ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_ENABLE_HYSTERESIS); + if (ret < 0) + return ret; + + *val = ret << (2 * (AD2S1210_RES_16 - st->resolution)); + return IIO_VAL_INT; +} + +static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val) +{ + guard(mutex)(&st->lock); + return regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_ENABLE_HYSTERESIS, + val ? AD2S1210_ENABLE_HYSTERESIS : 0); +} + +static int ad2s1210_get_phase_lock_range(struct ad2s1210_state *st, + int *val, int *val2) +{ + int ret; + + guard(mutex)(&st->lock); + ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_PHASE_LOCK_RANGE_44); + if (ret < 0) + return ret; + + if (ret) { + /* 44 degrees as radians */ + *val = PHASE_44_DEG_TO_RAD_INT; + *val2 = PHASE_44_DEG_TO_RAD_MICRO; + } else { + /* 360 degrees as radians */ + *val = PHASE_360_DEG_TO_RAD_INT; + *val2 = PHASE_360_DEG_TO_RAD_MICRO; + } + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad2s1210_set_phase_lock_range(struct ad2s1210_state *st, + int val, int val2) +{ + int deg; + + /* convert radians to degrees - only two allowable values */ + if (val == PHASE_44_DEG_TO_RAD_INT && val2 == PHASE_44_DEG_TO_RAD_MICRO) + deg = 44; + else if (val == PHASE_360_DEG_TO_RAD_INT && + val2 == PHASE_360_DEG_TO_RAD_MICRO) + deg = 360; + else + return -EINVAL; + + guard(mutex)(&st->lock); + return regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_PHASE_LOCK_RANGE_44, + deg == 44 ? AD2S1210_PHASE_LOCK_RANGE_44 : 0); +} + +/* map resolution to microradians/LSB for LOT registers */ +static const int ad2s1210_lot_threshold_urad_per_lsb[] = { + 6184, /* 10-bit: ~0.35 deg/LSB, 45 deg max */ + 2473, /* 12-bit: ~0.14 deg/LSB, 18 deg max */ + 1237, /* 14-bit: ~0.07 deg/LSB, 9 deg max */ + 1237, /* 16-bit: same as 14-bit */ +}; + +static int ad2s1210_get_voltage_threshold(struct ad2s1210_state *st, + unsigned int reg, int *val) +{ + unsigned int reg_val; + int ret; + + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, reg, ®_val); + if (ret < 0) + return ret; + + *val = reg_val * THRESHOLD_MILLIVOLT_PER_LSB; + return IIO_VAL_INT; +} + +static int ad2s1210_set_voltage_threshold(struct ad2s1210_state *st, + unsigned int reg, int val) +{ + unsigned int reg_val; + + reg_val = val / THRESHOLD_MILLIVOLT_PER_LSB; + + guard(mutex)(&st->lock); + return regmap_write(st->regmap, reg, reg_val); +} + +static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st, + int *val, int *val2) +{ + unsigned int reg_val; + int ret; + + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); + if (ret < 0) + return ret; + + *val = 0; + *val2 = reg_val * ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad2s1210_set_lot_high_threshold(struct ad2s1210_state *st, + int val, int val2) +{ + unsigned int high_reg_val, low_reg_val, hysteresis; + int ret; + + /* all valid values are between 0 and pi/4 radians */ + if (val != 0) + return -EINVAL; + + guard(mutex)(&st->lock); + /* + * We need to read both high and low registers first so we can preserve + * the hysteresis. + */ + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); + if (ret < 0) + return ret; + + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); + if (ret < 0) + return ret; + + hysteresis = high_reg_val - low_reg_val; + high_reg_val = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + low_reg_val = high_reg_val - hysteresis; + + ret = regmap_write(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, high_reg_val); + if (ret < 0) + return ret; + + return regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, low_reg_val); +} + +static int ad2s1210_get_lot_low_threshold(struct ad2s1210_state *st, + int *val, int *val2) +{ + unsigned int high_reg_val, low_reg_val; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); + if (ret < 0) + return ret; + + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); + if (ret < 0) + return ret; + + /* sysfs value is hysteresis rather than actual low value */ + *val = 0; + *val2 = (high_reg_val - low_reg_val) * + ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad2s1210_set_lot_low_threshold(struct ad2s1210_state *st, + int val, int val2) +{ + unsigned int reg_val, hysteresis; + int ret; + + /* all valid values are between 0 and pi/4 radians */ + if (val != 0) + return -EINVAL; + + hysteresis = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); + if (ret < 0) + return ret; + + return regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, + reg_val - hysteresis); +} + +static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val) +{ + unsigned int reg_val; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, ®_val); + if (ret < 0) + return ret; + + *val = reg_val * st->clkin_hz / (1 << 15); + return IIO_VAL_INT; +} + +static int ad2s1210_set_excitation_frequency(struct ad2s1210_state *st, int val) +{ + if (val < AD2S1210_MIN_EXCIT || val > AD2S1210_MAX_EXCIT) + return -EINVAL; + + guard(mutex)(&st->lock); + return ad2s1210_reinit_excitation_frequency(st, val); +} + +static const int ad2s1210_velocity_scale[] = { + 17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */ + 42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */ + 85445659, /* 8.192MHz / (2*pi * 500 / 2^15) */ + 341782638, /* 8.192MHz / (2*pi * 125 / 2^15) */ +}; + +static int ad2s1210_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return ad2s1210_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL: + /* approx 0.3 arc min converted to radians */ + *val = 0; + *val2 = 95874; + return IIO_VAL_INT_PLUS_NANO; + case IIO_ANGL_VEL: + *val = st->clkin_hz; + *val2 = ad2s1210_velocity_scale[st->resolution]; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->type) { + case IIO_ALTVOLTAGE: + return ad2s1210_get_excitation_frequency(st, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + return ad2s1210_get_hysteresis(st, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad2s1210_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + static const int excitation_frequency_available[] = { + AD2S1210_MIN_EXCIT, + 250, /* step */ + AD2S1210_MAX_EXCIT, + }; + + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->type) { + case IIO_ALTVOLTAGE: + *type = IIO_VAL_INT; + *vals = excitation_frequency_available; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + *vals = st->hysteresis_available; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(st->hysteresis_available); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad2s1210_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->type) { + case IIO_ALTVOLTAGE: + return ad2s1210_set_excitation_frequency(st, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + return ad2s1210_set_hysteresis(st, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_event_spec ad2s1210_position_event_spec[] = { + { + /* Tracking error exceeds LOT threshold fault. */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = + /* Loss of tracking high threshold. */ + BIT(IIO_EV_INFO_VALUE) | + /* Loss of tracking low threshold. */ + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +static const struct iio_event_spec ad2s1210_velocity_event_spec[] = { + { + /* Velocity exceeds maximum tracking rate fault. */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + }, +}; + +static const struct iio_event_spec ad2s1210_phase_event_spec[] = { + { + /* Phase error fault. */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + /* Phase lock range. */ + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { + { + /* Sine/cosine below LOS threshold fault. */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + /* Loss of signal threshold. */ + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + /* Sine/cosine DOS overrange fault.*/ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + /* Degredation of signal overrange threshold. */ + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + /* Sine/cosine DOS mismatch fault.*/ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const struct iio_event_spec ad2s1210_sin_cos_event_spec[] = { + { + /* Sine/cosine clipping fault. */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_EITHER, + }, +}; + +static const struct iio_chan_spec ad2s1210_channels[] = { + { + .type = IIO_ANGL, + .indexed = 1, + .channel = 0, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_HYSTERESIS), + }, { + .type = IIO_ANGL_VEL, + .indexed = 1, + .channel = 0, + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .event_spec = ad2s1210_velocity_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_velocity_event_spec), + }, + IIO_CHAN_SOFT_TIMESTAMP(2), + { + /* used to configure LOT thresholds and get tracking error */ + .type = IIO_ANGL, + .indexed = 1, + .channel = 1, + .scan_index = -1, + .event_spec = ad2s1210_position_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_position_event_spec), + }, + { + /* used to configure phase lock range and get phase lock error */ + .type = IIO_PHASE, + .indexed = 1, + .channel = 0, + .scan_index = -1, + .event_spec = ad2s1210_phase_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_phase_event_spec), + }, { + /* excitation frequency output */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 0, + .output = 1, + .scan_index = -1, + .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_FREQUENCY), + }, { + /* monitor signal */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 0, + .scan_index = -1, + .event_spec = ad2s1210_monitor_signal_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_monitor_signal_event_spec), + }, { + /* sine input */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 1, + .scan_index = -1, + .event_spec = ad2s1210_sin_cos_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec), + }, { + /* cosine input */ + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .channel = 2, + .scan_index = -1, + .event_spec = ad2s1210_sin_cos_event_spec, + .num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec), + }, +}; + +static ssize_t event_attr_voltage_reg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + unsigned int value; + int ret; + + guard(mutex)(&st->lock); + ret = regmap_read(st->regmap, iattr->address, &value); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", value * THRESHOLD_MILLIVOLT_PER_LSB); +} + +static ssize_t event_attr_voltage_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + u16 data; + int ret; + + ret = kstrtou16(buf, 10, &data); + if (ret) + return -EINVAL; + + guard(mutex)(&st->lock); + ret = regmap_write(st->regmap, iattr->address, + data / THRESHOLD_MILLIVOLT_PER_LSB); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t +in_angl1_thresh_rising_value_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + int step = ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + + return sysfs_emit(buf, "[0 0.%06d 0.%06d]\n", step, step * 0x7F); +} + +static ssize_t +in_angl1_thresh_rising_hysteresis_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + int step = ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; + + return sysfs_emit(buf, "[0 0.%06d 0.%06d]\n", step, step * 0x7F); +} + +static IIO_CONST_ATTR(in_phase0_mag_rising_value_available, + __stringify(PHASE_44_DEG_TO_RAD_INT) "." + __stringify(PHASE_44_DEG_TO_RAD_MICRO) " " + __stringify(PHASE_360_DEG_TO_RAD_INT) "." + __stringify(PHASE_360_DEG_TO_RAD_MICRO)); +static IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, + THRESHOLD_RANGE_STR); +static IIO_CONST_ATTR(in_altvoltage0_thresh_rising_value_available, + THRESHOLD_RANGE_STR); +static IIO_CONST_ATTR(in_altvoltage0_mag_rising_value_available, + THRESHOLD_RANGE_STR); +static IIO_DEVICE_ATTR(in_altvoltage0_mag_rising_reset_max, 0644, + event_attr_voltage_reg_show, event_attr_voltage_reg_store, + AD2S1210_REG_DOS_RST_MAX_THRD); +static IIO_CONST_ATTR(in_altvoltage0_mag_rising_reset_max_available, THRESHOLD_RANGE_STR); +static IIO_DEVICE_ATTR(in_altvoltage0_mag_rising_reset_min, 0644, + event_attr_voltage_reg_show, event_attr_voltage_reg_store, + AD2S1210_REG_DOS_RST_MIN_THRD); +static IIO_CONST_ATTR(in_altvoltage0_mag_rising_reset_min_available, THRESHOLD_RANGE_STR); +static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0); +static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0); + +static struct attribute *ad2s1210_event_attributes[] = { + &iio_const_attr_in_phase0_mag_rising_value_available.dev_attr.attr, + &iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr, + &iio_const_attr_in_altvoltage0_thresh_rising_value_available.dev_attr.attr, + &iio_const_attr_in_altvoltage0_mag_rising_value_available.dev_attr.attr, + &iio_dev_attr_in_altvoltage0_mag_rising_reset_max.dev_attr.attr, + &iio_const_attr_in_altvoltage0_mag_rising_reset_max_available.dev_attr.attr, + &iio_dev_attr_in_altvoltage0_mag_rising_reset_min.dev_attr.attr, + &iio_const_attr_in_altvoltage0_mag_rising_reset_min_available.dev_attr.attr, + &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr, + &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad2s1210_event_attribute_group = { + .attrs = ad2s1210_event_attributes, +}; + +static int ad2s1210_initial(struct ad2s1210_state *st) +{ + unsigned int data; + int ret; + + guard(mutex)(&st->lock); + + /* Use default config register value plus resolution from devicetree. */ + data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1); + data |= FIELD_PREP(AD2S1210_ENABLE_HYSTERESIS, 1); + data |= FIELD_PREP(AD2S1210_SET_ENRES, 0x3); + data |= FIELD_PREP(AD2S1210_SET_RES, st->resolution); + + ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); + if (ret < 0) + return ret; + + return ad2s1210_reinit_excitation_frequency(st, AD2S1210_DEF_EXCIT); +} + +static int ad2s1210_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->type == IIO_ANGL) { + if (chan->channel == 0) + return sprintf(label, "position\n"); + if (chan->channel == 1) + return sprintf(label, "tracking error\n"); + } + if (chan->type == IIO_ANGL_VEL) + return sprintf(label, "velocity\n"); + if (chan->type == IIO_PHASE) + return sprintf(label, "synthetic reference\n"); + if (chan->type == IIO_ALTVOLTAGE) { + if (chan->output) + return sprintf(label, "excitation\n"); + if (chan->channel == 0) + return sprintf(label, "monitor signal\n"); + if (chan->channel == 1) + return sprintf(label, "cosine\n"); + if (chan->channel == 2) + return sprintf(label, "sine\n"); + } + + return -EINVAL; +} + +static int ad2s1210_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_ANGL: + switch (info) { + case IIO_EV_INFO_VALUE: + return ad2s1210_get_lot_high_threshold(st, val, val2); + case IIO_EV_INFO_HYSTERESIS: + return ad2s1210_get_lot_low_threshold(st, val, val2); + default: + return -EINVAL; + } + case IIO_ALTVOLTAGE: + if (chan->output) + return -EINVAL; + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) + return ad2s1210_get_voltage_threshold(st, + AD2S1210_REG_LOS_THRD, val); + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) + return ad2s1210_get_voltage_threshold(st, + AD2S1210_REG_DOS_OVR_THRD, val); + if (type == IIO_EV_TYPE_MAG) + return ad2s1210_get_voltage_threshold(st, + AD2S1210_REG_DOS_MIS_THRD, val); + return -EINVAL; + case IIO_PHASE: + return ad2s1210_get_phase_lock_range(st, val, val2); + default: + return -EINVAL; + } +} + +static int ad2s1210_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_ANGL: + switch (info) { + case IIO_EV_INFO_VALUE: + return ad2s1210_set_lot_high_threshold(st, val, val2); + case IIO_EV_INFO_HYSTERESIS: + return ad2s1210_set_lot_low_threshold(st, val, val2); + default: + return -EINVAL; + } + case IIO_ALTVOLTAGE: + if (chan->output) + return -EINVAL; + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) + return ad2s1210_set_voltage_threshold(st, + AD2S1210_REG_LOS_THRD, val); + if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) + return ad2s1210_set_voltage_threshold(st, + AD2S1210_REG_DOS_OVR_THRD, val); + if (type == IIO_EV_TYPE_MAG) + return ad2s1210_set_voltage_threshold(st, + AD2S1210_REG_DOS_MIS_THRD, val); + return -EINVAL; + case IIO_PHASE: + return ad2s1210_set_phase_lock_range(st, val, val2); + default: + return -EINVAL; + } +} + +static int ad2s1210_read_event_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum iio_event_type type, + enum iio_event_direction dir, + char *label) +{ + if (chan->type == IIO_ANGL) + return sprintf(label, "LOT\n"); + if (chan->type == IIO_ANGL_VEL) + return sprintf(label, "max tracking rate\n"); + if (chan->type == IIO_PHASE) + return sprintf(label, "phase lock\n"); + if (chan->type == IIO_ALTVOLTAGE) { + if (chan->channel == 0) { + if (type == IIO_EV_TYPE_THRESH && + dir == IIO_EV_DIR_FALLING) + return sprintf(label, "LOS\n"); + if (type == IIO_EV_TYPE_THRESH && + dir == IIO_EV_DIR_RISING) + return sprintf(label, "DOS overrange\n"); + if (type == IIO_EV_TYPE_MAG) + return sprintf(label, "DOS mismatch\n"); + } + if (chan->channel == 1 || chan->channel == 2) + return sprintf(label, "clipped\n"); + } + + return -EINVAL; +} + +static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad2s1210_state *st = iio_priv(indio_dev); + size_t chan = 0; + int ret; + + guard(mutex)(&st->lock); + + memset(&st->scan, 0, sizeof(st->scan)); + ad2s1210_toggle_sample_line(st); + + if (test_bit(0, indio_dev->active_scan_mask)) { + ret = ad2s1210_set_mode(st, MOD_POS); + if (ret < 0) + goto error_ret; + + ret = spi_read(st->sdev, &st->sample, 3); + if (ret < 0) + goto error_ret; + + memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); + } + + if (test_bit(1, indio_dev->active_scan_mask)) { + ret = ad2s1210_set_mode(st, MOD_VEL); + if (ret < 0) + goto error_ret; + + ret = spi_read(st->sdev, &st->sample, 3); + if (ret < 0) + goto error_ret; + + memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); + } + + ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); + +error_ret: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_info ad2s1210_info = { + .event_attrs = &ad2s1210_event_attribute_group, + .read_raw = ad2s1210_read_raw, + .read_avail = ad2s1210_read_avail, + .write_raw = ad2s1210_write_raw, + .read_label = ad2s1210_read_label, + .read_event_value = ad2s1210_read_event_value, + .write_event_value = ad2s1210_write_event_value, + .read_event_label = ad2s1210_read_event_label, + .debugfs_reg_access = &ad2s1210_debugfs_reg_access, +}; + +static int ad2s1210_setup_properties(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + u32 val; + int ret; + + ret = device_property_read_u32(dev, "assigned-resolution-bits", &val); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to read assigned-resolution-bits property\n"); + + if (val < 10 || val > 16) + return dev_err_probe(dev, -EINVAL, + "resolution out of range: %u\n", val); + + st->resolution = (val - 10) >> 1; + /* + * These are values that correlate to the hysteresis bit in the Control + * register. 0 = disabled, 1 = enabled. When enabled, the actual + * hysteresis is +/- 1 LSB of the raw position value. Which bit is the + * LSB depends on the specified resolution. + */ + st->hysteresis_available[0] = 0; + st->hysteresis_available[1] = 1 << (2 * (AD2S1210_RES_16 - + st->resolution)); + + return 0; +} + +static int ad2s1210_setup_clocks(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + struct clk *clk; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); + + st->clkin_hz = clk_get_rate(clk); + if (st->clkin_hz < AD2S1210_MIN_CLKIN || st->clkin_hz > AD2S1210_MAX_CLKIN) + return dev_err_probe(dev, -EINVAL, + "clock frequency out of range: %lu\n", + st->clkin_hz); + + return 0; +} + +static int ad2s1210_setup_gpios(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + struct gpio_descs *resolution_gpios; + DECLARE_BITMAP(bitmap, 2); + int ret; + + /* should not be sampling on startup */ + st->sample_gpio = devm_gpiod_get(dev, "sample", GPIOD_OUT_LOW); + if (IS_ERR(st->sample_gpio)) + return dev_err_probe(dev, PTR_ERR(st->sample_gpio), + "failed to request sample GPIO\n"); + + /* both pins high means that we start in config mode */ + st->mode_gpios = devm_gpiod_get_array(dev, "mode", GPIOD_OUT_HIGH); + if (IS_ERR(st->mode_gpios)) + return dev_err_probe(dev, PTR_ERR(st->mode_gpios), + "failed to request mode GPIOs\n"); + + if (st->mode_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, + "requires exactly 2 mode-gpios\n"); + + /* + * If resolution gpios are provided, they get set to the required + * resolution, otherwise it is assumed the RES0 and RES1 pins are + * hard-wired to match the resolution indicated in the devicetree. + */ + resolution_gpios = devm_gpiod_get_array_optional(dev, "resolution", + GPIOD_ASIS); + if (IS_ERR(resolution_gpios)) + return dev_err_probe(dev, PTR_ERR(resolution_gpios), + "failed to request resolution GPIOs\n"); + + if (resolution_gpios) { + if (resolution_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, + "requires exactly 2 resolution-gpios\n"); + + bitmap[0] = st->resolution; + + ret = gpiod_set_array_value(resolution_gpios->ndescs, + resolution_gpios->desc, + resolution_gpios->info, + bitmap); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to set resolution gpios\n"); + } + + return 0; +} + +static const struct regmap_range ad2s1210_regmap_readable_ranges[] = { + regmap_reg_range(AD2S1210_REG_POSITION_MSB, AD2S1210_REG_VELOCITY_LSB), + regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), + regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), + regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), +}; + +static const struct regmap_access_table ad2s1210_regmap_rd_table = { + .yes_ranges = ad2s1210_regmap_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_readable_ranges), +}; + +static const struct regmap_range ad2s1210_regmap_writeable_ranges[] = { + regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), + regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), + regmap_reg_range(AD2S1210_REG_SOFT_RESET, AD2S1210_REG_SOFT_RESET), + regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), +}; + +static const struct regmap_access_table ad2s1210_regmap_wr_table = { + .yes_ranges = ad2s1210_regmap_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_writeable_ranges), +}; + +static int ad2s1210_setup_regmap(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .disable_locking = true, + .reg_read = ad2s1210_regmap_reg_read, + .reg_write = ad2s1210_regmap_reg_write, + .rd_table = &ad2s1210_regmap_rd_table, + .wr_table = &ad2s1210_regmap_wr_table, + .can_sleep = true, + }; + + st->regmap = devm_regmap_init(dev, NULL, st, &config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "failed to allocate register map\n"); + + return 0; +} + +static int ad2s1210_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad2s1210_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + st = iio_priv(indio_dev); + + mutex_init(&st->lock); + st->sdev = spi; + + ret = ad2s1210_setup_properties(st); + if (ret < 0) + return ret; + + ret = ad2s1210_setup_clocks(st); + if (ret < 0) + return ret; + + ret = ad2s1210_setup_gpios(st); + if (ret < 0) + return ret; + + ret = ad2s1210_setup_regmap(st); + if (ret < 0) + return ret; + + ret = ad2s1210_initial(st); + if (ret < 0) + return ret; + + indio_dev->info = &ad2s1210_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad2s1210_channels; + indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); + indio_dev->name = spi_get_device_id(spi)->name; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &ad2s1210_trigger_handler, NULL); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "iio triggered buffer setup failed\n"); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ad2s1210_of_match[] = { + { .compatible = "adi,ad2s1210", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad2s1210_of_match); + +static const struct spi_device_id ad2s1210_id[] = { + { "ad2s1210" }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad2s1210_id); + +static struct spi_driver ad2s1210_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(ad2s1210_of_match), + }, + .probe = ad2s1210_probe, + .id_table = ad2s1210_id, +}; +module_spi_driver(ad2s1210_driver); + +MODULE_AUTHOR("Graff Yang "); +MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 b/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 deleted file mode 100644 index f92c79342b93..000000000000 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 +++ /dev/null @@ -1,27 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_max -KernelVersion: 6.7 -Contact: linux-iio@vger.kernel.org -Description: - Reading returns the current Degradation of Signal Reset Maximum - Threshold value in millivolts. Writing sets the value. - -What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_max_available -KernelVersion: 6.7 -Contact: linux-iio@vger.kernel.org -Description: - Reading returns the allowable voltage range for - in_altvoltage0_mag_rising_reset_max. - -What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_min -KernelVersion: 6.7 -Contact: linux-iio@vger.kernel.org -Description: - Reading returns the current Degradation of Signal Reset Minimum - Threshold value in millivolts. Writing sets the value. - -What: /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0_mag_rising_reset_min_available -KernelVersion: 6.7 -Contact: linux-iio@vger.kernel.org -Description: - Reading returns the allowable voltage range for - in_altvoltage0_mag_rising_reset_min. diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index d3968fe2ebb8..a60631c1f449 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -10,6 +10,5 @@ source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/addac/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" -source "drivers/staging/iio/resolver/Kconfig" endmenu diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index c50f1019f829..628583535393 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -8,4 +8,3 @@ obj-y += adc/ obj-y += addac/ obj-y += frequency/ obj-y += impedance-analyzer/ -obj-y += resolver/ diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig deleted file mode 100644 index bebb35822c9e..000000000000 --- a/drivers/staging/iio/resolver/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Resolver/Synchro drivers -# -menu "Resolver to digital converters" - -config AD2S1210 - tristate "Analog Devices ad2s1210 driver" - depends on SPI - depends on COMMON_CLK - depends on GPIOLIB || COMPILE_TEST - help - Say yes here to build support for Analog Devices spi resolver - to digital converters, ad2s1210, provides direct access via sysfs. - - To compile this driver as a module, choose M here: the - module will be called ad2s1210. - -endmenu diff --git a/drivers/staging/iio/resolver/Makefile b/drivers/staging/iio/resolver/Makefile deleted file mode 100644 index 398631f7e79b..000000000000 --- a/drivers/staging/iio/resolver/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for Resolver/Synchro drivers -# - -obj-$(CONFIG_AD2S1210) += ad2s1210.o diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c deleted file mode 100644 index bd4a90c222b5..000000000000 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ /dev/null @@ -1,1522 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210 - * - * Copyright (c) 2010-2010 Analog Devices Inc. - * Copyright (c) 2023 BayLibre, SAS - * - * Device register to IIO ABI mapping: - * - * Register | Addr | IIO ABI (sysfs) - * ----------------------------|------|------------------------------------------- - * DOS Overrange Threshold | 0x89 | events/in_altvoltage0_thresh_rising_value - * DOS Mismatch Threshold | 0x8A | events/in_altvoltage0_mag_rising_value - * DOS Reset Maximum Threshold | 0x8B | events/in_altvoltage0_mag_rising_reset_max - * DOS Reset Minimum Threshold | 0x8C | events/in_altvoltage0_mag_rising_reset_min - * LOT High Threshold | 0x8D | events/in_angl1_thresh_rising_value - * LOT Low Threshold [1] | 0x8E | events/in_angl1_thresh_rising_hysteresis - * Excitation Frequency | 0x91 | out_altvoltage0_frequency - * Control | 0x92 | *as bit fields* - * Phase lock range | D5 | events/in_phase0_mag_rising_value - * Hysteresis | D4 | in_angl0_hysteresis - * Encoder resolution | D3:2 | *not implemented* - * Resolution | D1:0 | *device tree: assigned-resolution-bits* - * Soft Reset | 0xF0 | [2] - * Fault | 0xFF | *not implemented* - * - * [1]: The value written to the LOT low register is high value minus the - * hysteresis. - * [2]: Soft reset is performed when `out_altvoltage0_frequency` is written. - * - * Fault to event mapping: - * - * Fault | | Channel | Type | Direction - * ----------------------------------------|----|--------------------------------- - * Sine/cosine inputs clipped [3] | D7 | altvoltage1 | mag | either - * Sine/cosine inputs below LOS | D6 | altvoltage0 | thresh | falling - * Sine/cosine inputs exceed DOS overrange | D5 | altvoltage0 | thresh | rising - * Sine/cosine inputs exceed DOS mismatch | D4 | altvoltage0 | mag | rising - * Tracking error exceeds LOT | D3 | angl1 | thresh | rising - * Velocity exceeds maximum tracking rate | D2 | anglvel0 | mag | rising - * Phase error exceeds phase lock range | D1 | phase0 | mag | rising - * Configuration parity error | D0 | *writes to kernel log* - * - * [3]: The chip does not differentiate between fault on sine vs. cosine so - * there will also be an event on the altvoltage2 channel. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "ad2s1210" - -/* control register flags */ -#define AD2S1210_ADDRESS_DATA BIT(7) -#define AD2S1210_PHASE_LOCK_RANGE_44 BIT(5) -#define AD2S1210_ENABLE_HYSTERESIS BIT(4) -#define AD2S1210_SET_ENRES GENMASK(3, 2) -#define AD2S1210_SET_RES GENMASK(1, 0) - -/* fault register flags */ -#define AD2S1210_FAULT_CLIP BIT(7) -#define AD2S1210_FAULT_LOS BIT(6) -#define AD2S1210_FAULT_DOS_OVR BIT(5) -#define AD2S1210_FAULT_DOS_MIS BIT(4) -#define AD2S1210_FAULT_LOT BIT(3) -#define AD2S1210_FAULT_VELOCITY BIT(2) -#define AD2S1210_FAULT_PHASE BIT(1) -#define AD2S1210_FAULT_CONFIG_PARITY BIT(0) - -#define AD2S1210_REG_POSITION_MSB 0x80 -#define AD2S1210_REG_POSITION_LSB 0x81 -#define AD2S1210_REG_VELOCITY_MSB 0x82 -#define AD2S1210_REG_VELOCITY_LSB 0x83 -#define AD2S1210_REG_LOS_THRD 0x88 -#define AD2S1210_REG_DOS_OVR_THRD 0x89 -#define AD2S1210_REG_DOS_MIS_THRD 0x8A -#define AD2S1210_REG_DOS_RST_MAX_THRD 0x8B -#define AD2S1210_REG_DOS_RST_MIN_THRD 0x8C -#define AD2S1210_REG_LOT_HIGH_THRD 0x8D -#define AD2S1210_REG_LOT_LOW_THRD 0x8E -#define AD2S1210_REG_EXCIT_FREQ 0x91 -#define AD2S1210_REG_CONTROL 0x92 -#define AD2S1210_REG_SOFT_RESET 0xF0 -#define AD2S1210_REG_FAULT 0xFF - -#define AD2S1210_MIN_CLKIN 6144000 -#define AD2S1210_MAX_CLKIN 10240000 -#define AD2S1210_MIN_EXCIT 2000 -#define AD2S1210_DEF_EXCIT 10000 -#define AD2S1210_MAX_EXCIT 20000 -#define AD2S1210_MIN_FCW 0x4 -#define AD2S1210_MAX_FCW 0x50 - -/* 44 degrees ~= 0.767945 radians */ -#define PHASE_44_DEG_TO_RAD_INT 0 -#define PHASE_44_DEG_TO_RAD_MICRO 767945 -/* 360 degrees ~= 6.283185 radians */ -#define PHASE_360_DEG_TO_RAD_INT 6 -#define PHASE_360_DEG_TO_RAD_MICRO 283185 - -/* Threshold voltage registers have 1 LSB == 38 mV */ -#define THRESHOLD_MILLIVOLT_PER_LSB 38 -/* max voltage for threshold registers is 0x7F * 38 mV */ -#define THRESHOLD_RANGE_STR "[0 38 4826]" - -#define FAULT_ONESHOT(bit, new, old) (new & bit && !(old & bit)) - -enum ad2s1210_mode { - MOD_POS = 0b00, - MOD_VEL = 0b01, - MOD_RESERVED = 0b10, - MOD_CONFIG = 0b11, -}; - -enum ad2s1210_resolution { - AD2S1210_RES_10 = 0b00, - AD2S1210_RES_12 = 0b01, - AD2S1210_RES_14 = 0b10, - AD2S1210_RES_16 = 0b11, -}; - -struct ad2s1210_state { - struct mutex lock; - struct spi_device *sdev; - /** GPIO pin connected to SAMPLE line. */ - struct gpio_desc *sample_gpio; - /** GPIO pins connected to A0 and A1 lines. */ - struct gpio_descs *mode_gpios; - /** Used to access config registers. */ - struct regmap *regmap; - /** The external oscillator frequency in Hz. */ - unsigned long clkin_hz; - /** Available raw hysteresis values based on resolution. */ - int hysteresis_available[2]; - /** The selected resolution */ - enum ad2s1210_resolution resolution; - /** Copy of fault register from the previous read. */ - u8 prev_fault_flags; - /** For reading raw sample value via SPI. */ - struct { - __be16 raw; - u8 fault; - } sample __aligned(IIO_DMA_MINALIGN); - /** Scan buffer */ - struct { - __be16 chan[2]; - /* Ensure timestamp is naturally aligned. */ - s64 timestamp __aligned(8); - } scan; - /** SPI transmit buffer. */ - u8 rx[2]; - /** SPI receive buffer. */ - u8 tx[2]; -}; - -static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode) -{ - struct gpio_descs *gpios = st->mode_gpios; - DECLARE_BITMAP(bitmap, 2); - - bitmap[0] = mode; - - return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, - bitmap); -} - -/* - * Writes the given data to the given register address. - * - * If the mode is configurable, the device will first be placed in - * configuration mode. - */ -static int ad2s1210_regmap_reg_write(void *context, unsigned int reg, - unsigned int val) -{ - struct ad2s1210_state *st = context; - struct spi_transfer xfers[] = { - { - .len = 1, - .rx_buf = &st->rx[0], - .tx_buf = &st->tx[0], - .cs_change = 1, - }, { - .len = 1, - .rx_buf = &st->rx[1], - .tx_buf = &st->tx[1], - }, - }; - int ret; - - /* values can only be 7 bits, the MSB indicates an address */ - if (val & ~0x7F) - return -EINVAL; - - st->tx[0] = reg; - st->tx[1] = val; - - ret = ad2s1210_set_mode(st, MOD_CONFIG); - if (ret < 0) - return ret; - - ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); - if (ret < 0) - return ret; - - /* soft reset also clears the fault register */ - if (reg == AD2S1210_REG_SOFT_RESET) - st->prev_fault_flags = 0; - - return 0; -} - -/* - * Reads value from one of the registers. - * - * If the mode is configurable, the device will first be placed in - * configuration mode. - */ -static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, - unsigned int *val) -{ - struct ad2s1210_state *st = context; - struct spi_transfer xfers[] = { - { - .len = 1, - .rx_buf = &st->rx[0], - .tx_buf = &st->tx[0], - .cs_change = 1, - }, { - .len = 1, - .rx_buf = &st->rx[1], - .tx_buf = &st->tx[1], - }, - }; - int ret; - - ret = ad2s1210_set_mode(st, MOD_CONFIG); - if (ret < 0) - return ret; - - st->tx[0] = reg; - /* - * Must be valid register address here otherwise this could write data. - * It doesn't matter which one as long as reading doesn't have side- - * effects. - */ - st->tx[1] = AD2S1210_REG_CONTROL; - - ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); - if (ret < 0) - return ret; - - /* reading the fault register also clears it */ - if (reg == AD2S1210_REG_FAULT) - st->prev_fault_flags = 0; - - /* - * If the D7 bit is set on any read/write register, it indicates a - * parity error. The fault register is read-only and the D7 bit means - * something else there. - */ - if (reg != AD2S1210_REG_FAULT && st->rx[1] & AD2S1210_ADDRESS_DATA) - return -EBADMSG; - - *val = st->rx[1]; - - return 0; -} - -/* - * Toggles the SAMPLE line on the AD2S1210 to latch in the current position, - * velocity, and faults. - * - * Must be called with lock held. - */ -static void ad2s1210_toggle_sample_line(struct ad2s1210_state *st) -{ - /* - * Datasheet specifies minimum hold time t16 = 2 * tck + 20 ns. So the - * longest time needed is when CLKIN is 6.144 MHz, in which case t16 - * ~= 350 ns. The same delay is also needed before re-asserting the - * SAMPLE line. - */ - gpiod_set_value(st->sample_gpio, 1); - ndelay(350); - gpiod_set_value(st->sample_gpio, 0); - ndelay(350); -} - -/* - * Sets the excitation frequency and performs software reset. - * - * Must be called with lock held. - */ -static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st, - u16 fexcit) -{ - /* Map resolution to settle time in milliseconds. */ - static const int track_time_ms[] = { 10, 20, 25, 60 }; - unsigned int ignored; - int ret; - u8 fcw; - - fcw = fexcit * (1 << 15) / st->clkin_hz; - if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) - return -ERANGE; - - ret = regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw); - if (ret < 0) - return ret; - - /* - * Software reset reinitializes the excitation frequency output. - * It does not reset any of the configuration registers. - */ - ret = regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); - if (ret < 0) - return ret; - - /* - * Soft reset always triggers some faults due the change in the output - * signal so clear the faults too. We need to delay for some time - * (what datasheet calls t[track]) to allow things to settle before - * clearing the faults. - */ - msleep(track_time_ms[st->resolution] * 8192000 / st->clkin_hz); - - /* Reading the fault register clears the faults. */ - ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &ignored); - if (ret < 0) - return ret; - - /* Have to toggle sample line to get fault output pins to reset. */ - ad2s1210_toggle_sample_line(st); - - return 0; -} - -static void ad2s1210_push_events(struct iio_dev *indio_dev, - u8 flags, s64 timestamp) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - - /* Sine/cosine inputs clipped */ - if (FAULT_ONESHOT(AD2S1210_FAULT_CLIP, flags, st->prev_fault_flags)) { - /* - * The chip does not differentiate between fault on sine vs. - * cosine channel so we just send an event on both channels. - */ - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 1, - IIO_EV_TYPE_MAG, - IIO_EV_DIR_EITHER), - timestamp); - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 2, - IIO_EV_TYPE_MAG, - IIO_EV_DIR_EITHER), - timestamp); - } - - /* Sine/cosine inputs below LOS threshold */ - if (FAULT_ONESHOT(AD2S1210_FAULT_LOS, flags, st->prev_fault_flags)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - timestamp); - - /* Sine/cosine inputs exceed DOS overrange threshold */ - if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_OVR, flags, st->prev_fault_flags)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - timestamp); - - /* Sine/cosine inputs exceed DOS mismatch threshold */ - if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_MIS, flags, st->prev_fault_flags)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0, - IIO_EV_TYPE_MAG, - IIO_EV_DIR_RISING), - timestamp); - - /* Tracking error exceeds LOT threshold */ - if (FAULT_ONESHOT(AD2S1210_FAULT_LOT, flags, st->prev_fault_flags)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_ANGL, 1, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - timestamp); - - /* Velocity exceeds maximum tracking rate */ - if (FAULT_ONESHOT(AD2S1210_FAULT_VELOCITY, flags, st->prev_fault_flags)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_ANGL_VEL, 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - timestamp); - - /* Phase error exceeds phase lock range */ - if (FAULT_ONESHOT(AD2S1210_FAULT_PHASE, flags, st->prev_fault_flags)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_PHASE, 0, - IIO_EV_TYPE_MAG, - IIO_EV_DIR_RISING), - timestamp); - - /* Configuration parity error */ - if (FAULT_ONESHOT(AD2S1210_FAULT_CONFIG_PARITY, flags, - st->prev_fault_flags)) - /* - * Userspace should also get notified of this via error return - * when trying to write to any attribute that writes a register. - */ - dev_err_ratelimited(&indio_dev->dev, - "Configuration parity error\n"); - - st->prev_fault_flags = flags; -} - -static int ad2s1210_single_conversion(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - s64 timestamp; - int ret; - - guard(mutex)(&st->lock); - - ad2s1210_toggle_sample_line(st); - timestamp = iio_get_time_ns(indio_dev); - - switch (chan->type) { - case IIO_ANGL: - ret = ad2s1210_set_mode(st, MOD_POS); - break; - case IIO_ANGL_VEL: - ret = ad2s1210_set_mode(st, MOD_VEL); - break; - default: - return -EINVAL; - } - if (ret < 0) - return ret; - ret = spi_read(st->sdev, &st->sample, 3); - if (ret < 0) - return ret; - - switch (chan->type) { - case IIO_ANGL: - *val = be16_to_cpu(st->sample.raw); - ret = IIO_VAL_INT; - break; - case IIO_ANGL_VEL: - *val = (s16)be16_to_cpu(st->sample.raw); - ret = IIO_VAL_INT; - break; - default: - return -EINVAL; - } - - ad2s1210_push_events(indio_dev, st->sample.fault, timestamp); - - return ret; -} - -static int ad2s1210_get_hysteresis(struct ad2s1210_state *st, int *val) -{ - int ret; - - guard(mutex)(&st->lock); - ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, - AD2S1210_ENABLE_HYSTERESIS); - if (ret < 0) - return ret; - - *val = ret << (2 * (AD2S1210_RES_16 - st->resolution)); - return IIO_VAL_INT; -} - -static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val) -{ - guard(mutex)(&st->lock); - return regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, - AD2S1210_ENABLE_HYSTERESIS, - val ? AD2S1210_ENABLE_HYSTERESIS : 0); -} - -static int ad2s1210_get_phase_lock_range(struct ad2s1210_state *st, - int *val, int *val2) -{ - int ret; - - guard(mutex)(&st->lock); - ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, - AD2S1210_PHASE_LOCK_RANGE_44); - if (ret < 0) - return ret; - - if (ret) { - /* 44 degrees as radians */ - *val = PHASE_44_DEG_TO_RAD_INT; - *val2 = PHASE_44_DEG_TO_RAD_MICRO; - } else { - /* 360 degrees as radians */ - *val = PHASE_360_DEG_TO_RAD_INT; - *val2 = PHASE_360_DEG_TO_RAD_MICRO; - } - - return IIO_VAL_INT_PLUS_MICRO; -} - -static int ad2s1210_set_phase_lock_range(struct ad2s1210_state *st, - int val, int val2) -{ - int deg; - - /* convert radians to degrees - only two allowable values */ - if (val == PHASE_44_DEG_TO_RAD_INT && val2 == PHASE_44_DEG_TO_RAD_MICRO) - deg = 44; - else if (val == PHASE_360_DEG_TO_RAD_INT && - val2 == PHASE_360_DEG_TO_RAD_MICRO) - deg = 360; - else - return -EINVAL; - - guard(mutex)(&st->lock); - return regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, - AD2S1210_PHASE_LOCK_RANGE_44, - deg == 44 ? AD2S1210_PHASE_LOCK_RANGE_44 : 0); -} - -/* map resolution to microradians/LSB for LOT registers */ -static const int ad2s1210_lot_threshold_urad_per_lsb[] = { - 6184, /* 10-bit: ~0.35 deg/LSB, 45 deg max */ - 2473, /* 12-bit: ~0.14 deg/LSB, 18 deg max */ - 1237, /* 14-bit: ~0.07 deg/LSB, 9 deg max */ - 1237, /* 16-bit: same as 14-bit */ -}; - -static int ad2s1210_get_voltage_threshold(struct ad2s1210_state *st, - unsigned int reg, int *val) -{ - unsigned int reg_val; - int ret; - - guard(mutex)(&st->lock); - ret = regmap_read(st->regmap, reg, ®_val); - if (ret < 0) - return ret; - - *val = reg_val * THRESHOLD_MILLIVOLT_PER_LSB; - return IIO_VAL_INT; -} - -static int ad2s1210_set_voltage_threshold(struct ad2s1210_state *st, - unsigned int reg, int val) -{ - unsigned int reg_val; - - reg_val = val / THRESHOLD_MILLIVOLT_PER_LSB; - - guard(mutex)(&st->lock); - return regmap_write(st->regmap, reg, reg_val); -} - -static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st, - int *val, int *val2) -{ - unsigned int reg_val; - int ret; - - guard(mutex)(&st->lock); - ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); - if (ret < 0) - return ret; - - *val = 0; - *val2 = reg_val * ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; - return IIO_VAL_INT_PLUS_MICRO; -} - -static int ad2s1210_set_lot_high_threshold(struct ad2s1210_state *st, - int val, int val2) -{ - unsigned int high_reg_val, low_reg_val, hysteresis; - int ret; - - /* all valid values are between 0 and pi/4 radians */ - if (val != 0) - return -EINVAL; - - guard(mutex)(&st->lock); - /* - * We need to read both high and low registers first so we can preserve - * the hysteresis. - */ - ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); - if (ret < 0) - return ret; - - ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); - if (ret < 0) - return ret; - - hysteresis = high_reg_val - low_reg_val; - high_reg_val = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; - low_reg_val = high_reg_val - hysteresis; - - ret = regmap_write(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, high_reg_val); - if (ret < 0) - return ret; - - return regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, low_reg_val); -} - -static int ad2s1210_get_lot_low_threshold(struct ad2s1210_state *st, - int *val, int *val2) -{ - unsigned int high_reg_val, low_reg_val; - int ret; - - guard(mutex)(&st->lock); - - ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val); - if (ret < 0) - return ret; - - ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val); - if (ret < 0) - return ret; - - /* sysfs value is hysteresis rather than actual low value */ - *val = 0; - *val2 = (high_reg_val - low_reg_val) * - ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; - return IIO_VAL_INT_PLUS_MICRO; -} - -static int ad2s1210_set_lot_low_threshold(struct ad2s1210_state *st, - int val, int val2) -{ - unsigned int reg_val, hysteresis; - int ret; - - /* all valid values are between 0 and pi/4 radians */ - if (val != 0) - return -EINVAL; - - hysteresis = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; - - guard(mutex)(&st->lock); - - ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, ®_val); - if (ret < 0) - return ret; - - return regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD, - reg_val - hysteresis); -} - -static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val) -{ - unsigned int reg_val; - int ret; - - guard(mutex)(&st->lock); - - ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, ®_val); - if (ret < 0) - return ret; - - *val = reg_val * st->clkin_hz / (1 << 15); - return IIO_VAL_INT; -} - -static int ad2s1210_set_excitation_frequency(struct ad2s1210_state *st, int val) -{ - if (val < AD2S1210_MIN_EXCIT || val > AD2S1210_MAX_EXCIT) - return -EINVAL; - - guard(mutex)(&st->lock); - return ad2s1210_reinit_excitation_frequency(st, val); -} - -static const int ad2s1210_velocity_scale[] = { - 17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */ - 42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */ - 85445659, /* 8.192MHz / (2*pi * 500 / 2^15) */ - 341782638, /* 8.192MHz / (2*pi * 125 / 2^15) */ -}; - -static int ad2s1210_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long mask) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_RAW: - return ad2s1210_single_conversion(indio_dev, chan, val); - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_ANGL: - /* approx 0.3 arc min converted to radians */ - *val = 0; - *val2 = 95874; - return IIO_VAL_INT_PLUS_NANO; - case IIO_ANGL_VEL: - *val = st->clkin_hz; - *val2 = ad2s1210_velocity_scale[st->resolution]; - return IIO_VAL_FRACTIONAL; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_FREQUENCY: - switch (chan->type) { - case IIO_ALTVOLTAGE: - return ad2s1210_get_excitation_frequency(st, val); - default: - return -EINVAL; - } - case IIO_CHAN_INFO_HYSTERESIS: - switch (chan->type) { - case IIO_ANGL: - return ad2s1210_get_hysteresis(st, val); - default: - return -EINVAL; - } - default: - return -EINVAL; - } -} - -static int ad2s1210_read_avail(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - const int **vals, int *type, - int *length, long mask) -{ - static const int excitation_frequency_available[] = { - AD2S1210_MIN_EXCIT, - 250, /* step */ - AD2S1210_MAX_EXCIT, - }; - - struct ad2s1210_state *st = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_FREQUENCY: - switch (chan->type) { - case IIO_ALTVOLTAGE: - *type = IIO_VAL_INT; - *vals = excitation_frequency_available; - return IIO_AVAIL_RANGE; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_HYSTERESIS: - switch (chan->type) { - case IIO_ANGL: - *vals = st->hysteresis_available; - *type = IIO_VAL_INT; - *length = ARRAY_SIZE(st->hysteresis_available); - return IIO_AVAIL_LIST; - default: - return -EINVAL; - } - default: - return -EINVAL; - } -} - -static int ad2s1210_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_FREQUENCY: - switch (chan->type) { - case IIO_ALTVOLTAGE: - return ad2s1210_set_excitation_frequency(st, val); - default: - return -EINVAL; - } - case IIO_CHAN_INFO_HYSTERESIS: - switch (chan->type) { - case IIO_ANGL: - return ad2s1210_set_hysteresis(st, val); - default: - return -EINVAL; - } - default: - return -EINVAL; - } -} - -static const struct iio_event_spec ad2s1210_position_event_spec[] = { - { - /* Tracking error exceeds LOT threshold fault. */ - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_separate = - /* Loss of tracking high threshold. */ - BIT(IIO_EV_INFO_VALUE) | - /* Loss of tracking low threshold. */ - BIT(IIO_EV_INFO_HYSTERESIS), - }, -}; - -static const struct iio_event_spec ad2s1210_velocity_event_spec[] = { - { - /* Velocity exceeds maximum tracking rate fault. */ - .type = IIO_EV_TYPE_MAG, - .dir = IIO_EV_DIR_RISING, - }, -}; - -static const struct iio_event_spec ad2s1210_phase_event_spec[] = { - { - /* Phase error fault. */ - .type = IIO_EV_TYPE_MAG, - .dir = IIO_EV_DIR_RISING, - /* Phase lock range. */ - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, -}; - -static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { - { - /* Sine/cosine below LOS threshold fault. */ - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_FALLING, - /* Loss of signal threshold. */ - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, - { - /* Sine/cosine DOS overrange fault.*/ - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - /* Degredation of signal overrange threshold. */ - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, - { - /* Sine/cosine DOS mismatch fault.*/ - .type = IIO_EV_TYPE_MAG, - .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, -}; - -static const struct iio_event_spec ad2s1210_sin_cos_event_spec[] = { - { - /* Sine/cosine clipping fault. */ - .type = IIO_EV_TYPE_MAG, - .dir = IIO_EV_DIR_EITHER, - }, -}; - -static const struct iio_chan_spec ad2s1210_channels[] = { - { - .type = IIO_ANGL, - .indexed = 1, - .channel = 0, - .scan_index = 0, - .scan_type = { - .sign = 'u', - .realbits = 16, - .storagebits = 16, - .endianness = IIO_BE, - }, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_HYSTERESIS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_HYSTERESIS), - }, { - .type = IIO_ANGL_VEL, - .indexed = 1, - .channel = 0, - .scan_index = 1, - .scan_type = { - .sign = 's', - .realbits = 16, - .storagebits = 16, - .endianness = IIO_BE, - }, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), - .event_spec = ad2s1210_velocity_event_spec, - .num_event_specs = ARRAY_SIZE(ad2s1210_velocity_event_spec), - }, - IIO_CHAN_SOFT_TIMESTAMP(2), - { - /* used to configure LOT thresholds and get tracking error */ - .type = IIO_ANGL, - .indexed = 1, - .channel = 1, - .scan_index = -1, - .event_spec = ad2s1210_position_event_spec, - .num_event_specs = ARRAY_SIZE(ad2s1210_position_event_spec), - }, - { - /* used to configure phase lock range and get phase lock error */ - .type = IIO_PHASE, - .indexed = 1, - .channel = 0, - .scan_index = -1, - .event_spec = ad2s1210_phase_event_spec, - .num_event_specs = ARRAY_SIZE(ad2s1210_phase_event_spec), - }, { - /* excitation frequency output */ - .type = IIO_ALTVOLTAGE, - .indexed = 1, - .channel = 0, - .output = 1, - .scan_index = -1, - .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_FREQUENCY), - }, { - /* monitor signal */ - .type = IIO_ALTVOLTAGE, - .indexed = 1, - .channel = 0, - .scan_index = -1, - .event_spec = ad2s1210_monitor_signal_event_spec, - .num_event_specs = ARRAY_SIZE(ad2s1210_monitor_signal_event_spec), - }, { - /* sine input */ - .type = IIO_ALTVOLTAGE, - .indexed = 1, - .channel = 1, - .scan_index = -1, - .event_spec = ad2s1210_sin_cos_event_spec, - .num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec), - }, { - /* cosine input */ - .type = IIO_ALTVOLTAGE, - .indexed = 1, - .channel = 2, - .scan_index = -1, - .event_spec = ad2s1210_sin_cos_event_spec, - .num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec), - }, -}; - -static ssize_t event_attr_voltage_reg_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - struct iio_dev_attr *iattr = to_iio_dev_attr(attr); - unsigned int value; - int ret; - - guard(mutex)(&st->lock); - ret = regmap_read(st->regmap, iattr->address, &value); - if (ret < 0) - return ret; - - return sprintf(buf, "%d\n", value * THRESHOLD_MILLIVOLT_PER_LSB); -} - -static ssize_t event_attr_voltage_reg_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - struct iio_dev_attr *iattr = to_iio_dev_attr(attr); - u16 data; - int ret; - - ret = kstrtou16(buf, 10, &data); - if (ret) - return -EINVAL; - - guard(mutex)(&st->lock); - ret = regmap_write(st->regmap, iattr->address, - data / THRESHOLD_MILLIVOLT_PER_LSB); - if (ret < 0) - return ret; - - return len; -} - -static ssize_t -in_angl1_thresh_rising_value_available_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - int step = ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; - - return sysfs_emit(buf, "[0 0.%06d 0.%06d]\n", step, step * 0x7F); -} - -static ssize_t -in_angl1_thresh_rising_hysteresis_available_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - int step = ad2s1210_lot_threshold_urad_per_lsb[st->resolution]; - - return sysfs_emit(buf, "[0 0.%06d 0.%06d]\n", step, step * 0x7F); -} - -static IIO_CONST_ATTR(in_phase0_mag_rising_value_available, - __stringify(PHASE_44_DEG_TO_RAD_INT) "." - __stringify(PHASE_44_DEG_TO_RAD_MICRO) " " - __stringify(PHASE_360_DEG_TO_RAD_INT) "." - __stringify(PHASE_360_DEG_TO_RAD_MICRO)); -static IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, - THRESHOLD_RANGE_STR); -static IIO_CONST_ATTR(in_altvoltage0_thresh_rising_value_available, - THRESHOLD_RANGE_STR); -static IIO_CONST_ATTR(in_altvoltage0_mag_rising_value_available, - THRESHOLD_RANGE_STR); -static IIO_DEVICE_ATTR(in_altvoltage0_mag_rising_reset_max, 0644, - event_attr_voltage_reg_show, event_attr_voltage_reg_store, - AD2S1210_REG_DOS_RST_MAX_THRD); -static IIO_CONST_ATTR(in_altvoltage0_mag_rising_reset_max_available, THRESHOLD_RANGE_STR); -static IIO_DEVICE_ATTR(in_altvoltage0_mag_rising_reset_min, 0644, - event_attr_voltage_reg_show, event_attr_voltage_reg_store, - AD2S1210_REG_DOS_RST_MIN_THRD); -static IIO_CONST_ATTR(in_altvoltage0_mag_rising_reset_min_available, THRESHOLD_RANGE_STR); -static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0); -static IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0); - -static struct attribute *ad2s1210_event_attributes[] = { - &iio_const_attr_in_phase0_mag_rising_value_available.dev_attr.attr, - &iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr, - &iio_const_attr_in_altvoltage0_thresh_rising_value_available.dev_attr.attr, - &iio_const_attr_in_altvoltage0_mag_rising_value_available.dev_attr.attr, - &iio_dev_attr_in_altvoltage0_mag_rising_reset_max.dev_attr.attr, - &iio_const_attr_in_altvoltage0_mag_rising_reset_max_available.dev_attr.attr, - &iio_dev_attr_in_altvoltage0_mag_rising_reset_min.dev_attr.attr, - &iio_const_attr_in_altvoltage0_mag_rising_reset_min_available.dev_attr.attr, - &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr, - &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad2s1210_event_attribute_group = { - .attrs = ad2s1210_event_attributes, -}; - -static int ad2s1210_initial(struct ad2s1210_state *st) -{ - unsigned int data; - int ret; - - guard(mutex)(&st->lock); - - /* Use default config register value plus resolution from devicetree. */ - data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1); - data |= FIELD_PREP(AD2S1210_ENABLE_HYSTERESIS, 1); - data |= FIELD_PREP(AD2S1210_SET_ENRES, 0x3); - data |= FIELD_PREP(AD2S1210_SET_RES, st->resolution); - - ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); - if (ret < 0) - return ret; - - return ad2s1210_reinit_excitation_frequency(st, AD2S1210_DEF_EXCIT); -} - -static int ad2s1210_read_label(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - char *label) -{ - if (chan->type == IIO_ANGL) { - if (chan->channel == 0) - return sprintf(label, "position\n"); - if (chan->channel == 1) - return sprintf(label, "tracking error\n"); - } - if (chan->type == IIO_ANGL_VEL) - return sprintf(label, "velocity\n"); - if (chan->type == IIO_PHASE) - return sprintf(label, "synthetic reference\n"); - if (chan->type == IIO_ALTVOLTAGE) { - if (chan->output) - return sprintf(label, "excitation\n"); - if (chan->channel == 0) - return sprintf(label, "monitor signal\n"); - if (chan->channel == 1) - return sprintf(label, "cosine\n"); - if (chan->channel == 2) - return sprintf(label, "sine\n"); - } - - return -EINVAL; -} - -static int ad2s1210_read_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - - switch (chan->type) { - case IIO_ANGL: - switch (info) { - case IIO_EV_INFO_VALUE: - return ad2s1210_get_lot_high_threshold(st, val, val2); - case IIO_EV_INFO_HYSTERESIS: - return ad2s1210_get_lot_low_threshold(st, val, val2); - default: - return -EINVAL; - } - case IIO_ALTVOLTAGE: - if (chan->output) - return -EINVAL; - if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) - return ad2s1210_get_voltage_threshold(st, - AD2S1210_REG_LOS_THRD, val); - if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) - return ad2s1210_get_voltage_threshold(st, - AD2S1210_REG_DOS_OVR_THRD, val); - if (type == IIO_EV_TYPE_MAG) - return ad2s1210_get_voltage_threshold(st, - AD2S1210_REG_DOS_MIS_THRD, val); - return -EINVAL; - case IIO_PHASE: - return ad2s1210_get_phase_lock_range(st, val, val2); - default: - return -EINVAL; - } -} - -static int ad2s1210_write_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - - switch (chan->type) { - case IIO_ANGL: - switch (info) { - case IIO_EV_INFO_VALUE: - return ad2s1210_set_lot_high_threshold(st, val, val2); - case IIO_EV_INFO_HYSTERESIS: - return ad2s1210_set_lot_low_threshold(st, val, val2); - default: - return -EINVAL; - } - case IIO_ALTVOLTAGE: - if (chan->output) - return -EINVAL; - if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING) - return ad2s1210_set_voltage_threshold(st, - AD2S1210_REG_LOS_THRD, val); - if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_RISING) - return ad2s1210_set_voltage_threshold(st, - AD2S1210_REG_DOS_OVR_THRD, val); - if (type == IIO_EV_TYPE_MAG) - return ad2s1210_set_voltage_threshold(st, - AD2S1210_REG_DOS_MIS_THRD, val); - return -EINVAL; - case IIO_PHASE: - return ad2s1210_set_phase_lock_range(st, val, val2); - default: - return -EINVAL; - } -} - -static int ad2s1210_read_event_label(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - enum iio_event_type type, - enum iio_event_direction dir, - char *label) -{ - if (chan->type == IIO_ANGL) - return sprintf(label, "LOT\n"); - if (chan->type == IIO_ANGL_VEL) - return sprintf(label, "max tracking rate\n"); - if (chan->type == IIO_PHASE) - return sprintf(label, "phase lock\n"); - if (chan->type == IIO_ALTVOLTAGE) { - if (chan->channel == 0) { - if (type == IIO_EV_TYPE_THRESH && - dir == IIO_EV_DIR_FALLING) - return sprintf(label, "LOS\n"); - if (type == IIO_EV_TYPE_THRESH && - dir == IIO_EV_DIR_RISING) - return sprintf(label, "DOS overrange\n"); - if (type == IIO_EV_TYPE_MAG) - return sprintf(label, "DOS mismatch\n"); - } - if (chan->channel == 1 || chan->channel == 2) - return sprintf(label, "clipped\n"); - } - - return -EINVAL; -} - -static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, - unsigned int reg, unsigned int writeval, - unsigned int *readval) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - - guard(mutex)(&st->lock); - - if (readval) - return regmap_read(st->regmap, reg, readval); - - return regmap_write(st->regmap, reg, writeval); -} - -static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct ad2s1210_state *st = iio_priv(indio_dev); - size_t chan = 0; - int ret; - - guard(mutex)(&st->lock); - - memset(&st->scan, 0, sizeof(st->scan)); - ad2s1210_toggle_sample_line(st); - - if (test_bit(0, indio_dev->active_scan_mask)) { - ret = ad2s1210_set_mode(st, MOD_POS); - if (ret < 0) - goto error_ret; - - ret = spi_read(st->sdev, &st->sample, 3); - if (ret < 0) - goto error_ret; - - memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); - } - - if (test_bit(1, indio_dev->active_scan_mask)) { - ret = ad2s1210_set_mode(st, MOD_VEL); - if (ret < 0) - goto error_ret; - - ret = spi_read(st->sdev, &st->sample, 3); - if (ret < 0) - goto error_ret; - - memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); - } - - ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); - iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); - -error_ret: - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static const struct iio_info ad2s1210_info = { - .event_attrs = &ad2s1210_event_attribute_group, - .read_raw = ad2s1210_read_raw, - .read_avail = ad2s1210_read_avail, - .write_raw = ad2s1210_write_raw, - .read_label = ad2s1210_read_label, - .read_event_value = ad2s1210_read_event_value, - .write_event_value = ad2s1210_write_event_value, - .read_event_label = ad2s1210_read_event_label, - .debugfs_reg_access = &ad2s1210_debugfs_reg_access, -}; - -static int ad2s1210_setup_properties(struct ad2s1210_state *st) -{ - struct device *dev = &st->sdev->dev; - u32 val; - int ret; - - ret = device_property_read_u32(dev, "assigned-resolution-bits", &val); - if (ret < 0) - return dev_err_probe(dev, ret, - "failed to read assigned-resolution-bits property\n"); - - if (val < 10 || val > 16) - return dev_err_probe(dev, -EINVAL, - "resolution out of range: %u\n", val); - - st->resolution = (val - 10) >> 1; - /* - * These are values that correlate to the hysteresis bit in the Control - * register. 0 = disabled, 1 = enabled. When enabled, the actual - * hysteresis is +/- 1 LSB of the raw position value. Which bit is the - * LSB depends on the specified resolution. - */ - st->hysteresis_available[0] = 0; - st->hysteresis_available[1] = 1 << (2 * (AD2S1210_RES_16 - - st->resolution)); - - return 0; -} - -static int ad2s1210_setup_clocks(struct ad2s1210_state *st) -{ - struct device *dev = &st->sdev->dev; - struct clk *clk; - - clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) - return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); - - st->clkin_hz = clk_get_rate(clk); - if (st->clkin_hz < AD2S1210_MIN_CLKIN || st->clkin_hz > AD2S1210_MAX_CLKIN) - return dev_err_probe(dev, -EINVAL, - "clock frequency out of range: %lu\n", - st->clkin_hz); - - return 0; -} - -static int ad2s1210_setup_gpios(struct ad2s1210_state *st) -{ - struct device *dev = &st->sdev->dev; - struct gpio_descs *resolution_gpios; - DECLARE_BITMAP(bitmap, 2); - int ret; - - /* should not be sampling on startup */ - st->sample_gpio = devm_gpiod_get(dev, "sample", GPIOD_OUT_LOW); - if (IS_ERR(st->sample_gpio)) - return dev_err_probe(dev, PTR_ERR(st->sample_gpio), - "failed to request sample GPIO\n"); - - /* both pins high means that we start in config mode */ - st->mode_gpios = devm_gpiod_get_array(dev, "mode", GPIOD_OUT_HIGH); - if (IS_ERR(st->mode_gpios)) - return dev_err_probe(dev, PTR_ERR(st->mode_gpios), - "failed to request mode GPIOs\n"); - - if (st->mode_gpios->ndescs != 2) - return dev_err_probe(dev, -EINVAL, - "requires exactly 2 mode-gpios\n"); - - /* - * If resolution gpios are provided, they get set to the required - * resolution, otherwise it is assumed the RES0 and RES1 pins are - * hard-wired to match the resolution indicated in the devicetree. - */ - resolution_gpios = devm_gpiod_get_array_optional(dev, "resolution", - GPIOD_ASIS); - if (IS_ERR(resolution_gpios)) - return dev_err_probe(dev, PTR_ERR(resolution_gpios), - "failed to request resolution GPIOs\n"); - - if (resolution_gpios) { - if (resolution_gpios->ndescs != 2) - return dev_err_probe(dev, -EINVAL, - "requires exactly 2 resolution-gpios\n"); - - bitmap[0] = st->resolution; - - ret = gpiod_set_array_value(resolution_gpios->ndescs, - resolution_gpios->desc, - resolution_gpios->info, - bitmap); - if (ret < 0) - return dev_err_probe(dev, ret, - "failed to set resolution gpios\n"); - } - - return 0; -} - -static const struct regmap_range ad2s1210_regmap_readable_ranges[] = { - regmap_reg_range(AD2S1210_REG_POSITION_MSB, AD2S1210_REG_VELOCITY_LSB), - regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), - regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), - regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), -}; - -static const struct regmap_access_table ad2s1210_regmap_rd_table = { - .yes_ranges = ad2s1210_regmap_readable_ranges, - .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_readable_ranges), -}; - -static const struct regmap_range ad2s1210_regmap_writeable_ranges[] = { - regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), - regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), - regmap_reg_range(AD2S1210_REG_SOFT_RESET, AD2S1210_REG_SOFT_RESET), - regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), -}; - -static const struct regmap_access_table ad2s1210_regmap_wr_table = { - .yes_ranges = ad2s1210_regmap_writeable_ranges, - .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_writeable_ranges), -}; - -static int ad2s1210_setup_regmap(struct ad2s1210_state *st) -{ - struct device *dev = &st->sdev->dev; - const struct regmap_config config = { - .reg_bits = 8, - .val_bits = 8, - .disable_locking = true, - .reg_read = ad2s1210_regmap_reg_read, - .reg_write = ad2s1210_regmap_reg_write, - .rd_table = &ad2s1210_regmap_rd_table, - .wr_table = &ad2s1210_regmap_wr_table, - .can_sleep = true, - }; - - st->regmap = devm_regmap_init(dev, NULL, st, &config); - if (IS_ERR(st->regmap)) - return dev_err_probe(dev, PTR_ERR(st->regmap), - "failed to allocate register map\n"); - - return 0; -} - -static int ad2s1210_probe(struct spi_device *spi) -{ - struct iio_dev *indio_dev; - struct ad2s1210_state *st; - int ret; - - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (!indio_dev) - return -ENOMEM; - st = iio_priv(indio_dev); - - mutex_init(&st->lock); - st->sdev = spi; - - ret = ad2s1210_setup_properties(st); - if (ret < 0) - return ret; - - ret = ad2s1210_setup_clocks(st); - if (ret < 0) - return ret; - - ret = ad2s1210_setup_gpios(st); - if (ret < 0) - return ret; - - ret = ad2s1210_setup_regmap(st); - if (ret < 0) - return ret; - - ret = ad2s1210_initial(st); - if (ret < 0) - return ret; - - indio_dev->info = &ad2s1210_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ad2s1210_channels; - indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); - indio_dev->name = spi_get_device_id(spi)->name; - - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, - &iio_pollfunc_store_time, - &ad2s1210_trigger_handler, NULL); - if (ret < 0) - return dev_err_probe(&spi->dev, ret, - "iio triggered buffer setup failed\n"); - - return devm_iio_device_register(&spi->dev, indio_dev); -} - -static const struct of_device_id ad2s1210_of_match[] = { - { .compatible = "adi,ad2s1210", }, - { } -}; -MODULE_DEVICE_TABLE(of, ad2s1210_of_match); - -static const struct spi_device_id ad2s1210_id[] = { - { "ad2s1210" }, - {} -}; -MODULE_DEVICE_TABLE(spi, ad2s1210_id); - -static struct spi_driver ad2s1210_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(ad2s1210_of_match), - }, - .probe = ad2s1210_probe, - .id_table = ad2s1210_id, -}; -module_spi_driver(ad2s1210_driver); - -MODULE_AUTHOR("Graff Yang "); -MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 0f7fa242b355ed102deb49f8b85e0d0f0d323717 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 12 Oct 2023 11:18:12 -0500 Subject: iio: resolver: ad2s1210: remove DRV_NAME macro The DRV_NAME macro is only used in one place in the ad2s1210 driver and is not really needed so let's remove it. Suggested-by: Jonathan Cameron Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231012-ad2s1210-mainline-v1-1-b2ee31c0e9dd@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/resolver/ad2s1210.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index bd4a90c222b5..00562bc542bb 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -68,8 +68,6 @@ #include #include -#define DRV_NAME "ad2s1210" - /* control register flags */ #define AD2S1210_ADDRESS_DATA BIT(7) #define AD2S1210_PHASE_LOCK_RANGE_44 BIT(5) @@ -1509,7 +1507,7 @@ MODULE_DEVICE_TABLE(spi, ad2s1210_id); static struct spi_driver ad2s1210_driver = { .driver = { - .name = DRV_NAME, + .name = "ad2s1210", .of_match_table = of_match_ptr(ad2s1210_of_match), }, .probe = ad2s1210_probe, -- cgit v1.2.3 From 73006239ef2313f1986f86af86f8b06150a807e9 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 12 Oct 2023 11:18:13 -0500 Subject: iio: resolver: ad2s1210: remove of_match_ptr() To be consistent with the rest of iio, remove of_match_ptr(). It does not do anything useful here. Suggested-by: Jonathan Cameron Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20231012-ad2s1210-mainline-v1-2-b2ee31c0e9dd@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/resolver/ad2s1210.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 00562bc542bb..1bd1b950e7cc 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -1508,7 +1507,7 @@ MODULE_DEVICE_TABLE(spi, ad2s1210_id); static struct spi_driver ad2s1210_driver = { .driver = { .name = "ad2s1210", - .of_match_table = of_match_ptr(ad2s1210_of_match), + .of_match_table = ad2s1210_of_match, }, .probe = ad2s1210_probe, .id_table = ad2s1210_id, -- cgit v1.2.3 From e9d8add6e72b1642bcf895601146c713d068775a Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Thu, 12 Oct 2023 14:20:41 +0000 Subject: dt-bindings: iio: imu: st,lsm6dsx: add mount-matrix property Add the mount-matrix optional property to the binding since it's supported and very useful when using the chip on a board. Signed-off-by: Martin Kepplinger Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20231012142041.253332-1-martink@posteo.de Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml index ee8724ad33ab..28b667a9cb76 100644 --- a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml +++ b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml @@ -93,6 +93,9 @@ properties: wakeup-source: $ref: /schemas/types.yaml#/definitions/flag + mount-matrix: + description: an optional 3x3 mounting rotation matrix + required: - compatible - reg -- cgit v1.2.3 From ba251b20152c3bc6edaa05413090ca5b5f8deb7f Mon Sep 17 00:00:00 2001 From: Ramona Gradinariu Date: Thu, 12 Oct 2023 15:24:56 +0300 Subject: drivers: imu: adis16475.c: Remove scan index from delta channels Some devices do not support delta angle and delta velocity burst readings, which means there should be no buffer support for these types of channels. A new list of channels is created which contains the delta channels structures with the scan index equal to -1 to allow for raw register readings, without buffer support. This list of channels is assigned to the devices which do not support delta angle and delta velocity burst readings. Fixes: 8f6bc87d67c0 ("iio: imu: adis16475.c: Add delta angle and delta velocity channels") Signed-off-by: Ramona Gradinariu Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20231012122456.765709-2-ramona.gradinariu@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16475.c | 67 +++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 9af07fec0d89..b7cbe1565aee 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -604,7 +604,15 @@ static int adis16475_write_raw(struct iio_dev *indio_dev, ADIS16475_MOD_CHAN_DELTA(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \ ADIS16475_REG_ ## _mod ## _DELTVEL_L, ADIS16475_SCAN_DELTVEL_ ## _mod, 32, 32) -static const struct iio_chan_spec adis16475_channels[] = { +#define ADIS16475_DELTANG_CHAN_NO_SCAN(_mod) \ + ADIS16475_MOD_CHAN_DELTA(IIO_DELTA_ANGL, IIO_MOD_ ## _mod, \ + ADIS16475_REG_ ## _mod ## _DELTANG_L, -1, 32, 32) + +#define ADIS16475_DELTVEL_CHAN_NO_SCAN(_mod) \ + ADIS16475_MOD_CHAN_DELTA(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \ + ADIS16475_REG_ ## _mod ## _DELTVEL_L, -1, 32, 32) + +static const struct iio_chan_spec adis16477_channels[] = { ADIS16475_GYRO_CHANNEL(X), ADIS16475_GYRO_CHANNEL(Y), ADIS16475_GYRO_CHANNEL(Z), @@ -621,6 +629,23 @@ static const struct iio_chan_spec adis16475_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(13) }; +static const struct iio_chan_spec adis16475_channels[] = { + ADIS16475_GYRO_CHANNEL(X), + ADIS16475_GYRO_CHANNEL(Y), + ADIS16475_GYRO_CHANNEL(Z), + ADIS16475_ACCEL_CHANNEL(X), + ADIS16475_ACCEL_CHANNEL(Y), + ADIS16475_ACCEL_CHANNEL(Z), + ADIS16475_TEMP_CHANNEL(), + ADIS16475_DELTANG_CHAN_NO_SCAN(X), + ADIS16475_DELTANG_CHAN_NO_SCAN(Y), + ADIS16475_DELTANG_CHAN_NO_SCAN(Z), + ADIS16475_DELTVEL_CHAN_NO_SCAN(X), + ADIS16475_DELTVEL_CHAN_NO_SCAN(Y), + ADIS16475_DELTVEL_CHAN_NO_SCAN(Z), + IIO_CHAN_SOFT_TIMESTAMP(7) +}; + enum adis16475_variant { ADIS16470, ADIS16475_1, @@ -782,8 +807,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16477_1] = { .name = "adis16477-1", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(160 << 16), .accel_max_val = 1, @@ -800,8 +825,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16477_2] = { .name = "adis16477-2", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(40 << 16), .accel_max_val = 1, @@ -818,8 +843,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16477_3] = { .name = "adis16477-3", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(10 << 16), .accel_max_val = 1, @@ -938,8 +963,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16500] = { .name = "adis16500", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(10 << 16), .accel_max_val = 392, @@ -957,8 +982,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16505_1] = { .name = "adis16505-1", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(160 << 16), .accel_max_val = 78, @@ -976,8 +1001,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16505_2] = { .name = "adis16505-2", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(40 << 16), .accel_max_val = 78, @@ -995,8 +1020,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16505_3] = { .name = "adis16505-3", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(10 << 16), .accel_max_val = 78, @@ -1014,8 +1039,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16507_1] = { .name = "adis16507-1", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(160 << 16), .accel_max_val = 392, @@ -1033,8 +1058,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16507_2] = { .name = "adis16507-2", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(40 << 16), .accel_max_val = 392, @@ -1052,8 +1077,8 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { }, [ADIS16507_3] = { .name = "adis16507-3", - .num_channels = ARRAY_SIZE(adis16475_channels), - .channels = adis16475_channels, + .num_channels = ARRAY_SIZE(adis16477_channels), + .channels = adis16477_channels, .gyro_max_val = 1, .gyro_max_scale = IIO_RAD_TO_DEGREE(10 << 16), .accel_max_val = 392, -- cgit v1.2.3 From b1a078a8b0dbfcac8b61618282295124e30a9dce Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 12 Oct 2023 22:07:07 +0200 Subject: iio: si7005: Add device tree support This device supports ACPI detection but lacks of the device tree counterpart. Add device tree support. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20231012-topic-si7005_devicetree-v1-1-6c8a6fa7b3ec@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/si7005.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/humidity/si7005.c b/drivers/iio/humidity/si7005.c index ebfb79bc9edc..9465908cc65e 100644 --- a/drivers/iio/humidity/si7005.c +++ b/drivers/iio/humidity/si7005.c @@ -169,9 +169,16 @@ static const struct i2c_device_id si7005_id[] = { }; MODULE_DEVICE_TABLE(i2c, si7005_id); +static const struct of_device_id si7005_dt_ids[] = { + { .compatible = "silabs,si7005" }, + { } +}; +MODULE_DEVICE_TABLE(of, si7005_dt_ids); + static struct i2c_driver si7005_driver = { .driver = { .name = "si7005", + .of_match_table = si7005_dt_ids, }, .probe = si7005_probe, .id_table = si7005_id, -- cgit v1.2.3 From e16247acaeb9cae49ca92133ac18a1bfc4c40060 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 12 Oct 2023 22:07:08 +0200 Subject: dt-bindings: trivial-devices: add silabs,si7005 This simple I2C humidity sensor does not have any additional properties and can be added to the trivial-devices binding. Signed-off-by: Javier Carrasco Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231012-topic-si7005_devicetree-v1-2-6c8a6fa7b3ec@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index cd58179ae337..216d9ae9f206 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -348,6 +348,8 @@ properties: # Silicon Labs SI3210 Programmable CMOS SLIC/CODEC with SPI interface - silabs,si3210 # Relative Humidity and Temperature Sensors + - silabs,si7005 + # Relative Humidity and Temperature Sensors - silabs,si7020 # Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply - skyworks,sky81452 -- cgit v1.2.3 From 349c1b49ffafd9f0e49153599efd5cd251ba60b1 Mon Sep 17 00:00:00 2001 From: Marius Cristea Date: Fri, 13 Oct 2023 16:23:33 +0300 Subject: iio: adc: MCP3564: fix warn: unsigned '__x' is never less than zero. The patch 33ec3e5fc1ea: "iio: adc: adding support for MCP3564 ADC" leads to the following Smatch static checker warning: smatch warnings: drivers/iio/adc/mcp3564.c:1105 mcp3564_fill_scale_tbls() warn: unsigned '__x' is never less than zero. vim +/__x +1105 drivers/iio/adc/mcp3564.c 1094 1095 static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc) 1096 { ..... 1103 for (i = 0; i < MCP3564_MAX_PGA; i++) { 1104 ref = adc->vref_mv; > 1105 tmp1 = shift_right((u64)ref * NANO, pow); 1106 div_u64_rem(tmp1, NANO, &tmp0); 1107 ..... 1113 } Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202309280738.NWjVfVt4-lkp@intel.com/ Fixes: 33ec3e5fc1ea (iio: adc: adding support for MCP3564 ADC) Signed-off-by: Marius Cristea Link: https://lore.kernel.org/r/20231013132333.10582-1-marius.cristea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3564.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index 9ede1a5d5d7b..e3f1de5fcc5a 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -1102,7 +1102,7 @@ static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc) for (i = 0; i < MCP3564_MAX_PGA; i++) { ref = adc->vref_mv; - tmp1 = shift_right((u64)ref * NANO, pow); + tmp1 = ((u64)ref * NANO) >> pow; div_u64_rem(tmp1, NANO, &tmp0); tmp1 = tmp1 * mcp3564_hwgain_frac[(2 * i) + 1]; -- cgit v1.2.3 From 89a1d2f064d2ae77a9cd6a8c7ac42b3c1647efa5 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 6 Oct 2023 17:44:39 -0500 Subject: iio: Use device_get_match_data() Use preferred device_get_match_data() instead of of_match_device() to get the driver match data. With this, adjust the includes to explicitly include the correct headers. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20231006224440.442864-1-robh@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 11 +++-------- drivers/iio/adc/twl6030-gpadc.c | 10 ++++------ drivers/iio/dac/stm32-dac-core.c | 9 ++++----- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index c19506b0aac8..616dd729666a 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -17,10 +17,11 @@ #include #include #include -#include +#include #include #include #include +#include #include #include #include @@ -708,8 +709,6 @@ static int stm32_adc_probe(struct platform_device *pdev) struct stm32_adc_priv *priv; struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id; - struct resource *res; u32 max_rate; int ret; @@ -722,11 +721,7 @@ static int stm32_adc_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, &priv->common); - of_id = of_match_device(dev->driver->of_match_table, dev); - if (!of_id) - return -ENODEV; - - priv->cfg = (const struct stm32_adc_priv_cfg *)of_id->data; + priv->cfg = device_get_match_data(dev); priv->nb_adc_max = priv->cfg->num_adcs; spin_lock_init(&priv->common.lock); diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 224e9cb5e147..78bf55438b2c 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -16,9 +16,10 @@ */ #include #include +#include #include #include -#include +#include #include #include #include @@ -879,17 +880,14 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct twl6030_gpadc_data *gpadc; const struct twl6030_gpadc_platform_data *pdata; - const struct of_device_id *match; struct iio_dev *indio_dev; int irq; int ret; - match = of_match_device(of_twl6030_match_tbl, dev); - if (!match) + pdata = device_get_match_data(&pdev->dev); + if (!pdata) return -EINVAL; - pdata = match->data; - indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); if (!indio_dev) return -ENOMEM; diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 15abe048729e..e150ac729154 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -9,9 +9,12 @@ #include #include +#include #include #include +#include #include +#include #include #include @@ -94,16 +97,12 @@ static int stm32_dac_probe(struct platform_device *pdev) struct reset_control *rst; int ret; - if (!dev->of_node) - return -ENODEV; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; platform_set_drvdata(pdev, &priv->common); - cfg = (const struct stm32_dac_cfg *) - of_match_device(dev->driver->of_match_table, dev)->data; + cfg = device_get_match_data(dev); mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mmio)) -- cgit v1.2.3