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 | |
14 | #include "reset-stm32.h" |
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; |
22 | }; |
23 | |
24 | static inline struct stm32_reset_data * |
25 | to_stm32_reset_data(struct reset_controller_dev *rcdev) |
26 | { |
27 | return container_of(rcdev, struct stm32_reset_data, rcdev); |
28 | } |
29 | |
30 | static int stm32_reset_update(struct reset_controller_dev *rcdev, |
31 | unsigned long id, bool assert) |
32 | { |
33 | struct stm32_reset_data *data = to_stm32_reset_data(rcdev); |
34 | int reg_width = sizeof(u32); |
35 | int bank = id / (reg_width * BITS_PER_BYTE); |
36 | int offset = id % (reg_width * BITS_PER_BYTE); |
37 | |
38 | if (data->clear_offset) { |
39 | void __iomem *addr; |
40 | |
41 | addr = data->membase + (bank * reg_width); |
42 | if (!assert) |
43 | addr += data->clear_offset; |
44 | |
45 | writel(BIT(offset), addr); |
46 | |
47 | } else { |
48 | unsigned long flags; |
49 | u32 reg; |
50 | |
51 | spin_lock_irqsave(&data->lock, flags); |
52 | |
53 | reg = readl(addr: data->membase + (bank * reg_width)); |
54 | |
55 | if (assert) |
56 | reg |= BIT(offset); |
57 | else |
58 | reg &= ~BIT(offset); |
59 | |
60 | writel(val: reg, addr: data->membase + (bank * reg_width)); |
61 | |
62 | spin_unlock_irqrestore(lock: &data->lock, flags); |
63 | } |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static int stm32_reset_assert(struct reset_controller_dev *rcdev, |
69 | unsigned long id) |
70 | { |
71 | return stm32_reset_update(rcdev, id, assert: true); |
72 | } |
73 | |
74 | static int stm32_reset_deassert(struct reset_controller_dev *rcdev, |
75 | unsigned long id) |
76 | { |
77 | return stm32_reset_update(rcdev, id, assert: false); |
78 | } |
79 | |
80 | static int stm32_reset_status(struct reset_controller_dev *rcdev, |
81 | unsigned long id) |
82 | { |
83 | struct stm32_reset_data *data = to_stm32_reset_data(rcdev); |
84 | int reg_width = sizeof(u32); |
85 | int bank = id / (reg_width * BITS_PER_BYTE); |
86 | int offset = id % (reg_width * BITS_PER_BYTE); |
87 | u32 reg; |
88 | |
89 | reg = readl(addr: data->membase + (bank * reg_width)); |
90 | |
91 | return !!(reg & BIT(offset)); |
92 | } |
93 | |
94 | static const struct reset_control_ops stm32_reset_ops = { |
95 | .assert = stm32_reset_assert, |
96 | .deassert = stm32_reset_deassert, |
97 | .status = stm32_reset_status, |
98 | }; |
99 | |
100 | int stm32_rcc_reset_init(struct device *dev, struct clk_stm32_reset_data *data, |
101 | void __iomem *base) |
102 | { |
103 | struct stm32_reset_data *reset_data; |
104 | |
105 | reset_data = kzalloc(size: sizeof(*reset_data), GFP_KERNEL); |
106 | if (!reset_data) |
107 | return -ENOMEM; |
108 | |
109 | spin_lock_init(&reset_data->lock); |
110 | |
111 | reset_data->membase = base; |
112 | reset_data->rcdev.owner = THIS_MODULE; |
113 | reset_data->rcdev.ops = &stm32_reset_ops; |
114 | reset_data->rcdev.of_node = dev_of_node(dev); |
115 | reset_data->rcdev.nr_resets = data->nr_lines; |
116 | reset_data->clear_offset = data->clear_offset; |
117 | |
118 | return reset_controller_register(rcdev: &reset_data->rcdev); |
119 | } |
120 | |