blob: 68072dcf3820a45aec36daeba39a1b78d0b4f615 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/clk.h>
#include "efuse.h"
#include <linux/amlogic/efuse.h>
#include <linux/io.h>
#include <linux/compat.h>
#include "efuse_burn.h"
#define EFUSE_BURN_DEVICE_NAME "efuse_burn"
#define EFUSE_BURN_CLASS_NAME "efuse_burn"
static struct aml_efuse_burn_dev *pefuse_burn_dev;
static int efuse_burn_open(struct inode *inode, struct file *file)
{
struct aml_efuse_burn_dev *devp;
devp = container_of(inode->i_cdev, struct aml_efuse_burn_dev, cdev);
file->private_data = devp;
//printk(KERN_NOTICE "%s:%d\n", __func__, __LINE__);
pr_notice("%s:%d\n", __func__, __LINE__);
return 0;
}
static int efuse_burn_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static loff_t efuse_burn_llseek(struct file *filp, loff_t off, int whence)
{
return 0;
}
static long efuse_burn_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
long ret = -ENOTTY;
void __user *argp = (void __user *)arg;
struct efuse_burn_info info;
pr_notice("%s:%d\n", __func__, __LINE__);
switch (cmd) {
case EFUSE_BURN_CHECK_KEY:
if (copy_from_user((void *)&info, argp, sizeof(info))) {
pr_err("%s: copy_from_user fail\n", __func__);
return -EFAULT;
}
if (efuse_burn_lockable_is_cfg(info.itemname) == 0) {
info.status = efuse_burn_check_burned(info.itemname);
} else {
pr_err("%s: efuse_burn check item not cfg\n", __func__);
return -EFAULT;
}
if (copy_to_user(argp, &info, sizeof(info))) {
pr_err("%s: copy_to_user fail\n", __func__);
return -EFAULT;
}
break;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long efuse_burn_compat_ioctl(struct file *filp,
unsigned int cmd, unsigned long args)
{
long ret;
args = (unsigned long)compat_ptr(args);
ret = efuse_burn_unlocked_ioctl(filp, cmd, args);
return ret;
}
#endif
static ssize_t efuse_burn_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
pr_notice("%s:%d\n", __func__, __LINE__);
return 0;
}
static ssize_t efuse_burn_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
struct aml_efuse_burn_dev *devp;
ssize_t ret;
char *op = NULL;
devp = file->private_data;
//pr_notice("%s:%d, sizeof(loff_t):%d\n",
// __func__, __LINE__, sizeof(loff_t));
//pr_notice("pos=%lld,count=%d\n", *ppos, count);
if (count != devp->efuse_pattern_size) {
ret = -EINVAL;
pr_err("efuse burn: bad pattern size, only support size %d!\n",
devp->efuse_pattern_size);
goto exit;
}
op = kzalloc(sizeof(char) * count, GFP_KERNEL);
if (!op) {
ret = -ENOMEM;
pr_err("efuse burn: failed to allocate memory!\n");
goto exit;
}
memset(op, 0, count);
if (copy_from_user(op, buf, count)) {
pr_err("%s: copy_from_user fail\n", __func__);
kfree(op);
ret = -EFAULT;
goto exit;
}
ret = efuse_amlogic_set(op, count);
kfree(op);
if (ret) {
pr_err("efuse burn: pattern programming fail! ret: %d\n",
(unsigned int)ret);
ret = -EINVAL;
goto exit;
}
pr_info("efuse burn: pattern programming success!\n");
ret = count;
exit:
return ret;
}
static const struct file_operations efuse_burn_fops = {
.owner = THIS_MODULE,
.llseek = efuse_burn_llseek,
.open = efuse_burn_open,
.release = efuse_burn_release,
.read = efuse_burn_read,
.write = efuse_burn_write,
.unlocked_ioctl = efuse_burn_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = efuse_burn_compat_ioctl,
#endif
};
static ssize_t version_show(struct class *cla,
struct class_attribute *attr, char *buf)
{
ssize_t n = 0;
n = sprintf(buf, "ver1.0");
return n;
}
static ssize_t version_store(struct class *cla,
struct class_attribute *attr,
const char *buf, size_t count)
{
pr_notice("%s:%d,buf=0x%lx,count=%zu\n",
__func__, __LINE__, (long)buf, count);
return count;
}
static EFUSE_CLASS_ATTR(version);
static struct attribute *efuse_burn_class_attrs[] = {
&class_attr_version.attr,
NULL,
};
ATTRIBUTE_GROUPS(efuse_burn_class);
static int efuse_burn_probe(struct platform_device *pdev)
{
int ret;
struct device *devp;
struct aml_efuse_burn_dev *efuse_burn_dev;
struct device_node *np = pdev->dev.of_node;
efuse_burn_dev = devm_kzalloc(&pdev->dev, sizeof(*efuse_burn_dev),
GFP_KERNEL);
if (!efuse_burn_dev) {
ret = -ENOMEM;
dev_err(&pdev->dev, "failed to alloc enough mem for efuse_dev\n");
goto out;
}
efuse_burn_dev->pdev = pdev;
platform_set_drvdata(pdev, efuse_burn_dev);
of_node_get(np);
ret = of_property_read_u32(np, "efuse_pattern_size",
&efuse_burn_dev->efuse_pattern_size);
if (ret) {
pr_err("can't get efuse_pattern_size, please configurate size\n");
goto error1;
}
ret = alloc_chrdev_region(&efuse_burn_dev->devno, 0, 1,
EFUSE_BURN_DEVICE_NAME);
if (ret < 0) {
dev_err(&pdev->dev, "fail to allocate major number\n ");
goto error1;
}
efuse_burn_dev->cls.name = EFUSE_BURN_CLASS_NAME;
efuse_burn_dev->cls.owner = THIS_MODULE;
efuse_burn_dev->cls.class_groups = efuse_burn_class_groups;
ret = class_register(&efuse_burn_dev->cls);
if (ret)
goto error2;
cdev_init(&efuse_burn_dev->cdev, &efuse_burn_fops);
efuse_burn_dev->cdev.owner = THIS_MODULE;
ret = cdev_add(&efuse_burn_dev->cdev, efuse_burn_dev->devno, 1);
if (ret) {
dev_err(&pdev->dev, "failed to add device\n");
goto error3;
}
devp = device_create(&efuse_burn_dev->cls, NULL,
efuse_burn_dev->devno, efuse_burn_dev, "efuse_burn");
if (IS_ERR(devp)) {
dev_err(&pdev->dev, "failed to create device node\n");
ret = PTR_ERR(devp);
goto error4;
}
pefuse_burn_dev = efuse_burn_dev;
dev_info(&pdev->dev, "device %s created OK\n", EFUSE_BURN_DEVICE_NAME);
return 0;
error4:
cdev_del(&efuse_burn_dev->cdev);
error3:
class_unregister(&efuse_burn_dev->cls);
error2:
unregister_chrdev_region(efuse_burn_dev->devno, 1);
error1:
devm_kfree(&pdev->dev, efuse_burn_dev);
out:
return ret;
}
static int efuse_burn_remove(struct platform_device *pdev)
{
struct aml_efuse_burn_dev *efuse_burn_dev;
efuse_burn_dev = platform_get_drvdata(pdev);
unregister_chrdev_region(efuse_burn_dev->devno, 1);
device_destroy(&efuse_burn_dev->cls, efuse_burn_dev->devno);
cdev_del(&efuse_burn_dev->cdev);
class_unregister(&efuse_burn_dev->cls);
platform_set_drvdata(pdev, NULL);
devm_kfree(&pdev->dev, efuse_burn_dev);
return 0;
}
static const struct of_device_id efuse_burn_dt_match[] = {
{ .compatible = "amlogic, efuseburn",
},
{},
};
static struct platform_driver efuse_burn_driver = {
.probe = efuse_burn_probe,
.remove = efuse_burn_remove,
.driver = {
.name = EFUSE_BURN_DEVICE_NAME,
.of_match_table = efuse_burn_dt_match,
.owner = THIS_MODULE,
},
};
int __init aml_efuse_burn_init(void)
{
return platform_driver_register(&efuse_burn_driver);
}
void aml_efuse_burn_exit(void)
{
platform_driver_unregister(&efuse_burn_driver);
}