1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. |
4 | * |
5 | * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and |
6 | * Mingkai Hu from Freescale Semiconductor, Inc. |
7 | */ |
8 | |
9 | #include <linux/list.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/of_irq.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/errno.h> |
15 | #include <linux/err.h> |
16 | #include <linux/export.h> |
17 | #include <linux/slab.h> |
18 | #include <asm/hw_irq.h> |
19 | #include <asm/ppc-pci.h> |
20 | #include <asm/mpic_msgr.h> |
21 | |
22 | #define MPIC_MSGR_REGISTERS_PER_BLOCK 4 |
23 | #define MPIC_MSGR_STRIDE 0x10 |
24 | #define MPIC_MSGR_MER_OFFSET (0x100 / sizeof(u32)) |
25 | #define MSGR_INUSE 0 |
26 | #define MSGR_FREE 1 |
27 | |
28 | static struct mpic_msgr **mpic_msgrs; |
29 | static unsigned int mpic_msgr_count; |
30 | static DEFINE_RAW_SPINLOCK(msgrs_lock); |
31 | |
32 | static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) |
33 | { |
34 | out_be32(msgr->mer, value); |
35 | } |
36 | |
37 | static inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr) |
38 | { |
39 | return in_be32(msgr->mer); |
40 | } |
41 | |
42 | static inline void _mpic_msgr_disable(struct mpic_msgr *msgr) |
43 | { |
44 | u32 mer = _mpic_msgr_mer_read(msgr); |
45 | |
46 | _mpic_msgr_mer_write(msgr, value: mer & ~(1 << msgr->num)); |
47 | } |
48 | |
49 | struct mpic_msgr *mpic_msgr_get(unsigned int reg_num) |
50 | { |
51 | unsigned long flags; |
52 | struct mpic_msgr *msgr; |
53 | |
54 | /* Assume busy until proven otherwise. */ |
55 | msgr = ERR_PTR(error: -EBUSY); |
56 | |
57 | if (reg_num >= mpic_msgr_count) |
58 | return ERR_PTR(error: -ENODEV); |
59 | |
60 | raw_spin_lock_irqsave(&msgrs_lock, flags); |
61 | msgr = mpic_msgrs[reg_num]; |
62 | if (msgr->in_use == MSGR_FREE) |
63 | msgr->in_use = MSGR_INUSE; |
64 | raw_spin_unlock_irqrestore(&msgrs_lock, flags); |
65 | |
66 | return msgr; |
67 | } |
68 | EXPORT_SYMBOL_GPL(mpic_msgr_get); |
69 | |
70 | void mpic_msgr_put(struct mpic_msgr *msgr) |
71 | { |
72 | unsigned long flags; |
73 | |
74 | raw_spin_lock_irqsave(&msgr->lock, flags); |
75 | msgr->in_use = MSGR_FREE; |
76 | _mpic_msgr_disable(msgr); |
77 | raw_spin_unlock_irqrestore(&msgr->lock, flags); |
78 | } |
79 | EXPORT_SYMBOL_GPL(mpic_msgr_put); |
80 | |
81 | void mpic_msgr_enable(struct mpic_msgr *msgr) |
82 | { |
83 | unsigned long flags; |
84 | u32 mer; |
85 | |
86 | raw_spin_lock_irqsave(&msgr->lock, flags); |
87 | mer = _mpic_msgr_mer_read(msgr); |
88 | _mpic_msgr_mer_write(msgr, value: mer | (1 << msgr->num)); |
89 | raw_spin_unlock_irqrestore(&msgr->lock, flags); |
90 | } |
91 | EXPORT_SYMBOL_GPL(mpic_msgr_enable); |
92 | |
93 | void mpic_msgr_disable(struct mpic_msgr *msgr) |
94 | { |
95 | unsigned long flags; |
96 | |
97 | raw_spin_lock_irqsave(&msgr->lock, flags); |
98 | _mpic_msgr_disable(msgr); |
99 | raw_spin_unlock_irqrestore(&msgr->lock, flags); |
100 | } |
101 | EXPORT_SYMBOL_GPL(mpic_msgr_disable); |
102 | |
103 | /* The following three functions are used to compute the order and number of |
104 | * the message register blocks. They are clearly very inefficient. However, |
105 | * they are called *only* a few times during device initialization. |
106 | */ |
107 | static unsigned int mpic_msgr_number_of_blocks(void) |
108 | { |
109 | unsigned int count; |
110 | struct device_node *aliases; |
111 | |
112 | count = 0; |
113 | aliases = of_find_node_by_name(NULL, name: "aliases" ); |
114 | |
115 | if (aliases) { |
116 | char buf[32]; |
117 | |
118 | for (;;) { |
119 | snprintf(buf, size: sizeof(buf), fmt: "mpic-msgr-block%d" , count); |
120 | if (!of_property_present(np: aliases, propname: buf)) |
121 | break; |
122 | |
123 | count += 1; |
124 | } |
125 | of_node_put(node: aliases); |
126 | } |
127 | |
128 | return count; |
129 | } |
130 | |
131 | static unsigned int mpic_msgr_number_of_registers(void) |
132 | { |
133 | return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK; |
134 | } |
135 | |
136 | static int mpic_msgr_block_number(struct device_node *node) |
137 | { |
138 | struct device_node *aliases; |
139 | unsigned int index, number_of_blocks; |
140 | char buf[64]; |
141 | |
142 | number_of_blocks = mpic_msgr_number_of_blocks(); |
143 | aliases = of_find_node_by_name(NULL, name: "aliases" ); |
144 | if (!aliases) |
145 | return -1; |
146 | |
147 | for (index = 0; index < number_of_blocks; ++index) { |
148 | struct property *prop; |
149 | struct device_node *tn; |
150 | |
151 | snprintf(buf, size: sizeof(buf), fmt: "mpic-msgr-block%d" , index); |
152 | prop = of_find_property(np: aliases, name: buf, NULL); |
153 | tn = of_find_node_by_path(path: prop->value); |
154 | if (node == tn) { |
155 | of_node_put(node: tn); |
156 | break; |
157 | } |
158 | of_node_put(node: tn); |
159 | } |
160 | of_node_put(node: aliases); |
161 | |
162 | return index == number_of_blocks ? -1 : index; |
163 | } |
164 | |
165 | /* The probe function for a single message register block. |
166 | */ |
167 | static int mpic_msgr_probe(struct platform_device *dev) |
168 | { |
169 | void __iomem *msgr_block_addr; |
170 | int block_number; |
171 | struct resource rsrc; |
172 | unsigned int i; |
173 | unsigned int irq_index; |
174 | struct device_node *np = dev->dev.of_node; |
175 | unsigned int receive_mask; |
176 | const unsigned int *prop; |
177 | |
178 | if (!np) { |
179 | dev_err(&dev->dev, "Device OF-Node is NULL" ); |
180 | return -EFAULT; |
181 | } |
182 | |
183 | /* Allocate the message register array upon the first device |
184 | * registered. |
185 | */ |
186 | if (!mpic_msgrs) { |
187 | mpic_msgr_count = mpic_msgr_number_of_registers(); |
188 | dev_info(&dev->dev, "Found %d message registers\n" , |
189 | mpic_msgr_count); |
190 | |
191 | mpic_msgrs = kcalloc(n: mpic_msgr_count, size: sizeof(*mpic_msgrs), |
192 | GFP_KERNEL); |
193 | if (!mpic_msgrs) { |
194 | dev_err(&dev->dev, |
195 | "No memory for message register blocks\n" ); |
196 | return -ENOMEM; |
197 | } |
198 | } |
199 | dev_info(&dev->dev, "Of-device full name %pOF\n" , np); |
200 | |
201 | /* IO map the message register block. */ |
202 | of_address_to_resource(dev: np, index: 0, r: &rsrc); |
203 | msgr_block_addr = devm_ioremap(dev: &dev->dev, offset: rsrc.start, size: resource_size(res: &rsrc)); |
204 | if (!msgr_block_addr) { |
205 | dev_err(&dev->dev, "Failed to iomap MPIC message registers" ); |
206 | return -EFAULT; |
207 | } |
208 | |
209 | /* Ensure the block has a defined order. */ |
210 | block_number = mpic_msgr_block_number(node: np); |
211 | if (block_number < 0) { |
212 | dev_err(&dev->dev, |
213 | "Failed to find message register block alias\n" ); |
214 | return -ENODEV; |
215 | } |
216 | dev_info(&dev->dev, "Setting up message register block %d\n" , |
217 | block_number); |
218 | |
219 | /* Grab the receive mask which specifies what registers can receive |
220 | * interrupts. |
221 | */ |
222 | prop = of_get_property(node: np, name: "mpic-msgr-receive-mask" , NULL); |
223 | receive_mask = (prop) ? *prop : 0xF; |
224 | |
225 | /* Build up the appropriate message register data structures. */ |
226 | for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) { |
227 | struct mpic_msgr *msgr; |
228 | unsigned int reg_number; |
229 | |
230 | msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL); |
231 | if (!msgr) { |
232 | dev_err(&dev->dev, "No memory for message register\n" ); |
233 | return -ENOMEM; |
234 | } |
235 | |
236 | reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; |
237 | msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; |
238 | msgr->mer = msgr->base + MPIC_MSGR_MER_OFFSET; |
239 | msgr->in_use = MSGR_FREE; |
240 | msgr->num = i; |
241 | raw_spin_lock_init(&msgr->lock); |
242 | |
243 | if (receive_mask & (1 << i)) { |
244 | msgr->irq = irq_of_parse_and_map(node: np, index: irq_index); |
245 | if (!msgr->irq) { |
246 | dev_err(&dev->dev, |
247 | "Missing interrupt specifier" ); |
248 | kfree(objp: msgr); |
249 | return -EFAULT; |
250 | } |
251 | irq_index += 1; |
252 | } else { |
253 | msgr->irq = 0; |
254 | } |
255 | |
256 | mpic_msgrs[reg_number] = msgr; |
257 | mpic_msgr_disable(msgr); |
258 | dev_info(&dev->dev, "Register %d initialized: irq %d\n" , |
259 | reg_number, msgr->irq); |
260 | |
261 | } |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | static const struct of_device_id mpic_msgr_ids[] = { |
267 | { |
268 | .compatible = "fsl,mpic-v3.1-msgr" , |
269 | .data = NULL, |
270 | }, |
271 | {} |
272 | }; |
273 | |
274 | static struct platform_driver mpic_msgr_driver = { |
275 | .driver = { |
276 | .name = "mpic-msgr" , |
277 | .of_match_table = mpic_msgr_ids, |
278 | }, |
279 | .probe = mpic_msgr_probe, |
280 | }; |
281 | |
282 | static __init int mpic_msgr_init(void) |
283 | { |
284 | return platform_driver_register(&mpic_msgr_driver); |
285 | } |
286 | subsys_initcall(mpic_msgr_init); |
287 | |