/* * Copyright 2016 IBM Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #define AST_IO_VA 0xf0000000 #define AST_IO_PA 0x1e600000 #define AST_IO_SZ 0x00200000 #define AST_IO(__pa) ((void __iomem *)(((__pa) & 0x001fffff) | AST_IO_VA)) struct aspeed_controller { struct device *dev; const char *name; unsigned int base; unsigned int nregs; }; static ssize_t aspeed_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { struct aspeed_controller *ctrl = filp->private_data; char *kbuf; int ret, n = 0; size_t size; int i; size = ctrl->nregs * 23 + 1; kbuf = kzalloc(size, GFP_KERNEL); if (!kbuf) return -ENOMEM; for (i = 0; i < ctrl->nregs; i += 4) { n += scnprintf(kbuf + n, size - n, "0x%08X: 0x%08X\n", ctrl->base | i, readl(AST_IO(ctrl->base | i))); } ret = simple_read_from_buffer(ubuf, count, ppos, kbuf, n); kfree(kbuf); return ret; } static ssize_t aspeed_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *ppos) { struct aspeed_controller *ctrl = filp->private_data; unsigned int reg, val; char *kbuf = kmalloc(count + 1, GFP_KERNEL); int ret; if (!kbuf) return -ENOMEM; ret = simple_write_to_buffer(kbuf, count, ppos, ubuf, count); if (ret != count) { kfree(kbuf); return ret >= 0 ? -EIO : ret; } kbuf[count] = '\0'; ret = sscanf(kbuf, "%x=%x", ®, &val); kfree(kbuf); if (ret != 2) return -EINVAL; if ((ctrl->base & reg) != ctrl->base) { dev_err(ctrl->dev, "wrong base address: %x\n", reg); return -EINVAL; } if ((~ctrl->base & reg) >= ctrl->nregs) { dev_err(ctrl->dev, "wrong register: %x\n", reg); return -EINVAL; } writel(val, AST_IO(reg)); return count; } static const struct file_operations aspeed_fops = { .open = simple_open, .read = aspeed_read, .write = aspeed_write, .llseek = generic_file_llseek, }; static struct dentry *aspeed_debugfs_root; static int aspeed_device_show(struct device *dev, void *data) { struct platform_device *pdev = to_platform_device(dev); struct aspeed_controller *ctrl; struct resource *r; if (!pdev) return 0; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) return 0; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) return -ENOMEM; ctrl->dev = dev; ctrl->name = pdev->name; ctrl->base = r->start; ctrl->nregs = resource_size(r); debugfs_create_file(pdev->name, 0600, aspeed_debugfs_root, ctrl, &aspeed_fops); return 0; } static int __init aspeed_debugfs_init(void) { aspeed_debugfs_root = debugfs_create_dir("aspeed", NULL); bus_for_each_dev(&platform_bus_type, NULL, NULL, aspeed_device_show); return 0; } arch_initcall(aspeed_debugfs_init);