blob: 31b9ce5636bd32b431547cb6f2a380f59a67d903 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Google LWIS Test Device Driver
*
* Copyright (c) 2022 Google, LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME "-test-dev: " fmt
#include "lwis_device_test.h"
#include <linux/module.h>
#include <linux/platform_device.h>
#include "lwis_commands.h"
#include "lwis_device.h"
#include "lwis_init.h"
#include "lwis_platform.h"
#ifdef CONFIG_OF
#include "lwis_dt.h"
#endif
#define LWIS_DRIVER_NAME "lwis-test"
static int lwis_test_device_enable(struct lwis_device *lwis_dev);
static int lwis_test_device_disable(struct lwis_device *lwis_dev);
static int lwis_test_register_io(struct lwis_device *lwis_dev, struct lwis_io_entry *entry,
int access_size);
static struct lwis_device_subclass_operations test_vops = {
.register_io = lwis_test_register_io,
.register_io_barrier = NULL,
.device_enable = lwis_test_device_enable,
.device_disable = lwis_test_device_disable,
.event_enable = NULL,
.event_flags_updated = NULL,
.close = NULL,
};
static struct lwis_event_subscribe_operations test_subscribe_ops = {
.subscribe_event = NULL,
.unsubscribe_event = NULL,
.notify_event_subscriber = NULL,
.release = NULL,
};
static int lwis_test_device_enable(struct lwis_device *lwis_dev)
{
return 0;
}
static int lwis_test_device_disable(struct lwis_device *lwis_dev)
{
return 0;
}
static int lwis_test_register_io(struct lwis_device *lwis_dev, struct lwis_io_entry *entry,
int access_size)
{
struct lwis_test_device *test_dev =
container_of(lwis_dev, struct lwis_test_device, base_dev);
struct lwis_io_entry_rw_batch *rw_batch;
int i;
uint64_t reg_value;
if (!entry) {
dev_err(test_dev->base_dev.dev, "IO entry is NULL.\n");
return -EINVAL;
}
lwis_save_register_io_info(lwis_dev, entry, access_size);
if (entry->type == LWIS_IO_ENTRY_READ) {
if (entry->rw.offset >= SCRATCH_TEST_DEV_MEMORY_SIZE) {
dev_err(test_dev->base_dev.dev, "Offset (%llu) must be < %d\n",
entry->rw.offset, SCRATCH_TEST_DEV_MEMORY_SIZE);
return -EINVAL;
}
entry->rw.val = test_dev->scratch_mem[entry->rw.offset];
} else if (entry->type == LWIS_IO_ENTRY_READ_BATCH) {
rw_batch = &entry->rw_batch;
if (rw_batch->offset >= SCRATCH_TEST_DEV_MEMORY_SIZE ||
SCRATCH_TEST_DEV_MEMORY_SIZE - rw_batch->offset < rw_batch->size_in_bytes) {
dev_err(test_dev->base_dev.dev,
"Read range[offset(%llu) + size_in_bytes(%zu)] exceeds scratch memory (%d)\n",
rw_batch->offset, rw_batch->size_in_bytes,
SCRATCH_TEST_DEV_MEMORY_SIZE);
return -EINVAL;
}
for (i = 0; i < rw_batch->size_in_bytes; ++i) {
rw_batch->buf[i] = test_dev->scratch_mem[rw_batch->offset + i];
}
} else if (entry->type == LWIS_IO_ENTRY_WRITE) {
if (entry->rw.offset >= SCRATCH_TEST_DEV_MEMORY_SIZE) {
dev_err(test_dev->base_dev.dev, "Offset (%llu) must be < %d\n",
entry->rw.offset, SCRATCH_TEST_DEV_MEMORY_SIZE);
return -EINVAL;
}
test_dev->scratch_mem[entry->rw.offset] = entry->rw.val;
} else if (entry->type == LWIS_IO_ENTRY_WRITE_BATCH) {
rw_batch = &entry->rw_batch;
if (rw_batch->offset >= SCRATCH_TEST_DEV_MEMORY_SIZE ||
SCRATCH_TEST_DEV_MEMORY_SIZE - rw_batch->offset < rw_batch->size_in_bytes) {
dev_err(test_dev->base_dev.dev,
"Write range[offset(%llu) + size_in_bytes(%zu)] exceeds scratch memory (%d)\n",
rw_batch->offset, rw_batch->size_in_bytes,
SCRATCH_TEST_DEV_MEMORY_SIZE);
return -EINVAL;
}
for (i = 0; i < rw_batch->size_in_bytes; ++i) {
test_dev->scratch_mem[rw_batch->offset + i] = rw_batch->buf[i];
}
} else if (entry->type == LWIS_IO_ENTRY_MODIFY) {
if (entry->mod.offset >= SCRATCH_TEST_DEV_MEMORY_SIZE) {
dev_err(test_dev->base_dev.dev, "Offset (%llu) must be < %d\n",
entry->mod.offset, SCRATCH_TEST_DEV_MEMORY_SIZE);
return -EINVAL;
}
reg_value = test_dev->scratch_mem[entry->mod.offset];
reg_value &= ~entry->mod.val_mask;
reg_value |= entry->mod.val_mask & entry->mod.val;
test_dev->scratch_mem[entry->rw.offset] = reg_value;
} else {
dev_err(test_dev->base_dev.dev, "Invalid IO entry type: %d\n", entry->type);
return -EINVAL;
}
return 0;
}
static int lwis_test_device_setup(struct lwis_test_device *test_dev)
{
int ret = 0;
#ifdef CONFIG_OF
/* Parse device tree for device configurations */
ret = lwis_test_device_parse_dt(test_dev);
if (ret) {
dev_err(test_dev->base_dev.dev, "Failed to parse device tree\n");
}
#else
/* Non-device-tree init: Save for future implementation */
ret = -ENOSYS;
#endif
return ret;
}
static int lwis_test_device_probe(struct platform_device *plat_dev)
{
int ret = 0;
struct lwis_test_device *test_dev;
struct device *dev = &plat_dev->dev;
/* Allocate test device specific data construct */
test_dev = devm_kzalloc(dev, sizeof(struct lwis_test_device), GFP_KERNEL);
if (!test_dev) {
dev_err(dev, "Failed to allocate TEST device structure\n");
return -ENOMEM;
}
test_dev->base_dev.type = DEVICE_TYPE_TEST;
test_dev->base_dev.vops = test_vops;
test_dev->base_dev.subscribe_ops = test_subscribe_ops;
/* Call the base device probe function */
ret = lwis_base_probe(&test_dev->base_dev, plat_dev);
if (ret) {
dev_err(dev, "TEST device: Error in lwis base probe: %d\n", ret);
goto error_probe;
}
/* Call TEST device specific setup function */
ret = lwis_test_device_setup(test_dev);
if (ret) {
dev_err(test_dev->base_dev.dev, "Error in TEST device initialization\n");
lwis_base_unprobe(&test_dev->base_dev);
goto error_probe;
}
dev_info(test_dev->base_dev.dev, "TEST Device Probe: Success\n");
return 0;
error_probe:
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id lwis_id_match[] = {
{ .compatible = LWIS_TEST_DEVICE_COMPAT },
{},
};
// MODULE_DEVICE_TABLE(of, lwis_id_match);
static struct platform_driver lwis_driver = {
.probe = lwis_test_device_probe,
.driver = {
.name = LWIS_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = lwis_id_match,
},
};
#else /* CONFIG_OF not defined */
static struct platform_device_id lwis_driver_id[] = {
{
.name = LWIS_DRIVER_NAME,
.driver_data = 0,
},
{},
};
MODULE_DEVICE_TABLE(platform, lwis_driver_id);
static struct platform_driver lwis_driver = { .probe = lwis_test_device_probe,
.id_table = lwis_driver_id,
.driver = {
.name = LWIS_DRIVER_NAME,
.owner = THIS_MODULE,
} };
#endif /* CONFIG_OF */
/*
* lwis_test_device_init: Init function that will be called by the kernel
* initialization routines.
*/
int __init lwis_test_device_init(void)
{
int ret = 0;
pr_info("TEST device initialization\n");
ret = platform_driver_register(&lwis_driver);
if (ret)
pr_err("platform_driver_register failed: %d\n", ret);
return ret;
}
int lwis_test_device_deinit(void)
{
platform_driver_unregister(&lwis_driver);
return 0;
}