.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * |
---|
3 | 4 | * Copyright (C) STMicroelectronics SA 2017 |
---|
4 | 5 | * Author(s): M'boumba Cedric Madianga <cedric.madianga@gmail.com> |
---|
5 | 6 | * Pierre-Yves Mordret <pierre-yves.mordret@st.com> |
---|
6 | 7 | * |
---|
7 | | - * License terms: GPL V2.0. |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or modify it |
---|
10 | | - * under the terms of the GNU General Public License version 2 as published by |
---|
11 | | - * the Free Software Foundation. |
---|
12 | | - * |
---|
13 | | - * This program is distributed in the hope that it will be useful, but |
---|
14 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
15 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
---|
16 | | - * details. |
---|
17 | | - * |
---|
18 | 8 | * DMA Router driver for STM32 DMA MUX |
---|
19 | 9 | * |
---|
20 | 10 | * Based on TI DMA Crossbar driver |
---|
21 | | - * |
---|
22 | 11 | */ |
---|
23 | 12 | |
---|
24 | 13 | #include <linux/clk.h> |
---|
.. | .. |
---|
28 | 17 | #include <linux/module.h> |
---|
29 | 18 | #include <linux/of_device.h> |
---|
30 | 19 | #include <linux/of_dma.h> |
---|
| 20 | +#include <linux/pm_runtime.h> |
---|
31 | 21 | #include <linux/reset.h> |
---|
32 | 22 | #include <linux/slab.h> |
---|
33 | 23 | #include <linux/spinlock.h> |
---|
.. | .. |
---|
45 | 35 | struct stm32_dmamux_data { |
---|
46 | 36 | struct dma_router dmarouter; |
---|
47 | 37 | struct clk *clk; |
---|
48 | | - struct reset_control *rst; |
---|
49 | 38 | void __iomem *iomem; |
---|
50 | 39 | u32 dma_requests; /* Number of DMA requests connected to DMAMUX */ |
---|
51 | 40 | u32 dmamux_requests; /* Number of DMA requests routed toward DMAs */ |
---|
52 | 41 | spinlock_t lock; /* Protects register access */ |
---|
53 | 42 | unsigned long *dma_inuse; /* Used DMA channel */ |
---|
| 43 | + u32 ccr[STM32_DMAMUX_MAX_DMA_REQUESTS]; /* Used to backup CCR register |
---|
| 44 | + * in suspend |
---|
| 45 | + */ |
---|
54 | 46 | u32 dma_reqs[]; /* Number of DMA Request per DMA masters. |
---|
55 | 47 | * [0] holds number of DMA Masters. |
---|
56 | 48 | * To be kept at very end end of this structure |
---|
.. | .. |
---|
79 | 71 | stm32_dmamux_write(dmamux->iomem, STM32_DMAMUX_CCR(mux->chan_id), 0); |
---|
80 | 72 | clear_bit(mux->chan_id, dmamux->dma_inuse); |
---|
81 | 73 | |
---|
82 | | - if (!IS_ERR(dmamux->clk)) |
---|
83 | | - clk_disable(dmamux->clk); |
---|
| 74 | + pm_runtime_put_sync(dev); |
---|
84 | 75 | |
---|
85 | 76 | spin_unlock_irqrestore(&dmamux->lock, flags); |
---|
86 | 77 | |
---|
.. | .. |
---|
146 | 137 | |
---|
147 | 138 | /* Set dma request */ |
---|
148 | 139 | spin_lock_irqsave(&dmamux->lock, flags); |
---|
149 | | - if (!IS_ERR(dmamux->clk)) { |
---|
150 | | - ret = clk_enable(dmamux->clk); |
---|
151 | | - if (ret < 0) { |
---|
152 | | - spin_unlock_irqrestore(&dmamux->lock, flags); |
---|
153 | | - dev_err(&pdev->dev, "clk_prep_enable issue: %d\n", ret); |
---|
154 | | - goto error; |
---|
155 | | - } |
---|
| 140 | + ret = pm_runtime_resume_and_get(&pdev->dev); |
---|
| 141 | + if (ret < 0) { |
---|
| 142 | + spin_unlock_irqrestore(&dmamux->lock, flags); |
---|
| 143 | + goto error; |
---|
156 | 144 | } |
---|
157 | 145 | spin_unlock_irqrestore(&dmamux->lock, flags); |
---|
158 | 146 | |
---|
.. | .. |
---|
193 | 181 | struct stm32_dmamux_data *stm32_dmamux; |
---|
194 | 182 | struct resource *res; |
---|
195 | 183 | void __iomem *iomem; |
---|
| 184 | + struct reset_control *rst; |
---|
196 | 185 | int i, count, ret; |
---|
197 | 186 | u32 dma_req; |
---|
198 | 187 | |
---|
199 | 188 | if (!node) |
---|
200 | 189 | return -ENODEV; |
---|
201 | 190 | |
---|
202 | | - count = device_property_read_u32_array(&pdev->dev, "dma-masters", |
---|
203 | | - NULL, 0); |
---|
| 191 | + count = device_property_count_u32(&pdev->dev, "dma-masters"); |
---|
204 | 192 | if (count < 0) { |
---|
205 | 193 | dev_err(&pdev->dev, "Can't get DMA master(s) node\n"); |
---|
206 | 194 | return -ENODEV; |
---|
.. | .. |
---|
254 | 242 | dev_warn(&pdev->dev, "DMAMUX defaulting on %u requests\n", |
---|
255 | 243 | stm32_dmamux->dmamux_requests); |
---|
256 | 244 | } |
---|
| 245 | + pm_runtime_get_noresume(&pdev->dev); |
---|
257 | 246 | |
---|
258 | 247 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
259 | 248 | iomem = devm_ioremap_resource(&pdev->dev, res); |
---|
.. | .. |
---|
263 | 252 | spin_lock_init(&stm32_dmamux->lock); |
---|
264 | 253 | |
---|
265 | 254 | stm32_dmamux->clk = devm_clk_get(&pdev->dev, NULL); |
---|
266 | | - if (IS_ERR(stm32_dmamux->clk)) { |
---|
267 | | - ret = PTR_ERR(stm32_dmamux->clk); |
---|
268 | | - if (ret == -EPROBE_DEFER) |
---|
269 | | - dev_info(&pdev->dev, "Missing controller clock\n"); |
---|
| 255 | + if (IS_ERR(stm32_dmamux->clk)) |
---|
| 256 | + return dev_err_probe(&pdev->dev, PTR_ERR(stm32_dmamux->clk), |
---|
| 257 | + "Missing clock controller\n"); |
---|
| 258 | + |
---|
| 259 | + ret = clk_prepare_enable(stm32_dmamux->clk); |
---|
| 260 | + if (ret < 0) { |
---|
| 261 | + dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); |
---|
270 | 262 | return ret; |
---|
271 | 263 | } |
---|
272 | 264 | |
---|
273 | | - stm32_dmamux->rst = devm_reset_control_get(&pdev->dev, NULL); |
---|
274 | | - if (!IS_ERR(stm32_dmamux->rst)) { |
---|
275 | | - reset_control_assert(stm32_dmamux->rst); |
---|
| 265 | + rst = devm_reset_control_get(&pdev->dev, NULL); |
---|
| 266 | + if (IS_ERR(rst)) { |
---|
| 267 | + ret = PTR_ERR(rst); |
---|
| 268 | + if (ret == -EPROBE_DEFER) |
---|
| 269 | + goto err_clk; |
---|
| 270 | + } else { |
---|
| 271 | + reset_control_assert(rst); |
---|
276 | 272 | udelay(2); |
---|
277 | | - reset_control_deassert(stm32_dmamux->rst); |
---|
| 273 | + reset_control_deassert(rst); |
---|
278 | 274 | } |
---|
279 | 275 | |
---|
280 | 276 | stm32_dmamux->iomem = iomem; |
---|
.. | .. |
---|
282 | 278 | stm32_dmamux->dmarouter.route_free = stm32_dmamux_free; |
---|
283 | 279 | |
---|
284 | 280 | platform_set_drvdata(pdev, stm32_dmamux); |
---|
| 281 | + pm_runtime_set_active(&pdev->dev); |
---|
| 282 | + pm_runtime_enable(&pdev->dev); |
---|
285 | 283 | |
---|
286 | | - if (!IS_ERR(stm32_dmamux->clk)) { |
---|
287 | | - ret = clk_prepare_enable(stm32_dmamux->clk); |
---|
288 | | - if (ret < 0) { |
---|
289 | | - dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); |
---|
290 | | - return ret; |
---|
291 | | - } |
---|
292 | | - } |
---|
| 284 | + pm_runtime_get_noresume(&pdev->dev); |
---|
293 | 285 | |
---|
294 | 286 | /* Reset the dmamux */ |
---|
295 | 287 | for (i = 0; i < stm32_dmamux->dma_requests; i++) |
---|
296 | 288 | stm32_dmamux_write(stm32_dmamux->iomem, STM32_DMAMUX_CCR(i), 0); |
---|
297 | 289 | |
---|
298 | | - if (!IS_ERR(stm32_dmamux->clk)) |
---|
299 | | - clk_disable(stm32_dmamux->clk); |
---|
| 290 | + pm_runtime_put(&pdev->dev); |
---|
300 | 291 | |
---|
301 | | - return of_dma_router_register(node, stm32_dmamux_route_allocate, |
---|
| 292 | + ret = of_dma_router_register(node, stm32_dmamux_route_allocate, |
---|
302 | 293 | &stm32_dmamux->dmarouter); |
---|
| 294 | + if (ret) |
---|
| 295 | + goto pm_disable; |
---|
| 296 | + |
---|
| 297 | + return 0; |
---|
| 298 | + |
---|
| 299 | +pm_disable: |
---|
| 300 | + pm_runtime_disable(&pdev->dev); |
---|
| 301 | +err_clk: |
---|
| 302 | + clk_disable_unprepare(stm32_dmamux->clk); |
---|
| 303 | + |
---|
| 304 | + return ret; |
---|
303 | 305 | } |
---|
| 306 | + |
---|
| 307 | +#ifdef CONFIG_PM |
---|
| 308 | +static int stm32_dmamux_runtime_suspend(struct device *dev) |
---|
| 309 | +{ |
---|
| 310 | + struct platform_device *pdev = to_platform_device(dev); |
---|
| 311 | + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); |
---|
| 312 | + |
---|
| 313 | + clk_disable_unprepare(stm32_dmamux->clk); |
---|
| 314 | + |
---|
| 315 | + return 0; |
---|
| 316 | +} |
---|
| 317 | + |
---|
| 318 | +static int stm32_dmamux_runtime_resume(struct device *dev) |
---|
| 319 | +{ |
---|
| 320 | + struct platform_device *pdev = to_platform_device(dev); |
---|
| 321 | + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); |
---|
| 322 | + int ret; |
---|
| 323 | + |
---|
| 324 | + ret = clk_prepare_enable(stm32_dmamux->clk); |
---|
| 325 | + if (ret) { |
---|
| 326 | + dev_err(&pdev->dev, "failed to prepare_enable clock\n"); |
---|
| 327 | + return ret; |
---|
| 328 | + } |
---|
| 329 | + |
---|
| 330 | + return 0; |
---|
| 331 | +} |
---|
| 332 | +#endif |
---|
| 333 | + |
---|
| 334 | +#ifdef CONFIG_PM_SLEEP |
---|
| 335 | +static int stm32_dmamux_suspend(struct device *dev) |
---|
| 336 | +{ |
---|
| 337 | + struct platform_device *pdev = to_platform_device(dev); |
---|
| 338 | + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); |
---|
| 339 | + int i, ret; |
---|
| 340 | + |
---|
| 341 | + ret = pm_runtime_resume_and_get(dev); |
---|
| 342 | + if (ret < 0) |
---|
| 343 | + return ret; |
---|
| 344 | + |
---|
| 345 | + for (i = 0; i < stm32_dmamux->dma_requests; i++) |
---|
| 346 | + stm32_dmamux->ccr[i] = stm32_dmamux_read(stm32_dmamux->iomem, |
---|
| 347 | + STM32_DMAMUX_CCR(i)); |
---|
| 348 | + |
---|
| 349 | + pm_runtime_put_sync(dev); |
---|
| 350 | + |
---|
| 351 | + pm_runtime_force_suspend(dev); |
---|
| 352 | + |
---|
| 353 | + return 0; |
---|
| 354 | +} |
---|
| 355 | + |
---|
| 356 | +static int stm32_dmamux_resume(struct device *dev) |
---|
| 357 | +{ |
---|
| 358 | + struct platform_device *pdev = to_platform_device(dev); |
---|
| 359 | + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); |
---|
| 360 | + int i, ret; |
---|
| 361 | + |
---|
| 362 | + ret = pm_runtime_force_resume(dev); |
---|
| 363 | + if (ret < 0) |
---|
| 364 | + return ret; |
---|
| 365 | + |
---|
| 366 | + ret = pm_runtime_resume_and_get(dev); |
---|
| 367 | + if (ret < 0) |
---|
| 368 | + return ret; |
---|
| 369 | + |
---|
| 370 | + for (i = 0; i < stm32_dmamux->dma_requests; i++) |
---|
| 371 | + stm32_dmamux_write(stm32_dmamux->iomem, STM32_DMAMUX_CCR(i), |
---|
| 372 | + stm32_dmamux->ccr[i]); |
---|
| 373 | + |
---|
| 374 | + pm_runtime_put_sync(dev); |
---|
| 375 | + |
---|
| 376 | + return 0; |
---|
| 377 | +} |
---|
| 378 | +#endif |
---|
| 379 | + |
---|
| 380 | +static const struct dev_pm_ops stm32_dmamux_pm_ops = { |
---|
| 381 | + SET_SYSTEM_SLEEP_PM_OPS(stm32_dmamux_suspend, stm32_dmamux_resume) |
---|
| 382 | + SET_RUNTIME_PM_OPS(stm32_dmamux_runtime_suspend, |
---|
| 383 | + stm32_dmamux_runtime_resume, NULL) |
---|
| 384 | +}; |
---|
304 | 385 | |
---|
305 | 386 | static const struct of_device_id stm32_dmamux_match[] = { |
---|
306 | 387 | { .compatible = "st,stm32h7-dmamux" }, |
---|
.. | .. |
---|
312 | 393 | .driver = { |
---|
313 | 394 | .name = "stm32-dmamux", |
---|
314 | 395 | .of_match_table = stm32_dmamux_match, |
---|
| 396 | + .pm = &stm32_dmamux_pm_ops, |
---|
315 | 397 | }, |
---|
316 | 398 | }; |
---|
317 | 399 | |
---|