* 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>
#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;
#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)
{
};
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,
};
};
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;
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)
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);
/*
* STM32 DFSDM - global register map
- * ________________________________________________________
- * | Offset | Registers block |
- * --------------------------------------------------------
- * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
- * --------------------------------------------------------
- * | 0x020 | CHANNEL 1 |
- * --------------------------------------------------------
- * | ... | ..... |
- * --------------------------------------------------------
- * | 0x0E0 | CHANNEL 7 |
- * --------------------------------------------------------
- * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
- * --------------------------------------------------------
- * | 0x200 | FILTER 1 |
- * --------------------------------------------------------
- * | 0x300 | FILTER 2 |
- * --------------------------------------------------------
- * | 0x400 | FILTER 3 |
- * --------------------------------------------------------
+ * __________________________________________________________
+ * | Offset | Registers block |
+ * ----------------------------------------------------------
+ * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
+ * ----------------------------------------------------------
+ * | 0x020 | CHANNEL 1 |
+ * ----------------------------------------------------------
+ * | ... | ..... |
+ * ----------------------------------------------------------
+ * | 0x20 x n | CHANNEL n |
+ * ----------------------------------------------------------
+ * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
+ * ----------------------------------------------------------
+ * | 0x200 | FILTER 1 |
+ * ----------------------------------------------------------
+ * | | ..... |
+ * ----------------------------------------------------------
+ * | 0x100 x m | FILTER m |
+ * ----------------------------------------------------------
+ * | | ..... |
+ * ----------------------------------------------------------
+ * | 0x7F0-7FC | Identification registers |
+ * ----------------------------------------------------------
*/
/*
#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8)
#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
+/*
+ * Identification register definitions
+ */
+#define DFSDM_HWCFGR 0x7F0
+#define DFSDM_VERR 0x7F4
+#define DFSDM_IPIDR 0x7F8
+#define DFSDM_SIDR 0x7FC
+
+/* HWCFGR: Hardware configuration register */
+#define DFSDM_HWCFGR_NBT_MASK GENMASK(7, 0)
+#define DFSDM_HWCFGR_NBF_MASK GENMASK(15, 8)
+
+/* VERR: Version register */
+#define DFSDM_VERR_MINREV_MASK GENMASK(3, 0)
+#define DFSDM_VERR_MAJREV_MASK GENMASK(7, 4)
+
+#define STM32MP15_IPIDR_NUMBER 0x00110031
+
/* DFSDM filter order */
enum stm32_dfsdm_sinc_order {
DFSDM_FASTSINC_ORDER, /* FastSinc filter type */