// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #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(const struct class *class, const 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(const struct class *class, const 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(const struct class *class, const 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", .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