Commit edc57ea9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rproc-v4.10' of git://github.com/andersson/remoteproc

Pull remoteproc updates from Bjorn Andersson:

 - introduce remoteproc "subdevice" support, which allows remoteproc
   driver to associate devices to the "running" state of the remoteproc,
   allowing devices to be probed and removed as the remote processor is
   booted, shut down or recovering from a crash.

 - handling of virtio device resources was improved, vring memory is now
   allocated as part of other memory allocation. This ensures that all
   vrings for all virtio devices are allocated before we boot the remote
   processor.

 - the debugfs mechanism for starting and stopping remoteproc instances
   was replaced with a sysfs interface, also providing a mechanism for
   specifying firmware to use by the instance. This allows user space to
   load and boot use case specific firmware on remote processors.

 - new drivers for the ST Slimcore and Qualcomm Hexagon DSP as well as
   removal of the unused StE modem loader.

 - finally support for crash recovery in the Qualcomm Wirelss subsystem
   (used for WiFi/BT/FM on a number of platforms) and a number of bug
   fixes and cleanups

* tag 'rproc-v4.10' of git://github.com/andersson/remoteproc: (49 commits)
  remoteproc: qcom_adsp_pil: select qcom_scm
  remoteproc: Drop wait in __rproc_boot()
  remoteproc/ste: Delete unused driver
  remoteproc: Remove "experimental" warning
  remoteproc: qcom_adsp_pil: select qcom_scm
  dt-binding: soc: qcom: smd: Add label property
  remoteproc: qcom: mdt_loader: add include for sizes
  remoteproc: Update last rproc_put users to rproc_free
  remoteproc: qcom: adsp: Add missing MODULE_DEVICE_TABLE
  remoteproc: wcnss-pil: add QCOM_SMD dependency
  dmaengine: st_fdma: Revert: "Revert: Update st_fdma to 'depends on REMOTEPROC'"
  remoteproc: Add support for xo clock
  remoteproc: adsp-pil: fix recursive dependency
  remoteproc: Introduce Qualcomm ADSP PIL
  dt-binding: remoteproc: Introduce ADSP loader binding
  remoteproc: qcom_wcnss: Fix circular module dependency
  remoteproc: Merge table_ptr and cached_table pointers
  remoteproc: Remove custom vdev handler list
  remoteproc: Update max_notifyid as we allocate vrings
  remoteproc: Decouple vdev resources and devices
  ...
parents 5233c331 e2a32b6b
What: /sys/class/remoteproc/.../firmware
Date: October 2016
Contact: Matt Redfearn <matt.redfearn@imgtec.com>
Description: Remote processor firmware
Reports the name of the firmware currently loaded to the
remote processor.
To change the running firmware, ensure the remote processor is
stopped (using /sys/class/remoteproc/.../state) and write a new filename.
What: /sys/class/remoteproc/.../state
Date: October 2016
Contact: Matt Redfearn <matt.redfearn@imgtec.com>
Description: Remote processor state
Reports the state of the remote processor, which will be one of:
"offline"
"suspended"
"running"
"crashed"
"invalid"
"offline" means the remote processor is powered off.
"suspended" means that the remote processor is suspended and
must be woken to receive messages.
"running" is the normal state of an available remote processor
"crashed" indicates that a problem/crash has been detected on
the remote processor.
"invalid" is returned if the remote processor is in an
unknown state.
Writing this file controls the state of the remote processor.
The following states can be written:
"start"
"stop"
Writing "start" will attempt to start the processor running the
firmware indicated by, or written to,
/sys/class/remoteproc/.../firmware. The remote processor should
transition to "running" state.
Writing "stop" will attempt to halt the remote processor and
return it to the "offline" state.
* STMicroelectronics Flexible Direct Memory Access Device Tree bindings
The FDMA is a general-purpose direct memory access controller capable of
supporting 16 independent DMA channels. It accepts up to 32 DMA requests.
The FDMA is based on a Slim processor which requires a firmware.
* FDMA Controller
Required properties:
- compatible : Should be one of
- st,stih407-fdma-mpe31-11, "st,slim-rproc";
- st,stih407-fdma-mpe31-12, "st,slim-rproc";
- st,stih407-fdma-mpe31-13, "st,slim-rproc";
- reg : Should contain an entry for each name in reg-names
- reg-names : Must contain "slimcore", "dmem", "peripherals", "imem" entries
- interrupts : Should contain one interrupt shared by all channels
- dma-channels : Number of channels supported by the controller
- #dma-cells : Must be <3>. See DMA client section below
- clocks : Must contain an entry for each clock
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
Example:
fdma0: dma-controller@8e20000 {
compatible = "st,stih407-fdma-mpe31-11", "st,slim-rproc";
reg = <0x8e20000 0x8000>,
<0x8e30000 0x3000>,
<0x8e37000 0x1000>,
<0x8e38000 0x8000>;
reg-names = "slimcore", "dmem", "peripherals", "imem";
clocks = <&clk_s_c0_flexgen CLK_FDMA>,
<&clk_s_c0_flexgen CLK_EXT2F_A9>,
<&clk_s_c0_flexgen CLK_EXT2F_A9>,
<&clk_s_c0_flexgen CLK_EXT2F_A9>;
interrupts = <GIC_SPI 5 IRQ_TYPE_NONE>;
dma-channels = <16>;
#dma-cells = <3>;
};
* DMA client
Required properties:
- dmas: Comma separated list of dma channel requests
- dma-names: Names of the aforementioned requested channels
Each dmas request consists of 4 cells:
1. A phandle pointing to the FDMA controller
2. The request line number
3. A 32bit mask specifying (see include/linux/platform_data/dma-st-fdma.h)
-bit 2-0: Holdoff value, dreq will be masked for
0x0: 0-0.5us
0x1: 0.5-1us
0x2: 1-1.5us
-bit 17: data swap
0x0: disabled
0x1: enabled
-bit 21: Increment Address
0x0: no address increment between transfers
0x1: increment address between transfers
-bit 22: 2 STBus Initiator Coprocessor interface
0x0: high priority port
0x1: low priority port
4. transfers type
0 free running
1 paced
Example:
sti_uni_player2: sti-uni-player@2 {
compatible = "st,sti-uni-player";
status = "disabled";
#sound-dai-cells = <0>;
st,syscfg = <&syscfg_core>;
clocks = <&clk_s_d0_flexgen CLK_PCM_2>;
assigned-clocks = <&clk_s_d0_flexgen CLK_PCM_2>;
assigned-clock-parents = <&clk_s_d0_quadfs 2>;
assigned-clock-rates = <50000000>;
reg = <0x8D82000 0x158>;
interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
dmas = <&fdma0 4 0 1>;
dai-name = "Uni Player #1 (DAC)";
dma-names = "tx";
st,uniperiph-id = <2>;
st,version = <5>;
st,mode = "PCM";
};
Qualcomm ADSP Peripheral Image Loader
This document defines the binding for a component that loads and boots firmware
on the Qualcomm ADSP Hexagon core.
- compatible:
Usage: required
Value type: <string>
Definition: must be one of:
"qcom,msm8974-adsp-pil"
"qcom,msm8996-adsp-pil"
- interrupts-extended:
Usage: required
Value type: <prop-encoded-array>
Definition: must list the watchdog, fatal IRQs ready, handover and
stop-ack IRQs
- interrupt-names:
Usage: required
Value type: <stringlist>
Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack"
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: reference to the xo clock to be held on behalf of the
booting Hexagon core
- clock-names:
Usage: required
Value type: <stringlist>
Definition: must be "xo"
- cx-supply:
Usage: required
Value type: <phandle>
Definition: reference to the regulator to be held on behalf of the
booting Hexagon core
- memory-region:
Usage: required
Value type: <phandle>
Definition: reference to the reserved-memory for the ADSP
- qcom,smem-states:
Usage: required
Value type: <phandle>
Definition: reference to the smem state for requesting the ADSP to
shut down
- qcom,smem-state-names:
Usage: required
Value type: <stringlist>
Definition: must be "stop"
= SUBNODES
The adsp node may have an subnode named "smd-edge" that describes the SMD edge,
channels and devices related to the ADSP. See ../soc/qcom/qcom,smd.txt for
details on how to describe the SMD edge.
= EXAMPLE
The following example describes the resources needed to boot control the
ADSP, as it is found on MSM8974 boards.
adsp {
compatible = "qcom,msm8974-adsp-pil";
interrupts-extended = <&intc 0 162 IRQ_TYPE_EDGE_RISING>,
<&adsp_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
<&adsp_smp2p_in 1 IRQ_TYPE_EDGE_RISING>,
<&adsp_smp2p_in 2 IRQ_TYPE_EDGE_RISING>,
<&adsp_smp2p_in 3 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "wdog",
"fatal",
"ready",
"handover",
"stop-ack";
clocks = <&rpmcc RPM_CXO_CLK>;
clock-names = "xo";
cx-supply = <&pm8841_s2>;
memory-region = <&adsp_region>;
qcom,smem-states = <&adsp_smp2p_out 0>;
qcom,smem-state-names = "stop";
smd-edge {
interrupts = <0 156 IRQ_TYPE_EDGE_RISING>;
qcom,ipc = <&apcs 8 8>;
qcom,smd-edge = <1>;
};
};
......@@ -60,8 +60,8 @@ on the Qualcomm WCNSS core.
see ../reserved-memory/reserved-memory.txt
= SUBNODES
A single subnode of the WCNSS PIL describes the attached rf module and its
resource dependencies.
A required subnode of the WCNSS PIL is used to describe the attached rf module
and its resource dependencies. It is described by the following properties:
- compatible:
Usage: required
......@@ -90,6 +90,11 @@ resource dependencies.
Definition: reference to the regulators to be held on behalf of the
booting of the WCNSS core
The wcnss node can also have an subnode named "smd-edge" that describes the SMD
edge, channels and devices related to the WCNSS.
See ../soc/qcom/qcom,smd.txt for details on how to describe the SMD edge.
= EXAMPLE
The following example describes the resources needed to boot control the WCNSS,
with attached WCN3680, as it is commonly found on MSM8974 boards.
......@@ -129,4 +134,25 @@ pronto@fb204000 {
vddpa-supply = <&pm8941_l19>;
vdddig-supply = <&pm8941_s3>;
};
smd-edge {
interrupts = <0 142 1>;
qcom,ipc = <&apcs 8 17>;
qcom,smd-edge = <6>;
qcom,remote-pid = <4>;
label = "pronto";
wcnss {
compatible = "qcom,wcnss";
qcom,smd-channels = "WCNSS_CTRL";
qcom,mmio = <&pronto>;
bt {
compatible = "qcom,wcnss-bt";
};
};
};
};
......@@ -43,6 +43,13 @@ The edge is described by the following properties:
Definition: the identifier for the remote processor as known by the rest
of the system.
- label:
Usage: optional
Value type: <string>
Definition: name of the edge, used for debugging and identification
purposes. The node name will be used if this is not
present.
= SMD DEVICES
In turn, subnodes of the "edges" represent devices tied to SMD channels on that
......
......@@ -1792,6 +1792,7 @@ F: drivers/char/hw_random/st-rng.c
F: drivers/clocksource/arm_global_timer.c
F: drivers/clocksource/clksrc_st_lpc.c
F: drivers/cpufreq/sti-cpufreq.c
F: drivers/dma/st_fdma*
F: drivers/i2c/busses/i2c-st.c
F: drivers/media/rc/st_rc.c
F: drivers/media/platform/sti/c8sectpfe/
......@@ -1802,6 +1803,7 @@ F: drivers/phy/phy-stih407-usb.c
F: drivers/phy/phy-stih41x-usb.c
F: drivers/pinctrl/pinctrl-st.c
F: drivers/remoteproc/st_remoteproc.c
F: drivers/remoteproc/st_slim_rproc.c
F: drivers/reset/sti/
F: drivers/rtc/rtc-st-lpc.c
F: drivers/tty/serial/st-asc.c
......@@ -1810,6 +1812,7 @@ F: drivers/usb/host/ehci-st.c
F: drivers/usb/host/ohci-st.c
F: drivers/watchdog/st_lpc_wdt.c
F: drivers/ata/ahci_st.c
F: include/linux/remoteproc/st_slim_rproc.h
ARM/STM32 ARCHITECTURE
M: Maxime Coquelin <mcoquelin.stm32@gmail.com>
......
......@@ -649,6 +649,9 @@ CONFIG_SND_SOC_AK4642=m
CONFIG_SND_SOC_SGTL5000=m
CONFIG_SND_SOC_SPDIF=m
CONFIG_SND_SOC_WM8978=m
CONFIG_SND_SOC_STI=m
CONFIG_SND_SOC_STI_SAS=m
CONFIG_SND_SIMPLE_CARD=m
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_MVEBU=y
......@@ -790,6 +793,7 @@ CONFIG_DMA_OMAP=y
CONFIG_QCOM_BAM_DMA=y
CONFIG_XILINX_DMA=y
CONFIG_DMA_SUN6I=y
CONFIG_ST_FDMA=m
CONFIG_STAGING=y
CONFIG_SENSORS_ISL29018=y
CONFIG_SENSORS_ISL29028=y
......@@ -823,6 +827,8 @@ CONFIG_HWSPINLOCK_QCOM=y
CONFIG_ROCKCHIP_IOMMU=y
CONFIG_TEGRA_IOMMU_GART=y
CONFIG_TEGRA_IOMMU_SMMU=y
CONFIG_REMOTEPROC=m
CONFIG_ST_REMOTEPROC=m
CONFIG_PM_DEVFREQ=y
CONFIG_ARM_TEGRA_DEVFREQ=m
CONFIG_MEMORY=y
......
......@@ -436,6 +436,20 @@ config STE_DMA40
help
Support for ST-Ericsson DMA40 controller
config ST_FDMA
tristate "ST FDMA dmaengine support"
depends on ARCH_STI
depends on REMOTEPROC
select ST_SLIM_REMOTEPROC
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Enable support for ST FDMA controller.
It supports 16 independent DMA channels, accepts up to 32 DMA requests
Say Y here if you have such a chipset.
If unsure, say N.
config STM32_DMA
bool "STMicroelectronics STM32 DMA support"
depends on ARCH_STM32 || COMPILE_TEST
......
......@@ -67,6 +67,7 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
obj-$(CONFIG_ST_FDMA) += st_fdma.o
obj-y += qcom/
obj-y += xilinx/
This diff is collapsed.
/*
* DMA driver header for STMicroelectronics STi FDMA controller
*
* Copyright (C) 2014 STMicroelectronics
*
* Author: Ludovic Barre <Ludovic.barre@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __DMA_ST_FDMA_H
#define __DMA_ST_FDMA_H
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/io.h>
#include <linux/remoteproc/st_slim_rproc.h>
#include "virt-dma.h"
#define ST_FDMA_NR_DREQS 32
#define FW_NAME_SIZE 30
#define DRIVER_NAME "st-fdma"
/**
* struct st_fdma_generic_node - Free running/paced generic node
*
* @length: Length in bytes of a line in a 2D mem to mem
* @sstride: Stride, in bytes, between source lines in a 2D data move
* @dstride: Stride, in bytes, between destination lines in a 2D data move
*/
struct st_fdma_generic_node {
u32 length;
u32 sstride;
u32 dstride;
};
/**
* struct st_fdma_hw_node - Node structure used by fdma hw
*
* @next: Pointer to next node
* @control: Transfer Control Parameters
* @nbytes: Number of Bytes to read
* @saddr: Source address
* @daddr: Destination address
*
* @generic: generic node for free running/paced transfert type
* 2 others transfert type are possible, but not yet implemented
*
* The NODE structures must be aligned to a 32 byte boundary
*/
struct st_fdma_hw_node {
u32 next;
u32 control;
u32 nbytes;
u32 saddr;
u32 daddr;
union {
struct st_fdma_generic_node generic;
};
} __aligned(32);
/*
* node control parameters
*/
#define FDMA_NODE_CTRL_REQ_MAP_MASK GENMASK(4, 0)
#define FDMA_NODE_CTRL_REQ_MAP_FREE_RUN 0x0
#define FDMA_NODE_CTRL_REQ_MAP_DREQ(n) ((n)&FDMA_NODE_CTRL_REQ_MAP_MASK)
#define FDMA_NODE_CTRL_REQ_MAP_EXT FDMA_NODE_CTRL_REQ_MAP_MASK
#define FDMA_NODE_CTRL_SRC_MASK GENMASK(6, 5)
#define FDMA_NODE_CTRL_SRC_STATIC BIT(5)
#define FDMA_NODE_CTRL_SRC_INCR BIT(6)
#define FDMA_NODE_CTRL_DST_MASK GENMASK(8, 7)
#define FDMA_NODE_CTRL_DST_STATIC BIT(7)
#define FDMA_NODE_CTRL_DST_INCR BIT(8)
#define FDMA_NODE_CTRL_SECURE BIT(15)
#define FDMA_NODE_CTRL_PAUSE_EON BIT(30)
#define FDMA_NODE_CTRL_INT_EON BIT(31)
/**
* struct st_fdma_sw_node - descriptor structure for link list
*
* @pdesc: Physical address of desc
* @node: link used for putting this into a channel queue
*/
struct st_fdma_sw_node {
dma_addr_t pdesc;
struct st_fdma_hw_node *desc;
};
#define NAME_SZ 10
struct st_fdma_driverdata {
u32 id;
char name[NAME_SZ];
};
struct st_fdma_desc {
struct virt_dma_desc vdesc;
struct st_fdma_chan *fchan;
bool iscyclic;
unsigned int n_nodes;
struct st_fdma_sw_node node[];
};
enum st_fdma_type {
ST_FDMA_TYPE_FREE_RUN,
ST_FDMA_TYPE_PACED,
};
struct st_fdma_cfg {
struct device_node *of_node;
enum st_fdma_type type;
dma_addr_t dev_addr;
enum dma_transfer_direction dir;
int req_line; /* request line */
long req_ctrl; /* Request control */
};
struct st_fdma_chan {
struct st_fdma_dev *fdev;
struct dma_pool *node_pool;
struct dma_slave_config scfg;
struct st_fdma_cfg cfg;
int dreq_line;
struct virt_dma_chan vchan;
struct st_fdma_desc *fdesc;
enum dma_status status;
};
struct st_fdma_dev {
struct device *dev;
const struct st_fdma_driverdata *drvdata;
struct dma_device dma_device;
struct st_slim_rproc *slim_rproc;
int irq;
struct st_fdma_chan *chans;
spinlock_t dreq_lock;
unsigned long dreq_mask;
u32 nr_channels;
char fw_name[FW_NAME_SIZE];
};
/* Peripheral Registers*/
#define FDMA_CMD_STA_OFST 0xFC0
#define FDMA_CMD_SET_OFST 0xFC4
#define FDMA_CMD_CLR_OFST 0xFC8
#define FDMA_CMD_MASK_OFST 0xFCC
#define FDMA_CMD_START(ch) (0x1 << (ch << 1))
#define FDMA_CMD_PAUSE(ch) (0x2 << (ch << 1))
#define FDMA_CMD_FLUSH(ch) (0x3 << (ch << 1))
#define FDMA_INT_STA_OFST 0xFD0
#define FDMA_INT_STA_CH 0x1
#define FDMA_INT_STA_ERR 0x2
#define FDMA_INT_SET_OFST 0xFD4
#define FDMA_INT_CLR_OFST 0xFD8
#define FDMA_INT_MASK_OFST 0xFDC
#define fdma_read(fdev, name) \
readl((fdev)->slim_rproc->peri + name)
#define fdma_write(fdev, val, name) \
writel((val), (fdev)->slim_rproc->peri + name)
/* fchan interface (dmem) */
#define FDMA_CH_CMD_OFST 0x200
#define FDMA_CH_CMD_STA_MASK GENMASK(1, 0)
#define FDMA_CH_CMD_STA_IDLE (0x0)
#define FDMA_CH_CMD_STA_START (0x1)
#define FDMA_CH_CMD_STA_RUNNING (0x2)
#define FDMA_CH_CMD_STA_PAUSED (0x3)
#define FDMA_CH_CMD_ERR_MASK GENMASK(4, 2)
#define FDMA_CH_CMD_ERR_INT (0x0 << 2)
#define FDMA_CH_CMD_ERR_NAND (0x1 << 2)
#define FDMA_CH_CMD_ERR_MCHI (0x2 << 2)
#define FDMA_CH_CMD_DATA_MASK GENMASK(31, 5)
#define fchan_read(fchan, name) \
readl((fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+ (fchan)->vchan.chan.chan_id * 0x4 \
+ name)
#define fchan_write(fchan, val, name) \
writel((val), (fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+ (fchan)->vchan.chan.chan_id * 0x4 \
+ name)
/* req interface */
#define FDMA_REQ_CTRL_OFST 0x240
#define dreq_write(fchan, val, name) \
writel((val), (fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+ fchan->dreq_line * 0x04 \
+ name)
/* node interface */
#define FDMA_NODE_SZ 128
#define FDMA_PTRN_OFST 0x800
#define FDMA_CNTN_OFST 0x808
#define FDMA_SADDRN_OFST 0x80c
#define FDMA_DADDRN_OFST 0x810
#define fnode_read(fchan, name) \
readl((fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+ (fchan)->vchan.chan.chan_id * FDMA_NODE_SZ \
+ name)
#define fnode_write(fchan, val, name) \
writel((val), (fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+ (fchan)->vchan.chan.chan_id * FDMA_NODE_SZ \
+ name)
/*
* request control bits
*/
#define FDMA_REQ_CTRL_NUM_OPS_MASK GENMASK(31, 24)
#define FDMA_REQ_CTRL_NUM_OPS(n) (FDMA_REQ_CTRL_NUM_OPS_MASK & \
((n) << 24))
#define FDMA_REQ_CTRL_INITIATOR_MASK BIT(22)
#define FDMA_REQ_CTRL_INIT0 (0x0 << 22)
#define FDMA_REQ_CTRL_INIT1 (0x1 << 22)
#define FDMA_REQ_CTRL_INC_ADDR_ON BIT(21)
#define FDMA_REQ_CTRL_DATA_SWAP_ON BIT(17)
#define FDMA_REQ_CTRL_WNR BIT(14)
#define FDMA_REQ_CTRL_OPCODE_MASK GENMASK(7, 4)
#define FDMA_REQ_CTRL_OPCODE_LD_ST1 (0x0 << 4)
#define FDMA_REQ_CTRL_OPCODE_LD_ST2 (0x1 << 4)
#define FDMA_REQ_CTRL_OPCODE_LD_ST4 (0x2 << 4)
#define FDMA_REQ_CTRL_OPCODE_LD_ST8 (0x3 << 4)
#define FDMA_REQ_CTRL_OPCODE_LD_ST16 (0x4 << 4)
#define FDMA_REQ_CTRL_OPCODE_LD_ST32 (0x5 << 4)
#define FDMA_REQ_CTRL_OPCODE_LD_ST64 (0x6 << 4)
#define FDMA_REQ_CTRL_HOLDOFF_MASK GENMASK(2, 0)
#define FDMA_REQ_CTRL_HOLDOFF(n) ((n) & FDMA_REQ_CTRL_HOLDOFF_MASK)
/* bits used by client to configure request control */
#define FDMA_REQ_CTRL_CFG_MASK (FDMA_REQ_CTRL_HOLDOFF_MASK | \
FDMA_REQ_CTRL_DATA_SWAP_ON | \
FDMA_REQ_CTRL_INC_ADDR_ON | \
FDMA_REQ_CTRL_INITIATOR_MASK)
#endif /* __DMA_ST_FDMA_H */
menu "Remoteproc drivers"
# REMOTEPROC gets selected by whoever wants it
config REMOTEPROC
tristate
tristate "Support for Remote Processor subsystem"
depends on HAS_DMA
select CRC32
select FW_LOADER
select VIRTIO
select VIRTUALIZATION
if REMOTEPROC
config OMAP_REMOTEPROC
tristate "OMAP remoteproc support"
depends on HAS_DMA
depends on ARCH_OMAP4 || SOC_OMAP5
depends on OMAP_IOMMU
select REMOTEPROC
depends on REMOTEPROC
select MAILBOX
select OMAP2PLUS_MBOX
select RPMSG_VIRTIO
......@@ -31,20 +32,10 @@ config OMAP_REMOTEPROC
It's safe to say n here if you're not interested in multimedia
offloading or just want a bare minimum kernel.
config STE_MODEM_RPROC
tristate "STE-Modem remoteproc support"
depends on HAS_DMA
select REMOTEPROC
default n
help
Say y or m here to support STE-Modem shared memory driver.
This can be either built-in or a loadable module.
If unsure say N.
config WKUP_M3_RPROC
tristate "AMx3xx Wakeup M3 remoteproc support"
depends on SOC_AM33XX || SOC_AM43XX
select REMOTEPROC
depends on REMOTEPROC
help
Say y here to support Wakeup M3 remote processor on TI AM33xx
and AM43xx family of SoCs.
......@@ -57,8 +48,8 @@ config WKUP_M3_RPROC
config DA8XX_REMOTEPROC
tristate "DA8xx/OMAP-L13x remoteproc support"
depends on ARCH_DAVINCI_DA8XX
depends on REMOTEPROC
select CMA if MMU
select REMOTEPROC
select RPMSG_VIRTIO
help
Say y here to support DA8xx/OMAP-L13x remote processors via the
......@@ -77,6 +68,18 @@ config DA8XX_REMOTEPROC
It's safe to say n here if you're not interested in multimedia
offloading.
config QCOM_ADSP_PIL
tristate "Qualcomm ADSP Peripheral Image Loader"
depends on OF && ARCH_QCOM
depends on REMOTEPROC
depends on QCOM_SMEM
select MFD_SYSCON
select QCOM_MDT_LOADER
select QCOM_SCM
help
Say y here to support the TrustZone based Peripherial Image Loader
for the Qualcomm ADSP remote processors.
config QCOM_MDT_LOADER
tristate
......@@ -84,25 +87,22 @@ config QCOM_Q6V5_PIL
tristate "Qualcomm Hexagon V5 Peripherial Image Loader"
depends on OF && ARCH_QCOM
depends on QCOM_SMEM
depends on REMOTEPROC
select MFD_SYSCON
select QCOM_MDT_LOADER
select REMOTEPROC
select QCOM_SCM
help
Say y here to support the Qualcomm Peripherial Image Loader for the
Hexagon V5 based remote processors.
config QCOM_WCNSS_IRIS
tristate
depends on OF && ARCH_QCOM
config QCOM_WCNSS_PIL
tristate "Qualcomm WCNSS Peripheral Image Loader"
depends on OF && ARCH_QCOM
depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n)
depends on QCOM_SMEM
depends on REMOTEPROC
select QCOM_MDT_LOADER
select QCOM_SCM
select QCOM_WCNSS_IRIS
select REMOTEPROC
help
Say y here to support the Peripheral Image Loader for the Qualcomm
Wireless Connectivity Subsystem.
......@@ -110,10 +110,16 @@ config QCOM_WCNSS_PIL
config ST_REMOTEPROC
tristate "ST remoteproc support"
depends on ARCH_STI
select REMOTEPROC
depends on REMOTEPROC
help
Say y here to support ST's adjunct processors via the remote
processor framework.
This can be either built-in or a loadable module.
config ST_SLIM_REMOTEPROC
tristate
depends on REMOTEPROC
endif # REMOTEPROC
endmenu
......@@ -5,14 +5,17 @@
obj-$(CONFIG_REMOTEPROC) += remoteproc.o
remoteproc-y := remoteproc_core.o
remoteproc-y += remoteproc_debugfs.o
remoteproc-y += remoteproc_sysfs.o
remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o
obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
obj-$(CONFIG_QCOM_WCNSS_IRIS) += qcom_wcnss_iris.o
obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss.o
obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
qcom_wcnss_pil-y += qcom_wcnss.o
qcom_wcnss_pil-y += qcom_wcnss_iris.o
obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o
/*
* Qualcomm ADSP Peripheral Image Loader for MSM8974 and MSM8996
*
* Copyright (C) 2016 Linaro Ltd
* Copyright (C) 2014 Sony Mobile Communications AB
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* 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/clk.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/qcom_scm.h>
#include <linux/regulator/consumer.h>
#include <linux/remoteproc.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
#include "qcom_mdt_loader.h"
#include "remoteproc_internal.h"
#define ADSP_CRASH_REASON_SMEM 423
#define ADSP_FIRMWARE_NAME "adsp.mdt"
#define ADSP_PAS_ID 1
struct qcom_adsp {
struct device *dev;
struct rproc *rproc;
int wdog_irq;
int fatal_irq;
int ready_irq;
int handover_irq;
int stop_ack_irq;
struct qcom_smem_state *state;
unsigned stop_bit;
struct clk *xo;
struct regulator *cx_supply;
struct completion start_done;
struct completion stop_done;
phys_addr_t mem_phys;
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
};
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
phys_addr_t fw_addr;
size_t fw_size;
bool relocate;
int ret;
ret = qcom_scm_pas_init_image(ADSP_PAS_ID, fw->data, fw->size);
if (ret) {
dev_err(&rproc->dev, "invalid firmware metadata\n");
return ret;
}
ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate);
if (ret) {
dev_err(&rproc->dev, "failed to parse mdt header\n");
return ret;
}
if (relocate) {
adsp->mem_reloc = fw_addr;
ret = qcom_scm_pas_mem_setup(ADSP_PAS_ID, adsp->mem_phys, fw_size);
if (ret) {
dev_err(&rproc->dev, "unable to setup memory for image\n");
return ret;
}
}
return qcom_mdt_load(rproc, fw, rproc->firmware);
}
static const struct rproc_fw_ops adsp_fw_ops = {
.find_rsc_table = qcom_mdt_find_rsc_table,
.load = adsp_load,
};
static int adsp_start(struct rproc *rproc)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int ret;
ret = clk_prepare_enable(adsp->xo);
if (ret)
return ret;
ret = regulator_enable(adsp->cx_supply);
if (ret)
goto disable_clocks;
ret = qcom_scm_pas_auth_and_reset(ADSP_PAS_ID);
if (ret) {
dev_err(adsp->dev,
"failed to authenticate image and release reset\n");
goto disable_regulators;
}
ret = wait_for_completion_timeout(&adsp->start_done,
msecs_to_jiffies(5000));
if (!ret) {
dev_err(adsp->dev, "start timed out\n");
qcom_scm_pas_shutdown(ADSP_PAS_ID);
ret = -ETIMEDOUT;
goto disable_regulators;
}
ret = 0;
disable_regulators:
regulator_disable(adsp->cx_supply);
disable_clocks:
clk_disable_unprepare(adsp->xo);
return ret;
}
static int adsp_stop(struct rproc *rproc)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int ret;
qcom_smem_state_update_bits(adsp->state,
BIT(adsp->stop_bit),
BIT(adsp->stop_bit));
ret = wait_for_completion_timeout(&adsp->stop_done,
msecs_to_jiffies(5000));
if (ret == 0)
dev_err(adsp->dev, "timed out on wait\n");
qcom_smem_state_update_bits(adsp->state,
BIT(adsp->stop_bit),
0);
ret = qcom_scm_pas_shutdown(ADSP_PAS_ID);
if (ret)
dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
return ret;
}
static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
{
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
int offset;
offset = da - adsp->mem_reloc;
if (offset < 0 || offset + len > adsp->mem_size)
return NULL;
return adsp->mem_region + offset;
}
static const struct rproc_ops adsp_ops = {
.start = adsp_start,
.stop = adsp_stop,
.da_to_va = adsp_da_to_va,
};
static irqreturn_t adsp_wdog_interrupt(int irq, void *dev)
{
struct qcom_adsp *adsp = dev;
rproc_report_crash(adsp->rproc, RPROC_WATCHDOG);
return IRQ_HANDLED;
}
static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
{
struct qcom_adsp *adsp = dev;
size_t len;
char *msg;
msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, ADSP_CRASH_REASON_SMEM, &len);
if (!IS_ERR(msg) && len > 0 && msg[0])
dev_err(adsp->dev, "fatal error received: %s\n", msg);
rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
if (!IS_ERR(msg))
msg[0] = '\0';
return IRQ_HANDLED;
}
static irqreturn_t adsp_ready_interrupt(int irq, void *dev)
{
return IRQ_HANDLED;
}
static irqreturn_t adsp_handover_interrupt(int irq, void *dev)
{
struct qcom_adsp *adsp = dev;
complete(&adsp->start_done);
return IRQ_HANDLED;
}
static irqreturn_t adsp_stop_ack_interrupt(int irq, void *dev)
{
struct qcom_adsp *adsp = dev;
complete(&adsp->stop_done);
return IRQ_HANDLED;
}
static int adsp_init_clock(struct qcom_adsp *adsp)
{
int ret;
adsp->xo = devm_clk_get(adsp->dev, "xo");
if (IS_ERR(adsp->xo)) {
ret = PTR_ERR(adsp->xo);
if (ret != -EPROBE_DEFER)
dev_err(adsp->dev, "failed to get xo clock");
return ret;
}
return 0;
}
static int adsp_init_regulator(struct qcom_adsp *adsp)
{
adsp->cx_supply = devm_regulator_get(adsp->dev, "cx");
if (IS_ERR(adsp->cx_supply))
return PTR_ERR(adsp->cx_supply);
regulator_set_load(adsp->cx_supply, 100000);
return 0;
}
static int adsp_request_irq(struct qcom_adsp *adsp,
struct platform_device *pdev,
const char *name,
irq_handler_t thread_fn)
{
int ret;
ret = platform_get_irq_byname(pdev, name);
if (ret < 0) {
dev_err(&pdev->dev, "no %s IRQ defined\n", name);
return ret;
}
ret = devm_request_threaded_irq(&pdev->dev, ret,
NULL, thread_fn,
IRQF_ONESHOT,
"adsp", adsp);
if (ret)
dev_err(&pdev->dev, "request %s IRQ failed\n", name);
return ret;
}
static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
{
struct device_node *node;
struct resource r;
int ret;
node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0);
if (!node) {
dev_err(adsp->dev, "no memory-region specified\n");
return -EINVAL;
}
ret = of_address_to_resource(node, 0, &r);
if (ret)
return ret;
adsp->mem_phys = adsp->mem_reloc = r.start;
adsp->mem_size = resource_size(&r);
adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size);
if (!adsp->mem_region) {
dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n",
&r.start, adsp->mem_size);
return -EBUSY;
}
return 0;
}
static int adsp_probe(struct platform_device *pdev)
{
struct qcom_adsp *adsp;
struct rproc *rproc;
int ret;
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
if (!qcom_scm_pas_supported(ADSP_PAS_ID)) {
dev_err(&pdev->dev, "PAS is not available for ADSP\n");
return -ENXIO;
}
rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops,
ADSP_FIRMWARE_NAME, sizeof(*adsp));
if (!rproc) {
dev_err(&pdev->dev, "unable to allocate remoteproc\n");
return -ENOMEM;
}
rproc->fw_ops = &adsp_fw_ops;
adsp = (struct qcom_adsp *)rproc->priv;
adsp->dev = &pdev->dev;
adsp->rproc = rproc;
platform_set_drvdata(pdev, adsp);
init_completion(&adsp->start_done);
init_completion(&adsp->stop_done);
ret = adsp_alloc_memory_region(adsp);
if (ret)
goto free_rproc;
ret = adsp_init_clock(adsp);
if (ret)
goto free_rproc;
ret = adsp_init_regulator(adsp);
if (ret)
goto free_rproc;
ret = adsp_request_irq(adsp, pdev, "wdog", adsp_wdog_interrupt);
if (ret < 0)
goto free_rproc;
adsp->wdog_irq = ret;
ret = adsp_request_irq(adsp, pdev, "fatal", adsp_fatal_interrupt);
if (ret < 0)
goto free_rproc;
adsp->fatal_irq = ret;
ret = adsp_request_irq(adsp, pdev, "ready", adsp_ready_interrupt);
if (ret < 0)
goto free_rproc;
adsp->ready_irq = ret;
ret = adsp_request_irq(adsp, pdev, "handover", adsp_handover_interrupt);
if (ret < 0)
goto free_rproc;
adsp->handover_irq = ret;
ret = adsp_request_irq(adsp, pdev, "stop-ack", adsp_stop_ack_interrupt);
if (ret < 0)
goto free_rproc;
adsp->stop_ack_irq = ret;
adsp->state = qcom_smem_state_get(&pdev->dev, "stop",
&adsp->stop_bit);
if (IS_ERR(adsp->state)) {
ret = PTR_ERR(adsp->state);
goto free_rproc;
}
ret = rproc_add(rproc);
if (ret)
goto free_rproc;
return 0;
free_rproc:
rproc_free(rproc);
return ret;
}
static int adsp_remove(struct platform_device *pdev)
{
struct qcom_adsp *adsp = platform_get_drvdata(pdev);
qcom_smem_state_put(adsp->state);
rproc_del(adsp->rproc);
rproc_free(adsp->rproc);
return 0;
}
static const struct of_device_id adsp_of_match[] = {
{ .compatible = "qcom,msm8974-adsp-pil" },
{ .compatible = "qcom,msm8996-adsp-pil" },
{ },
};
MODULE_DEVICE_TABLE(of, adsp_of_match);
static struct platform_driver adsp_driver = {
.probe = adsp_probe,
.remove = adsp_remove,
.driver = {
.name = "qcom_adsp_pil",
.of_match_table = adsp_of_match,
},
};
module_platform_driver(adsp_driver);
MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader");
MODULE_LICENSE("GPL v2");
......@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/remoteproc.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include "remoteproc_internal.h"
......
......@@ -894,6 +894,7 @@ static const struct of_device_id q6v5_of_match[] = {
{ .compatible = "qcom,q6v5-pil", },
{ },
};
MODULE_DEVICE_TABLE(of, q6v5_of_match);
static struct platform_driver q6v5_driver = {
.probe = q6v5_probe,
......
......@@ -30,6 +30,7 @@
#include <linux/remoteproc.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
#include <linux/rpmsg/qcom_smd.h>
#include "qcom_mdt_loader.h"
#include "remoteproc_internal.h"
......@@ -94,6 +95,10 @@ struct qcom_wcnss {
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
struct device_node *smd_node;
struct qcom_smd_edge *smd_edge;
struct rproc_subdev smd_subdev;
};
static const struct wcnss_data riva_data = {
......@@ -143,7 +148,6 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
mutex_unlock(&wcnss->iris_lock);
}
EXPORT_SYMBOL_GPL(qcom_wcnss_assign_iris);
static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
{
......@@ -396,6 +400,23 @@ static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev)
return IRQ_HANDLED;
}
static int wcnss_smd_probe(struct rproc_subdev *subdev)
{
struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev);
wcnss->smd_edge = qcom_smd_register_edge(wcnss->dev, wcnss->smd_node);
return IS_ERR(wcnss->smd_edge) ? PTR_ERR(wcnss->smd_edge) : 0;
}
static void wcnss_smd_remove(struct rproc_subdev *subdev)
{
struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev);
qcom_smd_unregister_edge(wcnss->smd_edge);
wcnss->smd_edge = NULL;
}
static int wcnss_init_regulators(struct qcom_wcnss *wcnss,
const struct wcnss_vreg_info *info,
int num_vregs)
......@@ -578,6 +599,10 @@ static int wcnss_probe(struct platform_device *pdev)
}
}
wcnss->smd_node = of_get_child_by_name(pdev->dev.of_node, "smd-edge");
if (wcnss->smd_node)
rproc_add_subdev(rproc, &wcnss->smd_subdev, wcnss_smd_probe, wcnss_smd_remove);
ret = rproc_add(rproc);
if (ret)
goto free_rproc;
......@@ -596,6 +621,7 @@ static int wcnss_remove(struct platform_device *pdev)
of_platform_depopulate(&pdev->dev);
of_node_put(wcnss->smd_node);
qcom_smem_state_put(wcnss->state);
rproc_del(wcnss->rproc);
rproc_free(wcnss->rproc);
......@@ -609,6 +635,7 @@ static const struct of_device_id wcnss_of_match[] = {
{ .compatible = "qcom,pronto-v2-pil", &pronto_v2_data },
{ },
};
MODULE_DEVICE_TABLE(of, wcnss_of_match);
static struct platform_driver wcnss_driver = {
.probe = wcnss_probe,
......@@ -619,6 +646,28 @@ static struct platform_driver wcnss_driver = {
},
};
module_platform_driver(wcnss_driver);
static int __init wcnss_init(void)
{
int ret;
ret = platform_driver_register(&wcnss_driver);
if (ret)
return ret;
ret = platform_driver_register(&qcom_iris_driver);
if (ret)
platform_driver_unregister(&wcnss_driver);
return ret;
}
module_init(wcnss_init);
static void __exit wcnss_exit(void)
{
platform_driver_unregister(&qcom_iris_driver);
platform_driver_unregister(&wcnss_driver);
}
module_exit(wcnss_exit);
MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem");
MODULE_LICENSE("GPL v2");
......@@ -4,6 +4,8 @@
struct qcom_iris;
struct qcom_wcnss;
extern struct platform_driver qcom_iris_driver;
struct wcnss_vreg_info {
const char * const name;
int min_voltage;
......
......@@ -94,14 +94,12 @@ int qcom_iris_enable(struct qcom_iris *iris)
return ret;
}
EXPORT_SYMBOL_GPL(qcom_iris_enable);
void qcom_iris_disable(struct qcom_iris *iris)
{
clk_disable_unprepare(iris->xo_clk);
regulator_bulk_disable(iris->num_vregs, iris->vregs);
}
EXPORT_SYMBOL_GPL(qcom_iris_disable);
static int qcom_iris_probe(struct platform_device *pdev)
{
......@@ -173,8 +171,9 @@ static const struct of_device_id iris_of_match[] = {
{ .compatible = "qcom,wcn3680", .data = &wcn3680_data },
{}
};
MODULE_DEVICE_TABLE(of, iris_of_match);
static struct platform_driver wcnss_driver = {
struct platform_driver qcom_iris_driver = {
.probe = qcom_iris_probe,
.remove = qcom_iris_remove,
.driver = {
......@@ -182,7 +181,3 @@ static struct platform_driver wcnss_driver = {
.of_match_table = iris_of_match,
},
};
module_platform_driver(wcnss_driver);
MODULE_DESCRIPTION("Qualcomm Wireless Subsystem Iris driver");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
......@@ -59,75 +59,6 @@ static const struct file_operations trace_rproc_ops = {
.llseek = generic_file_llseek,
};
/*
* A state-to-string lookup table, for exposing a human readable state
* via debugfs. Always keep in sync with enum rproc_state
*/
static const char * const rproc_state_string[] = {
"offline",
"suspended",
"running",
"crashed",
"invalid",
};
/* expose the state of the remote processor via debugfs */
static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct rproc *rproc = filp->private_data;
unsigned int state;
char buf[30];
int i;
state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
i = scnprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state],
rproc->state);
return simple_read_from_buffer(userbuf, count, ppos, buf, i);
}
static ssize_t rproc_state_write(struct file *filp, const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct rproc *rproc = filp->private_data;
char buf[10];
int ret;
if (count > sizeof(buf) || count <= 0)
return -EINVAL;
ret = copy_from_user(buf, userbuf, count);
if (ret)
return -EFAULT;
if (buf[count - 1] == '\n')
buf[count - 1] = '\0';
if (!strncmp(buf, "start", count)) {
ret = rproc_boot(rproc);
if (ret) {
dev_err(&rproc->dev, "Boot failed: %d\n", ret);
return ret;
}
} else if (!strncmp(buf, "stop", count)) {
rproc_shutdown(rproc);
} else {
dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
return -EINVAL;
}
return count;
}
static const struct file_operations rproc_state_ops = {
.read = rproc_state_read,
.write = rproc_state_write,
.open = simple_open,
.llseek = generic_file_llseek,
};
/* expose the name of the remote processor via debugfs */
static ssize_t rproc_name_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *ppos)
......@@ -265,8 +196,6 @@ void rproc_create_debug_dir(struct rproc *rproc)
debugfs_create_file("name", 0400, rproc->dbg_dir,
rproc, &rproc_name_ops);
debugfs_create_file("state", 0400, rproc->dbg_dir,
rproc, &rproc_state_ops);
debugfs_create_file("recovery", 0400, rproc->dbg_dir,
rproc, &rproc_recovery_ops);
}
......
......@@ -49,6 +49,7 @@ struct rproc_fw_ops {
void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
int rproc_boot_nowait(struct rproc *rproc);
void rproc_vdev_release(struct kref *ref);
/* from remoteproc_virtio.c */
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
......@@ -63,6 +64,11 @@ void rproc_create_debug_dir(struct rproc *rproc);
void rproc_init_debugfs(void);
void rproc_exit_debugfs(void);
/* from remoteproc_sysfs.c */
extern struct class rproc_class;
int rproc_init_sysfs(void);
void rproc_exit_sysfs(void);
void rproc_free_vring(struct rproc_vring *rvring);
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
......
/*
* Remote Processor Framework
*
* 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/remoteproc.h>
#include "remoteproc_internal.h"
#define to_rproc(d) container_of(d, struct rproc, dev)
/* Expose the loaded / running firmware name via sysfs */
static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct rproc *rproc = to_rproc(dev);
return sprintf(buf, "%s\n", rproc->firmware);
}
/* Change firmware name via sysfs */
static ssize_t firmware_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct rproc *rproc = to_rproc(dev);
char *p;
int err, len = count;
err = mutex_lock_interruptible(&rproc->lock);
if (err) {
dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err);
return -EINVAL;
}
if (rproc->state != RPROC_OFFLINE) {
dev_err(dev, "can't change firmware while running\n");
err = -EBUSY;
goto out;
}
len = strcspn(buf, "\n");
p = kstrndup(buf, len, GFP_KERNEL);
if (!p) {
err = -ENOMEM;
goto out;
}
kfree(rproc->firmware);
rproc->firmware = p;
out:
mutex_unlock(&rproc->lock);
return err ? err : count;
}
static DEVICE_ATTR_RW(firmware);
/*
* A state-to-string lookup table, for exposing a human readable state
* via sysfs. Always keep in sync with enum rproc_state
*/
static const char * const rproc_state_string[] = {
[RPROC_OFFLINE] = "offline",
[RPROC_SUSPENDED] = "suspended",
[RPROC_RUNNING] = "running",
[RPROC_CRASHED] = "crashed",
[RPROC_LAST] = "invalid",
};
/* Expose the state of the remote processor via sysfs */
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct rproc *rproc = to_rproc(dev);
unsigned int state;
state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
return sprintf(buf, "%s\n", rproc_state_string[state]);
}
/* Change remote processor state via sysfs */
static ssize_t state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct rproc *rproc = to_rproc(dev);
int ret = 0;
if (sysfs_streq(buf, "start")) {
if (rproc->state == RPROC_RUNNING)
return -EBUSY;
ret = rproc_boot(rproc);
if (ret)
dev_err(&rproc->dev, "Boot failed: %d\n", ret);
} else if (sysfs_streq(buf, "stop")) {
if (rproc->state != RPROC_RUNNING)
return -EINVAL;
rproc_shutdown(rproc);
} else {
dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
ret = -EINVAL;
}
return ret ? ret : count;
}
static DEVICE_ATTR_RW(state);
static struct attribute *rproc_attrs[] = {
&dev_attr_firmware.attr,
&dev_attr_state.attr,
NULL
};
static const struct attribute_group rproc_devgroup = {
.attrs = rproc_attrs
};
static const struct attribute_group *rproc_devgroups[] = {
&rproc_devgroup,
NULL
};
struct class rproc_class = {
.name = "remoteproc",
.dev_groups = rproc_devgroups,
};
int __init rproc_init_sysfs(void)
{
/* create remoteproc device class for sysfs */
int err = class_register(&rproc_class);
if (err)
pr_err("remoteproc: unable to register class\n");
return err;
}
void __exit rproc_exit_sysfs(void)
{
class_unregister(&rproc_class);
}
......@@ -79,7 +79,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
struct rproc_vring *rvring;
struct virtqueue *vq;
void *addr;
int len, size, ret;
int len, size;
/* we're temporarily limited to two virtqueues per rvdev */
if (id >= ARRAY_SIZE(rvdev->vring))
......@@ -88,10 +88,6 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
if (!name)
return NULL;
ret = rproc_alloc_vring(rvdev, id);
if (ret)
return ERR_PTR(ret);
rvring = &rvdev->vring[id];
addr = rvring->va;
len = rvring->len;
......@@ -130,7 +126,6 @@ static void __rproc_virtio_del_vqs(struct virtio_device *vdev)
rvring = vq->priv;
rvring->vq = NULL;
vring_del_virtqueue(vq);
rproc_free_vring(rvring);
}
}
......@@ -282,14 +277,13 @@ static const struct virtio_config_ops rproc_virtio_config_ops = {
* Never call this function directly; it will be called by the driver
* core when needed.
*/
static void rproc_vdev_release(struct device *dev)
static void rproc_virtio_dev_release(struct device *dev)
{
struct virtio_device *vdev = dev_to_virtio(dev);
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev);
list_del(&rvdev->node);
kfree(rvdev);
kref_put(&rvdev->refcount, rproc_vdev_release);
put_device(&rproc->dev);
}
......@@ -313,7 +307,7 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
vdev->id.device = id,
vdev->config = &rproc_virtio_config_ops,
vdev->dev.parent = dev;
vdev->dev.release = rproc_vdev_release;
vdev->dev.release = rproc_virtio_dev_release;
/*
* We're indirectly making a non-temporary copy of the rproc pointer
......@@ -325,6 +319,9 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
*/
get_device(&rproc->dev);
/* Reference the vdev and vring allocations */
kref_get(&rvdev->refcount);
ret = register_virtio_device(vdev);
if (ret) {
put_device(&rproc->dev);
......
......@@ -245,8 +245,10 @@ static int st_rproc_probe(struct platform_device *pdev)
goto free_rproc;
enabled = st_rproc_state(pdev);
if (enabled < 0)
if (enabled < 0) {
ret = enabled;
goto free_rproc;
}
if (enabled) {
atomic_inc(&rproc->power);
......
/*
* SLIM core rproc driver
*
* Copyright (C) 2016 STMicroelectronics
*
* Author: Peter Griffin <peter.griffin@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/remoteproc.h>
#include <linux/remoteproc/st_slim_rproc.h>
#include "remoteproc_internal.h"
/* SLIM core registers */
#define SLIM_ID_OFST 0x0
#define SLIM_VER_OFST 0x4
#define SLIM_EN_OFST 0x8
#define SLIM_EN_RUN BIT(0)
#define SLIM_CLK_GATE_OFST 0xC
#define SLIM_CLK_GATE_DIS BIT(0)
#define SLIM_CLK_GATE_RESET BIT(2)
#define SLIM_SLIM_PC_OFST 0x20
/* DMEM registers */
#define SLIM_REV_ID_OFST 0x0
#define SLIM_REV_ID_MIN_MASK GENMASK(15, 8)
#define SLIM_REV_ID_MIN(id) ((id & SLIM_REV_ID_MIN_MASK) >> 8)
#define SLIM_REV_ID_MAJ_MASK GENMASK(23, 16)
#define SLIM_REV_ID_MAJ(id) ((id & SLIM_REV_ID_MAJ_MASK) >> 16)
/* peripherals registers */
#define SLIM_STBUS_SYNC_OFST 0xF88
#define SLIM_STBUS_SYNC_DIS BIT(0)
#define SLIM_INT_SET_OFST 0xFD4
#define SLIM_INT_CLR_OFST 0xFD8
#define SLIM_INT_MASK_OFST 0xFDC
#define SLIM_CMD_CLR_OFST 0xFC8
#define SLIM_CMD_MASK_OFST 0xFCC
static const char *mem_names[ST_SLIM_MEM_MAX] = {
[ST_SLIM_DMEM] = "dmem",
[ST_SLIM_IMEM] = "imem",
};
static int slim_clk_get(struct st_slim_rproc *slim_rproc, struct device *dev)
{
int clk, err;
for (clk = 0; clk < ST_SLIM_MAX_CLK; clk++) {
slim_rproc->clks[clk] = of_clk_get(dev->of_node, clk);
if (IS_ERR(slim_rproc->clks[clk])) {
err = PTR_ERR(slim_rproc->clks[clk]);
if (err == -EPROBE_DEFER)
goto err_put_clks;
slim_rproc->clks[clk] = NULL;
break;
}
}
return 0;
err_put_clks:
while (--clk >= 0)
clk_put(slim_rproc->clks[clk]);
return err;
}
static void slim_clk_disable(struct st_slim_rproc *slim_rproc)
{
int clk;
for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)
clk_disable_unprepare(slim_rproc->clks[clk]);
}
static int slim_clk_enable(struct st_slim_rproc *slim_rproc)
{
int clk, ret;
for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) {
ret = clk_prepare_enable(slim_rproc->clks[clk]);
if (ret)
goto err_disable_clks;
}
return 0;
err_disable_clks:
while (--clk >= 0)
clk_disable_unprepare(slim_rproc->clks[clk]);
return ret;
}
/*
* Remoteproc slim specific device handlers
*/
static int slim_rproc_start(struct rproc *rproc)
{
struct device *dev = &rproc->dev;
struct st_slim_rproc *slim_rproc = rproc->priv;
unsigned long hw_id, hw_ver, fw_rev;
u32 val;
/* disable CPU pipeline clock & reset CPU pipeline */
val = SLIM_CLK_GATE_DIS | SLIM_CLK_GATE_RESET;
writel(val, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
/* disable SLIM core STBus sync */
writel(SLIM_STBUS_SYNC_DIS, slim_rproc->peri + SLIM_STBUS_SYNC_OFST);
/* enable cpu pipeline clock */
writel(!SLIM_CLK_GATE_DIS,
slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
/* clear int & cmd mailbox */
writel(~0U, slim_rproc->peri + SLIM_INT_CLR_OFST);
writel(~0U, slim_rproc->peri + SLIM_CMD_CLR_OFST);
/* enable all channels cmd & int */
writel(~0U, slim_rproc->peri + SLIM_INT_MASK_OFST);
writel(~0U, slim_rproc->peri + SLIM_CMD_MASK_OFST);
/* enable cpu */
writel(SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);
hw_id = readl_relaxed(slim_rproc->slimcore + SLIM_ID_OFST);
hw_ver = readl_relaxed(slim_rproc->slimcore + SLIM_VER_OFST);
fw_rev = readl(slim_rproc->mem[ST_SLIM_DMEM].cpu_addr +
SLIM_REV_ID_OFST);
dev_info(dev, "fw rev:%ld.%ld on SLIM %ld.%ld\n",
SLIM_REV_ID_MAJ(fw_rev), SLIM_REV_ID_MIN(fw_rev),
hw_id, hw_ver);
return 0;
}
static int slim_rproc_stop(struct rproc *rproc)
{
struct st_slim_rproc *slim_rproc = rproc->priv;
u32 val;
/* mask all (cmd & int) channels */
writel(0UL, slim_rproc->peri + SLIM_INT_MASK_OFST);
writel(0UL, slim_rproc->peri + SLIM_CMD_MASK_OFST);
/* disable cpu pipeline clock */
writel(SLIM_CLK_GATE_DIS, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
writel(!SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);
val = readl(slim_rproc->slimcore + SLIM_EN_OFST);
if (val & SLIM_EN_RUN)
dev_warn(&rproc->dev, "Failed to disable SLIM");
dev_dbg(&rproc->dev, "slim stopped\n");
return 0;
}
static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
{
struct st_slim_rproc *slim_rproc = rproc->priv;
void *va = NULL;
int i;
for (i = 0; i < ST_SLIM_MEM_MAX; i++) {
if (da != slim_rproc->mem[i].bus_addr)
continue;
if (len <= slim_rproc->mem[i].size) {
/* __force to make sparse happy with type conversion */
va = (__force void *)slim_rproc->mem[i].cpu_addr;
break;
}
}
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va);
return va;
}
static struct rproc_ops slim_rproc_ops = {
.start = slim_rproc_start,
.stop = slim_rproc_stop,
.da_to_va = slim_rproc_da_to_va,
};
/*
* Firmware handler operations: sanity, boot address, load ...
*/
static struct resource_table empty_rsc_tbl = {
.ver = 1,
.num = 0,
};
static struct resource_table *slim_rproc_find_rsc_table(struct rproc *rproc,
const struct firmware *fw,
int *tablesz)
{
*tablesz = sizeof(empty_rsc_tbl);
return &empty_rsc_tbl;
}
static struct rproc_fw_ops slim_rproc_fw_ops = {
.find_rsc_table = slim_rproc_find_rsc_table,
};
/**
* st_slim_rproc_alloc() - allocate and initialise slim rproc
* @pdev: Pointer to the platform_device struct
* @fw_name: Name of firmware for rproc to use
*
* Function for allocating and initialising a slim rproc for use by
* device drivers whose IP is based around the SLIM core. It
* obtains and enables any clocks required by the SLIM core and also
* ioremaps the various IO.
*
* Returns st_slim_rproc pointer or PTR_ERR() on error.
*/
struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev,
char *fw_name)
{
struct device *dev = &pdev->dev;
struct st_slim_rproc *slim_rproc;
struct device_node *np = dev->of_node;
struct rproc *rproc;
struct resource *res;
int err, i;
const struct rproc_fw_ops *elf_ops;
if (!fw_name)
return ERR_PTR(-EINVAL);
if (!of_device_is_compatible(np, "st,slim-rproc"))
return ERR_PTR(-EINVAL);
rproc = rproc_alloc(dev, np->name, &slim_rproc_ops,
fw_name, sizeof(*slim_rproc));
if (!rproc)
return ERR_PTR(-ENOMEM);
rproc->has_iommu = false;
slim_rproc = rproc->priv;
slim_rproc->rproc = rproc;
elf_ops = rproc->fw_ops;
/* Use some generic elf ops */
slim_rproc_fw_ops.load = elf_ops->load;
slim_rproc_fw_ops.sanity_check = elf_ops->sanity_check;
rproc->fw_ops = &slim_rproc_fw_ops;
/* get imem and dmem */
for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
mem_names[i]);
slim_rproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(slim_rproc->mem[i].cpu_addr)) {
dev_err(&pdev->dev, "devm_ioremap_resource failed\n");
err = PTR_ERR(slim_rproc->mem[i].cpu_addr);
goto err;
}
slim_rproc->mem[i].bus_addr = res->start;
slim_rproc->mem[i].size = resource_size(res);
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slimcore");
slim_rproc->slimcore = devm_ioremap_resource(dev, res);
if (IS_ERR(slim_rproc->slimcore)) {
dev_err(&pdev->dev, "failed to ioremap slimcore IO\n");
err = PTR_ERR(slim_rproc->slimcore);
goto err;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "peripherals");
slim_rproc->peri = devm_ioremap_resource(dev, res);
if (IS_ERR(slim_rproc->peri)) {
dev_err(&pdev->dev, "failed to ioremap peripherals IO\n");
err = PTR_ERR(slim_rproc->peri);
goto err;
}
err = slim_clk_get(slim_rproc, dev);
if (err)
goto err;
err = slim_clk_enable(slim_rproc);
if (err) {
dev_err(dev, "Failed to enable clocks\n");
goto err_clk_put;
}
/* Register as a remoteproc device */
err = rproc_add(rproc);
if (err) {
dev_err(dev, "registration of slim remoteproc failed\n");
goto err_clk_dis;
}
return slim_rproc;
err_clk_dis:
slim_clk_disable(slim_rproc);
err_clk_put:
for (i = 0; i < ST_SLIM_MAX_CLK && slim_rproc->clks[i]; i++)
clk_put(slim_rproc->clks[i]);
err:
rproc_free(rproc);
return ERR_PTR(err);
}
EXPORT_SYMBOL(st_slim_rproc_alloc);
/**
* st_slim_rproc_put() - put slim rproc resources
* @slim_rproc: Pointer to the st_slim_rproc struct
*
* Function for calling respective _put() functions on slim_rproc resources.
*
*/
void st_slim_rproc_put(struct st_slim_rproc *slim_rproc)
{
int clk;
if (!slim_rproc)
return;
slim_clk_disable(slim_rproc);
for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)
clk_put(slim_rproc->clks[clk]);
rproc_del(slim_rproc->rproc);
rproc_free(slim_rproc->rproc);
}
EXPORT_SYMBOL(st_slim_rproc_put);
MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
MODULE_DESCRIPTION("STMicroelectronics SLIM core rproc driver");
MODULE_LICENSE("GPL v2");
/*
* Copyright (C) ST-Ericsson AB 2012
* Author: Sjur Brændeland <sjur.brandeland@stericsson.com>
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/remoteproc.h>
#include <linux/ste_modem_shm.h>
#include "remoteproc_internal.h"
#define SPROC_FW_SIZE (50 * 4096)
#define SPROC_MAX_TOC_ENTRIES 32
#define SPROC_MAX_NOTIFY_ID 14
#define SPROC_RESOURCE_NAME "rsc-table"
#define SPROC_MODEM_NAME "ste-modem"
#define SPROC_MODEM_FIRMWARE SPROC_MODEM_NAME "-fw.bin"
#define sproc_dbg(sproc, fmt, ...) \
dev_dbg(&sproc->mdev->pdev.dev, fmt, ##__VA_ARGS__)
#define sproc_err(sproc, fmt, ...) \
dev_err(&sproc->mdev->pdev.dev, fmt, ##__VA_ARGS__)
/* STE-modem control structure */
struct sproc {
struct rproc *rproc;
struct ste_modem_device *mdev;
int error;
void *fw_addr;
size_t fw_size;
dma_addr_t fw_dma_addr;
};
/* STE-Modem firmware entry */
struct ste_toc_entry {
__le32 start;
__le32 size;
__le32 flags;
__le32 entry_point;
__le32 load_addr;
char name[12];
};
/*
* The Table Of Content is located at the start of the firmware image and
* at offset zero in the shared memory region. The resource table typically
* contains the initial boot image (boot strap) and other information elements
* such as remoteproc resource table. Each entry is identified by a unique
* name.
*/
struct ste_toc {
struct ste_toc_entry table[SPROC_MAX_TOC_ENTRIES];
};
/* Loads the firmware to shared memory. */
static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw)
{
struct sproc *sproc = rproc->priv;
memcpy(sproc->fw_addr, fw->data, fw->size);
return 0;
}
/* Find the entry for resource table in the Table of Content */
static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data)
{
int i;
const struct ste_toc *toc = data;
/* Search the table for the resource table */
for (i = 0; i < SPROC_MAX_TOC_ENTRIES &&
toc->table[i].start != 0xffffffff; i++) {
if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME,
sizeof(toc->table[i].name)))
return &toc->table[i];
}
return NULL;
}
/* Find the resource table inside the remote processor's firmware. */
static struct resource_table *
sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
int *tablesz)
{
struct sproc *sproc = rproc->priv;
struct resource_table *table;
const struct ste_toc_entry *entry;
if (!fw)
return NULL;
entry = sproc_find_rsc_entry(fw->data);
if (!entry) {
sproc_err(sproc, "resource table not found in fw\n");
return NULL;
}
table = (void *)(fw->data + entry->start);
/* sanity check size and offset of resource table */
if (entry->start > SPROC_FW_SIZE ||
entry->size > SPROC_FW_SIZE ||
fw->size > SPROC_FW_SIZE ||
entry->start + entry->size > fw->size ||
sizeof(struct resource_table) > entry->size) {
sproc_err(sproc, "bad size of fw or resource table\n");
return NULL;
}
/* we don't support any version beyond the first */
if (table->ver != 1) {
sproc_err(sproc, "unsupported fw ver: %d\n", table->ver);
return NULL;
}
/* make sure reserved bytes are zeroes */
if (table->reserved[0] || table->reserved[1]) {
sproc_err(sproc, "non zero reserved bytes\n");
return NULL;
}
/* make sure the offsets array isn't truncated */
if (table->num > SPROC_MAX_TOC_ENTRIES ||
table->num * sizeof(table->offset[0]) +
sizeof(struct resource_table) > entry->size) {
sproc_err(sproc, "resource table incomplete\n");
return NULL;
}
/* If the fw size has grown, release the previous fw allocation */
if (SPROC_FW_SIZE < fw->size) {
sproc_err(sproc, "Insufficient space for fw (%d < %zd)\n",
SPROC_FW_SIZE, fw->size);
return NULL;
}
sproc->fw_size = fw->size;
*tablesz = entry->size;
return table;
}
/* Find the resource table inside the remote processor's firmware. */
static struct resource_table *
sproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
{
struct sproc *sproc = rproc->priv;
const struct ste_toc_entry *entry;
if (!fw || !sproc->fw_addr)
return NULL;
entry = sproc_find_rsc_entry(sproc->fw_addr);
if (!entry) {
sproc_err(sproc, "resource table not found in fw\n");
return NULL;
}
return sproc->fw_addr + entry->start;
}
/* STE modem firmware handler operations */
static const struct rproc_fw_ops sproc_fw_ops = {
.load = sproc_load_segments,
.find_rsc_table = sproc_find_rsc_table,
.find_loaded_rsc_table = sproc_find_loaded_rsc_table,
};
/* Kick the modem with specified notification id */
static void sproc_kick(struct rproc *rproc, int vqid)
{
struct sproc *sproc = rproc->priv;
sproc_dbg(sproc, "kick vqid:%d\n", vqid);
/*
* We need different notification IDs for RX and TX so add
* an offset on TX notification IDs.
*/
sproc->mdev->ops.kick(sproc->mdev, vqid + SPROC_MAX_NOTIFY_ID);
}
/* Received a kick from a modem, kick the virtqueue */
static void sproc_kick_callback(struct ste_modem_device *mdev, int vqid)
{
struct sproc *sproc = mdev->drv_data;
if (rproc_vq_interrupt(sproc->rproc, vqid) == IRQ_NONE)
sproc_dbg(sproc, "no message was found in vqid %d\n", vqid);
}
static struct ste_modem_dev_cb sproc_dev_cb = {
.kick = sproc_kick_callback,
};
/* Start the STE modem */
static int sproc_start(struct rproc *rproc)
{
struct sproc *sproc = rproc->priv;
int i, err;
sproc_dbg(sproc, "start ste-modem\n");
/* Sanity test the max_notifyid */
if (rproc->max_notifyid > SPROC_MAX_NOTIFY_ID) {
sproc_err(sproc, "Notification IDs too high:%d\n",
rproc->max_notifyid);
return -EINVAL;
}
/* Subscribe to notifications */
for (i = 0; i <= rproc->max_notifyid; i++) {
err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i);
if (err) {
sproc_err(sproc,
"subscription of kicks failed:%d\n", err);
return err;
}
}
/* Request modem start-up*/
return sproc->mdev->ops.power(sproc->mdev, true);
}
/* Stop the STE modem */
static int sproc_stop(struct rproc *rproc)
{
struct sproc *sproc = rproc->priv;
sproc_dbg(sproc, "stop ste-modem\n");
return sproc->mdev->ops.power(sproc->mdev, false);
}
static struct rproc_ops sproc_ops = {
.start = sproc_start,
.stop = sproc_stop,
.kick = sproc_kick,
};
/* STE modem device is unregistered */
static int sproc_drv_remove(struct platform_device *pdev)
{
struct ste_modem_device *mdev =
container_of(pdev, struct ste_modem_device, pdev);
struct sproc *sproc = mdev->drv_data;
sproc_dbg(sproc, "remove ste-modem\n");
/* Reset device callback functions */
sproc->mdev->ops.setup(sproc->mdev, NULL);
/* Unregister as remoteproc device */
rproc_del(sproc->rproc);
dma_free_coherent(sproc->rproc->dev.parent, SPROC_FW_SIZE,
sproc->fw_addr, sproc->fw_dma_addr);
rproc_free(sproc->rproc);
mdev->drv_data = NULL;
return 0;
}
/* Handle probe of a modem device */
static int sproc_probe(struct platform_device *pdev)
{
struct ste_modem_device *mdev =
container_of(pdev, struct ste_modem_device, pdev);
struct sproc *sproc;
struct rproc *rproc;
int err;
dev_dbg(&mdev->pdev.dev, "probe ste-modem\n");
if (!mdev->ops.setup || !mdev->ops.kick || !mdev->ops.kick_subscribe ||
!mdev->ops.power) {
dev_err(&mdev->pdev.dev, "invalid mdev ops\n");
return -EINVAL;
}
rproc = rproc_alloc(&mdev->pdev.dev, mdev->pdev.name, &sproc_ops,
SPROC_MODEM_FIRMWARE, sizeof(*sproc));
if (!rproc)
return -ENOMEM;
sproc = rproc->priv;
sproc->mdev = mdev;
sproc->rproc = rproc;
rproc->has_iommu = false;
mdev->drv_data = sproc;
/* Provide callback functions to modem device */
sproc->mdev->ops.setup(sproc->mdev, &sproc_dev_cb);
/* Set the STE-modem specific firmware handler */
rproc->fw_ops = &sproc_fw_ops;
/*
* STE-modem requires the firmware to be located
* at the start of the shared memory region. So we need to
* reserve space for firmware at the start.
*/
sproc->fw_addr = dma_alloc_coherent(rproc->dev.parent, SPROC_FW_SIZE,
&sproc->fw_dma_addr,
GFP_KERNEL);
if (!sproc->fw_addr) {
sproc_err(sproc, "Cannot allocate memory for fw\n");
err = -ENOMEM;
goto free_rproc;
}
/* Register as a remoteproc device */
err = rproc_add(rproc);
if (err)
goto free_mem;
return 0;
free_mem:
dma_free_coherent(rproc->dev.parent, SPROC_FW_SIZE,
sproc->fw_addr, sproc->fw_dma_addr);
free_rproc:
/* Reset device data upon error */
mdev->drv_data = NULL;
rproc_free(rproc);
return err;
}
static struct platform_driver sproc_driver = {
.driver = {
.name = SPROC_MODEM_NAME,
},
.probe = sproc_probe,
.remove = sproc_drv_remove,
};
module_platform_driver(sproc_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("STE Modem driver using the Remote Processor Framework");
......@@ -25,6 +25,7 @@
#include <linux/soc/qcom/smem.h>
#include <linux/wait.h>
#include <linux/rpmsg.h>
#include <linux/rpmsg/qcom_smd.h>
#include "rpmsg_internal.h"
......
......@@ -400,6 +400,7 @@ enum rproc_crash_type {
* @firmware_loading_complete: marks e/o asynchronous firmware loading
* @bootaddr: address of first instruction to boot rproc with (optional)
* @rvdevs: list of remote virtio devices
* @subdevs: list of subdevices, to following the running state
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids
* @index: index of this rproc device
* @crash_handler: workqueue for handling a crash
......@@ -407,15 +408,14 @@ enum rproc_crash_type {
* @crash_comp: completion used to sync crash handler and the rproc reload
* @recovery_disabled: flag that state if recovery was disabled
* @max_notifyid: largest allocated notify id.
* @table_ptr: pointer to the resource table in effect
* @cached_table: copy of the resource table
* @table_ptr: our copy of the resource table
* @has_iommu: flag to indicate if remote processor is behind an MMU
*/
struct rproc {
struct list_head node;
struct iommu_domain *domain;
const char *name;
const char *firmware;
char *firmware;
void *priv;
const struct rproc_ops *ops;
struct device dev;
......@@ -431,6 +431,7 @@ struct rproc {
struct completion firmware_loading_complete;
u32 bootaddr;
struct list_head rvdevs;
struct list_head subdevs;
struct idr notifyids;
int index;
struct work_struct crash_handler;
......@@ -439,11 +440,23 @@ struct rproc {
bool recovery_disabled;
int max_notifyid;
struct resource_table *table_ptr;
struct resource_table *cached_table;
bool has_iommu;
bool auto_boot;
};
/**
* struct rproc_subdev - subdevice tied to a remoteproc
* @node: list node related to the rproc subdevs list
* @probe: probe function, called as the rproc is started
* @remove: remove function, called as the rproc is stopped
*/
struct rproc_subdev {
struct list_head node;
int (*probe)(struct rproc_subdev *subdev);
void (*remove)(struct rproc_subdev *subdev);
};
/* we currently support only two vrings per rvdev */
#define RVDEV_NUM_VRINGS 2
......@@ -472,6 +485,9 @@ struct rproc_vring {
/**
* struct rproc_vdev - remoteproc state for a supported virtio device
* @refcount: reference counter for the vdev and vring allocations
* @subdev: handle for registering the vdev as a rproc subdevice
* @id: virtio device id (as in virtio_ids.h)
* @node: list node
* @rproc: the rproc handle
* @vdev: the virio device
......@@ -479,6 +495,11 @@ struct rproc_vring {
* @rsc_offset: offset of the vdev's resource entry
*/
struct rproc_vdev {
struct kref refcount;
struct rproc_subdev subdev;
unsigned int id;
struct list_head node;
struct rproc *rproc;
struct virtio_device vdev;
......@@ -511,4 +532,11 @@ static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev)
return rvdev->rproc;
}
void rproc_add_subdev(struct rproc *rproc,
struct rproc_subdev *subdev,
int (*probe)(struct rproc_subdev *subdev),
void (*remove)(struct rproc_subdev *subdev));
void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev);
#endif /* REMOTEPROC_H */
/*
* SLIM core rproc driver header
*
* Copyright (C) 2016 STMicroelectronics
*
* Author: Peter Griffin <peter.griffin@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _ST_REMOTEPROC_SLIM_H
#define _ST_REMOTEPROC_SLIM_H
#define ST_SLIM_MEM_MAX 2
#define ST_SLIM_MAX_CLK 4
enum {
ST_SLIM_DMEM,
ST_SLIM_IMEM,
};
/**
* struct st_slim_mem - slim internal memory structure
* @cpu_addr: MPU virtual address of the memory region
* @bus_addr: Bus address used to access the memory region
* @size: Size of the memory region
*/
struct st_slim_mem {
void __iomem *cpu_addr;
phys_addr_t bus_addr;
size_t size;
};
/**
* struct st_slim_rproc - SLIM slim core
* @rproc: rproc handle
* @mem: slim memory information
* @slimcore: slim slimcore regs
* @peri: slim peripheral regs
* @clks: slim clocks
*/
struct st_slim_rproc {
struct rproc *rproc;
struct st_slim_mem mem[ST_SLIM_MEM_MAX];
void __iomem *slimcore;
void __iomem *peri;
/* st_slim_rproc private */
struct clk *clks[ST_SLIM_MAX_CLK];
};
struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev,
char *fw_name);
void st_slim_rproc_put(struct st_slim_rproc *slim_rproc);
#endif
#ifndef _LINUX_RPMSG_QCOM_SMD_H
#define _LINUX_RPMSG_QCOM_SMD_H
#include <linux/device.h>
struct qcom_smd_edge;
#if IS_ENABLED(CONFIG_RPMSG_QCOM_SMD) || IS_ENABLED(CONFIG_QCOM_SMD)
struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
struct device_node *node);
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge);
#else
static inline struct qcom_smd_edge *
qcom_smd_register_edge(struct device *parent,
struct device_node *node)
{
return ERR_PTR(-ENXIO);
}
static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
{
/* This shouldn't be possible */
WARN_ON(1);
return -ENXIO;
}
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment