cregit-Linux how code gets into the kernel

Release 4.14 drivers/remoteproc/keystone_remoteproc.c

/*
 * TI Keystone DSP remoteproc driver
 *
 * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/remoteproc.h>
#include <linux/reset.h>

#include "remoteproc_internal.h"


#define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK	(SZ_16M - 1)

/**
 * 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;
	
u32 boot_offset;
	
int irq_ring;
	
int irq_fault;
	
int kick_gpio;
	
struct work_struct workqueue;
};

/* Put the DSP processor into reset */

static void keystone_rproc_dsp_reset(struct keystone_rproc *ksproc) { reset_control_assert(ksproc->reset); }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna18100.00%1100.00%
Total18100.00%1100.00%

/* Configure the boot address and boot the DSP processor */
static int 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; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna89100.00%1100.00%
Total89100.00%1100.00%

/* * 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; rproc_report_crash(ksproc->rproc, RPROC_FATAL_ERROR); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna32100.00%1100.00%
Total32100.00%1100.00%

/* * 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. */
static void handle_event(struct work_struct *work) { struct keystone_rproc *ksproc = container_of(work, struct keystone_rproc, workqueue); rproc_vq_interrupt(ksproc->rproc, 0); rproc_vq_interrupt(ksproc->rproc, 1); }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna44100.00%1100.00%
Total44100.00%1100.00%

/* * Interrupt handler for processing vring kicks from remote processor */
static irqreturn_t keystone_rproc_vring_interrupt(int irq, void *dev_id) { struct keystone_rproc *ksproc = dev_id; schedule_work(&ksproc->workqueue); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna31100.00%1100.00%
Total31100.00%1100.00%

/* * 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. */
static int 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; return 0; free_exc_irq: free_irq(ksproc->irq_fault, ksproc); free_vring_irq: free_irq(ksproc->irq_ring, ksproc); flush_work(&ksproc->workqueue); out: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna173100.00%1100.00%
Total173100.00%1100.00%

/* * Stop the DSP remote processor. * * This function puts the DSP processor into reset, and finishes processing * of any pending messages. */
static int keystone_rproc_stop(struct rproc *rproc) { struct keystone_rproc *ksproc = rproc->priv; keystone_rproc_dsp_reset(ksproc); free_irq(ksproc->irq_fault, ksproc); free_irq(ksproc->irq_ring, ksproc); flush_work(&ksproc->workqueue); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna54100.00%1100.00%
Total54100.00%1100.00%

/* * 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. */
static void keystone_rproc_kick(struct rproc *rproc, int vqid) { struct keystone_rproc *ksproc = rproc->priv; if (WARN_ON(ksproc->kick_gpio < 0)) return; gpio_set_value(ksproc->kick_gpio, 1); }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna44100.00%1100.00%
Total44100.00%1100.00%

/* * 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. */
static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len) { 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; } } } return (__force void *)va; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna214100.00%1100.00%
Total214100.00%1100.00%

static const struct rproc_ops keystone_rproc_ops = { .start = keystone_rproc_start, .stop = keystone_rproc_stop, .kick = keystone_rproc_kick, .da_to_va = keystone_rproc_da_to_va, };
static int keystone_rproc_of_get_memories(struct platform_device *pdev, struct keystone_rproc *ksproc) { static const char * const mem_names[] = {"l2sram", "l1pram", "l1dram"}; struct device *dev = &pdev->dev; struct resource *res; int num_mems = 0; int i; num_mems = ARRAY_SIZE(mem_names); ksproc->mem = devm_kcalloc(ksproc->dev, num_mems, sizeof(*ksproc->mem), GFP_KERNEL); if (!ksproc->mem) return -ENOMEM; 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; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna262100.00%1100.00%
Total262100.00%1100.00%


static int keystone_rproc_of_get_dev_syscon(struct platform_device *pdev, struct keystone_rproc *ksproc) { struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; if (!of_property_read_bool(np, "ti,syscon-dev")) { dev_err(dev, "ti,syscon-dev property is absent\n"); return -EINVAL; } ksproc->dev_ctrl = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); if (IS_ERR(ksproc->dev_ctrl)) { ret = PTR_ERR(ksproc->dev_ctrl); return ret; } if (of_property_read_u32_index(np, "ti,syscon-dev", 1, &ksproc->boot_offset)) { dev_err(dev, "couldn't read the boot register offset\n"); return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna129100.00%1100.00%
Total129100.00%1100.00%


static int keystone_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct keystone_rproc *ksproc; struct rproc *rproc; int dsp_id; char *fw_name = NULL; char *template = "keystone-dsp%d-fw"; int name_len = 0; int ret = 0; 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 */ name_len = strlen(template); /* assuming a single digit alias */ fw_name = devm_kzalloc(dev, name_len, GFP_KERNEL); if (!fw_name) return -ENOMEM; snprintf(fw_name, name_len, template, dsp_id); rproc = rproc_alloc(dev, dev_name(dev), &keystone_rproc_ops, fw_name, sizeof(*ksproc)); if (!rproc) return -ENOMEM; rproc->has_iommu = false; ksproc = rproc->priv; ksproc->rproc = rproc; ksproc->dev = dev; ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc); if (ret) goto free_rproc; ksproc->reset = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(ksproc->reset)) { ret = PTR_ERR(ksproc->reset); goto free_rproc; } /* enable clock for accessing DSP internal memories */ pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { dev_err(dev, "failed to enable clock, status = %d\n", ret); pm_runtime_put_noidle(dev); 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; dev_err(dev, "failed to get vring interrupt, status = %d\n", ret); goto disable_clk; } ksproc->irq_fault = platform_get_irq_byname(pdev, "exception"); if (ksproc->irq_fault < 0) { ret = ksproc->irq_fault; dev_err(dev, "failed to get exception interrupt, status = %d\n", ret); goto disable_clk; } ksproc->kick_gpio = of_get_named_gpio_flags(np, "kick-gpios", 0, NULL); if (ksproc->kick_gpio < 0) { ret = ksproc->kick_gpio; 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; } else if (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; } platform_set_drvdata(pdev, ksproc); return 0; release_mem: of_reserved_mem_device_release(dev); disable_clk: pm_runtime_put_sync(dev); disable_rpm: pm_runtime_disable(dev); free_rproc: rproc_free(rproc); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna51090.75%133.33%
Andrew F. Davis519.07%133.33%
Philipp Zabel10.18%133.33%
Total562100.00%3100.00%


static int keystone_rproc_remove(struct platform_device *pdev) { struct keystone_rproc *ksproc = platform_get_drvdata(pdev); rproc_del(ksproc->rproc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); rproc_free(ksproc->rproc); of_reserved_mem_device_release(&pdev->dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Suman Anna62100.00%1100.00%
Total62100.00%1100.00%

static const struct of_device_id keystone_rproc_of_match[] = { { .compatible = "ti,k2hk-dsp", }, { .compatible = "ti,k2l-dsp", }, { .compatible = "ti,k2e-dsp", }, { .compatible = "ti,k2g-dsp", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, keystone_rproc_of_match); static struct platform_driver keystone_rproc_driver = { .probe = keystone_rproc_probe, .remove = keystone_rproc_remove, .driver = { .name = "keystone-rproc", .of_match_table = keystone_rproc_of_match, }, }; module_platform_driver(keystone_rproc_driver); MODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("TI Keystone DSP Remoteproc driver");

Overall Contributors

PersonTokensPropCommitsCommitProp
Suman Anna191997.36%250.00%
Andrew F. Davis512.59%125.00%
Philipp Zabel10.05%125.00%
Total1971100.00%4100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.