1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright 2019 Google Inc |
4 | * |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License |
7 | * as published by the Free Software Foundation; either version |
8 | * 2 of the License, or (at your option) any later version. |
9 | * |
10 | * Provides a simple driver to control the ASPEED P2A interface which allows |
11 | * the host to read and write to various regions of the BMC's memory. |
12 | */ |
13 | |
14 | #include <linux/fs.h> |
15 | #include <linux/io.h> |
16 | #include <linux/mfd/syscon.h> |
17 | #include <linux/miscdevice.h> |
18 | #include <linux/mm.h> |
19 | #include <linux/module.h> |
20 | #include <linux/mutex.h> |
21 | #include <linux/of.h> |
22 | #include <linux/of_address.h> |
23 | #include <linux/platform_device.h> |
24 | #include <linux/regmap.h> |
25 | #include <linux/slab.h> |
26 | #include <linux/uaccess.h> |
27 | |
28 | #include <linux/aspeed-p2a-ctrl.h> |
29 | |
30 | #define DEVICE_NAME "aspeed-p2a-ctrl" |
31 | |
32 | /* SCU2C is a Misc. Control Register. */ |
33 | #define SCU2C 0x2c |
34 | /* SCU180 is the PCIe Configuration Setting Control Register. */ |
35 | #define SCU180 0x180 |
36 | /* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA device |
37 | * on the PCI bus. |
38 | */ |
39 | #define SCU180_ENP2A BIT(1) |
40 | |
41 | /* The ast2400/2500 both have six ranges. */ |
42 | #define P2A_REGION_COUNT 6 |
43 | |
44 | struct region { |
45 | u64 min; |
46 | u64 max; |
47 | u32 bit; |
48 | }; |
49 | |
50 | struct aspeed_p2a_model_data { |
51 | /* min, max, bit */ |
52 | struct region regions[P2A_REGION_COUNT]; |
53 | }; |
54 | |
55 | struct aspeed_p2a_ctrl { |
56 | struct miscdevice miscdev; |
57 | struct regmap *regmap; |
58 | |
59 | const struct aspeed_p2a_model_data *config; |
60 | |
61 | /* Access to these needs to be locked, held via probe, mapping ioctl, |
62 | * and release, remove. |
63 | */ |
64 | struct mutex tracking; |
65 | u32 readers; |
66 | u32 readerwriters[P2A_REGION_COUNT]; |
67 | |
68 | phys_addr_t mem_base; |
69 | resource_size_t mem_size; |
70 | }; |
71 | |
72 | struct aspeed_p2a_user { |
73 | struct file *file; |
74 | struct aspeed_p2a_ctrl *parent; |
75 | |
76 | /* The entire memory space is opened for reading once the bridge is |
77 | * enabled, therefore this needs only to be tracked once per user. |
78 | * If any user has it open for read, the bridge must stay enabled. |
79 | */ |
80 | u32 read; |
81 | |
82 | /* Each entry of the array corresponds to a P2A Region. If the user |
83 | * opens for read or readwrite, the reference goes up here. On |
84 | * release, this array is walked and references adjusted accordingly. |
85 | */ |
86 | u32 readwrite[P2A_REGION_COUNT]; |
87 | }; |
88 | |
89 | static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl) |
90 | { |
91 | regmap_update_bits(map: p2a_ctrl->regmap, |
92 | SCU180, SCU180_ENP2A, SCU180_ENP2A); |
93 | } |
94 | |
95 | static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl) |
96 | { |
97 | regmap_update_bits(map: p2a_ctrl->regmap, SCU180, SCU180_ENP2A, val: 0); |
98 | } |
99 | |
100 | static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct *vma) |
101 | { |
102 | unsigned long vsize; |
103 | pgprot_t prot; |
104 | struct aspeed_p2a_user *priv = file->private_data; |
105 | struct aspeed_p2a_ctrl *ctrl = priv->parent; |
106 | |
107 | if (ctrl->mem_base == 0 && ctrl->mem_size == 0) |
108 | return -EINVAL; |
109 | |
110 | vsize = vma->vm_end - vma->vm_start; |
111 | prot = vma->vm_page_prot; |
112 | |
113 | if (vma->vm_pgoff + vma_pages(vma) > ctrl->mem_size >> PAGE_SHIFT) |
114 | return -EINVAL; |
115 | |
116 | /* ast2400/2500 AHB accesses are not cache coherent */ |
117 | prot = pgprot_noncached(prot); |
118 | |
119 | if (remap_pfn_range(vma, addr: vma->vm_start, |
120 | pfn: (ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff, |
121 | size: vsize, prot)) |
122 | return -EAGAIN; |
123 | |
124 | return 0; |
125 | } |
126 | |
127 | static bool aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv, |
128 | struct aspeed_p2a_ctrl *ctrl, |
129 | struct aspeed_p2a_ctrl_mapping *map) |
130 | { |
131 | int i; |
132 | u64 base, end; |
133 | bool matched = false; |
134 | |
135 | base = map->addr; |
136 | end = map->addr + (map->length - 1); |
137 | |
138 | /* If the value is a legal u32, it will find a match. */ |
139 | for (i = 0; i < P2A_REGION_COUNT; i++) { |
140 | const struct region *curr = &ctrl->config->regions[i]; |
141 | |
142 | /* If the top of this region is lower than your base, skip it. |
143 | */ |
144 | if (curr->max < base) |
145 | continue; |
146 | |
147 | /* If the bottom of this region is higher than your end, bail. |
148 | */ |
149 | if (curr->min > end) |
150 | break; |
151 | |
152 | /* Lock this and update it, therefore it someone else is |
153 | * closing their file out, this'll preserve the increment. |
154 | */ |
155 | mutex_lock(&ctrl->tracking); |
156 | ctrl->readerwriters[i] += 1; |
157 | mutex_unlock(lock: &ctrl->tracking); |
158 | |
159 | /* Track with the user, so when they close their file, we can |
160 | * decrement properly. |
161 | */ |
162 | priv->readwrite[i] += 1; |
163 | |
164 | /* Enable the region as read-write. */ |
165 | regmap_update_bits(map: ctrl->regmap, SCU2C, mask: curr->bit, val: 0); |
166 | matched = true; |
167 | } |
168 | |
169 | return matched; |
170 | } |
171 | |
172 | static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd, |
173 | unsigned long data) |
174 | { |
175 | struct aspeed_p2a_user *priv = file->private_data; |
176 | struct aspeed_p2a_ctrl *ctrl = priv->parent; |
177 | void __user *arg = (void __user *)data; |
178 | struct aspeed_p2a_ctrl_mapping map; |
179 | |
180 | if (copy_from_user(to: &map, from: arg, n: sizeof(map))) |
181 | return -EFAULT; |
182 | |
183 | switch (cmd) { |
184 | case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW: |
185 | /* If they want a region to be read-only, since the entire |
186 | * region is read-only once enabled, we just need to track this |
187 | * user wants to read from the bridge, and if it's not enabled. |
188 | * Enable it. |
189 | */ |
190 | if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) { |
191 | mutex_lock(&ctrl->tracking); |
192 | ctrl->readers += 1; |
193 | mutex_unlock(lock: &ctrl->tracking); |
194 | |
195 | /* Track with the user, so when they close their file, |
196 | * we can decrement properly. |
197 | */ |
198 | priv->read += 1; |
199 | } else if (map.flags == ASPEED_P2A_CTRL_READWRITE) { |
200 | /* If we don't acquire any region return error. */ |
201 | if (!aspeed_p2a_region_acquire(priv, ctrl, map: &map)) { |
202 | return -EINVAL; |
203 | } |
204 | } else { |
205 | /* Invalid map flags. */ |
206 | return -EINVAL; |
207 | } |
208 | |
209 | aspeed_p2a_enable_bridge(p2a_ctrl: ctrl); |
210 | return 0; |
211 | case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG: |
212 | /* This is a request for the memory-region and corresponding |
213 | * length that is used by the driver for mmap. |
214 | */ |
215 | |
216 | map.flags = 0; |
217 | map.addr = ctrl->mem_base; |
218 | map.length = ctrl->mem_size; |
219 | |
220 | return copy_to_user(to: arg, from: &map, n: sizeof(map)) ? -EFAULT : 0; |
221 | } |
222 | |
223 | return -EINVAL; |
224 | } |
225 | |
226 | |
227 | /* |
228 | * When a user opens this file, we create a structure to track their mappings. |
229 | * |
230 | * A user can map a region as read-only (bridge enabled), or read-write (bit |
231 | * flipped, and bridge enabled). Either way, this tracking is used, s.t. when |
232 | * they release the device references are handled. |
233 | * |
234 | * The bridge is not enabled until a user calls an ioctl to map a region, |
235 | * simply opening the device does not enable it. |
236 | */ |
237 | static int aspeed_p2a_open(struct inode *inode, struct file *file) |
238 | { |
239 | struct aspeed_p2a_user *priv; |
240 | |
241 | priv = kmalloc(size: sizeof(*priv), GFP_KERNEL); |
242 | if (!priv) |
243 | return -ENOMEM; |
244 | |
245 | priv->file = file; |
246 | priv->read = 0; |
247 | memset(priv->readwrite, 0, sizeof(priv->readwrite)); |
248 | |
249 | /* The file's private_data is initialized to the p2a_ctrl. */ |
250 | priv->parent = file->private_data; |
251 | |
252 | /* Set the file's private_data to the user's data. */ |
253 | file->private_data = priv; |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | /* |
259 | * This will close the users mappings. It will go through what they had opened |
260 | * for readwrite, and decrement those counts. If at the end, this is the last |
261 | * user, it'll close the bridge. |
262 | */ |
263 | static int aspeed_p2a_release(struct inode *inode, struct file *file) |
264 | { |
265 | int i; |
266 | u32 bits = 0; |
267 | bool open_regions = false; |
268 | struct aspeed_p2a_user *priv = file->private_data; |
269 | |
270 | /* Lock others from changing these values until everything is updated |
271 | * in one pass. |
272 | */ |
273 | mutex_lock(&priv->parent->tracking); |
274 | |
275 | priv->parent->readers -= priv->read; |
276 | |
277 | for (i = 0; i < P2A_REGION_COUNT; i++) { |
278 | priv->parent->readerwriters[i] -= priv->readwrite[i]; |
279 | |
280 | if (priv->parent->readerwriters[i] > 0) |
281 | open_regions = true; |
282 | else |
283 | bits |= priv->parent->config->regions[i].bit; |
284 | } |
285 | |
286 | /* Setting a bit to 1 disables the region, so let's just OR with the |
287 | * above to disable any. |
288 | */ |
289 | |
290 | /* Note, if another user is trying to ioctl, they can't grab tracking, |
291 | * and therefore can't grab either register mutex. |
292 | * If another user is trying to close, they can't grab tracking either. |
293 | */ |
294 | regmap_update_bits(map: priv->parent->regmap, SCU2C, mask: bits, val: bits); |
295 | |
296 | /* If parent->readers is zero and open windows is 0, disable the |
297 | * bridge. |
298 | */ |
299 | if (!open_regions && priv->parent->readers == 0) |
300 | aspeed_p2a_disable_bridge(p2a_ctrl: priv->parent); |
301 | |
302 | mutex_unlock(lock: &priv->parent->tracking); |
303 | |
304 | kfree(objp: priv); |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | static const struct file_operations aspeed_p2a_ctrl_fops = { |
310 | .owner = THIS_MODULE, |
311 | .mmap = aspeed_p2a_mmap, |
312 | .unlocked_ioctl = aspeed_p2a_ioctl, |
313 | .open = aspeed_p2a_open, |
314 | .release = aspeed_p2a_release, |
315 | }; |
316 | |
317 | /* The regions are controlled by SCU2C */ |
318 | static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl) |
319 | { |
320 | int i; |
321 | u32 value = 0; |
322 | |
323 | for (i = 0; i < P2A_REGION_COUNT; i++) |
324 | value |= p2a_ctrl->config->regions[i].bit; |
325 | |
326 | regmap_update_bits(map: p2a_ctrl->regmap, SCU2C, mask: value, val: value); |
327 | |
328 | /* Disable the bridge. */ |
329 | aspeed_p2a_disable_bridge(p2a_ctrl); |
330 | } |
331 | |
332 | static int aspeed_p2a_ctrl_probe(struct platform_device *pdev) |
333 | { |
334 | struct aspeed_p2a_ctrl *misc_ctrl; |
335 | struct device *dev; |
336 | struct resource resm; |
337 | struct device_node *node; |
338 | int rc = 0; |
339 | |
340 | dev = &pdev->dev; |
341 | |
342 | misc_ctrl = devm_kzalloc(dev, size: sizeof(*misc_ctrl), GFP_KERNEL); |
343 | if (!misc_ctrl) |
344 | return -ENOMEM; |
345 | |
346 | mutex_init(&misc_ctrl->tracking); |
347 | |
348 | /* optional. */ |
349 | node = of_parse_phandle(np: dev->of_node, phandle_name: "memory-region" , index: 0); |
350 | if (node) { |
351 | rc = of_address_to_resource(dev: node, index: 0, r: &resm); |
352 | of_node_put(node); |
353 | if (rc) { |
354 | dev_err(dev, "Couldn't address to resource for reserved memory\n" ); |
355 | return -ENODEV; |
356 | } |
357 | |
358 | misc_ctrl->mem_size = resource_size(res: &resm); |
359 | misc_ctrl->mem_base = resm.start; |
360 | } |
361 | |
362 | misc_ctrl->regmap = syscon_node_to_regmap(np: pdev->dev.parent->of_node); |
363 | if (IS_ERR(ptr: misc_ctrl->regmap)) { |
364 | dev_err(dev, "Couldn't get regmap\n" ); |
365 | return -ENODEV; |
366 | } |
367 | |
368 | misc_ctrl->config = of_device_get_match_data(dev); |
369 | |
370 | dev_set_drvdata(dev: &pdev->dev, data: misc_ctrl); |
371 | |
372 | aspeed_p2a_disable_all(p2a_ctrl: misc_ctrl); |
373 | |
374 | misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; |
375 | misc_ctrl->miscdev.name = DEVICE_NAME; |
376 | misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops; |
377 | misc_ctrl->miscdev.parent = dev; |
378 | |
379 | rc = misc_register(misc: &misc_ctrl->miscdev); |
380 | if (rc) |
381 | dev_err(dev, "Unable to register device\n" ); |
382 | |
383 | return rc; |
384 | } |
385 | |
386 | static void aspeed_p2a_ctrl_remove(struct platform_device *pdev) |
387 | { |
388 | struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(dev: &pdev->dev); |
389 | |
390 | misc_deregister(misc: &p2a_ctrl->miscdev); |
391 | } |
392 | |
393 | #define SCU2C_DRAM BIT(25) |
394 | #define SCU2C_SPI BIT(24) |
395 | #define SCU2C_SOC BIT(23) |
396 | #define SCU2C_FLASH BIT(22) |
397 | |
398 | static const struct aspeed_p2a_model_data ast2400_model_data = { |
399 | .regions = { |
400 | {0x00000000, 0x17FFFFFF, SCU2C_FLASH}, |
401 | {0x18000000, 0x1FFFFFFF, SCU2C_SOC}, |
402 | {0x20000000, 0x2FFFFFFF, SCU2C_FLASH}, |
403 | {0x30000000, 0x3FFFFFFF, SCU2C_SPI}, |
404 | {0x40000000, 0x5FFFFFFF, SCU2C_DRAM}, |
405 | {0x60000000, 0xFFFFFFFF, SCU2C_SOC}, |
406 | } |
407 | }; |
408 | |
409 | static const struct aspeed_p2a_model_data ast2500_model_data = { |
410 | .regions = { |
411 | {0x00000000, 0x0FFFFFFF, SCU2C_FLASH}, |
412 | {0x10000000, 0x1FFFFFFF, SCU2C_SOC}, |
413 | {0x20000000, 0x3FFFFFFF, SCU2C_FLASH}, |
414 | {0x40000000, 0x5FFFFFFF, SCU2C_SOC}, |
415 | {0x60000000, 0x7FFFFFFF, SCU2C_SPI}, |
416 | {0x80000000, 0xFFFFFFFF, SCU2C_DRAM}, |
417 | } |
418 | }; |
419 | |
420 | static const struct of_device_id aspeed_p2a_ctrl_match[] = { |
421 | { .compatible = "aspeed,ast2400-p2a-ctrl" , |
422 | .data = &ast2400_model_data }, |
423 | { .compatible = "aspeed,ast2500-p2a-ctrl" , |
424 | .data = &ast2500_model_data }, |
425 | { }, |
426 | }; |
427 | |
428 | static struct platform_driver aspeed_p2a_ctrl_driver = { |
429 | .driver = { |
430 | .name = DEVICE_NAME, |
431 | .of_match_table = aspeed_p2a_ctrl_match, |
432 | }, |
433 | .probe = aspeed_p2a_ctrl_probe, |
434 | .remove_new = aspeed_p2a_ctrl_remove, |
435 | }; |
436 | |
437 | module_platform_driver(aspeed_p2a_ctrl_driver); |
438 | |
439 | MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match); |
440 | MODULE_LICENSE("GPL" ); |
441 | MODULE_AUTHOR("Patrick Venture <venture@google.com>" ); |
442 | MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC mappings" ); |
443 | |