summaryrefslogtreecommitdiffstats
path: root/drivers/misc/aspeed-lpc-ctrl.c
diff options
context:
space:
mode:
authorPatrick Venture <venture@google.com>2019-04-22 13:54:19 -0400
committerOlof Johansson <olof@lixom.net>2019-04-29 12:36:34 -0400
commit524feb799408e5d45c6aa82763a9f52489d1e19f (patch)
treef47cc1b266c661f288a07652bcda77de93026d3e /drivers/misc/aspeed-lpc-ctrl.c
parentf99552d9eb78e7d5cfd639c8f7a7457d71683074 (diff)
soc: add aspeed folder and misc drivers
Create a SoC folder for the ASPEED parts and place the misc drivers currently present into this folder. These drivers are not generic part drivers, but rather only apply to the ASPEED SoCs. Signed-off-by: Patrick Venture <venture@google.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers/misc/aspeed-lpc-ctrl.c')
-rw-r--r--drivers/misc/aspeed-lpc-ctrl.c300
1 files changed, 0 insertions, 300 deletions
diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c
deleted file mode 100644
index a024f8042259..000000000000
--- a/drivers/misc/aspeed-lpc-ctrl.c
+++ /dev/null
@@ -1,300 +0,0 @@
1/*
2 * Copyright 2017 IBM Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <linux/clk.h>
11#include <linux/mfd/syscon.h>
12#include <linux/miscdevice.h>
13#include <linux/mm.h>
14#include <linux/module.h>
15#include <linux/of_address.h>
16#include <linux/platform_device.h>
17#include <linux/poll.h>
18#include <linux/regmap.h>
19
20#include <linux/aspeed-lpc-ctrl.h>
21
22#define DEVICE_NAME "aspeed-lpc-ctrl"
23
24#define HICR5 0x0
25#define HICR5_ENL2H BIT(8)
26#define HICR5_ENFWH BIT(10)
27
28#define HICR7 0x8
29#define HICR8 0xc
30
31struct aspeed_lpc_ctrl {
32 struct miscdevice miscdev;
33 struct regmap *regmap;
34 struct clk *clk;
35 phys_addr_t mem_base;
36 resource_size_t mem_size;
37 u32 pnor_size;
38 u32 pnor_base;
39};
40
41static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file)
42{
43 return container_of(file->private_data, struct aspeed_lpc_ctrl,
44 miscdev);
45}
46
47static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma)
48{
49 struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
50 unsigned long vsize = vma->vm_end - vma->vm_start;
51 pgprot_t prot = vma->vm_page_prot;
52
53 if (vma->vm_pgoff + vsize > lpc_ctrl->mem_base + lpc_ctrl->mem_size)
54 return -EINVAL;
55
56 /* ast2400/2500 AHB accesses are not cache coherent */
57 prot = pgprot_noncached(prot);
58
59 if (remap_pfn_range(vma, vma->vm_start,
60 (lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
61 vsize, prot))
62 return -EAGAIN;
63
64 return 0;
65}
66
67static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
68 unsigned long param)
69{
70 struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
71 void __user *p = (void __user *)param;
72 struct aspeed_lpc_ctrl_mapping map;
73 u32 addr;
74 u32 size;
75 long rc;
76
77 if (copy_from_user(&map, p, sizeof(map)))
78 return -EFAULT;
79
80 if (map.flags != 0)
81 return -EINVAL;
82
83 switch (cmd) {
84 case ASPEED_LPC_CTRL_IOCTL_GET_SIZE:
85 /* The flash windows don't report their size */
86 if (map.window_type != ASPEED_LPC_CTRL_WINDOW_MEMORY)
87 return -EINVAL;
88
89 /* Support more than one window id in the future */
90 if (map.window_id != 0)
91 return -EINVAL;
92
93 map.size = lpc_ctrl->mem_size;
94
95 return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0;
96 case ASPEED_LPC_CTRL_IOCTL_MAP:
97
98 /*
99 * The top half of HICR7 is the MSB of the BMC address of the
100 * mapping.
101 * The bottom half of HICR7 is the MSB of the HOST LPC
102 * firmware space address of the mapping.
103 *
104 * The 1 bits in the top of half of HICR8 represent the bits
105 * (in the requested address) that should be ignored and
106 * replaced with those from the top half of HICR7.
107 * The 1 bits in the bottom half of HICR8 represent the bits
108 * (in the requested address) that should be kept and pass
109 * into the BMC address space.
110 */
111
112 /*
113 * It doesn't make sense to talk about a size or offset with
114 * low 16 bits set. Both HICR7 and HICR8 talk about the top 16
115 * bits of addresses and sizes.
116 */
117
118 if ((map.size & 0x0000ffff) || (map.offset & 0x0000ffff))
119 return -EINVAL;
120
121 /*
122 * Because of the way the masks work in HICR8 offset has to
123 * be a multiple of size.
124 */
125 if (map.offset & (map.size - 1))
126 return -EINVAL;
127
128 if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) {
129 addr = lpc_ctrl->pnor_base;
130 size = lpc_ctrl->pnor_size;
131 } else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) {
132 addr = lpc_ctrl->mem_base;
133 size = lpc_ctrl->mem_size;
134 } else {
135 return -EINVAL;
136 }
137
138 /* Check overflow first! */
139 if (map.offset + map.size < map.offset ||
140 map.offset + map.size > size)
141 return -EINVAL;
142
143 if (map.size == 0 || map.size > size)
144 return -EINVAL;
145
146 addr += map.offset;
147
148 /*
149 * addr (host lpc address) is safe regardless of values. This
150 * simply changes the address the host has to request on its
151 * side of the LPC bus. This cannot impact the hosts own
152 * memory space by surprise as LPC specific accessors are
153 * required. The only strange thing that could be done is
154 * setting the lower 16 bits but the shift takes care of that.
155 */
156
157 rc = regmap_write(lpc_ctrl->regmap, HICR7,
158 (addr | (map.addr >> 16)));
159 if (rc)
160 return rc;
161
162 rc = regmap_write(lpc_ctrl->regmap, HICR8,
163 (~(map.size - 1)) | ((map.size >> 16) - 1));
164 if (rc)
165 return rc;
166
167 /*
168 * Enable LPC FHW cycles. This is required for the host to
169 * access the regions specified.
170 */
171 return regmap_update_bits(lpc_ctrl->regmap, HICR5,
172 HICR5_ENFWH | HICR5_ENL2H,
173 HICR5_ENFWH | HICR5_ENL2H);
174 }
175
176 return -EINVAL;
177}
178
179static const struct file_operations aspeed_lpc_ctrl_fops = {
180 .owner = THIS_MODULE,
181 .mmap = aspeed_lpc_ctrl_mmap,
182 .unlocked_ioctl = aspeed_lpc_ctrl_ioctl,
183};
184
185static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
186{
187 struct aspeed_lpc_ctrl *lpc_ctrl;
188 struct device_node *node;
189 struct resource resm;
190 struct device *dev;
191 int rc;
192
193 dev = &pdev->dev;
194
195 lpc_ctrl = devm_kzalloc(dev, sizeof(*lpc_ctrl), GFP_KERNEL);
196 if (!lpc_ctrl)
197 return -ENOMEM;
198
199 node = of_parse_phandle(dev->of_node, "flash", 0);
200 if (!node) {
201 dev_err(dev, "Didn't find host pnor flash node\n");
202 return -ENODEV;
203 }
204
205 rc = of_address_to_resource(node, 1, &resm);
206 of_node_put(node);
207 if (rc) {
208 dev_err(dev, "Couldn't address to resource for flash\n");
209 return rc;
210 }
211
212 lpc_ctrl->pnor_size = resource_size(&resm);
213 lpc_ctrl->pnor_base = resm.start;
214
215 dev_set_drvdata(&pdev->dev, lpc_ctrl);
216
217 node = of_parse_phandle(dev->of_node, "memory-region", 0);
218 if (!node) {
219 dev_err(dev, "Didn't find reserved memory\n");
220 return -EINVAL;
221 }
222
223 rc = of_address_to_resource(node, 0, &resm);
224 of_node_put(node);
225 if (rc) {
226 dev_err(dev, "Couldn't address to resource for reserved memory\n");
227 return -ENOMEM;
228 }
229
230 lpc_ctrl->mem_size = resource_size(&resm);
231 lpc_ctrl->mem_base = resm.start;
232
233 lpc_ctrl->regmap = syscon_node_to_regmap(
234 pdev->dev.parent->of_node);
235 if (IS_ERR(lpc_ctrl->regmap)) {
236 dev_err(dev, "Couldn't get regmap\n");
237 return -ENODEV;
238 }
239
240 lpc_ctrl->clk = devm_clk_get(dev, NULL);
241 if (IS_ERR(lpc_ctrl->clk)) {
242 dev_err(dev, "couldn't get clock\n");
243 return PTR_ERR(lpc_ctrl->clk);
244 }
245 rc = clk_prepare_enable(lpc_ctrl->clk);
246 if (rc) {
247 dev_err(dev, "couldn't enable clock\n");
248 return rc;
249 }
250
251 lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
252 lpc_ctrl->miscdev.name = DEVICE_NAME;
253 lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
254 lpc_ctrl->miscdev.parent = dev;
255 rc = misc_register(&lpc_ctrl->miscdev);
256 if (rc) {
257 dev_err(dev, "Unable to register device\n");
258 goto err;
259 }
260
261 dev_info(dev, "Loaded at %pr\n", &resm);
262
263 return 0;
264
265err:
266 clk_disable_unprepare(lpc_ctrl->clk);
267 return rc;
268}
269
270static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
271{
272 struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
273
274 misc_deregister(&lpc_ctrl->miscdev);
275 clk_disable_unprepare(lpc_ctrl->clk);
276
277 return 0;
278}
279
280static const struct of_device_id aspeed_lpc_ctrl_match[] = {
281 { .compatible = "aspeed,ast2400-lpc-ctrl" },
282 { .compatible = "aspeed,ast2500-lpc-ctrl" },
283 { },
284};
285
286static struct platform_driver aspeed_lpc_ctrl_driver = {
287 .driver = {
288 .name = DEVICE_NAME,
289 .of_match_table = aspeed_lpc_ctrl_match,
290 },
291 .probe = aspeed_lpc_ctrl_probe,
292 .remove = aspeed_lpc_ctrl_remove,
293};
294
295module_platform_driver(aspeed_lpc_ctrl_driver);
296
297MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match);
298MODULE_LICENSE("GPL");
299MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
300MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings");