blob: a0c2ace523e1d320d073412aa04a6135e3b5f6bf [file]
/*
* drivers/amlogic/spicc/vmem.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/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;
struct device_node *np = dev->of_node;
u32 bus_num;
if (of_property_read_u32(np, "vmem-bus-num", &bus_num))
return NULL;
vmemctlr = vmem_get_controller(bus_num);
if (vmemctlr || !dev) {
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 test_vmem_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 (!strcmp(attr->attr.name, "create")) {
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;
}
else if (!strcmp(attr->attr.name, "destroy")) {
if (test_vmem) {
vmem_destroy_device(test_vmem);
test_vmem = NULL;
}
}
return count;
}
static ssize_t test_vmem_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int i;
if (test_vmem && !strcmp(attr->attr.name, "mem")) {
for (i = 0; i < test_vmem->size; i++)
pr_info("mem[%d]= 0x%x\n", i, test_vmem->mem[i]);
}
return 0;
}
static struct class_attribute test_vmem_class_attrs[] = {
__ATTR(create, 0200, NULL, test_vmem_store),
__ATTR(destroy, 0200, NULL, test_vmem_store),
__ATTR(mem, 0444, test_vmem_show, NULL),
__ATTR_NULL
};
static struct class test_vmem_class = {
.name = "test_vmem_device",
.owner = THIS_MODULE,
.class_attrs = test_vmem_class_attrs
};
static int __init vmem_init(void)
{
test_vmem = NULL;
return class_register(&test_vmem_class);
}
postcore_initcall(vmem_init);
#endif