summaryrefslogtreecommitdiff
path: root/drivers/iio/adc/stm32-dfsdm-core.c
diff options
context:
space:
mode:
authorOlivier Moysan <olivier.moysan@foss.st.com>2023-01-05 15:53:30 +0300
committerJonathan Cameron <Jonathan.Cameron@huawei.com>2023-01-08 16:11:03 +0300
commitbfcae956d9b50ea0e221cefc171604c569017d7e (patch)
tree2686e6cb31dddb21915626021046758a9c261904 /drivers/iio/adc/stm32-dfsdm-core.c
parent14e5b7ab196e6f18f76191cae4a4f6b41c0bfadd (diff)
downloadlinux-bfcae956d9b50ea0e221cefc171604c569017d7e.tar.xz
iio: adc: stm32-dfsdm: add id registers support
Add support of identification registers to STM32 DFSDM to allow hardware capabilities discovery and configuration check. The number of filters and channels, are read from registers, when they are available. Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com> Link: https://lore.kernel.org/r/20230105125331.328275-1-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Diffstat (limited to 'drivers/iio/adc/stm32-dfsdm-core.c')
-rw-r--r--drivers/iio/adc/stm32-dfsdm-core.c99
1 files changed, 83 insertions, 16 deletions
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index a3d4de6ba4c2..0362df285a57 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -6,6 +6,7 @@
* Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -19,7 +20,15 @@
#include "stm32-dfsdm.h"
+/**
+ * struct stm32_dfsdm_dev_data - DFSDM compatible configuration data
+ * @ipid: DFSDM identification number. Used only if hardware provides identification registers
+ * @num_filters: DFSDM number of filters. Unused if identification registers are available
+ * @num_channels: DFSDM number of channels. Unused if identification registers are available
+ * @regmap_cfg: SAI register map configuration pointer
+ */
struct stm32_dfsdm_dev_data {
+ u32 ipid;
unsigned int num_filters;
unsigned int num_channels;
const struct regmap_config *regmap_cfg;
@@ -27,8 +36,6 @@ struct stm32_dfsdm_dev_data {
#define STM32H7_DFSDM_NUM_FILTERS 4
#define STM32H7_DFSDM_NUM_CHANNELS 8
-#define STM32MP1_DFSDM_NUM_FILTERS 6
-#define STM32MP1_DFSDM_NUM_CHANNELS 8
static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
{
@@ -75,8 +82,7 @@ static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = {
};
static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = {
- .num_filters = STM32MP1_DFSDM_NUM_FILTERS,
- .num_channels = STM32MP1_DFSDM_NUM_CHANNELS,
+ .ipid = STM32MP15_IPIDR_NUMBER,
.regmap_cfg = &stm32mp1_dfsdm_regmap_cfg,
};
@@ -295,6 +301,65 @@ static const struct of_device_id stm32_dfsdm_of_match[] = {
};
MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
+static int stm32_dfsdm_probe_identification(struct platform_device *pdev,
+ struct dfsdm_priv *priv,
+ const struct stm32_dfsdm_dev_data *dev_data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ struct stm32_dfsdm *dfsdm = &priv->dfsdm;
+ const char *compat;
+ int ret, count = 0;
+ u32 id, val;
+
+ if (!dev_data->ipid) {
+ dfsdm->num_fls = dev_data->num_filters;
+ dfsdm->num_chs = dev_data->num_channels;
+ return 0;
+ }
+
+ ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &id);
+ if (ret)
+ return ret;
+
+ if (id != dev_data->ipid) {
+ dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(np, child) {
+ ret = of_property_read_string(child, "compatible", &compat);
+ if (ret)
+ continue;
+ /* Count only child nodes with dfsdm compatible */
+ if (strstr(compat, "dfsdm"))
+ count++;
+ }
+
+ ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val);
+ if (ret)
+ return ret;
+
+ dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val);
+ dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val);
+
+ if (count > dfsdm->num_fls) {
+ dev_err(&pdev->dev, "Unexpected child number: %d", count);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val);
+ if (ret)
+ return ret;
+
+ dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n",
+ FIELD_GET(DFSDM_VERR_MAJREV_MASK, val),
+ FIELD_GET(DFSDM_VERR_MINREV_MASK, val),
+ dfsdm->num_chs, dfsdm->num_fls);
+
+ return 0;
+}
+
static int stm32_dfsdm_probe(struct platform_device *pdev)
{
struct dfsdm_priv *priv;
@@ -311,18 +376,6 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
dev_data = of_device_get_match_data(&pdev->dev);
dfsdm = &priv->dfsdm;
- dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
- sizeof(*dfsdm->fl_list), GFP_KERNEL);
- if (!dfsdm->fl_list)
- return -ENOMEM;
-
- dfsdm->num_fls = dev_data->num_filters;
- dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
- sizeof(*dfsdm->ch_list),
- GFP_KERNEL);
- if (!dfsdm->ch_list)
- return -ENOMEM;
- dfsdm->num_chs = dev_data->num_channels;
ret = stm32_dfsdm_parse_of(pdev, priv);
if (ret < 0)
@@ -338,6 +391,20 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
return ret;
}
+ ret = stm32_dfsdm_probe_identification(pdev, priv, dev_data);
+ if (ret < 0)
+ return ret;
+
+ dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls,
+ sizeof(*dfsdm->fl_list), GFP_KERNEL);
+ if (!dfsdm->fl_list)
+ return -ENOMEM;
+
+ dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs,
+ sizeof(*dfsdm->ch_list), GFP_KERNEL);
+ if (!dfsdm->ch_list)
+ return -ENOMEM;
+
platform_set_drvdata(pdev, dfsdm);
ret = stm32_dfsdm_clk_prepare_enable(dfsdm);