Commit | Line | Data |
---|---|---|
637cee5f GF |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) STMicroelectronics 2022 - All Rights Reserved | |
4 | * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics. | |
5 | */ | |
6 | ||
7 | #include <linux/of.h> | |
8 | #include <linux/platform_device.h> | |
9 | #include <linux/regmap.h> | |
10 | #include <linux/reset-controller.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/spinlock.h> | |
13 | ||
30500c2a | 14 | #include "reset-stm32.h" |
637cee5f GF |
15 | |
16 | struct stm32_reset_data { | |
17 | /* reset lock */ | |
18 | spinlock_t lock; | |
19 | struct reset_controller_dev rcdev; | |
20 | void __iomem *membase; | |
21 | u32 clear_offset; | |
fd7a1c90 | 22 | const struct stm32_reset_cfg **reset_lines; |
637cee5f GF |
23 | }; |
24 | ||
25 | static inline struct stm32_reset_data * | |
26 | to_stm32_reset_data(struct reset_controller_dev *rcdev) | |
27 | { | |
28 | return container_of(rcdev, struct stm32_reset_data, rcdev); | |
29 | } | |
30 | ||
fd7a1c90 GF |
31 | static const struct stm32_reset_cfg *stm32_get_reset_line(struct reset_controller_dev *rcdev, |
32 | unsigned long id, | |
33 | struct stm32_reset_cfg *line) | |
34 | { | |
35 | struct stm32_reset_data *data = to_stm32_reset_data(rcdev); | |
36 | ||
37 | if (!data->reset_lines) { | |
38 | int reg_width = sizeof(u32); | |
39 | int bank = id / (reg_width * BITS_PER_BYTE); | |
40 | int offset = id % (reg_width * BITS_PER_BYTE); | |
41 | ||
42 | line->offset = bank * reg_width; | |
43 | line->bit_idx = offset; | |
44 | line->set_clr = (data->clear_offset ? true : false); | |
45 | ||
46 | return line; | |
47 | } | |
48 | ||
49 | return data->reset_lines[id]; | |
50 | } | |
51 | ||
637cee5f GF |
52 | static int stm32_reset_update(struct reset_controller_dev *rcdev, |
53 | unsigned long id, bool assert) | |
54 | { | |
55 | struct stm32_reset_data *data = to_stm32_reset_data(rcdev); | |
fd7a1c90 GF |
56 | struct stm32_reset_cfg line_reset; |
57 | const struct stm32_reset_cfg *ptr_line; | |
637cee5f | 58 | |
fd7a1c90 GF |
59 | ptr_line = stm32_get_reset_line(rcdev, id, &line_reset); |
60 | if (!ptr_line) | |
61 | return -EPERM; | |
62 | ||
63 | if (ptr_line->set_clr) { | |
637cee5f GF |
64 | void __iomem *addr; |
65 | ||
fd7a1c90 | 66 | addr = data->membase + ptr_line->offset; |
637cee5f GF |
67 | if (!assert) |
68 | addr += data->clear_offset; | |
69 | ||
fd7a1c90 | 70 | writel(BIT(ptr_line->bit_idx), addr); |
637cee5f GF |
71 | |
72 | } else { | |
73 | unsigned long flags; | |
74 | u32 reg; | |
75 | ||
76 | spin_lock_irqsave(&data->lock, flags); | |
77 | ||
fd7a1c90 | 78 | reg = readl(data->membase + ptr_line->offset); |
637cee5f GF |
79 | |
80 | if (assert) | |
fd7a1c90 | 81 | reg |= BIT(ptr_line->bit_idx); |
637cee5f | 82 | else |
fd7a1c90 | 83 | reg &= ~BIT(ptr_line->bit_idx); |
637cee5f | 84 | |
fd7a1c90 | 85 | writel(reg, data->membase + ptr_line->offset); |
637cee5f GF |
86 | |
87 | spin_unlock_irqrestore(&data->lock, flags); | |
88 | } | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | static int stm32_reset_assert(struct reset_controller_dev *rcdev, | |
94 | unsigned long id) | |
95 | { | |
96 | return stm32_reset_update(rcdev, id, true); | |
97 | } | |
98 | ||
99 | static int stm32_reset_deassert(struct reset_controller_dev *rcdev, | |
100 | unsigned long id) | |
101 | { | |
102 | return stm32_reset_update(rcdev, id, false); | |
103 | } | |
104 | ||
105 | static int stm32_reset_status(struct reset_controller_dev *rcdev, | |
106 | unsigned long id) | |
107 | { | |
108 | struct stm32_reset_data *data = to_stm32_reset_data(rcdev); | |
fd7a1c90 GF |
109 | struct stm32_reset_cfg line_reset; |
110 | const struct stm32_reset_cfg *ptr_line; | |
637cee5f GF |
111 | u32 reg; |
112 | ||
fd7a1c90 GF |
113 | ptr_line = stm32_get_reset_line(rcdev, id, &line_reset); |
114 | if (!ptr_line) | |
115 | return -EPERM; | |
116 | ||
117 | reg = readl(data->membase + ptr_line->offset); | |
637cee5f | 118 | |
fd7a1c90 | 119 | return !!(reg & BIT(ptr_line->bit_idx)); |
637cee5f GF |
120 | } |
121 | ||
122 | static const struct reset_control_ops stm32_reset_ops = { | |
123 | .assert = stm32_reset_assert, | |
124 | .deassert = stm32_reset_deassert, | |
125 | .status = stm32_reset_status, | |
126 | }; | |
127 | ||
30500c2a | 128 | int stm32_rcc_reset_init(struct device *dev, struct clk_stm32_reset_data *data, |
637cee5f GF |
129 | void __iomem *base) |
130 | { | |
30500c2a | 131 | struct stm32_reset_data *reset_data; |
637cee5f GF |
132 | |
133 | reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); | |
134 | if (!reset_data) | |
135 | return -ENOMEM; | |
136 | ||
a1ea0857 | 137 | spin_lock_init(&reset_data->lock); |
30500c2a | 138 | |
637cee5f GF |
139 | reset_data->membase = base; |
140 | reset_data->rcdev.owner = THIS_MODULE; | |
141 | reset_data->rcdev.ops = &stm32_reset_ops; | |
142 | reset_data->rcdev.of_node = dev_of_node(dev); | |
30500c2a | 143 | reset_data->rcdev.nr_resets = data->nr_lines; |
fd7a1c90 | 144 | reset_data->reset_lines = data->reset_lines; |
637cee5f GF |
145 | reset_data->clear_offset = data->clear_offset; |
146 | ||
147 | return reset_controller_register(&reset_data->rcdev); | |
148 | } |