mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
7f5461b0f1
PD#SWPL-68123 Problem: porting spi driver Solution: porting spi driver Verify: s4d Change-Id: I846bf0d796774ed5c85be80ec9dcba5600551da9 Signed-off-by: wanwei.jiang <wanwei.jiang@amlogic.com>
253 lines
5.3 KiB
C
253 lines
5.3 KiB
C
// 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
|
|
};
|
|
|
|
#ifdef MODULE
|
|
int __init vmem_init(void)
|
|
{
|
|
test_vmem = NULL;
|
|
return class_register(&test_vmem_class);
|
|
}
|
|
#else
|
|
static int __init vmem_init(void)
|
|
{
|
|
test_vmem = NULL;
|
|
return class_register(&test_vmem_class);
|
|
}
|
|
|
|
postcore_initcall(vmem_init);
|
|
#endif
|
|
#endif
|
|
|