blob: be254ad45364467ff86b9c7e46269cf9ebe06a31 [file] [log] [blame]
/*
* drivers/amlogic/soc_info/socdata.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* 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.
*
* 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/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#include <linux/of.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_address.h>
#include <linux/cma.h>
#include <linux/sysfs.h>
#include <linux/random.h>
#include <linux/amlogic/secmon.h>
#include "soc_info.h"
unsigned int read_nocsdata_cmd;
unsigned int write_nocsdata_cmd;
static void __iomem *soc_ver1_addr, *soc_poc_addr;
struct socdata_dev_t {
struct cdev cdev;
dev_t devno;
};
static struct socdata_dev_t *socdata_devp;
static int socdata_open(struct inode *inode, struct file *file)
{
struct socdata_dev_t *devp;
devp = container_of(inode->i_cdev, struct socdata_dev_t, cdev);
file->private_data = devp;
return 0;
}
static long socdata_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned long ret = 0;
void __user *argp = (void __user *)arg;
unsigned long __user *soc_info = argp;
unsigned char info[NOCS_DATA_LENGTH];
long long int offset = 0;
switch (cmd) {
case CMD_POC_DATA:
if (put_user(readl(soc_poc_addr), soc_info))
return -EFAULT;
break;
case CMD_SOCVER1_DATA:
if (put_user(readl(soc_ver1_addr), soc_info))
return -EFAULT;
break;
case CMD_SOCVER2_DATA:
if (put_user(readl(soc_ver1_addr + 4), soc_info))
return -EFAULT;
break;
case CMD_NOCSDATA_READ:
nocsdata_read(info, NOCS_DATA_LENGTH, &offset);
ret = copy_to_user(argp, info, sizeof(info));
if (ret != 0) {
pr_debug("%s:%d,copy_to_user fail\n",
__func__, __LINE__);
return ret;
}
break;
case CMD_NOCSDATA_WRITE:
ret = copy_from_user(info, argp, (NOCS_DATA_LENGTH - 28));
if (ret != 0) {
pr_debug("%s:%d,copy_from_user fail\n",
__func__, __LINE__);
return ret;
}
if (nocsdata_write(info, (NOCS_DATA_LENGTH - 28), &offset))
return -EFAULT;
break;
default:
return -EINVAL;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long socdata_compat_ioctl(struct file *filp,
unsigned int cmd, unsigned long args)
{
unsigned long ret;
args = (unsigned long)compat_ptr(args);
ret = socdata_unlocked_ioctl(filp, cmd, args);
return ret;
}
#endif
static const struct file_operations socdata_fops = {
.owner = THIS_MODULE,
.open = socdata_open,
.unlocked_ioctl = socdata_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = socdata_compat_ioctl,
#endif
};
static struct class socdata_class = {
.name = SOCDATA_CLASS_NAME,
};
static int aml_socdata_probe(struct platform_device *pdev)
{
int ret = -1;
struct device *devp;
socdata_devp = devm_kzalloc(&pdev->dev,
sizeof(struct socdata_dev_t), GFP_KERNEL);
if (!socdata_devp) {
ret = -ENOMEM;
dev_err(&pdev->dev, "socdata: failed to allocate memory\n ");
goto out;
}
soc_ver1_addr = of_iomap(pdev->dev.of_node, 0);
soc_poc_addr = of_iomap(pdev->dev.of_node, 1);
if (of_property_read_u32(pdev->dev.of_node, "read_nocsdata_cmd",
&read_nocsdata_cmd)) {
dev_err(&pdev->dev, "please config read nocsdata cmd\n");
return -1;
}
if (of_property_read_u32(pdev->dev.of_node, "write_nocsdata_cmd",
&write_nocsdata_cmd)) {
dev_err(&pdev->dev, "please config write nocsdata cmd\n");
return -1;
}
ret = alloc_chrdev_region(&socdata_devp->devno, 0, 1,
SOCDATA_DEVICE_NAME);
if (ret < 0) {
dev_err(&pdev->dev, "socdata: failed to allocate major number\n ");
ret = -ENODEV;
goto error1;
}
dev_info(&pdev->dev, "socdata_devno:%x\n", socdata_devp->devno);
ret = class_register(&socdata_class);
if (ret) {
dev_err(&pdev->dev, "socdata: failed to register class\n ");
goto error2;
}
/* connect the file operations with cdev */
cdev_init(&socdata_devp->cdev, &socdata_fops);
socdata_devp->cdev.owner = THIS_MODULE;
/* connect the major/minor number to the cdev */
ret = cdev_add(&socdata_devp->cdev, socdata_devp->devno, 1);
if (ret) {
dev_err(&pdev->dev, "socdata: failed to add device\n");
goto error3;
}
devp = device_create(&socdata_class, NULL, socdata_devp->devno,
NULL, SOCDATA_DEVICE_NAME);
if (IS_ERR(devp)) {
dev_err(&pdev->dev, "socdata: failed to create device node\n");
ret = PTR_ERR(devp);
goto error4;
}
return 0;
error4:
cdev_del(&socdata_devp->cdev);
error3:
class_unregister(&socdata_class);
error2:
unregister_chrdev_region(socdata_devp->devno, 1);
error1:
// devm_kfree(&(pdev->dev), socdata_devp);
out:
return ret;
}
static int aml_socdata_remove(struct platform_device *pdev)
{
unregister_chrdev_region(socdata_devp->devno, 1);
device_destroy(&socdata_class, socdata_devp->devno);
cdev_del(&socdata_devp->cdev);
class_unregister(&socdata_class);
return 0;
}
static const struct of_device_id meson_socdata_dt_match[] = {
{ .compatible = "amlogic, socdata",
},
{},
};
static struct platform_driver aml_socdata_driver = {
.probe = aml_socdata_probe,
.remove = aml_socdata_remove,
.driver = {
.name = SOCDATA_DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = meson_socdata_dt_match,
},
};
module_platform_driver(aml_socdata_driver);
MODULE_DESCRIPTION("AMLOGIC socdata driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("<huaihong.lei@amlogic.com>");