.. | .. |
---|
12 | 12 | #include <linux/interrupt.h> |
---|
13 | 13 | #include <linux/module.h> |
---|
14 | 14 | #include <linux/of_device.h> |
---|
| 15 | +#include <linux/pinctrl/consumer.h> |
---|
| 16 | +#include <linux/pm_runtime.h> |
---|
15 | 17 | #include <linux/regmap.h> |
---|
16 | 18 | #include <linux/slab.h> |
---|
17 | 19 | |
---|
.. | .. |
---|
90 | 92 | struct clk *aclk; /* audio clock */ |
---|
91 | 93 | }; |
---|
92 | 94 | |
---|
| 95 | +static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm) |
---|
| 96 | +{ |
---|
| 97 | + return container_of(dfsdm, struct dfsdm_priv, dfsdm); |
---|
| 98 | +} |
---|
| 99 | + |
---|
| 100 | +static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm) |
---|
| 101 | +{ |
---|
| 102 | + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
---|
| 103 | + int ret; |
---|
| 104 | + |
---|
| 105 | + ret = clk_prepare_enable(priv->clk); |
---|
| 106 | + if (ret || !priv->aclk) |
---|
| 107 | + return ret; |
---|
| 108 | + |
---|
| 109 | + ret = clk_prepare_enable(priv->aclk); |
---|
| 110 | + if (ret) |
---|
| 111 | + clk_disable_unprepare(priv->clk); |
---|
| 112 | + |
---|
| 113 | + return ret; |
---|
| 114 | +} |
---|
| 115 | + |
---|
| 116 | +static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm) |
---|
| 117 | +{ |
---|
| 118 | + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
---|
| 119 | + |
---|
| 120 | + if (priv->aclk) |
---|
| 121 | + clk_disable_unprepare(priv->aclk); |
---|
| 122 | + clk_disable_unprepare(priv->clk); |
---|
| 123 | +} |
---|
| 124 | + |
---|
93 | 125 | /** |
---|
94 | 126 | * stm32_dfsdm_start_dfsdm - start global dfsdm interface. |
---|
95 | 127 | * |
---|
.. | .. |
---|
98 | 130 | */ |
---|
99 | 131 | int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm) |
---|
100 | 132 | { |
---|
101 | | - struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm); |
---|
| 133 | + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
---|
102 | 134 | struct device *dev = &priv->pdev->dev; |
---|
103 | 135 | unsigned int clk_div = priv->spi_clk_out_div, clk_src; |
---|
104 | 136 | int ret; |
---|
105 | 137 | |
---|
106 | 138 | if (atomic_inc_return(&priv->n_active_ch) == 1) { |
---|
107 | | - ret = clk_prepare_enable(priv->clk); |
---|
| 139 | + ret = pm_runtime_get_sync(dev); |
---|
108 | 140 | if (ret < 0) { |
---|
109 | | - dev_err(dev, "Failed to start clock\n"); |
---|
| 141 | + pm_runtime_put_noidle(dev); |
---|
110 | 142 | goto error_ret; |
---|
111 | | - } |
---|
112 | | - if (priv->aclk) { |
---|
113 | | - ret = clk_prepare_enable(priv->aclk); |
---|
114 | | - if (ret < 0) { |
---|
115 | | - dev_err(dev, "Failed to start audio clock\n"); |
---|
116 | | - goto disable_clk; |
---|
117 | | - } |
---|
118 | 143 | } |
---|
119 | 144 | |
---|
120 | 145 | /* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */ |
---|
.. | .. |
---|
123 | 148 | DFSDM_CHCFGR1_CKOUTSRC_MASK, |
---|
124 | 149 | DFSDM_CHCFGR1_CKOUTSRC(clk_src)); |
---|
125 | 150 | if (ret < 0) |
---|
126 | | - goto disable_aclk; |
---|
| 151 | + goto pm_put; |
---|
127 | 152 | |
---|
128 | 153 | /* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */ |
---|
129 | 154 | ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), |
---|
130 | 155 | DFSDM_CHCFGR1_CKOUTDIV_MASK, |
---|
131 | 156 | DFSDM_CHCFGR1_CKOUTDIV(clk_div)); |
---|
132 | 157 | if (ret < 0) |
---|
133 | | - goto disable_aclk; |
---|
| 158 | + goto pm_put; |
---|
134 | 159 | |
---|
135 | 160 | /* Global enable of DFSDM interface */ |
---|
136 | 161 | ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), |
---|
137 | 162 | DFSDM_CHCFGR1_DFSDMEN_MASK, |
---|
138 | 163 | DFSDM_CHCFGR1_DFSDMEN(1)); |
---|
139 | 164 | if (ret < 0) |
---|
140 | | - goto disable_aclk; |
---|
| 165 | + goto pm_put; |
---|
141 | 166 | } |
---|
142 | 167 | |
---|
143 | 168 | dev_dbg(dev, "%s: n_active_ch %d\n", __func__, |
---|
.. | .. |
---|
145 | 170 | |
---|
146 | 171 | return 0; |
---|
147 | 172 | |
---|
148 | | -disable_aclk: |
---|
149 | | - clk_disable_unprepare(priv->aclk); |
---|
150 | | -disable_clk: |
---|
151 | | - clk_disable_unprepare(priv->clk); |
---|
152 | | - |
---|
| 173 | +pm_put: |
---|
| 174 | + pm_runtime_put_sync(dev); |
---|
153 | 175 | error_ret: |
---|
154 | 176 | atomic_dec(&priv->n_active_ch); |
---|
155 | 177 | |
---|
.. | .. |
---|
165 | 187 | */ |
---|
166 | 188 | int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm) |
---|
167 | 189 | { |
---|
168 | | - struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm); |
---|
| 190 | + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
---|
169 | 191 | int ret; |
---|
170 | 192 | |
---|
171 | 193 | if (atomic_dec_and_test(&priv->n_active_ch)) { |
---|
.. | .. |
---|
183 | 205 | if (ret < 0) |
---|
184 | 206 | return ret; |
---|
185 | 207 | |
---|
186 | | - clk_disable_unprepare(priv->clk); |
---|
187 | | - if (priv->aclk) |
---|
188 | | - clk_disable_unprepare(priv->aclk); |
---|
| 208 | + pm_runtime_put_sync(&priv->pdev->dev); |
---|
189 | 209 | } |
---|
190 | 210 | dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__, |
---|
191 | 211 | atomic_read(&priv->n_active_ch)); |
---|
.. | .. |
---|
199 | 219 | { |
---|
200 | 220 | struct device_node *node = pdev->dev.of_node; |
---|
201 | 221 | struct resource *res; |
---|
202 | | - unsigned long clk_freq; |
---|
| 222 | + unsigned long clk_freq, divider; |
---|
203 | 223 | unsigned int spi_freq, rem; |
---|
204 | 224 | int ret; |
---|
205 | 225 | |
---|
206 | 226 | if (!node) |
---|
207 | 227 | return -EINVAL; |
---|
208 | 228 | |
---|
209 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
210 | | - if (!res) { |
---|
211 | | - dev_err(&pdev->dev, "Failed to get memory resource\n"); |
---|
212 | | - return -ENODEV; |
---|
213 | | - } |
---|
214 | | - priv->dfsdm.phys_base = res->start; |
---|
215 | | - priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res); |
---|
| 229 | + priv->dfsdm.base = devm_platform_get_and_ioremap_resource(pdev, 0, |
---|
| 230 | + &res); |
---|
216 | 231 | if (IS_ERR(priv->dfsdm.base)) |
---|
217 | 232 | return PTR_ERR(priv->dfsdm.base); |
---|
| 233 | + |
---|
| 234 | + priv->dfsdm.phys_base = res->start; |
---|
218 | 235 | |
---|
219 | 236 | /* |
---|
220 | 237 | * "dfsdm" clock is mandatory for DFSDM peripheral clocking. |
---|
.. | .. |
---|
223 | 240 | * on use case. |
---|
224 | 241 | */ |
---|
225 | 242 | priv->clk = devm_clk_get(&pdev->dev, "dfsdm"); |
---|
226 | | - if (IS_ERR(priv->clk)) { |
---|
227 | | - ret = PTR_ERR(priv->clk); |
---|
228 | | - if (ret != -EPROBE_DEFER) |
---|
229 | | - dev_err(&pdev->dev, "Failed to get clock (%d)\n", ret); |
---|
230 | | - return ret; |
---|
231 | | - } |
---|
| 243 | + if (IS_ERR(priv->clk)) |
---|
| 244 | + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), |
---|
| 245 | + "Failed to get clock\n"); |
---|
232 | 246 | |
---|
233 | 247 | priv->aclk = devm_clk_get(&pdev->dev, "audio"); |
---|
234 | 248 | if (IS_ERR(priv->aclk)) |
---|
.. | .. |
---|
247 | 261 | return 0; |
---|
248 | 262 | } |
---|
249 | 263 | |
---|
250 | | - priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1; |
---|
251 | | - if (!priv->spi_clk_out_div) { |
---|
252 | | - /* spi_clk_out_div == 0 means ckout is OFF */ |
---|
| 264 | + divider = div_u64_rem(clk_freq, spi_freq, &rem); |
---|
| 265 | + /* Round up divider when ckout isn't precise, not to exceed spi_freq */ |
---|
| 266 | + if (rem) |
---|
| 267 | + divider++; |
---|
| 268 | + |
---|
| 269 | + /* programmable divider is in range of [2:256] */ |
---|
| 270 | + if (divider < 2 || divider > 256) { |
---|
253 | 271 | dev_err(&pdev->dev, "spi-max-frequency not achievable\n"); |
---|
254 | 272 | return -EINVAL; |
---|
255 | 273 | } |
---|
256 | | - priv->dfsdm.spi_master_freq = spi_freq; |
---|
| 274 | + |
---|
| 275 | + /* SPI clock output divider is: divider = CKOUTDIV + 1 */ |
---|
| 276 | + priv->spi_clk_out_div = divider - 1; |
---|
| 277 | + priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1); |
---|
257 | 278 | |
---|
258 | 279 | if (rem) { |
---|
259 | 280 | dev_warn(&pdev->dev, "SPI clock not accurate\n"); |
---|
.. | .. |
---|
322 | 343 | |
---|
323 | 344 | platform_set_drvdata(pdev, dfsdm); |
---|
324 | 345 | |
---|
325 | | - return devm_of_platform_populate(&pdev->dev); |
---|
| 346 | + ret = stm32_dfsdm_clk_prepare_enable(dfsdm); |
---|
| 347 | + if (ret) { |
---|
| 348 | + dev_err(&pdev->dev, "Failed to start clock\n"); |
---|
| 349 | + return ret; |
---|
| 350 | + } |
---|
| 351 | + |
---|
| 352 | + pm_runtime_get_noresume(&pdev->dev); |
---|
| 353 | + pm_runtime_set_active(&pdev->dev); |
---|
| 354 | + pm_runtime_enable(&pdev->dev); |
---|
| 355 | + |
---|
| 356 | + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); |
---|
| 357 | + if (ret) |
---|
| 358 | + goto pm_put; |
---|
| 359 | + |
---|
| 360 | + pm_runtime_put(&pdev->dev); |
---|
| 361 | + |
---|
| 362 | + return 0; |
---|
| 363 | + |
---|
| 364 | +pm_put: |
---|
| 365 | + pm_runtime_disable(&pdev->dev); |
---|
| 366 | + pm_runtime_set_suspended(&pdev->dev); |
---|
| 367 | + pm_runtime_put_noidle(&pdev->dev); |
---|
| 368 | + stm32_dfsdm_clk_disable_unprepare(dfsdm); |
---|
| 369 | + |
---|
| 370 | + return ret; |
---|
326 | 371 | } |
---|
| 372 | + |
---|
| 373 | +static int stm32_dfsdm_core_remove(struct platform_device *pdev) |
---|
| 374 | +{ |
---|
| 375 | + struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev); |
---|
| 376 | + |
---|
| 377 | + pm_runtime_get_sync(&pdev->dev); |
---|
| 378 | + of_platform_depopulate(&pdev->dev); |
---|
| 379 | + pm_runtime_disable(&pdev->dev); |
---|
| 380 | + pm_runtime_set_suspended(&pdev->dev); |
---|
| 381 | + pm_runtime_put_noidle(&pdev->dev); |
---|
| 382 | + stm32_dfsdm_clk_disable_unprepare(dfsdm); |
---|
| 383 | + |
---|
| 384 | + return 0; |
---|
| 385 | +} |
---|
| 386 | + |
---|
| 387 | +static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev) |
---|
| 388 | +{ |
---|
| 389 | + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); |
---|
| 390 | + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
---|
| 391 | + int ret; |
---|
| 392 | + |
---|
| 393 | + ret = pm_runtime_force_suspend(dev); |
---|
| 394 | + if (ret) |
---|
| 395 | + return ret; |
---|
| 396 | + |
---|
| 397 | + /* Balance devm_regmap_init_mmio_clk() clk_prepare() */ |
---|
| 398 | + clk_unprepare(priv->clk); |
---|
| 399 | + |
---|
| 400 | + return pinctrl_pm_select_sleep_state(dev); |
---|
| 401 | +} |
---|
| 402 | + |
---|
| 403 | +static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev) |
---|
| 404 | +{ |
---|
| 405 | + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); |
---|
| 406 | + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); |
---|
| 407 | + int ret; |
---|
| 408 | + |
---|
| 409 | + ret = pinctrl_pm_select_default_state(dev); |
---|
| 410 | + if (ret) |
---|
| 411 | + return ret; |
---|
| 412 | + |
---|
| 413 | + ret = clk_prepare(priv->clk); |
---|
| 414 | + if (ret) |
---|
| 415 | + return ret; |
---|
| 416 | + |
---|
| 417 | + return pm_runtime_force_resume(dev); |
---|
| 418 | +} |
---|
| 419 | + |
---|
| 420 | +static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev) |
---|
| 421 | +{ |
---|
| 422 | + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); |
---|
| 423 | + |
---|
| 424 | + stm32_dfsdm_clk_disable_unprepare(dfsdm); |
---|
| 425 | + |
---|
| 426 | + return 0; |
---|
| 427 | +} |
---|
| 428 | + |
---|
| 429 | +static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev) |
---|
| 430 | +{ |
---|
| 431 | + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); |
---|
| 432 | + |
---|
| 433 | + return stm32_dfsdm_clk_prepare_enable(dfsdm); |
---|
| 434 | +} |
---|
| 435 | + |
---|
| 436 | +static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = { |
---|
| 437 | + SET_SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, |
---|
| 438 | + stm32_dfsdm_core_resume) |
---|
| 439 | + SET_RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend, |
---|
| 440 | + stm32_dfsdm_core_runtime_resume, |
---|
| 441 | + NULL) |
---|
| 442 | +}; |
---|
327 | 443 | |
---|
328 | 444 | static struct platform_driver stm32_dfsdm_driver = { |
---|
329 | 445 | .probe = stm32_dfsdm_probe, |
---|
| 446 | + .remove = stm32_dfsdm_core_remove, |
---|
330 | 447 | .driver = { |
---|
331 | 448 | .name = "stm32-dfsdm", |
---|
332 | 449 | .of_match_table = stm32_dfsdm_of_match, |
---|
| 450 | + .pm = &stm32_dfsdm_core_pm_ops, |
---|
333 | 451 | }, |
---|
334 | 452 | }; |
---|
335 | 453 | |
---|