// SPDX-License-Identifier: GPL-2.0-only /* * PRU-ICSS remoteproc driver for various TI SoCs * * Copyright (C) 2014-2022 Texas Instruments Incorporated - https://www.ti.com/ * * Author(s): * Suman Anna <[email protected]> * Andrew F. Davis <[email protected]> * Grzegorz Jaszczyk <[email protected]> for Texas Instruments * Puranjay Mohan <[email protected]> * Md Danish Anwar <[email protected]> */ #include <linux/bitops.h> #include <linux/debugfs.h> #include <linux/irqdomain.h> #include <linux/module.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> #include "remoteproc_internal.h" #include "remoteproc_elf_helpers.h" #include "pru_rproc.h" /* PRU_ICSS_PRU_CTRL registers */ #define PRU_CTRL_CTRL … #define PRU_CTRL_STS … #define PRU_CTRL_WAKEUP_EN … #define PRU_CTRL_CYCLE … #define PRU_CTRL_STALL … #define PRU_CTRL_CTBIR0 … #define PRU_CTRL_CTBIR1 … #define PRU_CTRL_CTPPR0 … #define PRU_CTRL_CTPPR1 … /* CTRL register bit-fields */ #define CTRL_CTRL_SOFT_RST_N … #define CTRL_CTRL_EN … #define CTRL_CTRL_SLEEPING … #define CTRL_CTRL_CTR_EN … #define CTRL_CTRL_SINGLE_STEP … #define CTRL_CTRL_RUNSTATE … /* PRU_ICSS_PRU_DEBUG registers */ #define PRU_DEBUG_GPREG(x) … #define PRU_DEBUG_CT_REG(x) … /* PRU/RTU/Tx_PRU Core IRAM address masks */ #define PRU_IRAM_ADDR_MASK … #define PRU0_IRAM_ADDR_MASK … #define PRU1_IRAM_ADDR_MASK … #define RTU0_IRAM_ADDR_MASK … #define RTU1_IRAM_ADDR_MASK … #define TX_PRU0_IRAM_ADDR_MASK … #define TX_PRU1_IRAM_ADDR_MASK … /* PRU device addresses for various type of PRU RAMs */ #define PRU_IRAM_DA … #define PRU_PDRAM_DA … #define PRU_SDRAM_DA … #define PRU_SHRDRAM_DA … #define MAX_PRU_SYS_EVENTS … /** * enum pru_iomem - PRU core memory/register range identifiers * * @PRU_IOMEM_IRAM: PRU Instruction RAM range * @PRU_IOMEM_CTRL: PRU Control register range * @PRU_IOMEM_DEBUG: PRU Debug register range * @PRU_IOMEM_MAX: just keep this one at the end */ enum pru_iomem { … }; /** * 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 */ struct pru_private_data { … }; /** * struct pru_rproc - PRU remoteproc structure * @id: id of the PRU core within the PRUSS * @dev: PRU core device pointer * @pruss: back-reference to parent PRUSS structure * @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 { … }; static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg) { … } static inline void pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val) { … } static inline void pru_control_set_reg(struct pru_rproc *pru, unsigned int reg, u32 mask, u32 set) { … } /** * 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) { … } static struct rproc *__pru_rproc_get(struct device_node *np, int index) { … } /** * 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) { … } EXPORT_SYMBOL_GPL(…); /** * 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) { … } EXPORT_SYMBOL_GPL(…); /** * 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) { … } EXPORT_SYMBOL_GPL(…); static inline u32 pru_debug_read_reg(struct pru_rproc *pru, unsigned int reg) { … } static int regs_show(struct seq_file *s, void *data) { … } DEFINE_SHOW_ATTRIBUTE(…); /* * Control PRU single-step mode * * This is a debug helper function used for controlling the single-step * mode of the PRU. The PRU Debug registers are not accessible when the * PRU is in RUNNING state. * * Writing a non-zero value sets the PRU into single-step mode irrespective * of its previous state. The PRU mode is saved only on the first set into * a single-step mode. Writing a zero value will restore the PRU into its * original mode. */ static int pru_rproc_debug_ss_set(void *data, u64 val) { … } static int pru_rproc_debug_ss_get(void *data, u64 *val) { … } DEFINE_DEBUGFS_ATTRIBUTE(…); /* * Create PRU-specific debugfs entries * * The entries are created only if the parent remoteproc debugfs directory * exists, and will be cleaned up by the remoteproc core. */ static void pru_rproc_create_debug_entries(struct rproc *rproc) { … } static void pru_dispose_irq_mapping(struct pru_rproc *pru) { … } /* * Parse the custom PRU interrupt map resource and configure the INTC * appropriately. */ static int pru_handle_intrmap(struct rproc *rproc) { … } static int pru_rproc_start(struct rproc *rproc) { … } static int pru_rproc_stop(struct rproc *rproc) { … } /* * Convert PRU device address (data spaces only) to kernel virtual address. * * Each PRU has access to all data memories within the PRUSS, accessible at * different ranges. So, look through both its primary and secondary Data * RAMs as well as any shared Data RAM to convert a PRU device address to * kernel virtual address. Data RAM0 is primary Data RAM for PRU0 and Data * RAM1 is primary Data RAM for PRU1. */ static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, size_t len) { … } /* * Convert PRU device address (instruction space) to kernel virtual address. * * A PRU does not have an unified address space. Each PRU has its very own * private Instruction RAM, and its device address is identical to that of * its primary Data RAM device address. */ static void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, size_t len) { … } /* * Provide address translations for only PRU Data RAMs through the remoteproc * core for any PRU client drivers. The PRU Instruction RAM access is restricted * only to the PRU loader code. */ static void *pru_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) { … } /* PRU-specific address translator used by PRU loader. */ static void *pru_da_to_va(struct rproc *rproc, u64 da, size_t len, bool is_iram) { … } static struct rproc_ops pru_rproc_ops = …; /* * Custom memory copy implementation for ICSSG PRU/RTU/Tx_PRU Cores * * The ICSSG PRU/RTU/Tx_PRU cores have a memory copying issue with IRAM * memories, that is not seen on previous generation SoCs. The data is reflected * properly in the IRAM memories only for integer (4-byte) copies. Any unaligned * copies result in all the other pre-existing bytes zeroed out within that * 4-byte boundary, thereby resulting in wrong text/code in the IRAMs. Also, the * IRAM memory port interface does not allow any 8-byte copies (as commonly used * by ARM64 memcpy implementation) and throws an exception. The DRAM memory * ports do not show this behavior. */ static int pru_rproc_memcpy(void *dest, const void *src, size_t count) { … } static int pru_rproc_load_elf_segments(struct rproc *rproc, const struct firmware *fw) { … } static const void * pru_rproc_find_interrupt_map(struct device *dev, const struct firmware *fw) { … } /* * Use a custom parse_fw callback function for dealing with PRU firmware * specific sections. * * The firmware blob can contain optional ELF sections: .resource_table section * and .pru_irq_map one. The second one contains the PRUSS interrupt mapping * description, which needs to be setup before powering on the PRU core. To * avoid RAM wastage this ELF section is not mapped to any ELF segment (by the * firmware linker) and therefore is not loaded to PRU memory. */ static int pru_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { … } /* * Compute PRU id based on the IRAM addresses. The PRU IRAMs are * always at a particular offset within the PRUSS address space. */ static int pru_rproc_set_id(struct pru_rproc *pru) { … } static int pru_rproc_probe(struct platform_device *pdev) { … } static void pru_rproc_remove(struct platform_device *pdev) { … } static const struct pru_private_data pru_data = …; static const struct pru_private_data k3_pru_data = …; static const struct pru_private_data k3_rtu_data = …; static const struct pru_private_data k3_tx_pru_data = …; static const struct of_device_id pru_rproc_match[] = …; MODULE_DEVICE_TABLE(of, pru_rproc_match); static struct platform_driver pru_rproc_driver = …; module_platform_driver(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;