/** * struct keystone_rproc_mem - internal memory structure * @cpu_addr: MPU virtual address of the memory region * @bus_addr: Bus address used to access the memory region * @dev_addr: Device address of the memory region from DSP view * @size: Size of the memory region
*/ struct keystone_rproc_mem { void __iomem *cpu_addr;
phys_addr_t bus_addr;
u32 dev_addr;
size_t size;
};
/** * struct keystone_rproc - keystone remote processor driver structure * @dev: cached device pointer * @rproc: remoteproc device handle * @mem: internal memory regions data * @num_mems: number of internal memory regions * @dev_ctrl: device control regmap handle * @reset: reset control handle * @boot_offset: boot register offset in @dev_ctrl regmap * @irq_ring: irq entry for vring * @irq_fault: irq entry for exception * @kick_gpio: gpio used for virtio kicks * @workqueue: workqueue for processing virtio interrupts
*/ struct keystone_rproc { struct device *dev; struct rproc *rproc; struct keystone_rproc_mem *mem; int num_mems; struct regmap *dev_ctrl; struct reset_control *reset; struct gpio_desc *kick_gpio;
u32 boot_offset; int irq_ring; int irq_fault; struct work_struct workqueue;
};
/* Put the DSP processor into reset */ staticvoid keystone_rproc_dsp_reset(struct keystone_rproc *ksproc)
{
reset_control_assert(ksproc->reset);
}
/* Configure the boot address and boot the DSP processor */ staticint keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
{ int ret;
if (boot_addr & (SZ_1K - 1)) {
dev_err(ksproc->dev, "invalid boot address 0x%x, must be aligned on a 1KB boundary\n",
boot_addr); return -EINVAL;
}
ret = regmap_write(ksproc->dev_ctrl, ksproc->boot_offset, boot_addr); if (ret) {
dev_err(ksproc->dev, "regmap_write of boot address failed, status = %d\n",
ret); return ret;
}
reset_control_deassert(ksproc->reset);
return 0;
}
/* * Process the remoteproc exceptions * * The exception reporting on Keystone DSP remote processors is very simple * compared to the equivalent processors on the OMAP family, it is notified * through a software-designed specific interrupt source in the IPC interrupt * generation register. * * This function just invokes the rproc_report_crash to report the exception * to the remoteproc driver core, to trigger a recovery.
*/ static irqreturn_t keystone_rproc_exception_interrupt(int irq, void *dev_id)
{ struct keystone_rproc *ksproc = dev_id;
/* * Main virtqueue message workqueue function * * This function is executed upon scheduling of the keystone remoteproc * driver's workqueue. The workqueue is scheduled by the vring ISR handler. * * There is no payload message indicating the virtqueue index as is the * case with mailbox-based implementations on OMAP family. As such, this * handler processes both the Tx and Rx virtqueue indices on every invocation. * The rproc_vq_interrupt function can detect if there are new unprocessed * messages or not (returns IRQ_NONE vs IRQ_HANDLED), but there is no need * to check for these return values. The index 0 triggering will process all * pending Rx buffers, and the index 1 triggering will process all newly * available Tx buffers and will wakeup any potentially blocked senders. * * NOTE: * 1. A payload could be added by using some of the source bits in the * IPC interrupt generation registers, but this would need additional * changes to the overall IPC stack, and currently there are no benefits * of adapting that approach. * 2. The current logic is based on an inherent design assumption of supporting * only 2 vrings, but this can be changed if needed.
*/ staticvoid handle_event(struct work_struct *work)
{ struct keystone_rproc *ksproc =
container_of(work, struct keystone_rproc, workqueue);
/* * Power up the DSP remote processor. * * This function will be invoked only after the firmware for this rproc * was loaded, parsed successfully, and all of its resource requirements * were met.
*/ staticint keystone_rproc_start(struct rproc *rproc)
{ struct keystone_rproc *ksproc = rproc->priv; int ret;
INIT_WORK(&ksproc->workqueue, handle_event);
ret = request_irq(ksproc->irq_ring, keystone_rproc_vring_interrupt, 0,
dev_name(ksproc->dev), ksproc); if (ret) {
dev_err(ksproc->dev, "failed to enable vring interrupt, ret = %d\n",
ret); goto out;
}
ret = request_irq(ksproc->irq_fault, keystone_rproc_exception_interrupt,
0, dev_name(ksproc->dev), ksproc); if (ret) {
dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n",
ret); goto free_vring_irq;
}
ret = keystone_rproc_dsp_boot(ksproc, rproc->bootaddr); if (ret) goto free_exc_irq;
/* * Stop the DSP remote processor. * * This function puts the DSP processor into reset, and finishes processing * of any pending messages.
*/ staticint keystone_rproc_stop(struct rproc *rproc)
{ struct keystone_rproc *ksproc = rproc->priv;
/* * Kick the remote processor to notify about pending unprocessed messages. * The vqid usage is not used and is inconsequential, as the kick is performed * through a simulated GPIO (a bit in an IPC interrupt-triggering register), * the remote processor is expected to process both its Tx and Rx virtqueues.
*/ staticvoid keystone_rproc_kick(struct rproc *rproc, int vqid)
{ struct keystone_rproc *ksproc = rproc->priv;
if (!ksproc->kick_gpio) return;
gpiod_set_value(ksproc->kick_gpio, 1);
}
/* * Custom function to translate a DSP device address (internal RAMs only) to a * kernel virtual address. The DSPs can access their RAMs at either an internal * address visible only from a DSP, or at the SoC-level bus address. Both these * addresses need to be looked through for translation. The translated addresses * can be used either by the remoteproc core for loading (when using kernel * remoteproc loader), or by any rpmsg bus drivers.
*/ staticvoid *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{ struct keystone_rproc *ksproc = rproc->priv; void __iomem *va = NULL;
phys_addr_t bus_addr;
u32 dev_addr, offset;
size_t size; int i;
if (len == 0) return NULL;
for (i = 0; i < ksproc->num_mems; i++) {
bus_addr = ksproc->mem[i].bus_addr;
dev_addr = ksproc->mem[i].dev_addr;
size = ksproc->mem[i].size;
if (da < KEYSTONE_RPROC_LOCAL_ADDRESS_MASK) { /* handle DSP-view addresses */ if ((da >= dev_addr) &&
((da + len) <= (dev_addr + size))) {
offset = da - dev_addr;
va = ksproc->mem[i].cpu_addr + offset; break;
}
} else { /* handle SoC-view addresses */ if ((da >= bus_addr) &&
(da + len) <= (bus_addr + size)) {
offset = da - bus_addr;
va = ksproc->mem[i].cpu_addr + offset; break;
}
}
}
for (i = 0; i < num_mems; i++) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
mem_names[i]);
ksproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res); if (IS_ERR(ksproc->mem[i].cpu_addr)) {
dev_err(dev, "failed to parse and map %s memory\n",
mem_names[i]); return PTR_ERR(ksproc->mem[i].cpu_addr);
}
ksproc->mem[i].bus_addr = res->start;
ksproc->mem[i].dev_addr =
res->start & KEYSTONE_RPROC_LOCAL_ADDRESS_MASK;
ksproc->mem[i].size = resource_size(res);
/* zero out memories to start in a pristine state */
memset((__force void *)ksproc->mem[i].cpu_addr, 0,
ksproc->mem[i].size);
}
ksproc->num_mems = num_mems;
if (!np) {
dev_err(dev, "only DT-based devices are supported\n"); return -ENODEV;
}
dsp_id = of_alias_get_id(np, "rproc"); if (dsp_id < 0) {
dev_warn(dev, "device does not have an alias id\n"); return dsp_id;
}
/* construct a custom default fw name - subject to change in future */
fw_name = devm_kasprintf(dev, GFP_KERNEL, "keystone-dsp%d-fw", dsp_id); if (!fw_name) return -ENOMEM;
ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc); if (ret) return ret;
ksproc->reset = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(ksproc->reset)) return PTR_ERR(ksproc->reset);
/* enable clock for accessing DSP internal memories */
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev); if (ret < 0) {
dev_err(dev, "failed to enable clock, status = %d\n", ret); goto disable_rpm;
}
ret = keystone_rproc_of_get_memories(pdev, ksproc); if (ret) goto disable_clk;
ksproc->irq_ring = platform_get_irq_byname(pdev, "vring"); if (ksproc->irq_ring < 0) {
ret = ksproc->irq_ring; goto disable_clk;
}
ksproc->irq_fault = platform_get_irq_byname(pdev, "exception"); if (ksproc->irq_fault < 0) {
ret = ksproc->irq_fault; goto disable_clk;
}
ksproc->kick_gpio = gpiod_get(dev, "kick", GPIOD_ASIS);
ret = PTR_ERR_OR_ZERO(ksproc->kick_gpio); if (ret) {
dev_err(dev, "failed to get gpio for virtio kicks, status = %d\n",
ret); goto disable_clk;
}
if (of_reserved_mem_device_init(dev))
dev_warn(dev, "device does not have specific CMA pool\n");
/* ensure the DSP is in reset before loading firmware */
ret = reset_control_status(ksproc->reset); if (ret < 0) {
dev_err(dev, "failed to get reset status, status = %d\n", ret); goto release_mem;
} elseif (ret == 0) {
WARN(1, "device is not in reset\n");
keystone_rproc_dsp_reset(ksproc);
}
ret = rproc_add(rproc); if (ret) {
dev_err(dev, "failed to add register device with remoteproc core, status = %d\n",
ret); goto release_mem;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.