2 * Copyright 2017 IBM Corporation
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.
10 #include <linux/mfd/syscon.h>
11 #include <linux/miscdevice.h>
13 #include <linux/module.h>
14 #include <linux/of_address.h>
15 #include <linux/platform_device.h>
16 #include <linux/poll.h>
17 #include <linux/regmap.h>
19 #include <linux/aspeed-lpc-ctrl.h>
21 #define DEVICE_NAME "aspeed-lpc-ctrl"
26 struct aspeed_lpc_ctrl
{
27 struct miscdevice miscdev
;
28 struct regmap
*regmap
;
30 resource_size_t mem_size
;
35 static struct aspeed_lpc_ctrl
*file_aspeed_lpc_ctrl(struct file
*file
)
37 return container_of(file
->private_data
, struct aspeed_lpc_ctrl
,
41 static int aspeed_lpc_ctrl_mmap(struct file
*file
, struct vm_area_struct
*vma
)
43 struct aspeed_lpc_ctrl
*lpc_ctrl
= file_aspeed_lpc_ctrl(file
);
44 unsigned long vsize
= vma
->vm_end
- vma
->vm_start
;
45 pgprot_t prot
= vma
->vm_page_prot
;
47 if (vma
->vm_pgoff
+ vsize
> lpc_ctrl
->mem_base
+ lpc_ctrl
->mem_size
)
50 /* ast2400/2500 AHB accesses are not cache coherent */
51 prot
= pgprot_noncached(prot
);
53 if (remap_pfn_range(vma
, vma
->vm_start
,
54 (lpc_ctrl
->mem_base
>> PAGE_SHIFT
) + vma
->vm_pgoff
,
61 static long aspeed_lpc_ctrl_ioctl(struct file
*file
, unsigned int cmd
,
64 struct aspeed_lpc_ctrl
*lpc_ctrl
= file_aspeed_lpc_ctrl(file
);
65 void __user
*p
= (void __user
*)param
;
66 struct aspeed_lpc_ctrl_mapping map
;
71 if (copy_from_user(&map
, p
, sizeof(map
)))
78 case ASPEED_LPC_CTRL_IOCTL_GET_SIZE
:
79 /* The flash windows don't report their size */
80 if (map
.window_type
!= ASPEED_LPC_CTRL_WINDOW_MEMORY
)
83 /* Support more than one window id in the future */
84 if (map
.window_id
!= 0)
87 map
.size
= lpc_ctrl
->mem_size
;
89 return copy_to_user(p
, &map
, sizeof(map
)) ? -EFAULT
: 0;
90 case ASPEED_LPC_CTRL_IOCTL_MAP
:
93 * The top half of HICR7 is the MSB of the BMC address of the
95 * The bottom half of HICR7 is the MSB of the HOST LPC
96 * firmware space address of the mapping.
98 * The 1 bits in the top of half of HICR8 represent the bits
99 * (in the requested address) that should be ignored and
100 * replaced with those from the top half of HICR7.
101 * The 1 bits in the bottom half of HICR8 represent the bits
102 * (in the requested address) that should be kept and pass
103 * into the BMC address space.
107 * It doesn't make sense to talk about a size or offset with
108 * low 16 bits set. Both HICR7 and HICR8 talk about the top 16
109 * bits of addresses and sizes.
112 if ((map
.size
& 0x0000ffff) || (map
.offset
& 0x0000ffff))
116 * Because of the way the masks work in HICR8 offset has to
117 * be a multiple of size.
119 if (map
.offset
& (map
.size
- 1))
122 if (map
.window_type
== ASPEED_LPC_CTRL_WINDOW_FLASH
) {
123 addr
= lpc_ctrl
->pnor_base
;
124 size
= lpc_ctrl
->pnor_size
;
125 } else if (map
.window_type
== ASPEED_LPC_CTRL_WINDOW_MEMORY
) {
126 addr
= lpc_ctrl
->mem_base
;
127 size
= lpc_ctrl
->mem_size
;
132 /* Check overflow first! */
133 if (map
.offset
+ map
.size
< map
.offset
||
134 map
.offset
+ map
.size
> size
)
137 if (map
.size
== 0 || map
.size
> size
)
143 * addr (host lpc address) is safe regardless of values. This
144 * simply changes the address the host has to request on its
145 * side of the LPC bus. This cannot impact the hosts own
146 * memory space by surprise as LPC specific accessors are
147 * required. The only strange thing that could be done is
148 * setting the lower 16 bits but the shift takes care of that.
151 rc
= regmap_write(lpc_ctrl
->regmap
, HICR7
,
152 (addr
| (map
.addr
>> 16)));
156 return regmap_write(lpc_ctrl
->regmap
, HICR8
,
157 (~(map
.size
- 1)) | ((map
.size
>> 16) - 1));
163 static const struct file_operations aspeed_lpc_ctrl_fops
= {
164 .owner
= THIS_MODULE
,
165 .mmap
= aspeed_lpc_ctrl_mmap
,
166 .unlocked_ioctl
= aspeed_lpc_ctrl_ioctl
,
169 static int aspeed_lpc_ctrl_probe(struct platform_device
*pdev
)
171 struct aspeed_lpc_ctrl
*lpc_ctrl
;
172 struct device_node
*node
;
173 struct resource resm
;
179 lpc_ctrl
= devm_kzalloc(dev
, sizeof(*lpc_ctrl
), GFP_KERNEL
);
183 node
= of_parse_phandle(dev
->of_node
, "flash", 0);
185 dev_err(dev
, "Didn't find host pnor flash node\n");
189 rc
= of_address_to_resource(node
, 1, &resm
);
192 dev_err(dev
, "Couldn't address to resource for flash\n");
196 lpc_ctrl
->pnor_size
= resource_size(&resm
);
197 lpc_ctrl
->pnor_base
= resm
.start
;
199 dev_set_drvdata(&pdev
->dev
, lpc_ctrl
);
201 node
= of_parse_phandle(dev
->of_node
, "memory-region", 0);
203 dev_err(dev
, "Didn't find reserved memory\n");
207 rc
= of_address_to_resource(node
, 0, &resm
);
210 dev_err(dev
, "Couldn't address to resource for reserved memory\n");
214 lpc_ctrl
->mem_size
= resource_size(&resm
);
215 lpc_ctrl
->mem_base
= resm
.start
;
217 lpc_ctrl
->regmap
= syscon_node_to_regmap(
218 pdev
->dev
.parent
->of_node
);
219 if (IS_ERR(lpc_ctrl
->regmap
)) {
220 dev_err(dev
, "Couldn't get regmap\n");
224 lpc_ctrl
->miscdev
.minor
= MISC_DYNAMIC_MINOR
;
225 lpc_ctrl
->miscdev
.name
= DEVICE_NAME
;
226 lpc_ctrl
->miscdev
.fops
= &aspeed_lpc_ctrl_fops
;
227 lpc_ctrl
->miscdev
.parent
= dev
;
228 rc
= misc_register(&lpc_ctrl
->miscdev
);
230 dev_err(dev
, "Unable to register device\n");
232 dev_info(dev
, "Loaded at %pr\n", &resm
);
237 static int aspeed_lpc_ctrl_remove(struct platform_device
*pdev
)
239 struct aspeed_lpc_ctrl
*lpc_ctrl
= dev_get_drvdata(&pdev
->dev
);
241 misc_deregister(&lpc_ctrl
->miscdev
);
246 static const struct of_device_id aspeed_lpc_ctrl_match
[] = {
247 { .compatible
= "aspeed,ast2400-lpc-ctrl" },
248 { .compatible
= "aspeed,ast2500-lpc-ctrl" },
252 static struct platform_driver aspeed_lpc_ctrl_driver
= {
255 .of_match_table
= aspeed_lpc_ctrl_match
,
257 .probe
= aspeed_lpc_ctrl_probe
,
258 .remove
= aspeed_lpc_ctrl_remove
,
261 module_platform_driver(aspeed_lpc_ctrl_driver
);
263 MODULE_DEVICE_TABLE(of
, aspeed_lpc_ctrl_match
);
264 MODULE_LICENSE("GPL");
265 MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
266 MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings");