12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031 |
- /*
- * PRU-ICSS remoteproc driver for various TI SoCs
- *
- * Copyright (C) 2014-2017 Texas Instruments Incorporated - http://www.ti.com/
- * Suman Anna <s-anna@ti.com>
- * Andrew F. Davis <afd@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/bitops.h>
- #include <linux/debugfs.h>
- #include <linux/interrupt.h>
- #include <linux/mailbox_client.h>
- #include <linux/module.h>
- #include <linux/of_device.h>
- #include <linux/remoteproc.h>
- #include <linux/pruss.h>
- #include "remoteproc_internal.h"
- #include "pruss.h"
- #include "pru_rproc.h"
- /* PRU_ICSS_PRU_CTRL registers */
- #define PRU_CTRL_CTRL 0x0000
- #define PRU_CTRL_STS 0x0004
- #define PRU_CTRL_WAKEUP_EN 0x0008
- #define PRU_CTRL_CYCLE 0x000C
- #define PRU_CTRL_STALL 0x0010
- #define PRU_CTRL_CTBIR0 0x0020
- #define PRU_CTRL_CTBIR1 0x0024
- #define PRU_CTRL_CTPPR0 0x0028
- #define PRU_CTRL_CTPPR1 0x002C
- /* CTRL register bit-fields */
- #define CTRL_CTRL_SOFT_RST_N BIT(0)
- #define CTRL_CTRL_EN BIT(1)
- #define CTRL_CTRL_SLEEPING BIT(2)
- #define CTRL_CTRL_CTR_EN BIT(3)
- #define CTRL_CTRL_SINGLE_STEP BIT(8)
- #define CTRL_CTRL_RUNSTATE BIT(15)
- /* PRU_ICSS_PRU_DEBUG registers */
- #define PRU_DEBUG_GPREG(x) (0x0000 + (x) * 4)
- #define PRU_DEBUG_CT_REG(x) (0x0080 + (x) * 4)
- /**
- * enum pru_mem - PRU core memory range identifiers
- */
- enum pru_mem {
- PRU_MEM_IRAM = 0,
- PRU_MEM_CTRL,
- PRU_MEM_DEBUG,
- PRU_MEM_MAX,
- };
- /**
- * struct pru_private_data - PRU core private data
- * @id: PRU index
- * @fw_name: firmware name to be used for the PRU core
- */
- struct pru_private_data {
- u32 id;
- const char *fw_name;
- };
- /**
- * struct pru_match_private_data - private data to handle multiple instances
- * @device_name: device name of the PRU processor core instance
- * @priv_data: PRU driver private data for this PRU processor core instance
- */
- struct pru_match_private_data {
- const char *device_name;
- struct pru_private_data *priv_data;
- };
- /**
- * struct pru_rproc - PRU remoteproc structure
- * @id: id of the PRU core within the PRUSS
- * @pruss: back-reference to parent PRUSS structure
- * @rproc: remoteproc pointer for this PRU core
- * @mbox: mailbox channel handle used for vring signalling with MPU
- * @client: mailbox client to request the mailbox channel
- * @irq_ring: IRQ number to use for processing vring buffers
- * @irq_kick: IRQ number to use to perform virtio kick
- * @mem_regions: data for each of the PRU memory regions
- * @intc_config: PRU INTC configuration data
- * @rmw_lock: lock for read, modify, write operations on registers
- * @iram_da: device address of Instruction RAM for this PRU
- * @pdram_da: device address of primary Data RAM for this PRU
- * @sdram_da: device address of secondary Data RAM for this PRU
- * @shrdram_da: device address of shared Data RAM
- * @fw_name: name of firmware image used during loading
- * @dbg_single_step: debug state variable to set PRU into single step mode
- * @dbg_continuous: debug state variable to restore PRU execution mode
- */
- struct pru_rproc {
- int id;
- struct pruss *pruss;
- struct rproc *rproc;
- struct mbox_chan *mbox;
- struct mbox_client client;
- int irq_vring;
- int irq_kick;
- struct pruss_mem_region mem_regions[PRU_MEM_MAX];
- struct pruss_intc_config intc_config;
- spinlock_t rmw_lock; /* register access lock */
- u32 iram_da;
- u32 pdram_da;
- u32 sdram_da;
- u32 shrdram_da;
- const char *fw_name;
- u32 dbg_single_step;
- u32 dbg_continuous;
- };
- static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg)
- {
- return readl_relaxed(pru->mem_regions[PRU_MEM_CTRL].va + reg);
- }
- static inline
- void pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val)
- {
- writel_relaxed(val, pru->mem_regions[PRU_MEM_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_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
- */
- int pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr)
- {
- struct pru_rproc *pru = rproc->priv;
- unsigned int reg;
- u32 mask, set;
- u16 idx;
- u16 idx_mask;
- /* 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_MEM_DEBUG].va + reg);
- }
- static inline
- void pru_debug_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val)
- {
- writel_relaxed(val, pru->mem_regions[PRU_MEM_DEBUG].va + reg);
- }
- /*
- * 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, int len)
- {
- struct pruss_mem_region dram0, dram1, shrd_ram;
- struct pruss *pruss = pru->pruss;
- u32 offset;
- void *va = NULL;
- if (len <= 0)
- return NULL;
- 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)
- swap(dram0, dram1);
- shrd_ram = pruss->mem_regions[PRUSS_MEM_SHRD_RAM2];
- if (da >= pru->pdram_da && da + len <= pru->pdram_da + dram0.size) {
- offset = da - pru->pdram_da;
- va = (__force void *)(dram0.va + offset);
- } else if (da >= pru->sdram_da &&
- da + len <= pru->sdram_da + dram1.size) {
- offset = da - pru->sdram_da;
- va = (__force void *)(dram1.va + offset);
- } else if (da >= pru->shrdram_da &&
- da + len <= pru->shrdram_da + shrd_ram.size) {
- offset = da - pru->shrdram_da;
- va = (__force void *)(shrd_ram.va + offset);
- }
- return va;
- }
- /*
- * 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, int len)
- {
- u32 offset;
- void *va = NULL;
- if (len <= 0)
- return NULL;
- if (da >= pru->iram_da &&
- da + len <= pru->iram_da + pru->mem_regions[PRU_MEM_IRAM].size) {
- offset = da - pru->iram_da;
- va = (__force void *)(pru->mem_regions[PRU_MEM_IRAM].va +
- offset);
- }
- return va;
- }
- static int pru_rproc_debug_read_regs(struct seq_file *s, void *data)
- {
- struct rproc *rproc = s->private;
- struct pru_rproc *pru = rproc->priv;
- int i, nregs = 32;
- u32 pru_sts;
- int pru_is_running;
- seq_puts(s, "============== Control Registers ==============\n");
- seq_printf(s, "CTRL := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_CTRL));
- pru_sts = pru_control_read_reg(pru, PRU_CTRL_STS);
- seq_printf(s, "STS (PC) := 0x%08x (0x%08x)\n", pru_sts, pru_sts << 2);
- seq_printf(s, "WAKEUP_EN := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_WAKEUP_EN));
- seq_printf(s, "CYCLE := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_CYCLE));
- seq_printf(s, "STALL := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_STALL));
- seq_printf(s, "CTBIR0 := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_CTBIR0));
- seq_printf(s, "CTBIR1 := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_CTBIR1));
- seq_printf(s, "CTPPR0 := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_CTPPR0));
- seq_printf(s, "CTPPR1 := 0x%08x\n",
- pru_control_read_reg(pru, PRU_CTRL_CTPPR1));
- seq_puts(s, "=============== Debug Registers ===============\n");
- pru_is_running = pru_control_read_reg(pru, PRU_CTRL_CTRL) &
- CTRL_CTRL_RUNSTATE;
- if (pru_is_running) {
- seq_puts(s, "PRU is executing, cannot print/access debug registers.\n");
- return 0;
- }
- for (i = 0; i < nregs; i++) {
- seq_printf(s, "GPREG%-2d := 0x%08x\tCT_REG%-2d := 0x%08x\n",
- i, pru_debug_read_reg(pru, PRU_DEBUG_GPREG(i)),
- i, pru_debug_read_reg(pru, PRU_DEBUG_CT_REG(i)));
- }
- return 0;
- }
- static int pru_rproc_debug_regs_open(struct inode *inode, struct file *file)
- {
- return single_open(file, pru_rproc_debug_read_regs, inode->i_private);
- }
- static const struct file_operations pru_rproc_debug_regs_ops = {
- .open = pru_rproc_debug_regs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- /*
- * 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 non-zero value will restore the PRU into
- * its original mode.
- */
- static int pru_rproc_debug_ss_set(void *data, u64 val)
- {
- struct rproc *rproc = data;
- struct pru_rproc *pru = rproc->priv;
- u32 reg_val;
- val = val ? 1 : 0;
- if (!val && !pru->dbg_single_step)
- return 0;
- reg_val = pru_control_read_reg(pru, PRU_CTRL_CTRL);
- if (val && !pru->dbg_single_step)
- pru->dbg_continuous = reg_val;
- if (val)
- reg_val |= CTRL_CTRL_SINGLE_STEP | CTRL_CTRL_EN;
- else
- reg_val = pru->dbg_continuous;
- pru->dbg_single_step = val;
- pru_control_write_reg(pru, PRU_CTRL_CTRL, reg_val);
- return 0;
- }
- static int pru_rproc_debug_ss_get(void *data, u64 *val)
- {
- struct rproc *rproc = data;
- struct pru_rproc *pru = rproc->priv;
- *val = pru->dbg_single_step;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(pru_rproc_debug_ss_fops, pru_rproc_debug_ss_get,
- pru_rproc_debug_ss_set, "%llu\n");
- /*
- * 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)
- {
- if (!rproc->dbg_dir)
- return;
- debugfs_create_file("regs", 0400, rproc->dbg_dir,
- rproc, &pru_rproc_debug_regs_ops);
- debugfs_create_file("single_step", 0600, rproc->dbg_dir,
- rproc, &pru_rproc_debug_ss_fops);
- }
- /**
- * pru_rproc_mbox_callback() - inbound mailbox message handler
- * @client: mailbox client pointer used for requesting the mailbox channel
- * @data: mailbox payload
- *
- * This handler is invoked by omap's mailbox driver whenever a mailbox
- * message is received. Usually, the mailbox payload simply contains
- * the index of the virtqueue that is kicked by the PRU remote processor,
- * and we let remoteproc core handle it.
- *
- * In addition to virtqueue indices, we might also have some out-of-band
- * values that indicates different events. Those values are deliberately
- * very big so they don't coincide with virtqueue indices.
- */
- static void pru_rproc_mbox_callback(struct mbox_client *client, void *data)
- {
- struct pru_rproc *pru = container_of(client, struct pru_rproc, client);
- struct device *dev = &pru->rproc->dev;
- u32 msg = (u32)data;
- dev_dbg(dev, "mbox msg: 0x%x\n", msg);
- /* msg contains the index of the triggered vring */
- if (rproc_vq_interrupt(pru->rproc, msg) == IRQ_NONE)
- dev_dbg(dev, "no message was found in vqid %d\n", msg);
- }
- /**
- * pru_rproc_vring_interrupt() - interrupt handler for processing vrings
- * @irq: irq number associated with the PRU event MPU is listening on
- * @data: interrupt handler data, will be a PRU rproc structure
- *
- * This handler is used by the PRU remoteproc driver when using PRU system
- * events for processing the virtqueues. Unlike the mailbox IP, there is
- * no payload associated with an interrupt, so either a unique event is
- * used for each virtqueue kick, or a both virtqueues are processed on
- * a single event. The latter is chosen to conserve the usable PRU system
- * events.
- */
- static irqreturn_t pru_rproc_vring_interrupt(int irq, void *data)
- {
- struct pru_rproc *pru = data;
- dev_dbg(&pru->rproc->dev, "got vring irq\n");
- /* process incoming buffers on both the Rx and Tx vrings */
- rproc_vq_interrupt(pru->rproc, 0);
- rproc_vq_interrupt(pru->rproc, 1);
- return IRQ_HANDLED;
- }
- /* kick a virtqueue */
- static void pru_rproc_kick(struct rproc *rproc, int vq_id)
- {
- struct device *dev = &rproc->dev;
- struct pru_rproc *pru = rproc->priv;
- int ret;
- dev_dbg(dev, "kicking vqid %d on PRU%d\n", vq_id, pru->id);
- if (pru->mbox) {
- /*
- * send the index of the triggered virtqueue in the mailbox
- * payload
- */
- ret = mbox_send_message(pru->mbox, (void *)vq_id);
- if (ret < 0)
- dev_err(dev, "mbox_send_message failed: %d\n", ret);
- } else if (pru->irq_kick > 0) {
- ret = pruss_intc_trigger(pru->irq_kick);
- if (ret < 0)
- dev_err(dev, "pruss_intc_trigger failed: %d\n", ret);
- }
- }
- /* start a PRU core */
- static int pru_rproc_start(struct rproc *rproc)
- {
- struct device *dev = &rproc->dev;
- struct pru_rproc *pru = rproc->priv;
- u32 val;
- int ret;
- dev_dbg(dev, "starting PRU%d: entry-point = 0x%x\n",
- pru->id, (rproc->bootaddr >> 2));
- if (!list_empty(&pru->rproc->rvdevs)) {
- if (!pru->mbox && (pru->irq_vring <= 0 || pru->irq_kick <= 0)) {
- dev_err(dev, "virtio vring interrupt mechanisms are not provided\n");
- ret = -EINVAL;
- goto fail;
- }
- if (!pru->mbox && pru->irq_vring > 0) {
- ret = request_threaded_irq(pru->irq_vring, NULL,
- pru_rproc_vring_interrupt,
- IRQF_ONESHOT, dev_name(dev),
- pru);
- if (ret) {
- dev_err(dev, "failed to enable vring interrupt, ret = %d\n",
- ret);
- goto fail;
- }
- }
- }
- val = CTRL_CTRL_EN | ((rproc->bootaddr >> 2) << 16);
- pru_control_write_reg(pru, PRU_CTRL_CTRL, val);
- return 0;
- fail:
- pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
- return ret;
- }
- /* stop/disable a PRU core */
- static int pru_rproc_stop(struct rproc *rproc)
- {
- struct device *dev = &rproc->dev;
- struct pru_rproc *pru = rproc->priv;
- u32 val;
- dev_dbg(dev, "stopping PRU%d\n", pru->id);
- val = pru_control_read_reg(pru, PRU_CTRL_CTRL);
- val &= ~CTRL_CTRL_EN;
- pru_control_write_reg(pru, PRU_CTRL_CTRL, val);
- if (!list_empty(&pru->rproc->rvdevs) &&
- !pru->mbox && pru->irq_vring > 0)
- free_irq(pru->irq_vring, pru);
- /* undo INTC config */
- pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
- return 0;
- }
- /*
- * parse the custom interrupt map resource and configure the INTC
- * appropriately
- */
- static int pru_handle_custom_intrmap(struct rproc *rproc,
- struct fw_rsc_custom_intrmap *intr_rsc)
- {
- struct device *dev = rproc->dev.parent;
- struct pru_rproc *pru = rproc->priv;
- struct pruss *pruss = pru->pruss;
- struct pruss_event_chnl *event_chnl_map;
- int i, ret;
- s8 sys_evt, chnl, intr_no;
- dev_dbg(dev, "version %d event_chnl_map_size %d event_chnl_map %p\n",
- intr_rsc->version, intr_rsc->event_chnl_map_size,
- intr_rsc->event_chnl_map);
- if (intr_rsc->version != 0) {
- dev_err(dev, "only custom ints resource version 0 supported\n");
- return -EINVAL;
- }
- if (intr_rsc->event_chnl_map_size < 0 ||
- intr_rsc->event_chnl_map_size >= MAX_PRU_SYS_EVENTS) {
- dev_err(dev, "custom ints resource has more events than present on hardware\n");
- return -EINVAL;
- }
- /*
- * XXX: The event_chnl_map mapping is currently a pointer in device
- * memory, evaluate if this needs to be directly in firmware file.
- */
- event_chnl_map = pru_d_da_to_va(pru, (u32)intr_rsc->event_chnl_map,
- intr_rsc->event_chnl_map_size *
- sizeof(*event_chnl_map));
- if (!event_chnl_map) {
- dev_err(dev, "custom ints resource has inadequate event_chnl_map configuration\n");
- return -EINVAL;
- }
- /* init intc_config to defaults */
- for (i = 0; i < ARRAY_SIZE(pru->intc_config.sysev_to_ch); i++)
- pru->intc_config.sysev_to_ch[i] = -1;
- for (i = 0; i < ARRAY_SIZE(pru->intc_config.ch_to_host); i++)
- pru->intc_config.ch_to_host[i] = -1;
- /* parse and fill in system event to interrupt channel mapping */
- for (i = 0; i < intr_rsc->event_chnl_map_size; i++) {
- sys_evt = event_chnl_map[i].event;
- chnl = event_chnl_map[i].chnl;
- if (sys_evt < 0 || sys_evt >= MAX_PRU_SYS_EVENTS) {
- dev_err(dev, "[%d] bad sys event %d\n", i, sys_evt);
- return -EINVAL;
- }
- if (chnl < 0 || chnl >= MAX_PRU_CHANNELS) {
- dev_err(dev, "[%d] bad channel value %d\n", i, chnl);
- return -EINVAL;
- }
- pru->intc_config.sysev_to_ch[sys_evt] = chnl;
- dev_dbg(dev, "sysevt-to-ch[%d] -> %d\n", sys_evt, chnl);
- }
- /* parse and handle interrupt channel-to-host interrupt mapping */
- for (i = 0; i < MAX_PRU_CHANNELS; i++) {
- intr_no = intr_rsc->chnl_host_intr_map[i];
- if (intr_no < 0) {
- dev_dbg(dev, "skip intr mapping for chnl %d\n", i);
- continue;
- }
- if (intr_no >= MAX_PRU_HOST_INT) {
- dev_err(dev, "bad intr mapping for chnl %d, intr_no %d\n",
- i, intr_no);
- return -EINVAL;
- }
- pru->intc_config.ch_to_host[i] = intr_no;
- dev_dbg(dev, "chnl-to-host[%d] -> %d\n", i, intr_no);
- }
- ret = pruss_intc_configure(pruss, &pru->intc_config);
- if (ret)
- dev_err(dev, "failed to configure pruss intc %d\n", ret);
- return ret;
- }
- /* PRU-specific post loading custom resource handler */
- static int pru_rproc_handle_custom_rsc(struct rproc *rproc,
- struct fw_rsc_custom *rsc)
- {
- struct device *dev = rproc->dev.parent;
- int ret = -EINVAL;
- switch (rsc->sub_type) {
- case PRUSS_RSC_INTRS:
- ret = pru_handle_custom_intrmap(rproc,
- (struct fw_rsc_custom_intrmap *)
- rsc->data);
- break;
- default:
- dev_err(dev, "%s: handling unknown type %d\n", __func__,
- rsc->sub_type);
- }
- return ret;
- }
- /* PRU-specific address translator */
- static void *pru_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
- {
- struct pru_rproc *pru = rproc->priv;
- void *va;
- u32 exec_flag;
- exec_flag = ((flags & RPROC_FLAGS_ELF_SHDR) ? flags & SHF_EXECINSTR :
- ((flags & RPROC_FLAGS_ELF_PHDR) ? flags & PF_X : 0));
- if (exec_flag)
- va = pru_i_da_to_va(pru, da, len);
- else
- va = pru_d_da_to_va(pru, da, len);
- return va;
- }
- static struct rproc_ops pru_rproc_ops = {
- .start = pru_rproc_start,
- .stop = pru_rproc_stop,
- .kick = pru_rproc_kick,
- .handle_custom_rsc = pru_rproc_handle_custom_rsc,
- .da_to_va = pru_da_to_va,
- };
- static const struct of_device_id pru_rproc_match[];
- static const struct pru_private_data *pru_rproc_get_private_data(
- struct platform_device *pdev)
- {
- const struct pru_match_private_data *data;
- const struct of_device_id *match;
- match = of_match_device(pru_rproc_match, &pdev->dev);
- if (!match)
- return ERR_PTR(-ENODEV);
- for (data = match->data; data && data->device_name; data++) {
- if (!strcmp(dev_name(&pdev->dev), data->device_name))
- return data->priv_data;
- }
- return NULL;
- }
- static int pru_rproc_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct platform_device *ppdev = to_platform_device(dev->parent);
- struct pru_rproc *pru;
- const struct pru_private_data *pdata;
- struct rproc *rproc = NULL;
- struct mbox_client *client;
- struct resource *res;
- int i, ret;
- const char *mem_names[PRU_MEM_MAX] = { "iram", "control", "debug" };
- u32 mux_sel;
- if (!np) {
- dev_err(dev, "Non-DT platform device not supported\n");
- return -ENODEV;
- }
- pdata = pru_rproc_get_private_data(pdev);
- if (IS_ERR_OR_NULL(pdata) || !pdata->fw_name) {
- dev_err(dev, "missing or incomplete PRU-private data\n");
- return -ENODEV;
- }
- rproc = rproc_alloc(dev, pdev->name, &pru_rproc_ops, pdata->fw_name,
- sizeof(*pru));
- if (!rproc) {
- dev_err(dev, "rproc_alloc failed\n");
- return -ENOMEM;
- }
- /* error recovery is not supported for PRUs */
- rproc->recovery_disabled = true;
- /*
- * rproc_add will auto-boot the processor normally, but this is
- * not desired with PRU client driven boot-flow methodoly. A PRU
- * application/client driver will boot the corresponding PRU
- * remote-processor as part of its state machine either through
- * the remoteproc sysfs interface or through the equivalent kernel API
- */
- rproc->auto_boot = false;
- pru = rproc->priv;
- pru->id = pdata->id;
- pru->pruss = platform_get_drvdata(ppdev);
- pru->rproc = rproc;
- pru->fw_name = pdata->fw_name;
- spin_lock_init(&pru->rmw_lock);
- /* XXX: get this from match data if different in the future */
- pru->iram_da = 0;
- pru->pdram_da = 0;
- pru->sdram_da = 0x2000;
- pru->shrdram_da = 0x10000;
- for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- mem_names[i]);
- pru->mem_regions[i].va = devm_ioremap_resource(dev, res);
- if (IS_ERR(pru->mem_regions[i].va)) {
- dev_err(dev, "failed to parse and map memory resource %d %s\n",
- i, mem_names[i]);
- ret = PTR_ERR(pru->mem_regions[i].va);
- goto free_rproc;
- }
- pru->mem_regions[i].pa = res->start;
- pru->mem_regions[i].size = resource_size(res);
- dev_dbg(dev, "memory %8s: pa %pa size 0x%x va %p\n",
- mem_names[i], &pru->mem_regions[i].pa,
- pru->mem_regions[i].size, pru->mem_regions[i].va);
- }
- platform_set_drvdata(pdev, rproc);
- client = &pru->client;
- client->dev = dev;
- client->tx_done = NULL;
- client->rx_callback = pru_rproc_mbox_callback;
- client->tx_block = false;
- client->knows_txdone = false;
- pru->mbox = mbox_request_channel(client, 0);
- if (IS_ERR(pru->mbox)) {
- ret = PTR_ERR(pru->mbox);
- pru->mbox = NULL;
- dev_dbg(dev, "mbox_request_channel failed: %d\n", ret);
- }
- pru->irq_vring = platform_get_irq_byname(pdev, "vring");
- if (pru->irq_vring <= 0) {
- ret = pru->irq_vring;
- if (ret == -EPROBE_DEFER)
- goto free_rproc;
- dev_dbg(dev, "unable to get vring interrupt, status = %d\n",
- ret);
- }
- pru->irq_kick = platform_get_irq_byname(pdev, "kick");
- if (pru->irq_kick <= 0) {
- ret = pru->irq_kick;
- if (ret == -EPROBE_DEFER)
- goto free_rproc;
- dev_dbg(dev, "unable to get kick interrupt, status = %d\n",
- ret);
- }
- if (pru->mbox && (pru->irq_vring > 0 || pru->irq_kick > 0))
- dev_warn(dev, "both mailbox and vring/kick system events defined\n");
- ret = rproc_add(pru->rproc);
- if (ret) {
- dev_err(dev, "rproc_add failed: %d\n", ret);
- goto put_mbox;
- }
- if ((of_machine_is_compatible("ti,am5718-idk") ||
- of_machine_is_compatible("ti,k2g-ice")) &&
- !of_property_read_u32(np, "ti,pruss-gp-mux-sel", &mux_sel)) {
- if (mux_sel < PRUSS_GP_MUX_SEL_GP ||
- mux_sel >= PRUSS_GP_MUX_MAX) {
- dev_err(dev, "invalid gp_mux_sel %d\n", mux_sel);
- ret = -EINVAL;
- goto del_rproc;
- }
- ret = pruss_cfg_set_gpmux(pru->pruss, pru->id, mux_sel);
- if (ret)
- goto del_rproc;
- }
- pru_rproc_create_debug_entries(rproc);
- dev_info(dev, "PRU rproc node %s probed successfully\n", np->full_name);
- return 0;
- del_rproc:
- rproc_del(rproc);
- put_mbox:
- mbox_free_channel(pru->mbox);
- free_rproc:
- rproc_free(rproc);
- return ret;
- }
- static int pru_rproc_remove(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct rproc *rproc = platform_get_drvdata(pdev);
- struct pru_rproc *pru = rproc->priv;
- dev_info(dev, "%s: removing rproc %s\n", __func__, rproc->name);
- mbox_free_channel(pru->mbox);
- if ((of_machine_is_compatible("ti,am5718-idk") ||
- of_machine_is_compatible("ti,k2g-ice")))
- pruss_cfg_set_gpmux(pru->pruss, pru->id, PRUSS_GP_MUX_SEL_GP);
- rproc_del(rproc);
- rproc_free(rproc);
- return 0;
- }
- /* AM33xx PRU core-specific private data */
- static struct pru_private_data am335x_pru0_rproc_pdata = {
- .id = 0,
- .fw_name = "am335x-pru0-fw",
- };
- static struct pru_private_data am335x_pru1_rproc_pdata = {
- .id = 1,
- .fw_name = "am335x-pru1-fw",
- };
- /* AM437x PRUSS1 PRU core-specific private data */
- static struct pru_private_data am437x_pru1_0_rproc_pdata = {
- .id = 0,
- .fw_name = "am437x-pru1_0-fw",
- };
- static struct pru_private_data am437x_pru1_1_rproc_pdata = {
- .id = 1,
- .fw_name = "am437x-pru1_1-fw",
- };
- /* AM437x PRUSS0 PRU core-specific private data */
- static struct pru_private_data am437x_pru0_0_rproc_pdata = {
- .id = 0,
- .fw_name = "am437x-pru0_0-fw",
- };
- static struct pru_private_data am437x_pru0_1_rproc_pdata = {
- .id = 1,
- .fw_name = "am437x-pru0_1-fw",
- };
- /* AM57xx PRUSS1 PRU core-specific private data */
- static struct pru_private_data am57xx_pru1_0_rproc_pdata = {
- .id = 0,
- .fw_name = "am57xx-pru1_0-fw",
- };
- static struct pru_private_data am57xx_pru1_1_rproc_pdata = {
- .id = 1,
- .fw_name = "am57xx-pru1_1-fw",
- };
- /* AM57xx PRUSS2 PRU core-specific private data */
- static struct pru_private_data am57xx_pru2_0_rproc_pdata = {
- .id = 0,
- .fw_name = "am57xx-pru2_0-fw",
- };
- static struct pru_private_data am57xx_pru2_1_rproc_pdata = {
- .id = 1,
- .fw_name = "am57xx-pru2_1-fw",
- };
- /* 66AK2G PRUSS0 PRU core-specific private data */
- static struct pru_private_data k2g_pru0_0_rproc_pdata = {
- .id = 0,
- .fw_name = "k2g-pru0_0-fw",
- };
- static struct pru_private_data k2g_pru0_1_rproc_pdata = {
- .id = 1,
- .fw_name = "k2g-pru0_1-fw",
- };
- static struct pru_private_data k2g_pru1_0_rproc_pdata = {
- .id = 0,
- .fw_name = "k2g-pru1_0-fw",
- };
- static struct pru_private_data k2g_pru1_1_rproc_pdata = {
- .id = 1,
- .fw_name = "k2g-pru1_1-fw",
- };
- /* AM33xx SoC-specific PRU Device data */
- static struct pru_match_private_data am335x_pru_match_data[] = {
- {
- .device_name = "4a334000.pru0",
- .priv_data = &am335x_pru0_rproc_pdata,
- },
- {
- .device_name = "4a338000.pru1",
- .priv_data = &am335x_pru1_rproc_pdata,
- },
- {
- /* sentinel */
- },
- };
- /* AM43xx SoC-specific PRU Device data */
- static struct pru_match_private_data am437x_pru_match_data[] = {
- {
- .device_name = "54434000.pru0",
- .priv_data = &am437x_pru1_0_rproc_pdata,
- },
- {
- .device_name = "54438000.pru1",
- .priv_data = &am437x_pru1_1_rproc_pdata,
- },
- {
- .device_name = "54474000.pru0",
- .priv_data = &am437x_pru0_0_rproc_pdata,
- },
- {
- .device_name = "54478000.pru1",
- .priv_data = &am437x_pru0_1_rproc_pdata,
- },
- {
- /* sentinel */
- },
- };
- /* AM57xx SoC-specific PRU Device data */
- static struct pru_match_private_data am57xx_pru_match_data[] = {
- {
- .device_name = "4b234000.pru0",
- .priv_data = &am57xx_pru1_0_rproc_pdata,
- },
- {
- .device_name = "4b238000.pru1",
- .priv_data = &am57xx_pru1_1_rproc_pdata,
- },
- {
- .device_name = "4b2b4000.pru0",
- .priv_data = &am57xx_pru2_0_rproc_pdata,
- },
- {
- .device_name = "4b2b8000.pru1",
- .priv_data = &am57xx_pru2_1_rproc_pdata,
- },
- {
- /* sentinel */
- },
- };
- /* 66AK2G SoC-specific PRU Device data */
- static struct pru_match_private_data k2g_pru_match_data[] = {
- {
- .device_name = "20ab4000.pru0",
- .priv_data = &k2g_pru0_0_rproc_pdata,
- },
- {
- .device_name = "20ab8000.pru1",
- .priv_data = &k2g_pru0_1_rproc_pdata,
- },
- {
- .device_name = "20af4000.pru0",
- .priv_data = &k2g_pru1_0_rproc_pdata,
- },
- {
- .device_name = "20af8000.pru1",
- .priv_data = &k2g_pru1_1_rproc_pdata,
- },
- {
- /* sentinel */
- },
- };
- static const struct of_device_id pru_rproc_match[] = {
- { .compatible = "ti,am3356-pru", .data = am335x_pru_match_data, },
- { .compatible = "ti,am4376-pru", .data = am437x_pru_match_data, },
- { .compatible = "ti,am5728-pru", .data = am57xx_pru_match_data, },
- { .compatible = "ti,k2g-pru", .data = k2g_pru_match_data, },
- {},
- };
- MODULE_DEVICE_TABLE(of, pru_rproc_match);
- static struct platform_driver pru_rproc_driver = {
- .driver = {
- .name = "pru-rproc",
- .of_match_table = pru_rproc_match,
- .suppress_bind_attrs = true,
- },
- .probe = pru_rproc_probe,
- .remove = pru_rproc_remove,
- };
- module_platform_driver(pru_rproc_driver);
- MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
- MODULE_DESCRIPTION("PRU-ICSS Remote Processor Driver");
- MODULE_LICENSE("GPL v2");
|