Files
kernel_google_wahoo/drivers/misc/access_ramoops.c
Patrick Tjin 71228a1d7e misc: access-ramoops: add size of memory area to sysfs
Add the sysfs node size to the access ramoops driver so that
we know how large of an area we have to work with.

Bug: 37554629
Bug: 37553996
Signed-off-by: Patrick Tjin <pattjin@google.com>
Change-Id: Ie0ab62bcd37af1e4742e540c7b6a88d92e4f8f6a
2017-06-06 22:27:39 -07:00

251 lines
6.1 KiB
C

/*
* Persistent memory accessor
*
* Copyright (C) 2017 Google, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define DEVICE_NAME "access_ramoops"
#define MAX_OPEN 4
struct access_ramoops_info {
char *name;
phys_addr_t phys;
void *addr;
size_t size;
struct mutex lock;
int is_mapped;
int is_open;
const char *label;
struct miscdevice miscdev;
};
static ssize_t label_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct miscdevice *miscdev = dev_get_drvdata(dev);
struct access_ramoops_info *info =
container_of(miscdev, struct access_ramoops_info, miscdev);
return snprintf(buf, PAGE_SIZE, "%s\n", info->label);
}
static ssize_t size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct miscdevice *miscdev = dev_get_drvdata(dev);
struct access_ramoops_info *info =
container_of(miscdev, struct access_ramoops_info, miscdev);
return snprintf(buf, PAGE_SIZE, "%zd\n", info->size);
}
static DEVICE_ATTR_RO(label);
static DEVICE_ATTR_RO(size);
static struct attribute *access_ramoops_attrs[] = {
&dev_attr_label.attr,
&dev_attr_size.attr,
NULL,
};
ATTRIBUTE_GROUPS(access_ramoops);
static ssize_t access_ramoops_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct access_ramoops_info *info = filp->private_data;
return simple_read_from_buffer(buf, count, ppos,
info->addr, info->size);
}
static ssize_t access_ramoops_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct access_ramoops_info *info = filp->private_data;
return simple_write_to_buffer(info->addr, info->size,
ppos, buf, count);
}
static int access_ramoops_open(struct inode *inode, struct file *filp)
{
int res = 0;
struct access_ramoops_info *info =
container_of(filp->private_data, struct access_ramoops_info,
miscdev);
filp->private_data = info;
mutex_lock(&info->lock);
if (info->is_open == MAX_OPEN) {
res = -EBUSY;
goto out;
}
if (!info->is_open) {
if (!request_mem_region(info->phys, info->size,
"access_ramoops")) {
dev_err(info->miscdev.this_device,
"request mem region (%zx@%pa) failed\n",
info->size, &info->phys);
res = -ENOMEM;
goto out;
}
info->addr = ioremap(info->phys, info->size);
if (IS_ERR(info->addr)) {
dev_err(info->miscdev.this_device,
"unable to map region (%zx@%pa)\n",
info->size, &info->phys);
info->addr = NULL;
res = -ENOMEM;
goto out;
}
info->is_mapped = 1;
}
info->is_open++;
out:
mutex_unlock(&info->lock);
return res;
}
static int access_ramoops_release(struct inode *inode, struct file *filp)
{
struct access_ramoops_info *info = filp->private_data;
mutex_lock(&info->lock);
info->is_open--;
if (!info->is_open) {
info->is_mapped = 0;
iounmap(info->addr);
info->addr = NULL;
release_mem_region(info->phys, info->size);
}
filp->private_data = NULL;
mutex_unlock(&info->lock);
return 0;
}
static struct file_operations access_ramoops_fops = {
.owner = THIS_MODULE,
.open = access_ramoops_open,
.read = access_ramoops_read,
.write = access_ramoops_write,
.release = access_ramoops_release,
};
static int __init access_ramoops_probe(struct platform_device *pdev)
{
int ret = 0;
struct access_ramoops_info *info;
struct device_node *of_node = pdev->dev.of_node;
struct device_node *mem_region;
struct resource res;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
platform_set_drvdata(pdev, info);
mem_region = of_parse_phandle(of_node, "memory-region", 0);
if (!mem_region) {
dev_err(&pdev->dev, "no memory-region phandle\n");
return -ENODEV;
}
ret = of_property_read_string(of_node, "label", &info->label);
if (ret) {
dev_err(&pdev->dev, "failed to get region label: %d\n", ret);
return -EINVAL;
}
info->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "access-%s",
info->label);
if (!info->name) {
dev_err(&pdev->dev, "failed to alloc name\n");
return -ENOMEM;
}
ret = of_address_to_resource(mem_region, 0, &res);
of_node_put(mem_region);
if (ret) {
dev_err(&pdev->dev,
"failed to get memory-region resource: %d\n", ret);
return -ENOMEM;
}
info->phys = res.start;
info->size = resource_size(&res);
mutex_init(&info->lock);
info->miscdev.minor = MISC_DYNAMIC_MINOR;
info->miscdev.name = info->name;
info->miscdev.fops = &access_ramoops_fops;
info->miscdev.groups = access_ramoops_groups;
info->miscdev.parent = &pdev->dev;
ret = misc_register(&info->miscdev);
if (ret) {
dev_err(info->miscdev.this_device,
"failed to register misc device %d\n", ret);
return ret;
}
dev_info(info->miscdev.this_device,
"registered '%s' %d:%d, (%zx@%pa)\n", info->label,
MISC_MAJOR, info->miscdev.minor, info->size, &info->phys);
return ret;
}
static int __exit access_ramoops_remove(struct platform_device *pdev)
{
struct access_ramoops_info *info = platform_get_drvdata(pdev);
dev_info(info->miscdev.this_device, "removing");
misc_deregister(&info->miscdev);
if (!info->is_mapped)
return 0;
info->is_mapped = 0;
iounmap(info->addr);
release_mem_region(info->phys, info->size);
return 0;
}
static const struct of_device_id dt_match[] = {
{ .compatible = "access_ramoops" },
{}
};
static struct platform_driver access_ramoops_driver = {
.driver = {
.name = "access_ramoops",
.of_match_table = dt_match,
},
.remove = __exit_p(access_ramoops_remove),
};
module_platform_driver_probe(access_ramoops_driver, access_ramoops_probe);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick Tjin <pattjin@google.com>");
MODULE_DESCRIPTION("Persistent memory access driver");