0001
0002
0003
0004
0005
0006 #include <linux/clk.h>
0007 #include <linux/log2.h>
0008 #include <linux/mfd/syscon.h>
0009 #include <linux/miscdevice.h>
0010 #include <linux/mm.h>
0011 #include <linux/module.h>
0012 #include <linux/of_address.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/poll.h>
0015 #include <linux/regmap.h>
0016
0017 #include <linux/aspeed-lpc-ctrl.h>
0018
0019 #define DEVICE_NAME "aspeed-lpc-ctrl"
0020
0021 #define HICR5 0x80
0022 #define HICR5_ENL2H BIT(8)
0023 #define HICR5_ENFWH BIT(10)
0024
0025 #define HICR6 0x84
0026 #define SW_FWH2AHB BIT(17)
0027
0028 #define HICR7 0x88
0029 #define HICR8 0x8c
0030
0031 struct aspeed_lpc_ctrl {
0032 struct miscdevice miscdev;
0033 struct regmap *regmap;
0034 struct clk *clk;
0035 phys_addr_t mem_base;
0036 resource_size_t mem_size;
0037 u32 pnor_size;
0038 u32 pnor_base;
0039 bool fwh2ahb;
0040 struct regmap *scu;
0041 };
0042
0043 static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file)
0044 {
0045 return container_of(file->private_data, struct aspeed_lpc_ctrl,
0046 miscdev);
0047 }
0048
0049 static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma)
0050 {
0051 struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
0052 unsigned long vsize = vma->vm_end - vma->vm_start;
0053 pgprot_t prot = vma->vm_page_prot;
0054
0055 if (vma->vm_pgoff + vma_pages(vma) > lpc_ctrl->mem_size >> PAGE_SHIFT)
0056 return -EINVAL;
0057
0058
0059 prot = pgprot_noncached(prot);
0060
0061 if (remap_pfn_range(vma, vma->vm_start,
0062 (lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
0063 vsize, prot))
0064 return -EAGAIN;
0065
0066 return 0;
0067 }
0068
0069 static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
0070 unsigned long param)
0071 {
0072 struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
0073 struct device *dev = file->private_data;
0074 void __user *p = (void __user *)param;
0075 struct aspeed_lpc_ctrl_mapping map;
0076 u32 addr;
0077 u32 size;
0078 long rc;
0079
0080 if (copy_from_user(&map, p, sizeof(map)))
0081 return -EFAULT;
0082
0083 if (map.flags != 0)
0084 return -EINVAL;
0085
0086 switch (cmd) {
0087 case ASPEED_LPC_CTRL_IOCTL_GET_SIZE:
0088
0089 if (map.window_type != ASPEED_LPC_CTRL_WINDOW_MEMORY)
0090 return -EINVAL;
0091
0092
0093 if (map.window_id != 0)
0094 return -EINVAL;
0095
0096
0097 if (!lpc_ctrl->mem_size) {
0098 dev_dbg(dev, "Didn't find reserved memory\n");
0099 return -ENXIO;
0100 }
0101
0102 map.size = lpc_ctrl->mem_size;
0103
0104 return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0;
0105 case ASPEED_LPC_CTRL_IOCTL_MAP:
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127 if ((map.size & 0x0000ffff) || (map.offset & 0x0000ffff))
0128 return -EINVAL;
0129
0130
0131
0132
0133
0134 if (map.offset & (map.size - 1))
0135 return -EINVAL;
0136
0137 if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) {
0138 if (!lpc_ctrl->pnor_size) {
0139 dev_dbg(dev, "Didn't find host pnor flash\n");
0140 return -ENXIO;
0141 }
0142 addr = lpc_ctrl->pnor_base;
0143 size = lpc_ctrl->pnor_size;
0144 } else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) {
0145
0146 if (!lpc_ctrl->mem_size) {
0147 dev_dbg(dev, "Didn't find reserved memory\n");
0148 return -ENXIO;
0149 }
0150 addr = lpc_ctrl->mem_base;
0151 size = lpc_ctrl->mem_size;
0152 } else {
0153 return -EINVAL;
0154 }
0155
0156
0157 if (map.offset + map.size < map.offset ||
0158 map.offset + map.size > size)
0159 return -EINVAL;
0160
0161 if (map.size == 0 || map.size > size)
0162 return -EINVAL;
0163
0164 addr += map.offset;
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175 rc = regmap_write(lpc_ctrl->regmap, HICR7,
0176 (addr | (map.addr >> 16)));
0177 if (rc)
0178 return rc;
0179
0180 rc = regmap_write(lpc_ctrl->regmap, HICR8,
0181 (~(map.size - 1)) | ((map.size >> 16) - 1));
0182 if (rc)
0183 return rc;
0184
0185
0186
0187
0188 if (lpc_ctrl->fwh2ahb) {
0189
0190
0191
0192
0193
0194 regmap_update_bits(lpc_ctrl->scu, 0x0D8, BIT(2), 0);
0195
0196
0197
0198
0199
0200
0201 regmap_write(lpc_ctrl->regmap, HICR6, SW_FWH2AHB);
0202 }
0203
0204
0205
0206
0207
0208 return regmap_update_bits(lpc_ctrl->regmap, HICR5,
0209 HICR5_ENFWH | HICR5_ENL2H,
0210 HICR5_ENFWH | HICR5_ENL2H);
0211 }
0212
0213 return -EINVAL;
0214 }
0215
0216 static const struct file_operations aspeed_lpc_ctrl_fops = {
0217 .owner = THIS_MODULE,
0218 .mmap = aspeed_lpc_ctrl_mmap,
0219 .unlocked_ioctl = aspeed_lpc_ctrl_ioctl,
0220 };
0221
0222 static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
0223 {
0224 struct aspeed_lpc_ctrl *lpc_ctrl;
0225 struct device_node *node;
0226 struct resource resm;
0227 struct device *dev;
0228 struct device_node *np;
0229 int rc;
0230
0231 dev = &pdev->dev;
0232
0233 lpc_ctrl = devm_kzalloc(dev, sizeof(*lpc_ctrl), GFP_KERNEL);
0234 if (!lpc_ctrl)
0235 return -ENOMEM;
0236
0237
0238 node = of_parse_phandle(dev->of_node, "flash", 0);
0239 if (!node) {
0240 dev_dbg(dev, "Didn't find host pnor flash node\n");
0241 } else {
0242 rc = of_address_to_resource(node, 1, &resm);
0243 of_node_put(node);
0244 if (rc) {
0245 dev_err(dev, "Couldn't address to resource for flash\n");
0246 return rc;
0247 }
0248
0249 lpc_ctrl->pnor_size = resource_size(&resm);
0250 lpc_ctrl->pnor_base = resm.start;
0251 }
0252
0253
0254 dev_set_drvdata(&pdev->dev, lpc_ctrl);
0255
0256
0257 node = of_parse_phandle(dev->of_node, "memory-region", 0);
0258 if (!node) {
0259 dev_dbg(dev, "Didn't find reserved memory\n");
0260 } else {
0261 rc = of_address_to_resource(node, 0, &resm);
0262 of_node_put(node);
0263 if (rc) {
0264 dev_err(dev, "Couldn't address to resource for reserved memory\n");
0265 return -ENXIO;
0266 }
0267
0268 lpc_ctrl->mem_size = resource_size(&resm);
0269 lpc_ctrl->mem_base = resm.start;
0270
0271 if (!is_power_of_2(lpc_ctrl->mem_size)) {
0272 dev_err(dev, "Reserved memory size must be a power of 2, got %u\n",
0273 (unsigned int)lpc_ctrl->mem_size);
0274 return -EINVAL;
0275 }
0276
0277 if (!IS_ALIGNED(lpc_ctrl->mem_base, lpc_ctrl->mem_size)) {
0278 dev_err(dev, "Reserved memory must be naturally aligned for size %u\n",
0279 (unsigned int)lpc_ctrl->mem_size);
0280 return -EINVAL;
0281 }
0282 }
0283
0284 np = pdev->dev.parent->of_node;
0285 if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
0286 !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
0287 !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
0288 dev_err(dev, "unsupported LPC device binding\n");
0289 return -ENODEV;
0290 }
0291
0292 lpc_ctrl->regmap = syscon_node_to_regmap(np);
0293 if (IS_ERR(lpc_ctrl->regmap)) {
0294 dev_err(dev, "Couldn't get regmap\n");
0295 return -ENODEV;
0296 }
0297
0298 if (of_device_is_compatible(dev->of_node, "aspeed,ast2600-lpc-ctrl")) {
0299 lpc_ctrl->fwh2ahb = true;
0300
0301 lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu");
0302 if (IS_ERR(lpc_ctrl->scu)) {
0303 dev_err(dev, "couldn't find scu\n");
0304 return PTR_ERR(lpc_ctrl->scu);
0305 }
0306 }
0307
0308 lpc_ctrl->clk = devm_clk_get(dev, NULL);
0309 if (IS_ERR(lpc_ctrl->clk))
0310 return dev_err_probe(dev, PTR_ERR(lpc_ctrl->clk),
0311 "couldn't get clock\n");
0312 rc = clk_prepare_enable(lpc_ctrl->clk);
0313 if (rc) {
0314 dev_err(dev, "couldn't enable clock\n");
0315 return rc;
0316 }
0317
0318 lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
0319 lpc_ctrl->miscdev.name = DEVICE_NAME;
0320 lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
0321 lpc_ctrl->miscdev.parent = dev;
0322 rc = misc_register(&lpc_ctrl->miscdev);
0323 if (rc) {
0324 dev_err(dev, "Unable to register device\n");
0325 goto err;
0326 }
0327
0328 return 0;
0329
0330 err:
0331 clk_disable_unprepare(lpc_ctrl->clk);
0332 return rc;
0333 }
0334
0335 static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
0336 {
0337 struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
0338
0339 misc_deregister(&lpc_ctrl->miscdev);
0340 clk_disable_unprepare(lpc_ctrl->clk);
0341
0342 return 0;
0343 }
0344
0345 static const struct of_device_id aspeed_lpc_ctrl_match[] = {
0346 { .compatible = "aspeed,ast2400-lpc-ctrl" },
0347 { .compatible = "aspeed,ast2500-lpc-ctrl" },
0348 { .compatible = "aspeed,ast2600-lpc-ctrl" },
0349 { },
0350 };
0351
0352 static struct platform_driver aspeed_lpc_ctrl_driver = {
0353 .driver = {
0354 .name = DEVICE_NAME,
0355 .of_match_table = aspeed_lpc_ctrl_match,
0356 },
0357 .probe = aspeed_lpc_ctrl_probe,
0358 .remove = aspeed_lpc_ctrl_remove,
0359 };
0360
0361 module_platform_driver(aspeed_lpc_ctrl_driver);
0362
0363 MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match);
0364 MODULE_LICENSE("GPL");
0365 MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
0366 MODULE_DESCRIPTION("Control for ASPEED LPC HOST to BMC mappings");