diff options
Diffstat (limited to 'drivers/remoteproc/pru_rproc.c')
| -rw-r--r-- | drivers/remoteproc/pru_rproc.c | 284 |
1 files changed, 255 insertions, 29 deletions
diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index 128bf9912f2c..5e3eb7b86a0e 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -2,20 +2,24 @@ /* * PRU-ICSS remoteproc driver for various TI SoCs * - * Copyright (C) 2014-2020 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2014-2022 Texas Instruments Incorporated - https://www.ti.com/ * * Author(s): * Suman Anna <s-anna@ti.com> * Andrew F. Davis <afd@ti.com> * Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org> for Texas Instruments + * Puranjay Mohan <p-mohan@ti.com> + * Md Danish Anwar <danishanwar@ti.com> */ #include <linux/bitops.h> #include <linux/debugfs.h> #include <linux/irqdomain.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/remoteproc/pruss.h> #include <linux/pruss_driver.h> #include <linux/remoteproc.h> @@ -79,21 +83,6 @@ enum pru_iomem { }; /** - * enum pru_type - PRU core type identifier - * - * @PRU_TYPE_PRU: Programmable Real-time Unit - * @PRU_TYPE_RTU: Auxiliary Programmable Real-Time Unit - * @PRU_TYPE_TX_PRU: Transmit Programmable Real-Time Unit - * @PRU_TYPE_MAX: just keep this one at the end - */ -enum pru_type { - PRU_TYPE_PRU = 0, - PRU_TYPE_RTU, - PRU_TYPE_TX_PRU, - PRU_TYPE_MAX, -}; - -/** * struct pru_private_data - device data for a PRU core * @type: type of the PRU core (PRU, RTU, Tx_PRU) * @is_k3: flag used to identify the need for special load handling @@ -111,13 +100,17 @@ struct pru_private_data { * @rproc: remoteproc pointer for this PRU core * @data: PRU core specific data * @mem_regions: data for each of the PRU memory regions + * @client_np: client device node + * @lock: mutex to protect client usage * @fw_name: name of firmware image used during loading * @mapped_irq: virtual interrupt numbers of created fw specific mapping * @pru_interrupt_map: pointer to interrupt mapping description (firmware) * @pru_interrupt_map_sz: pru_interrupt_map size + * @rmw_lock: lock for read, modify, write operations on registers * @dbg_single_step: debug state variable to set PRU into single step mode * @dbg_continuous: debug state variable to restore PRU execution mode * @evt_count: number of mapped events + * @gpmux_save: saved value for gpmux config */ struct pru_rproc { int id; @@ -126,13 +119,17 @@ struct pru_rproc { struct rproc *rproc; const struct pru_private_data *data; struct pruss_mem_region mem_regions[PRU_IOMEM_MAX]; + struct device_node *client_np; + struct mutex lock; const char *fw_name; unsigned int *mapped_irq; struct pru_irq_rsc *pru_interrupt_map; size_t pru_interrupt_map_sz; + spinlock_t rmw_lock; u32 dbg_single_step; u32 dbg_continuous; u8 evt_count; + u8 gpmux_save; }; static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg) @@ -146,6 +143,233 @@ void pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val) writel_relaxed(val, pru->mem_regions[PRU_IOMEM_CTRL].va + reg); } +static inline +void pru_control_set_reg(struct pru_rproc *pru, unsigned int reg, + u32 mask, u32 set) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&pru->rmw_lock, flags); + + val = pru_control_read_reg(pru, reg); + val &= ~mask; + val |= (set & mask); + pru_control_write_reg(pru, reg, val); + + spin_unlock_irqrestore(&pru->rmw_lock, flags); +} + +/** + * pru_rproc_set_firmware() - set firmware for a PRU core + * @rproc: the rproc instance of the PRU + * @fw_name: the new firmware name, or NULL if default is desired + * + * Return: 0 on success, or errno in error case. + */ +static int pru_rproc_set_firmware(struct rproc *rproc, const char *fw_name) +{ + struct pru_rproc *pru = rproc->priv; + + if (!fw_name) + fw_name = pru->fw_name; + + return rproc_set_firmware(rproc, fw_name); +} + +static struct rproc *__pru_rproc_get(struct device_node *np, int index) +{ + struct rproc *rproc; + phandle rproc_phandle; + int ret; + + ret = of_property_read_u32_index(np, "ti,prus", index, &rproc_phandle); + if (ret) + return ERR_PTR(ret); + + rproc = rproc_get_by_phandle(rproc_phandle); + if (!rproc) { + ret = -EPROBE_DEFER; + return ERR_PTR(ret); + } + + /* make sure it is PRU rproc */ + if (!is_pru_rproc(rproc->dev.parent)) { + rproc_put(rproc); + return ERR_PTR(-ENODEV); + } + + return rproc; +} + +/** + * pru_rproc_get() - get the PRU rproc instance from a device node + * @np: the user/client device node + * @index: index to use for the ti,prus property + * @pru_id: optional pointer to return the PRU remoteproc processor id + * + * This function looks through a client device node's "ti,prus" property at + * index @index and returns the rproc handle for a valid PRU remote processor if + * found. The function allows only one user to own the PRU rproc resource at a + * time. Caller must call pru_rproc_put() when done with using the rproc, not + * required if the function returns a failure. + * + * When optional @pru_id pointer is passed the PRU remoteproc processor id is + * returned. + * + * Return: rproc handle on success, and an ERR_PTR on failure using one + * of the following error values + * -ENODEV if device is not found + * -EBUSY if PRU is already acquired by anyone + * -EPROBE_DEFER is PRU device is not probed yet + */ +struct rproc *pru_rproc_get(struct device_node *np, int index, + enum pruss_pru_id *pru_id) +{ + struct rproc *rproc; + struct pru_rproc *pru; + struct device *dev; + const char *fw_name; + int ret; + u32 mux; + + rproc = __pru_rproc_get(np, index); + if (IS_ERR(rproc)) + return rproc; + + pru = rproc->priv; + dev = &rproc->dev; + + mutex_lock(&pru->lock); + + if (pru->client_np) { + mutex_unlock(&pru->lock); + ret = -EBUSY; + goto err_no_rproc_handle; + } + + pru->client_np = np; + rproc->sysfs_read_only = true; + + mutex_unlock(&pru->lock); + + if (pru_id) + *pru_id = pru->id; + + ret = pruss_cfg_get_gpmux(pru->pruss, pru->id, &pru->gpmux_save); + if (ret) { + dev_err(dev, "failed to get cfg gpmux: %d\n", ret); + goto err; + } + + /* An error here is acceptable for backward compatibility */ + ret = of_property_read_u32_index(np, "ti,pruss-gp-mux-sel", index, + &mux); + if (!ret) { + ret = pruss_cfg_set_gpmux(pru->pruss, pru->id, mux); + if (ret) { + dev_err(dev, "failed to set cfg gpmux: %d\n", ret); + goto err; + } + } + + ret = of_property_read_string_index(np, "firmware-name", index, + &fw_name); + if (!ret) { + ret = pru_rproc_set_firmware(rproc, fw_name); + if (ret) { + dev_err(dev, "failed to set firmware: %d\n", ret); + goto err; + } + } + + return rproc; + +err_no_rproc_handle: + rproc_put(rproc); + return ERR_PTR(ret); + +err: + pru_rproc_put(rproc); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(pru_rproc_get); + +/** + * pru_rproc_put() - release the PRU rproc resource + * @rproc: the rproc resource to release + * + * Releases the PRU rproc resource and makes it available to other + * users. + */ +void pru_rproc_put(struct rproc *rproc) +{ + struct pru_rproc *pru; + + if (IS_ERR_OR_NULL(rproc) || !is_pru_rproc(rproc->dev.parent)) + return; + + pru = rproc->priv; + + pruss_cfg_set_gpmux(pru->pruss, pru->id, pru->gpmux_save); + + pru_rproc_set_firmware(rproc, NULL); + + mutex_lock(&pru->lock); + + if (!pru->client_np) { + mutex_unlock(&pru->lock); + return; + } + + pru->client_np = NULL; + rproc->sysfs_read_only = false; + mutex_unlock(&pru->lock); + + rproc_put(rproc); +} +EXPORT_SYMBOL_GPL(pru_rproc_put); + +/** + * pru_rproc_set_ctable() - set the constant table index for the PRU + * @rproc: the rproc instance of the PRU + * @c: constant table index to set + * @addr: physical address to set it to + * + * Return: 0 on success, or errno in error case. + */ +int pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr) +{ + struct pru_rproc *pru; + unsigned int reg; + u32 mask, set; + u16 idx; + u16 idx_mask; + + if (IS_ERR_OR_NULL(rproc)) + return -EINVAL; + + if (!rproc->dev.parent || !is_pru_rproc(rproc->dev.parent)) + return -ENODEV; + + pru = rproc->priv; + /* pointer is 16 bit and index is 8-bit so mask out the rest */ + idx_mask = (c >= PRU_C28) ? 0xFFFF : 0xFF; + + /* ctable uses bit 8 and upwards only */ + idx = (addr >> 8) & idx_mask; + + /* configurable ctable (i.e. C24) starts at PRU_CTRL_CTBIR0 */ + reg = PRU_CTRL_CTBIR0 + 4 * (c >> 1); + mask = idx_mask << (16 * (c & 1)); + set = idx << (16 * (c & 1)); + + pru_control_set_reg(pru, reg, mask, set); + + return 0; +} +EXPORT_SYMBOL_GPL(pru_rproc_set_ctable); + static inline u32 pru_debug_read_reg(struct pru_rproc *pru, unsigned int reg) { return readl_relaxed(pru->mem_regions[PRU_IOMEM_DEBUG].va + reg); @@ -340,7 +564,7 @@ static int pru_handle_intrmap(struct rproc *rproc) return -ENODEV; } - fwspec.fwnode = of_node_to_fwnode(irq_parent); + fwspec.fwnode = of_fwnode_handle(irq_parent); fwspec.param_count = 3; for (i = 0; i < pru->evt_count; i++) { fwspec.param[0] = rsc->pru_intc_map[i].event; @@ -438,11 +662,11 @@ static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, size_t len) dram0 = pruss->mem_regions[PRUSS_MEM_DRAM0]; dram1 = pruss->mem_regions[PRUSS_MEM_DRAM1]; /* PRU1 has its local RAM addresses reversed */ - if (pru->id == 1) + if (pru->id == PRUSS_PRU1) swap(dram0, dram1); shrd_ram = pruss->mem_regions[PRUSS_MEM_SHRD_RAM2]; - if (da >= PRU_PDRAM_DA && da + len <= PRU_PDRAM_DA + dram0.size) { + if (da + len <= PRU_PDRAM_DA + dram0.size) { offset = da - PRU_PDRAM_DA; va = (__force void *)(dram0.va + offset); } else if (da >= PRU_SDRAM_DA && @@ -491,8 +715,7 @@ static void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, size_t len) */ da &= 0xfffff; - if (da >= PRU_IRAM_DA && - da + len <= PRU_IRAM_DA + pru->mem_regions[PRU_IOMEM_IRAM].size) { + if (da + len <= PRU_IRAM_DA + pru->mem_regions[PRU_IOMEM_IRAM].size) { offset = da - PRU_IRAM_DA; va = (__force void *)(pru->mem_regions[PRU_IOMEM_IRAM].va + offset); @@ -747,14 +970,14 @@ static int pru_rproc_set_id(struct pru_rproc *pru) case RTU0_IRAM_ADDR_MASK: fallthrough; case PRU0_IRAM_ADDR_MASK: - pru->id = 0; + pru->id = PRUSS_PRU0; break; case TX_PRU1_IRAM_ADDR_MASK: fallthrough; case RTU1_IRAM_ADDR_MASK: fallthrough; case PRU1_IRAM_ADDR_MASK: - pru->id = 1; + pru->id = PRUSS_PRU1; break; default: ret = -EINVAL; @@ -816,6 +1039,9 @@ static int pru_rproc_probe(struct platform_device *pdev) pru->pruss = platform_get_drvdata(ppdev); pru->rproc = rproc; pru->fw_name = fw_name; + pru->client_np = NULL; + spin_lock_init(&pru->rmw_lock); + mutex_init(&pru->lock); for (i = 0; i < ARRAY_SIZE(mem_names); i++) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -830,7 +1056,7 @@ static int pru_rproc_probe(struct platform_device *pdev) pru->mem_regions[i].pa = res->start; pru->mem_regions[i].size = resource_size(res); - dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n", + dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %p\n", mem_names[i], &pru->mem_regions[i].pa, pru->mem_regions[i].size, pru->mem_regions[i].va); } @@ -854,14 +1080,12 @@ static int pru_rproc_probe(struct platform_device *pdev) return 0; } -static int pru_rproc_remove(struct platform_device *pdev) +static void pru_rproc_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rproc *rproc = platform_get_drvdata(pdev); dev_dbg(dev, "%s: removing rproc %s\n", __func__, rproc->name); - - return 0; } static const struct pru_private_data pru_data = { @@ -904,7 +1128,7 @@ MODULE_DEVICE_TABLE(of, pru_rproc_match); static struct platform_driver pru_rproc_driver = { .driver = { - .name = "pru-rproc", + .name = PRU_RPROC_DRVNAME, .of_match_table = pru_rproc_match, .suppress_bind_attrs = true, }, @@ -916,5 +1140,7 @@ module_platform_driver(pru_rproc_driver); MODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); MODULE_AUTHOR("Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org>"); +MODULE_AUTHOR("Puranjay Mohan <p-mohan@ti.com>"); +MODULE_AUTHOR("Md Danish Anwar <danishanwar@ti.com>"); MODULE_DESCRIPTION("PRU-ICSS Remote Processor Driver"); MODULE_LICENSE("GPL v2"); |
