Commit | Line | Data |
---|---|---|
bed73904 AP |
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 <arnaud.pouliquen@st.com> for STMicroelectronics. | |
7 | */ | |
8 | ||
bfcae956 | 9 | #include <linux/bitfield.h> |
bed73904 AP |
10 | #include <linux/clk.h> |
11 | #include <linux/iio/iio.h> | |
12 | #include <linux/iio/sysfs.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/of_device.h> | |
6ec417d2 FG |
16 | #include <linux/pinctrl/consumer.h> |
17 | #include <linux/pm_runtime.h> | |
bed73904 AP |
18 | #include <linux/regmap.h> |
19 | #include <linux/slab.h> | |
20 | ||
21 | #include "stm32-dfsdm.h" | |
22 | ||
bfcae956 OM |
23 | /** |
24 | * struct stm32_dfsdm_dev_data - DFSDM compatible configuration data | |
25 | * @ipid: DFSDM identification number. Used only if hardware provides identification registers | |
26 | * @num_filters: DFSDM number of filters. Unused if identification registers are available | |
27 | * @num_channels: DFSDM number of channels. Unused if identification registers are available | |
28 | * @regmap_cfg: SAI register map configuration pointer | |
29 | */ | |
bed73904 | 30 | struct stm32_dfsdm_dev_data { |
bfcae956 | 31 | u32 ipid; |
bed73904 AP |
32 | unsigned int num_filters; |
33 | unsigned int num_channels; | |
34 | const struct regmap_config *regmap_cfg; | |
35 | }; | |
36 | ||
37 | #define STM32H7_DFSDM_NUM_FILTERS 4 | |
38 | #define STM32H7_DFSDM_NUM_CHANNELS 8 | |
39 | ||
40 | static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) | |
41 | { | |
42 | if (reg < DFSDM_FILTER_BASE_ADR) | |
43 | return false; | |
44 | ||
45 | /* | |
46 | * Mask is done on register to avoid to list registers of all | |
47 | * filter instances. | |
48 | */ | |
49 | switch (reg & DFSDM_FILTER_REG_MASK) { | |
50 | case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK: | |
51 | case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK: | |
52 | case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK: | |
53 | case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK: | |
54 | return true; | |
55 | } | |
56 | ||
57 | return false; | |
58 | } | |
59 | ||
60 | static const struct regmap_config stm32h7_dfsdm_regmap_cfg = { | |
61 | .reg_bits = 32, | |
62 | .val_bits = 32, | |
63 | .reg_stride = sizeof(u32), | |
64 | .max_register = 0x2B8, | |
65 | .volatile_reg = stm32_dfsdm_volatile_reg, | |
66 | .fast_io = true, | |
67 | }; | |
68 | ||
69 | static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = { | |
70 | .num_filters = STM32H7_DFSDM_NUM_FILTERS, | |
71 | .num_channels = STM32H7_DFSDM_NUM_CHANNELS, | |
72 | .regmap_cfg = &stm32h7_dfsdm_regmap_cfg, | |
73 | }; | |
74 | ||
e2fad745 FG |
75 | static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = { |
76 | .reg_bits = 32, | |
77 | .val_bits = 32, | |
78 | .reg_stride = sizeof(u32), | |
79 | .max_register = 0x7fc, | |
80 | .volatile_reg = stm32_dfsdm_volatile_reg, | |
81 | .fast_io = true, | |
82 | }; | |
83 | ||
84 | static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = { | |
bfcae956 | 85 | .ipid = STM32MP15_IPIDR_NUMBER, |
e2fad745 FG |
86 | .regmap_cfg = &stm32mp1_dfsdm_regmap_cfg, |
87 | }; | |
88 | ||
bed73904 AP |
89 | struct dfsdm_priv { |
90 | struct platform_device *pdev; /* platform device */ | |
91 | ||
92 | struct stm32_dfsdm dfsdm; /* common data exported for all instances */ | |
93 | ||
94 | unsigned int spi_clk_out_div; /* SPI clkout divider value */ | |
95 | atomic_t n_active_ch; /* number of current active channels */ | |
96 | ||
97 | struct clk *clk; /* DFSDM clock */ | |
98 | struct clk *aclk; /* audio clock */ | |
99 | }; | |
100 | ||
6ec417d2 FG |
101 | static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm) |
102 | { | |
103 | return container_of(dfsdm, struct dfsdm_priv, dfsdm); | |
104 | } | |
105 | ||
106 | static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm) | |
107 | { | |
108 | struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); | |
109 | int ret; | |
110 | ||
111 | ret = clk_prepare_enable(priv->clk); | |
112 | if (ret || !priv->aclk) | |
113 | return ret; | |
114 | ||
115 | ret = clk_prepare_enable(priv->aclk); | |
116 | if (ret) | |
117 | clk_disable_unprepare(priv->clk); | |
118 | ||
119 | return ret; | |
120 | } | |
121 | ||
122 | static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm) | |
123 | { | |
124 | struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); | |
125 | ||
aa15e684 | 126 | clk_disable_unprepare(priv->aclk); |
6ec417d2 FG |
127 | clk_disable_unprepare(priv->clk); |
128 | } | |
129 | ||
bed73904 AP |
130 | /** |
131 | * stm32_dfsdm_start_dfsdm - start global dfsdm interface. | |
132 | * | |
133 | * Enable interface if n_active_ch is not null. | |
134 | * @dfsdm: Handle used to retrieve dfsdm context. | |
135 | */ | |
136 | int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm) | |
137 | { | |
6ec417d2 | 138 | struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
bed73904 | 139 | struct device *dev = &priv->pdev->dev; |
179858ef | 140 | unsigned int clk_div = priv->spi_clk_out_div, clk_src; |
bed73904 AP |
141 | int ret; |
142 | ||
143 | if (atomic_inc_return(&priv->n_active_ch) == 1) { | |
29534eb2 JC |
144 | ret = pm_runtime_resume_and_get(dev); |
145 | if (ret < 0) | |
bed73904 | 146 | goto error_ret; |
bed73904 | 147 | |
179858ef FG |
148 | /* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */ |
149 | clk_src = priv->aclk ? 1 : 0; | |
150 | ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), | |
151 | DFSDM_CHCFGR1_CKOUTSRC_MASK, | |
152 | DFSDM_CHCFGR1_CKOUTSRC(clk_src)); | |
153 | if (ret < 0) | |
6ec417d2 | 154 | goto pm_put; |
179858ef | 155 | |
bed73904 AP |
156 | /* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */ |
157 | ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), | |
158 | DFSDM_CHCFGR1_CKOUTDIV_MASK, | |
159 | DFSDM_CHCFGR1_CKOUTDIV(clk_div)); | |
160 | if (ret < 0) | |
6ec417d2 | 161 | goto pm_put; |
bed73904 AP |
162 | |
163 | /* Global enable of DFSDM interface */ | |
164 | ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), | |
165 | DFSDM_CHCFGR1_DFSDMEN_MASK, | |
166 | DFSDM_CHCFGR1_DFSDMEN(1)); | |
167 | if (ret < 0) | |
6ec417d2 | 168 | goto pm_put; |
bed73904 AP |
169 | } |
170 | ||
171 | dev_dbg(dev, "%s: n_active_ch %d\n", __func__, | |
172 | atomic_read(&priv->n_active_ch)); | |
173 | ||
174 | return 0; | |
175 | ||
6ec417d2 FG |
176 | pm_put: |
177 | pm_runtime_put_sync(dev); | |
bed73904 AP |
178 | error_ret: |
179 | atomic_dec(&priv->n_active_ch); | |
180 | ||
181 | return ret; | |
182 | } | |
183 | EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm); | |
184 | ||
185 | /** | |
186 | * stm32_dfsdm_stop_dfsdm - stop global DFSDM interface. | |
187 | * | |
188 | * Disable interface if n_active_ch is null | |
189 | * @dfsdm: Handle used to retrieve dfsdm context. | |
190 | */ | |
191 | int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm) | |
192 | { | |
6ec417d2 | 193 | struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
bed73904 AP |
194 | int ret; |
195 | ||
196 | if (atomic_dec_and_test(&priv->n_active_ch)) { | |
197 | /* Global disable of DFSDM interface */ | |
198 | ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), | |
199 | DFSDM_CHCFGR1_DFSDMEN_MASK, | |
200 | DFSDM_CHCFGR1_DFSDMEN(0)); | |
201 | if (ret < 0) | |
202 | return ret; | |
203 | ||
204 | /* Stop SPI CLKOUT */ | |
205 | ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), | |
206 | DFSDM_CHCFGR1_CKOUTDIV_MASK, | |
207 | DFSDM_CHCFGR1_CKOUTDIV(0)); | |
208 | if (ret < 0) | |
209 | return ret; | |
210 | ||
6ec417d2 | 211 | pm_runtime_put_sync(&priv->pdev->dev); |
bed73904 AP |
212 | } |
213 | dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__, | |
214 | atomic_read(&priv->n_active_ch)); | |
215 | ||
216 | return 0; | |
217 | } | |
218 | EXPORT_SYMBOL_GPL(stm32_dfsdm_stop_dfsdm); | |
219 | ||
220 | static int stm32_dfsdm_parse_of(struct platform_device *pdev, | |
221 | struct dfsdm_priv *priv) | |
222 | { | |
223 | struct device_node *node = pdev->dev.of_node; | |
224 | struct resource *res; | |
2e19ba66 | 225 | unsigned long clk_freq, divider; |
bed73904 AP |
226 | unsigned int spi_freq, rem; |
227 | int ret; | |
228 | ||
229 | if (!node) | |
230 | return -EINVAL; | |
231 | ||
57e5b8bf WS |
232 | priv->dfsdm.base = devm_platform_get_and_ioremap_resource(pdev, 0, |
233 | &res); | |
d2fc0156 FD |
234 | if (IS_ERR(priv->dfsdm.base)) |
235 | return PTR_ERR(priv->dfsdm.base); | |
bed73904 | 236 | |
57e5b8bf WS |
237 | priv->dfsdm.phys_base = res->start; |
238 | ||
bed73904 AP |
239 | /* |
240 | * "dfsdm" clock is mandatory for DFSDM peripheral clocking. | |
241 | * "dfsdm" or "audio" clocks can be used as source clock for | |
242 | * the SPI clock out signal and internal processing, depending | |
243 | * on use case. | |
244 | */ | |
245 | priv->clk = devm_clk_get(&pdev->dev, "dfsdm"); | |
ce30eeb6 KK |
246 | if (IS_ERR(priv->clk)) |
247 | return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), | |
248 | "Failed to get clock\n"); | |
bed73904 AP |
249 | |
250 | priv->aclk = devm_clk_get(&pdev->dev, "audio"); | |
251 | if (IS_ERR(priv->aclk)) | |
252 | priv->aclk = NULL; | |
253 | ||
254 | if (priv->aclk) | |
255 | clk_freq = clk_get_rate(priv->aclk); | |
256 | else | |
257 | clk_freq = clk_get_rate(priv->clk); | |
258 | ||
259 | /* SPI clock out frequency */ | |
260 | ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", | |
261 | &spi_freq); | |
262 | if (ret < 0) { | |
263 | /* No SPI master mode */ | |
264 | return 0; | |
265 | } | |
266 | ||
2e19ba66 FG |
267 | divider = div_u64_rem(clk_freq, spi_freq, &rem); |
268 | /* Round up divider when ckout isn't precise, not to exceed spi_freq */ | |
269 | if (rem) | |
270 | divider++; | |
271 | ||
272 | /* programmable divider is in range of [2:256] */ | |
273 | if (divider < 2 || divider > 256) { | |
4cfcb2bf FG |
274 | dev_err(&pdev->dev, "spi-max-frequency not achievable\n"); |
275 | return -EINVAL; | |
276 | } | |
2e19ba66 FG |
277 | |
278 | /* SPI clock output divider is: divider = CKOUTDIV + 1 */ | |
279 | priv->spi_clk_out_div = divider - 1; | |
280 | priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1); | |
bed73904 AP |
281 | |
282 | if (rem) { | |
283 | dev_warn(&pdev->dev, "SPI clock not accurate\n"); | |
284 | dev_warn(&pdev->dev, "%ld = %d * %d + %d\n", | |
285 | clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem); | |
286 | } | |
287 | ||
288 | return 0; | |
289 | }; | |
290 | ||
291 | static const struct of_device_id stm32_dfsdm_of_match[] = { | |
292 | { | |
293 | .compatible = "st,stm32h7-dfsdm", | |
294 | .data = &stm32h7_dfsdm_data, | |
295 | }, | |
e2fad745 FG |
296 | { |
297 | .compatible = "st,stm32mp1-dfsdm", | |
298 | .data = &stm32mp1_dfsdm_data, | |
299 | }, | |
bed73904 AP |
300 | {} |
301 | }; | |
302 | MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); | |
303 | ||
bfcae956 OM |
304 | static int stm32_dfsdm_probe_identification(struct platform_device *pdev, |
305 | struct dfsdm_priv *priv, | |
306 | const struct stm32_dfsdm_dev_data *dev_data) | |
307 | { | |
308 | struct device_node *np = pdev->dev.of_node; | |
309 | struct device_node *child; | |
310 | struct stm32_dfsdm *dfsdm = &priv->dfsdm; | |
311 | const char *compat; | |
312 | int ret, count = 0; | |
313 | u32 id, val; | |
314 | ||
315 | if (!dev_data->ipid) { | |
316 | dfsdm->num_fls = dev_data->num_filters; | |
317 | dfsdm->num_chs = dev_data->num_channels; | |
318 | return 0; | |
319 | } | |
320 | ||
321 | ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &id); | |
322 | if (ret) | |
323 | return ret; | |
324 | ||
325 | if (id != dev_data->ipid) { | |
326 | dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id); | |
327 | return -EINVAL; | |
328 | } | |
329 | ||
330 | for_each_child_of_node(np, child) { | |
331 | ret = of_property_read_string(child, "compatible", &compat); | |
332 | if (ret) | |
333 | continue; | |
334 | /* Count only child nodes with dfsdm compatible */ | |
335 | if (strstr(compat, "dfsdm")) | |
336 | count++; | |
337 | } | |
338 | ||
339 | ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val); | |
340 | if (ret) | |
341 | return ret; | |
342 | ||
343 | dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val); | |
344 | dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val); | |
345 | ||
346 | if (count > dfsdm->num_fls) { | |
347 | dev_err(&pdev->dev, "Unexpected child number: %d", count); | |
348 | return -EINVAL; | |
349 | } | |
350 | ||
351 | ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val); | |
352 | if (ret) | |
353 | return ret; | |
354 | ||
355 | dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n", | |
356 | FIELD_GET(DFSDM_VERR_MAJREV_MASK, val), | |
357 | FIELD_GET(DFSDM_VERR_MINREV_MASK, val), | |
358 | dfsdm->num_chs, dfsdm->num_fls); | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
bed73904 AP |
363 | static int stm32_dfsdm_probe(struct platform_device *pdev) |
364 | { | |
365 | struct dfsdm_priv *priv; | |
bed73904 AP |
366 | const struct stm32_dfsdm_dev_data *dev_data; |
367 | struct stm32_dfsdm *dfsdm; | |
368 | int ret; | |
369 | ||
370 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
371 | if (!priv) | |
372 | return -ENOMEM; | |
373 | ||
374 | priv->pdev = pdev; | |
375 | ||
abaca806 | 376 | dev_data = of_device_get_match_data(&pdev->dev); |
bed73904 | 377 | |
bed73904 | 378 | dfsdm = &priv->dfsdm; |
bed73904 AP |
379 | |
380 | ret = stm32_dfsdm_parse_of(pdev, priv); | |
381 | if (ret < 0) | |
382 | return ret; | |
383 | ||
384 | dfsdm->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dfsdm", | |
385 | dfsdm->base, | |
4e4f9fbc | 386 | dev_data->regmap_cfg); |
bed73904 AP |
387 | if (IS_ERR(dfsdm->regmap)) { |
388 | ret = PTR_ERR(dfsdm->regmap); | |
389 | dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n", | |
390 | __func__, ret); | |
391 | return ret; | |
392 | } | |
393 | ||
bfcae956 OM |
394 | ret = stm32_dfsdm_probe_identification(pdev, priv, dev_data); |
395 | if (ret < 0) | |
396 | return ret; | |
397 | ||
398 | dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls, | |
399 | sizeof(*dfsdm->fl_list), GFP_KERNEL); | |
400 | if (!dfsdm->fl_list) | |
401 | return -ENOMEM; | |
402 | ||
403 | dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs, | |
404 | sizeof(*dfsdm->ch_list), GFP_KERNEL); | |
405 | if (!dfsdm->ch_list) | |
406 | return -ENOMEM; | |
407 | ||
bed73904 AP |
408 | platform_set_drvdata(pdev, dfsdm); |
409 | ||
6ec417d2 FG |
410 | ret = stm32_dfsdm_clk_prepare_enable(dfsdm); |
411 | if (ret) { | |
412 | dev_err(&pdev->dev, "Failed to start clock\n"); | |
413 | return ret; | |
414 | } | |
415 | ||
416 | pm_runtime_get_noresume(&pdev->dev); | |
417 | pm_runtime_set_active(&pdev->dev); | |
418 | pm_runtime_enable(&pdev->dev); | |
419 | ||
420 | ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); | |
421 | if (ret) | |
422 | goto pm_put; | |
423 | ||
424 | pm_runtime_put(&pdev->dev); | |
425 | ||
426 | return 0; | |
427 | ||
428 | pm_put: | |
429 | pm_runtime_disable(&pdev->dev); | |
430 | pm_runtime_set_suspended(&pdev->dev); | |
431 | pm_runtime_put_noidle(&pdev->dev); | |
432 | stm32_dfsdm_clk_disable_unprepare(dfsdm); | |
433 | ||
434 | return ret; | |
435 | } | |
436 | ||
437 | static int stm32_dfsdm_core_remove(struct platform_device *pdev) | |
438 | { | |
439 | struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev); | |
440 | ||
441 | pm_runtime_get_sync(&pdev->dev); | |
442 | of_platform_depopulate(&pdev->dev); | |
443 | pm_runtime_disable(&pdev->dev); | |
444 | pm_runtime_set_suspended(&pdev->dev); | |
445 | pm_runtime_put_noidle(&pdev->dev); | |
446 | stm32_dfsdm_clk_disable_unprepare(dfsdm); | |
447 | ||
448 | return 0; | |
449 | } | |
450 | ||
ade59a7a | 451 | static int stm32_dfsdm_core_suspend(struct device *dev) |
6ec417d2 FG |
452 | { |
453 | struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); | |
454 | struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); | |
455 | int ret; | |
456 | ||
457 | ret = pm_runtime_force_suspend(dev); | |
458 | if (ret) | |
459 | return ret; | |
460 | ||
461 | /* Balance devm_regmap_init_mmio_clk() clk_prepare() */ | |
462 | clk_unprepare(priv->clk); | |
463 | ||
464 | return pinctrl_pm_select_sleep_state(dev); | |
465 | } | |
466 | ||
ade59a7a | 467 | static int stm32_dfsdm_core_resume(struct device *dev) |
6ec417d2 FG |
468 | { |
469 | struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); | |
470 | struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); | |
471 | int ret; | |
472 | ||
473 | ret = pinctrl_pm_select_default_state(dev); | |
474 | if (ret) | |
475 | return ret; | |
476 | ||
477 | ret = clk_prepare(priv->clk); | |
478 | if (ret) | |
479 | return ret; | |
480 | ||
481 | return pm_runtime_force_resume(dev); | |
bed73904 AP |
482 | } |
483 | ||
ade59a7a | 484 | static int stm32_dfsdm_core_runtime_suspend(struct device *dev) |
6ec417d2 FG |
485 | { |
486 | struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); | |
487 | ||
488 | stm32_dfsdm_clk_disable_unprepare(dfsdm); | |
489 | ||
490 | return 0; | |
491 | } | |
492 | ||
ade59a7a | 493 | static int stm32_dfsdm_core_runtime_resume(struct device *dev) |
6ec417d2 FG |
494 | { |
495 | struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); | |
496 | ||
497 | return stm32_dfsdm_clk_prepare_enable(dfsdm); | |
498 | } | |
499 | ||
500 | static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = { | |
ade59a7a JC |
501 | SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, stm32_dfsdm_core_resume) |
502 | RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend, | |
503 | stm32_dfsdm_core_runtime_resume, | |
504 | NULL) | |
6ec417d2 FG |
505 | }; |
506 | ||
bed73904 AP |
507 | static struct platform_driver stm32_dfsdm_driver = { |
508 | .probe = stm32_dfsdm_probe, | |
6ec417d2 | 509 | .remove = stm32_dfsdm_core_remove, |
bed73904 AP |
510 | .driver = { |
511 | .name = "stm32-dfsdm", | |
512 | .of_match_table = stm32_dfsdm_of_match, | |
ade59a7a | 513 | .pm = pm_ptr(&stm32_dfsdm_core_pm_ops), |
bed73904 AP |
514 | }, |
515 | }; | |
516 | ||
517 | module_platform_driver(stm32_dfsdm_driver); | |
518 | ||
519 | MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); | |
520 | MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver"); | |
521 | MODULE_LICENSE("GPL v2"); |