diff options
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r-- | drivers/remoteproc/Kconfig | 24 | ||||
-rw-r--r-- | drivers/remoteproc/Makefile | 10 | ||||
-rw-r--r-- | drivers/remoteproc/rproc-uclass.c | 417 | ||||
-rw-r--r-- | drivers/remoteproc/sandbox_testproc.c | 336 |
4 files changed, 787 insertions, 0 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig new file mode 100644 index 0000000000..437224b549 --- /dev/null +++ b/drivers/remoteproc/Kconfig @@ -0,0 +1,24 @@ +# +# (C) Copyright 2015 +# Texas Instruments Incorporated - http://www.ti.com/ +# SPDX-License-Identifier: GPL-2.0+ +# + +menu "Remote Processor drivers" + +# REMOTEPROC gets selected by drivers as needed +# All users should depend on DM +config REMOTEPROC + bool + depends on DM + +# Please keep the configuration alphabetically sorted. +config REMOTEPROC_SANDBOX + bool "Support for Test processor for Sandbox" + select REMOTEPROC + depends on DM + depends on SANDBOX + help + Say 'y' here to add support for test processor which does dummy + operations for sandbox platform. +endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile new file mode 100644 index 0000000000..720aa6e647 --- /dev/null +++ b/drivers/remoteproc/Makefile @@ -0,0 +1,10 @@ +# +# (C) Copyright 2015 +# Texas Instruments Incorporated - http://www.ti.com/ +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_REMOTEPROC) += rproc-uclass.o + +# Remote proc drivers - Please keep this list alphabetically sorted. +obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o diff --git a/drivers/remoteproc/rproc-uclass.c b/drivers/remoteproc/rproc-uclass.c new file mode 100644 index 0000000000..a421e12e5d --- /dev/null +++ b/drivers/remoteproc/rproc-uclass.c @@ -0,0 +1,417 @@ +/* + * (C) Copyright 2015 + * Texas Instruments Incorporated - http://www.ti.com/ + * SPDX-License-Identifier: GPL-2.0+ + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <remoteproc.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * for_each_remoteproc_device() - iterate through the list of rproc devices + * @fn: check function to call per match, if this function returns fail, + * iteration is aborted with the resultant error value + * @skip_dev: Device to skip calling the callback about. + * @data: Data to pass to the callback function + * + * Return: 0 if none of the callback returned a non 0 result, else returns the + * result from the callback function + */ +static int for_each_remoteproc_device(int (*fn) (struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data), + struct udevice *skip_dev, + const void *data) +{ + struct udevice *dev; + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + for (ret = uclass_find_first_device(UCLASS_REMOTEPROC, &dev); dev; + ret = uclass_find_next_device(&dev)) { + if (ret || dev == skip_dev) + continue; + uc_pdata = dev_get_uclass_platdata(dev); + ret = fn(dev, uc_pdata, data); + if (ret) + return ret; + } + + return 0; +} + +/** + * _rproc_name_is_unique() - iteration helper to check if rproc name is unique + * @dev: device that we are checking name for + * @uc_pdata: uclass platform data + * @data: compare data (this is the name we want to ensure is unique) + * + * Return: 0 is there is no match(is unique); if there is a match(we dont + * have a unique name), return -EINVAL. + */ +static int _rproc_name_is_unique(struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data) +{ + const char *check_name = data; + + /* devices not yet populated with data - so skip them */ + if (!uc_pdata->name && check_name) + return 0; + + /* Return 0 to search further if we dont match */ + if (strlen(uc_pdata->name) != strlen(check_name)) + return 0; + + if (!strcmp(uc_pdata->name, check_name)) + return -EINVAL; + + return 0; +} + +/** + * rproc_name_is_unique() - Check if the rproc name is unique + * @check_dev: Device we are attempting to ensure is unique + * @check_name: Name we are trying to ensure is unique. + * + * Return: true if we have a unique name, false if name is not unique. + */ +static bool rproc_name_is_unique(struct udevice *check_dev, + const char *check_name) +{ + int ret; + + ret = for_each_remoteproc_device(_rproc_name_is_unique, + check_dev, check_name); + return ret ? false : true; +} + +/** + * rproc_pre_probe() - Pre probe accessor for the uclass + * @dev: device for which we are preprobing + * + * Parses and fills up the uclass pdata for use as needed by core and + * remote proc drivers. + * + * Return: 0 if all wernt ok, else appropriate error value. + */ +static int rproc_pre_probe(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + const struct dm_rproc_ops *ops; + + uc_pdata = dev_get_uclass_platdata(dev); + + /* See if we need to populate via fdt */ + + if (!dev->platdata) { +#if CONFIG_IS_ENABLED(OF_CONTROL) + int node = dev->of_offset; + const void *blob = gd->fdt_blob; + bool tmp; + if (!blob) { + debug("'%s' no dt?\n", dev->name); + return -EINVAL; + } + debug("'%s': using fdt\n", dev->name); + uc_pdata->name = fdt_getprop(blob, node, + "remoteproc-name", NULL); + + /* Default is internal memory mapped */ + uc_pdata->mem_type = RPROC_INTERNAL_MEMORY_MAPPED; + tmp = fdtdec_get_bool(blob, node, + "remoteproc-internal-memory-mapped"); + if (tmp) + uc_pdata->mem_type = RPROC_INTERNAL_MEMORY_MAPPED; +#else + /* Nothing much we can do about this, can we? */ + return -EINVAL; +#endif + + } else { + struct dm_rproc_uclass_pdata *pdata = dev->platdata; + + debug("'%s': using legacy data\n", dev->name); + if (pdata->name) + uc_pdata->name = pdata->name; + uc_pdata->mem_type = pdata->mem_type; + uc_pdata->driver_plat_data = pdata->driver_plat_data; + } + + /* Else try using device Name */ + if (!uc_pdata->name) + uc_pdata->name = dev->name; + if (!uc_pdata->name) { + debug("Unnamed device!"); + return -EINVAL; + } + + if (!rproc_name_is_unique(dev, uc_pdata->name)) { + debug("%s duplicate name '%s'\n", dev->name, uc_pdata->name); + return -EINVAL; + } + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + + if (!ops->load || !ops->start) { + debug("%s driver has missing mandatory ops?\n", dev->name); + return -EINVAL; + } + + return 0; +} + +/** + * rproc_post_probe() - post probe accessor for the uclass + * @dev: deivce we finished probing + * + * initiate init function after the probe is completed. This allows + * the remote processor drivers to split up the initializations between + * probe and init as needed. + * + * Return: if the remote proc driver has a init routine, invokes it and + * hands over the return value. overall, 0 if all went well, else appropriate + * error value. + */ +static int rproc_post_probe(struct udevice *dev) +{ + const struct dm_rproc_ops *ops; + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + + if (ops->init) + return ops->init(dev); + + return 0; +} + +UCLASS_DRIVER(rproc) = { + .id = UCLASS_REMOTEPROC, + .name = "remoteproc", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .pre_probe = rproc_pre_probe, + .post_probe = rproc_post_probe, + .per_device_platdata_auto_alloc_size = + sizeof(struct dm_rproc_uclass_pdata), +}; + +/* Remoteproc subsystem access functions */ +/** + * _rproc_probe_dev() - iteration helper to probe a rproc device + * @dev: device to probe + * @uc_pdata: uclass data allocated for the device + * @data: unused + * + * Return: 0 if all ok, else appropriate error value. + */ +static int _rproc_probe_dev(struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data) +{ + int ret; + + ret = device_probe(dev); + + if (ret) + debug("%s: Failed to initialize - %d\n", dev->name, ret); + return ret; +} + +/** + * _rproc_dev_is_probed() - check if the device has been probed + * @dev: device to check + * @uc_pdata: unused + * @data: unused + * + * Return: -EAGAIN if not probed else return 0 + */ +static int _rproc_dev_is_probed(struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data) +{ + if (dev->flags & DM_FLAG_ACTIVATED) + return 0; + + return -EAGAIN; +} + +bool rproc_is_initialized(void) +{ + int ret = for_each_remoteproc_device(_rproc_dev_is_probed, NULL, NULL); + return ret ? false : true; +} + +int rproc_init(void) +{ + int ret; + + if (rproc_is_initialized()) { + debug("Already initialized\n"); + return -EINVAL; + } + + ret = for_each_remoteproc_device(_rproc_probe_dev, NULL, NULL); + return ret; +} + +int rproc_load(int id, ulong addr, ulong size) +{ + struct udevice *dev = NULL; + struct dm_rproc_uclass_pdata *uc_pdata; + const struct dm_rproc_ops *ops; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); + if (ret) { + debug("Unknown remote processor id '%d' requested(%d)\n", + id, ret); + return ret; + } + + uc_pdata = dev_get_uclass_platdata(dev); + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + + debug("Loading to '%s' from address 0x%08lX size of %lu bytes\n", + uc_pdata->name, addr, size); + if (ops->load) + return ops->load(dev, addr, size); + + debug("%s: data corruption?? mandatory function is missing!\n", + dev->name); + + return -EINVAL; +}; + +/* + * Completely internal helper enums.. + * Keeping this isolated helps this code evolve independent of other + * parts.. + */ +enum rproc_ops { + RPROC_START, + RPROC_STOP, + RPROC_RESET, + RPROC_PING, + RPROC_RUNNING, +}; + +/** + * _rproc_ops_wrapper() - wrapper for invoking remote proc driver callback + * @id: id of the remote processor + * @op: one of rproc_ops that indicate what operation to invoke + * + * Most of the checks and verification for remoteproc operations are more + * or less same for almost all operations. This allows us to put a wrapper + * and use the common checks to allow the driver to function appropriately. + * + * Return: 0 if all ok, else appropriate error value. + */ +static int _rproc_ops_wrapper(int id, enum rproc_ops op) +{ + struct udevice *dev = NULL; + struct dm_rproc_uclass_pdata *uc_pdata; + const struct dm_rproc_ops *ops; + int (*fn)(struct udevice *dev); + bool mandatory = false; + char *op_str; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); + if (ret) { + debug("Unknown remote processor id '%d' requested(%d)\n", + id, ret); + return ret; + } + + uc_pdata = dev_get_uclass_platdata(dev); + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + switch (op) { + case RPROC_START: + fn = ops->start; + mandatory = true; + op_str = "Starting"; + break; + case RPROC_STOP: + fn = ops->stop; + op_str = "Stopping"; + break; + case RPROC_RESET: + fn = ops->reset; + op_str = "Resetting"; + break; + case RPROC_RUNNING: + fn = ops->is_running; + op_str = "Checking if running:"; + break; + case RPROC_PING: + fn = ops->ping; + op_str = "Pinging"; + break; + default: + debug("what is '%d' operation??\n", op); + return -EINVAL; + } + + debug("%s %s...\n", op_str, uc_pdata->name); + if (fn) + return fn(dev); + + if (mandatory) + debug("%s: data corruption?? mandatory function is missing!\n", + dev->name); + + return -ENOSYS; +} + +int rproc_start(int id) +{ + return _rproc_ops_wrapper(id, RPROC_START); +}; + +int rproc_stop(int id) +{ + return _rproc_ops_wrapper(id, RPROC_STOP); +}; + +int rproc_reset(int id) +{ + return _rproc_ops_wrapper(id, RPROC_RESET); +}; + +int rproc_ping(int id) +{ + return _rproc_ops_wrapper(id, RPROC_PING); +}; + +int rproc_is_running(int id) +{ + return _rproc_ops_wrapper(id, RPROC_RUNNING); +}; diff --git a/drivers/remoteproc/sandbox_testproc.c b/drivers/remoteproc/sandbox_testproc.c new file mode 100644 index 0000000000..004c7792d1 --- /dev/null +++ b/drivers/remoteproc/sandbox_testproc.c @@ -0,0 +1,336 @@ +/* + * (C) Copyright 2015 + * Texas Instruments Incorporated - http://www.ti.com/ + * SPDX-License-Identifier: GPL-2.0+ + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <remoteproc.h> + +/** + * enum sandbox_state - different device states + * @sb_booted: Entry condition, just booted + * @sb_init: Initialized (basic environment is ready) + * @sb_reset: Held in reset (accessible, but not running) + * @sb_loaded: Loaded with image (but not running) + * @sb_running: Processor is running + */ +enum sandbox_state { + sb_booted, + sb_init, + sb_reset, + sb_loaded, + sb_running +}; + +/** + * struct sandbox_test_devdata - private data per device + * @current_state: device current state + */ +struct sandbox_test_devdata { + enum sandbox_state current_state; +}; + +/** + * sandbox_dev_move_to_state() - statemachine for our dummy device + * @dev: device to switch state + * @next_state: next proposed state + * + * This tries to follow the following statemachine: + * Entry + * | + * v + * +-------+ + * +---+ init | + * | | | <---------------------+ + * | +-------+ | + * | | + * | | + * | +--------+ | + * Load| | reset | | + * | | | <----------+ | + * | +--------+ | | + * | |Load | | + * | | | | + * | +----v----+ reset | | + * +-> | | (opt) | | + * | Loaded +-----------+ | + * | | | + * +----+----+ | + * | Start | + * +---v-----+ (opt) | + * +->| Running | Stop | + * Ping +- | +--------------------+ + * (opt) +---------+ + * + * (is_running does not change state) + * + * Return: 0 when valid state transition is seen, else returns -EINVAL + */ +static int sandbox_dev_move_to_state(struct udevice *dev, + enum sandbox_state next_state) +{ + struct sandbox_test_devdata *ddata = dev_get_priv(dev); + + /* No state transition is OK */ + if (ddata->current_state == next_state) + return 0; + + debug("current_state=%d, next_state=%d\n", ddata->current_state, + next_state); + switch (ddata->current_state) { + case sb_booted: + if (next_state == sb_init) + goto ok_state; + break; + + case sb_init: + if (next_state == sb_reset || next_state == sb_loaded) + goto ok_state; + break; + + case sb_reset: + if (next_state == sb_loaded || next_state == sb_init) + goto ok_state; + break; + + case sb_loaded: + if (next_state == sb_reset || next_state == sb_init || + next_state == sb_running) + goto ok_state; + break; + + case sb_running: + if (next_state == sb_reset || next_state == sb_init) + goto ok_state; + break; + }; + return -EINVAL; + +ok_state: + ddata->current_state = next_state; + return 0; +} + +/** + * sandbox_testproc_probe() - basic probe function + * @dev: test proc device that is being probed. + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_probe(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct sandbox_test_devdata *ddata; + int ret; + + uc_pdata = dev_get_uclass_platdata(dev); + ddata = dev_get_priv(dev); + if (!ddata) { + debug("%s: platform private data missing\n", uc_pdata->name); + return -EINVAL; + } + ret = sandbox_dev_move_to_state(dev, sb_booted); + debug("%s: called(%d)\n", uc_pdata->name, ret); + + return ret; +} + +/** + * sandbox_testproc_init() - Simple initialization function + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_init(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_platdata(dev); + + ret = sandbox_dev_move_to_state(dev, sb_init); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + if (ret) + debug("%s init failed\n", uc_pdata->name); + + return ret; +} + +/** + * sandbox_testproc_reset() - Reset the remote processor + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_reset(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_platdata(dev); + + ret = sandbox_dev_move_to_state(dev, sb_reset); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + + if (ret) + debug("%s reset failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_load() - (replace: short desc) + * @dev: device to operate upon + * @addr: Address of the binary image to load + * @size: Size (in bytes) of the binary image to load + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_load(struct udevice *dev, ulong addr, ulong size) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_platdata(dev); + + ret = sandbox_dev_move_to_state(dev, sb_loaded); + + debug("%s: called(%d) Loading to %08lX %lu size\n", + uc_pdata->name, ret, addr, size); + + if (ret) + debug("%s load failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_start() - Start the remote processor + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_start(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_platdata(dev); + + ret = sandbox_dev_move_to_state(dev, sb_running); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + + if (ret) + debug("%s start failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_stop() - Stop the remote processor + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_stop(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_platdata(dev); + + ret = sandbox_dev_move_to_state(dev, sb_init); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + + if (ret) + debug("%s stop failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_is_running() - Check if remote processor is running + * @dev: device to operate upon + * + * Return: 0 if running, 1 if not running + */ +static int sandbox_testproc_is_running(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct sandbox_test_devdata *ddata; + int ret = 1; + + uc_pdata = dev_get_uclass_platdata(dev); + ddata = dev_get_priv(dev); + + if (ddata->current_state == sb_running) + ret = 0; + debug("%s: called(%d)\n", uc_pdata->name, ret); + + return ret; +} + +/** + * sandbox_testproc_ping() - Try pinging remote processor + * @dev: device to operate upon + * + * Return: 0 if running, -EINVAL if not running + */ +static int sandbox_testproc_ping(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct sandbox_test_devdata *ddata; + int ret; + + uc_pdata = dev_get_uclass_platdata(dev); + ddata = dev_get_priv(dev); + + if (ddata->current_state == sb_running) + ret = 0; + else + ret = -EINVAL; + + debug("%s: called(%d)\n", uc_pdata->name, ret); + if (ret) + debug("%s: No response.(Not started?)\n", uc_pdata->name); + + return ret; +} + +static const struct dm_rproc_ops sandbox_testproc_ops = { + .init = sandbox_testproc_init, + .reset = sandbox_testproc_reset, + .load = sandbox_testproc_load, + .start = sandbox_testproc_start, + .stop = sandbox_testproc_stop, + .is_running = sandbox_testproc_is_running, + .ping = sandbox_testproc_ping, +}; + +static const struct udevice_id sandbox_ids[] = { + {.compatible = "sandbox,test-processor"}, + {} +}; + +U_BOOT_DRIVER(sandbox_testproc) = { + .name = "sandbox_test_proc", + .of_match = sandbox_ids, + .id = UCLASS_REMOTEPROC, + .ops = &sandbox_testproc_ops, + .probe = sandbox_testproc_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_test_devdata), +}; + +/* TODO(nm@ti.com): Remove this along with non-DT support */ +static struct dm_rproc_uclass_pdata proc_3_test = { + .name = "proc_3_legacy", + .mem_type = RPROC_INTERNAL_MEMORY_MAPPED, +}; + +U_BOOT_DEVICE(proc_3_demo) = { + .name = "sandbox_test_proc", + .platdata = &proc_3_test, +}; |