blob: 3890c568bcf1113cb84477cd5ae405fe46e64628 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/amlogic/vmem.h>
#define TEST_VMEM_ENTRY
static LIST_HEAD(vmem_controller_list);
struct vmem_controller *vmem_get_controller(int bus_num)
{
struct vmem_controller *vmemctlr;
list_for_each_entry(vmemctlr, &vmem_controller_list, list)
if (vmemctlr && vmemctlr->bus_num == bus_num)
return vmemctlr;
return NULL;
}
struct vmem_device *
vmem_get_device(struct vmem_controller *vmemctlr, int dev_num)
{
struct vmem_device *vmemdev;
list_for_each_entry(vmemdev, &vmemctlr->device_list, list)
if (vmemdev && vmemdev->dev_num == dev_num)
return vmemdev;
return NULL;
}
struct vmem_controller *vmem_create_controller(struct device *dev, int size)
{
struct vmem_controller *vmemctlr;
u32 bus_num;
if (!dev)
return NULL;
if (of_property_read_u32(dev->of_node, "vmem-bus-num", &bus_num))
bus_num = 0;
vmemctlr = vmem_get_controller(bus_num);
if (vmemctlr) {
dev_err(dev, "vmemctlr created already\n");
return NULL;
}
vmemctlr = kzalloc(sizeof(*vmemctlr) + size, GFP_KERNEL);
if (!vmemctlr)
return NULL;
vmemctlr->dev.parent = dev;
device_initialize(&vmemctlr->dev);
dev_set_drvdata(&vmemctlr->dev, &vmemctlr[1]);
dev_set_name(&vmemctlr->dev, "vmemctlr%u", bus_num);
if (device_add(&vmemctlr->dev)) {
kfree(vmemctlr);
dev_err(dev, "vmemctlr add device failed\n");
return NULL;
}
vmemctlr->bus_num = bus_num;
list_add_tail(&vmemctlr->list, &vmem_controller_list);
INIT_LIST_HEAD(&vmemctlr->device_list);
dev_info(dev, "vmemctlr %s created!\n", dev_name(&vmemctlr->dev));
return vmemctlr;
}
void vmem_destroy_controller(struct vmem_controller *vmemctlr)
{
device_del(&vmemctlr->dev);
list_del(&vmemctlr->list);
kfree(vmemctlr);
}
struct vmem_device *vmem_create_device(struct vmem_controller *vmemctlr,
int dev_num, int mem_size)
{
struct device *dev;
struct vmem_device *vmemdev;
dev = &vmemctlr->dev;
vmemdev = vmem_get_device(vmemctlr, dev_num);
if (vmemdev) {
dev_err(dev, "vmemdev created already\n");
return NULL;
}
vmemdev = kzalloc(sizeof(*vmemdev) + mem_size, GFP_KERNEL);
if (!vmemdev)
return NULL;
vmemdev->dev.parent = dev;
device_initialize(&vmemdev->dev);
vmemdev->mem = (u8 *)&vmemdev[1];
vmemdev->size = mem_size;
dev_set_name(&vmemdev->dev, "%s.%u", dev_name(dev), dev_num);
if (device_add(&vmemdev->dev)) {
kfree(vmemdev);
dev_err(dev, "vmemdev add device failed\n");
return NULL;
}
vmemdev->vmemctlr = vmemctlr;
vmemdev->dev_num = dev_num;
list_add_tail(&vmemdev->list, &vmemctlr->device_list);
dev_info(dev, "vmemdev %s created!\n", dev_name(&vmemdev->dev));
return vmemdev;
}
void vmem_destroy_device(struct vmem_device *vmemdev)
{
device_del(&vmemdev->dev);
list_del(&vmemdev->list);
kfree(vmemdev);
}
int vmem_setup(struct vmem_device *vmemdev)
{
struct vmem_controller *vmemctlr = vmemdev->vmemctlr;
int ret = 0;
if (vmemctlr->setup)
ret = vmemctlr->setup(vmemdev);
return ret;
}
int vmem_cleanup(struct vmem_device *vmemdev)
{
struct vmem_controller *vmemctlr = vmemdev->vmemctlr;
int ret = 0;
if (vmemctlr->cleanup)
ret = vmemctlr->cleanup(vmemdev);
return ret;
}
#ifdef TEST_VMEM_ENTRY
static struct vmem_device *test_vmem;
static int test_vmem_cmd_notify(u8 cmd_val, int offset, int size)
{
return 0;
}
static int test_vmem_data_notify(u8 cmd_val, int offset, int size)
{
pr_info("\ncmd 0x%x, offset 0x%x, size 0x%x\n", cmd_val, offset, size);
return 0;
}
static ssize_t create_store(struct class *class, struct class_attribute *attr,
const char *buf, size_t count)
{
struct vmem_controller *vmemctlr;
unsigned int bus_num, dev_num, size;
int i;
if (test_vmem) {
pr_info("error: test vmem existing\n");
return -EINVAL;
}
if (sscanf(buf, "%d%d%d", &bus_num, &dev_num, &size) != 3) {
bus_num = 0;
dev_num = 0;
size = 32;
}
vmemctlr = vmem_get_controller(bus_num);
if (!vmemctlr)
return -ENODEV;
test_vmem = vmem_create_device(vmemctlr, dev_num, size);
test_vmem->cmd_notify = test_vmem_cmd_notify;
test_vmem->data_notify = test_vmem_data_notify;
for (i = 0; i < size; i++)
test_vmem->mem[i] = i + 0x80;
return count;
}
static ssize_t destroy_store(struct class *class, struct class_attribute *attr,
const char *buf, size_t count)
{
if (test_vmem) {
vmem_destroy_device(test_vmem);
test_vmem = NULL;
}
return count;
}
static ssize_t data_show(struct class *class, struct class_attribute *attr,
char *buf)
{
int i;
if (test_vmem) {
for (i = 0; i < test_vmem->size; i++)
pr_info("mem[%d]= 0x%x\n", i, test_vmem->mem[i]);
}
return 0;
}
static CLASS_ATTR_WO(create);
static CLASS_ATTR_WO(destroy);
static CLASS_ATTR_RO(data);
static struct attribute *test_vmem_class_attrs[] = {
&class_attr_create.attr,
&class_attr_destroy.attr,
&class_attr_data.attr,
NULL,
};
ATTRIBUTE_GROUPS(test_vmem_class);
static struct class test_vmem_class = {
.name = "test_vmem",
.owner = THIS_MODULE,
.class_groups = test_vmem_class_groups
};
static int __init vmem_init(void)
{
test_vmem = NULL;
return class_register(&test_vmem_class);
}
postcore_initcall(vmem_init);
#endif