]>
Commit | Line | Data |
---|---|---|
b70b009b MC |
1 | /* |
2 | * Copyright (C) Maxime Coquelin 2015 | |
3 | * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com> | |
4 | * License terms: GNU General Public License (GPL), version 2 | |
5 | * | |
6 | * Heavily based on sunxi driver from Maxime Ripard. | |
7 | */ | |
8 | ||
9 | #include <linux/err.h> | |
10 | #include <linux/io.h> | |
11 | #include <linux/of.h> | |
12 | #include <linux/of_address.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/reset-controller.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/spinlock.h> | |
17 | #include <linux/types.h> | |
18 | ||
19 | struct stm32_reset_data { | |
20 | spinlock_t lock; | |
21 | void __iomem *membase; | |
22 | struct reset_controller_dev rcdev; | |
23 | }; | |
24 | ||
25 | static int stm32_reset_assert(struct reset_controller_dev *rcdev, | |
26 | unsigned long id) | |
27 | { | |
28 | struct stm32_reset_data *data = container_of(rcdev, | |
29 | struct stm32_reset_data, | |
30 | rcdev); | |
31 | int bank = id / BITS_PER_LONG; | |
32 | int offset = id % BITS_PER_LONG; | |
33 | unsigned long flags; | |
34 | u32 reg; | |
35 | ||
36 | spin_lock_irqsave(&data->lock, flags); | |
37 | ||
38 | reg = readl(data->membase + (bank * 4)); | |
39 | writel(reg | BIT(offset), data->membase + (bank * 4)); | |
40 | ||
41 | spin_unlock_irqrestore(&data->lock, flags); | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
46 | static int stm32_reset_deassert(struct reset_controller_dev *rcdev, | |
47 | unsigned long id) | |
48 | { | |
49 | struct stm32_reset_data *data = container_of(rcdev, | |
50 | struct stm32_reset_data, | |
51 | rcdev); | |
52 | int bank = id / BITS_PER_LONG; | |
53 | int offset = id % BITS_PER_LONG; | |
54 | unsigned long flags; | |
55 | u32 reg; | |
56 | ||
57 | spin_lock_irqsave(&data->lock, flags); | |
58 | ||
59 | reg = readl(data->membase + (bank * 4)); | |
60 | writel(reg & ~BIT(offset), data->membase + (bank * 4)); | |
61 | ||
62 | spin_unlock_irqrestore(&data->lock, flags); | |
63 | ||
64 | return 0; | |
65 | } | |
66 | ||
67 | static const struct reset_control_ops stm32_reset_ops = { | |
68 | .assert = stm32_reset_assert, | |
69 | .deassert = stm32_reset_deassert, | |
70 | }; | |
71 | ||
72 | static const struct of_device_id stm32_reset_dt_ids[] = { | |
73 | { .compatible = "st,stm32-rcc", }, | |
74 | { /* sentinel */ }, | |
75 | }; | |
76 | ||
77 | static int stm32_reset_probe(struct platform_device *pdev) | |
78 | { | |
79 | struct stm32_reset_data *data; | |
80 | struct resource *res; | |
81 | ||
82 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | |
83 | if (!data) | |
84 | return -ENOMEM; | |
85 | ||
86 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
87 | data->membase = devm_ioremap_resource(&pdev->dev, res); | |
88 | if (IS_ERR(data->membase)) | |
89 | return PTR_ERR(data->membase); | |
90 | ||
91 | spin_lock_init(&data->lock); | |
92 | ||
93 | data->rcdev.owner = THIS_MODULE; | |
94 | data->rcdev.nr_resets = resource_size(res) * 8; | |
95 | data->rcdev.ops = &stm32_reset_ops; | |
96 | data->rcdev.of_node = pdev->dev.of_node; | |
97 | ||
98 | return devm_reset_controller_register(&pdev->dev, &data->rcdev); | |
99 | } | |
100 | ||
101 | static struct platform_driver stm32_reset_driver = { | |
102 | .probe = stm32_reset_probe, | |
103 | .driver = { | |
104 | .name = "stm32-rcc-reset", | |
105 | .of_match_table = stm32_reset_dt_ids, | |
106 | }, | |
107 | }; | |
108 | builtin_platform_driver(stm32_reset_driver); |