Skip to content

Commit bed7390

Browse files
arnopobroonie
authored andcommitted
IIO: ADC: add stm32 DFSDM core support
Add driver for stm32 DFSDM pheripheral. Its converts a sigma delta stream in n bit samples through a low pass filter and an integrator. stm32-dfsdm-core driver is the core part supporting the filter instances dedicated to sigma-delta ADC or audio PDM microphone purpose. Signed-off-by: Arnaud Pouliquen <[email protected]> Reviewed-by: Jonathan Cameron <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent 6c82f94 commit bed7390

File tree

4 files changed

+632
-0
lines changed

4 files changed

+632
-0
lines changed

drivers/iio/adc/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,18 @@ config STM32_ADC
668668
This driver can also be built as a module. If so, the module
669669
will be called stm32-adc.
670670

671+
config STM32_DFSDM_CORE
672+
tristate "STMicroelectronics STM32 DFSDM core"
673+
depends on (ARCH_STM32 && OF) || COMPILE_TEST
674+
select REGMAP
675+
select REGMAP_MMIO
676+
help
677+
Select this option to enable the driver for STMicroelectronics
678+
STM32 digital filter for sigma delta converter.
679+
680+
This driver can also be built as a module. If so, the module
681+
will be called stm32-dfsdm-core.
682+
671683
config STX104
672684
tristate "Apex Embedded Systems STX104 driver"
673685
depends on PC104 && X86 && ISA_BUS_API

drivers/iio/adc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ obj-$(CONFIG_STX104) += stx104.o
6464
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
6565
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
6666
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
67+
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
6768
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
6869
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
6970
obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o

drivers/iio/adc/stm32-dfsdm-core.c

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* This file is part the core part STM32 DFSDM driver
4+
*
5+
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
6+
* Author(s): Arnaud Pouliquen <[email protected]> for STMicroelectronics.
7+
*/
8+
9+
#include <linux/clk.h>
10+
#include <linux/iio/iio.h>
11+
#include <linux/iio/sysfs.h>
12+
#include <linux/interrupt.h>
13+
#include <linux/module.h>
14+
#include <linux/of_device.h>
15+
#include <linux/regmap.h>
16+
#include <linux/slab.h>
17+
18+
#include "stm32-dfsdm.h"
19+
20+
struct stm32_dfsdm_dev_data {
21+
unsigned int num_filters;
22+
unsigned int num_channels;
23+
const struct regmap_config *regmap_cfg;
24+
};
25+
26+
#define STM32H7_DFSDM_NUM_FILTERS 4
27+
#define STM32H7_DFSDM_NUM_CHANNELS 8
28+
29+
static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
30+
{
31+
if (reg < DFSDM_FILTER_BASE_ADR)
32+
return false;
33+
34+
/*
35+
* Mask is done on register to avoid to list registers of all
36+
* filter instances.
37+
*/
38+
switch (reg & DFSDM_FILTER_REG_MASK) {
39+
case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
40+
case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
41+
case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
42+
case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
43+
return true;
44+
}
45+
46+
return false;
47+
}
48+
49+
static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
50+
.reg_bits = 32,
51+
.val_bits = 32,
52+
.reg_stride = sizeof(u32),
53+
.max_register = 0x2B8,
54+
.volatile_reg = stm32_dfsdm_volatile_reg,
55+
.fast_io = true,
56+
};
57+
58+
static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
59+
.num_filters = STM32H7_DFSDM_NUM_FILTERS,
60+
.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
61+
.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
62+
};
63+
64+
struct dfsdm_priv {
65+
struct platform_device *pdev; /* platform device */
66+
67+
struct stm32_dfsdm dfsdm; /* common data exported for all instances */
68+
69+
unsigned int spi_clk_out_div; /* SPI clkout divider value */
70+
atomic_t n_active_ch; /* number of current active channels */
71+
72+
struct clk *clk; /* DFSDM clock */
73+
struct clk *aclk; /* audio clock */
74+
};
75+
76+
/**
77+
* stm32_dfsdm_start_dfsdm - start global dfsdm interface.
78+
*
79+
* Enable interface if n_active_ch is not null.
80+
* @dfsdm: Handle used to retrieve dfsdm context.
81+
*/
82+
int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
83+
{
84+
struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
85+
struct device *dev = &priv->pdev->dev;
86+
unsigned int clk_div = priv->spi_clk_out_div;
87+
int ret;
88+
89+
if (atomic_inc_return(&priv->n_active_ch) == 1) {
90+
ret = clk_prepare_enable(priv->clk);
91+
if (ret < 0) {
92+
dev_err(dev, "Failed to start clock\n");
93+
goto error_ret;
94+
}
95+
if (priv->aclk) {
96+
ret = clk_prepare_enable(priv->aclk);
97+
if (ret < 0) {
98+
dev_err(dev, "Failed to start audio clock\n");
99+
goto disable_clk;
100+
}
101+
}
102+
103+
/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
104+
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
105+
DFSDM_CHCFGR1_CKOUTDIV_MASK,
106+
DFSDM_CHCFGR1_CKOUTDIV(clk_div));
107+
if (ret < 0)
108+
goto disable_aclk;
109+
110+
/* Global enable of DFSDM interface */
111+
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
112+
DFSDM_CHCFGR1_DFSDMEN_MASK,
113+
DFSDM_CHCFGR1_DFSDMEN(1));
114+
if (ret < 0)
115+
goto disable_aclk;
116+
}
117+
118+
dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
119+
atomic_read(&priv->n_active_ch));
120+
121+
return 0;
122+
123+
disable_aclk:
124+
clk_disable_unprepare(priv->aclk);
125+
disable_clk:
126+
clk_disable_unprepare(priv->clk);
127+
128+
error_ret:
129+
atomic_dec(&priv->n_active_ch);
130+
131+
return ret;
132+
}
133+
EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm);
134+
135+
/**
136+
* stm32_dfsdm_stop_dfsdm - stop global DFSDM interface.
137+
*
138+
* Disable interface if n_active_ch is null
139+
* @dfsdm: Handle used to retrieve dfsdm context.
140+
*/
141+
int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
142+
{
143+
struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
144+
int ret;
145+
146+
if (atomic_dec_and_test(&priv->n_active_ch)) {
147+
/* Global disable of DFSDM interface */
148+
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
149+
DFSDM_CHCFGR1_DFSDMEN_MASK,
150+
DFSDM_CHCFGR1_DFSDMEN(0));
151+
if (ret < 0)
152+
return ret;
153+
154+
/* Stop SPI CLKOUT */
155+
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
156+
DFSDM_CHCFGR1_CKOUTDIV_MASK,
157+
DFSDM_CHCFGR1_CKOUTDIV(0));
158+
if (ret < 0)
159+
return ret;
160+
161+
clk_disable_unprepare(priv->clk);
162+
if (priv->aclk)
163+
clk_disable_unprepare(priv->aclk);
164+
}
165+
dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
166+
atomic_read(&priv->n_active_ch));
167+
168+
return 0;
169+
}
170+
EXPORT_SYMBOL_GPL(stm32_dfsdm_stop_dfsdm);
171+
172+
static int stm32_dfsdm_parse_of(struct platform_device *pdev,
173+
struct dfsdm_priv *priv)
174+
{
175+
struct device_node *node = pdev->dev.of_node;
176+
struct resource *res;
177+
unsigned long clk_freq;
178+
unsigned int spi_freq, rem;
179+
int ret;
180+
181+
if (!node)
182+
return -EINVAL;
183+
184+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
185+
if (!res) {
186+
dev_err(&pdev->dev, "Failed to get memory resource\n");
187+
return -ENODEV;
188+
}
189+
priv->dfsdm.phys_base = res->start;
190+
priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
191+
192+
/*
193+
* "dfsdm" clock is mandatory for DFSDM peripheral clocking.
194+
* "dfsdm" or "audio" clocks can be used as source clock for
195+
* the SPI clock out signal and internal processing, depending
196+
* on use case.
197+
*/
198+
priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
199+
if (IS_ERR(priv->clk)) {
200+
dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
201+
return -EINVAL;
202+
}
203+
204+
priv->aclk = devm_clk_get(&pdev->dev, "audio");
205+
if (IS_ERR(priv->aclk))
206+
priv->aclk = NULL;
207+
208+
if (priv->aclk)
209+
clk_freq = clk_get_rate(priv->aclk);
210+
else
211+
clk_freq = clk_get_rate(priv->clk);
212+
213+
/* SPI clock out frequency */
214+
ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
215+
&spi_freq);
216+
if (ret < 0) {
217+
/* No SPI master mode */
218+
return 0;
219+
}
220+
221+
priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
222+
priv->dfsdm.spi_master_freq = spi_freq;
223+
224+
if (rem) {
225+
dev_warn(&pdev->dev, "SPI clock not accurate\n");
226+
dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
227+
clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
228+
}
229+
230+
return 0;
231+
};
232+
233+
static const struct of_device_id stm32_dfsdm_of_match[] = {
234+
{
235+
.compatible = "st,stm32h7-dfsdm",
236+
.data = &stm32h7_dfsdm_data,
237+
},
238+
{}
239+
};
240+
MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
241+
242+
static int stm32_dfsdm_probe(struct platform_device *pdev)
243+
{
244+
struct dfsdm_priv *priv;
245+
struct device_node *pnode = pdev->dev.of_node;
246+
const struct of_device_id *of_id;
247+
const struct stm32_dfsdm_dev_data *dev_data;
248+
struct stm32_dfsdm *dfsdm;
249+
int ret;
250+
251+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
252+
if (!priv)
253+
return -ENOMEM;
254+
255+
priv->pdev = pdev;
256+
257+
of_id = of_match_node(stm32_dfsdm_of_match, pnode);
258+
if (!of_id->data) {
259+
dev_err(&pdev->dev, "Data associated to device is missing\n");
260+
return -EINVAL;
261+
}
262+
263+
dev_data = (const struct stm32_dfsdm_dev_data *)of_id->data;
264+
dfsdm = &priv->dfsdm;
265+
dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
266+
sizeof(*dfsdm->fl_list), GFP_KERNEL);
267+
if (!dfsdm->fl_list)
268+
return -ENOMEM;
269+
270+
dfsdm->num_fls = dev_data->num_filters;
271+
dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
272+
sizeof(*dfsdm->ch_list),
273+
GFP_KERNEL);
274+
if (!dfsdm->ch_list)
275+
return -ENOMEM;
276+
dfsdm->num_chs = dev_data->num_channels;
277+
278+
ret = stm32_dfsdm_parse_of(pdev, priv);
279+
if (ret < 0)
280+
return ret;
281+
282+
dfsdm->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dfsdm",
283+
dfsdm->base,
284+
&stm32h7_dfsdm_regmap_cfg);
285+
if (IS_ERR(dfsdm->regmap)) {
286+
ret = PTR_ERR(dfsdm->regmap);
287+
dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
288+
__func__, ret);
289+
return ret;
290+
}
291+
292+
platform_set_drvdata(pdev, dfsdm);
293+
294+
return devm_of_platform_populate(&pdev->dev);
295+
}
296+
297+
static struct platform_driver stm32_dfsdm_driver = {
298+
.probe = stm32_dfsdm_probe,
299+
.driver = {
300+
.name = "stm32-dfsdm",
301+
.of_match_table = stm32_dfsdm_of_match,
302+
},
303+
};
304+
305+
module_platform_driver(stm32_dfsdm_driver);
306+
307+
MODULE_AUTHOR("Arnaud Pouliquen <[email protected]>");
308+
MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
309+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)