Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
9b2e827
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
comedi
connector
counter
cpufreq
cpuidle
crypto
cxl
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hte
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
parisc
parport
pci
pcmcia
peci
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
Kconfig
Makefile
da8xx_remoteproc.c
imx_dsp_rproc.c
imx_rproc.c
imx_rproc.h
ingenic_rproc.c
keystone_remoteproc.c
meson_mx_ao_arc.c
mtk_common.h
mtk_scp.c
mtk_scp_ipi.c
omap_remoteproc.c
omap_remoteproc.h
pru_rproc.c
pru_rproc.h
qcom_common.c
qcom_common.h
qcom_pil_info.c
qcom_pil_info.h
qcom_q6v5.c
qcom_q6v5.h
qcom_q6v5_adsp.c
qcom_q6v5_mss.c
qcom_q6v5_pas.c
qcom_q6v5_wcss.c
qcom_sysmon.c
qcom_wcnss.c
qcom_wcnss.h
qcom_wcnss_iris.c
rcar_rproc.c
remoteproc_cdev.c
remoteproc_core.c
remoteproc_coredump.c
remoteproc_debugfs.c
remoteproc_elf_helpers.h
remoteproc_elf_loader.c
remoteproc_internal.h
remoteproc_sysfs.c
remoteproc_virtio.c
st_remoteproc.c
st_slim_rproc.c
stm32_rproc.c
ti_k3_dsp_remoteproc.c
ti_k3_r5_remoteproc.c
ti_sci_proc.h
wkup_m3_rproc.c
reset
rpmsg
rtc
s390
sbus
scsi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
ufs
uio
usb
vdpa
vfio
vhost
video
virt
virtio
vlynq
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
io_uring
ipc
kernel
lib
mm
net
rust
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
.rustfmt.toml
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
remoteproc
/
stm32_rproc.c
Copy path
Blame
Blame
Latest commit
History
History
932 lines (768 loc) · 21.7 KB
Breadcrumbs
linux
/
drivers
/
remoteproc
/
stm32_rproc.c
Top
File metadata and controls
Code
Blame
932 lines (768 loc) · 21.7 KB
Raw
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics 2018 - All Rights Reserved * Authors: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics. * Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. */ #include <linux/arm-smccc.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mailbox_client.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_reserved_mem.h> #include <linux/pm_wakeirq.h> #include <linux/regmap.h> #include <linux/remoteproc.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/workqueue.h> #include "remoteproc_internal.h" #define HOLD_BOOT 0 #define RELEASE_BOOT 1 #define MBOX_NB_VQ 2 #define MBOX_NB_MBX 4 #define STM32_SMC_RCC 0x82001000 #define STM32_SMC_REG_WRITE 0x1 #define STM32_MBX_VQ0 "vq0" #define STM32_MBX_VQ0_ID 0 #define STM32_MBX_VQ1 "vq1" #define STM32_MBX_VQ1_ID 1 #define STM32_MBX_SHUTDOWN "shutdown" #define STM32_MBX_DETACH "detach" #define RSC_TBL_SIZE 1024 #define M4_STATE_OFF 0 #define M4_STATE_INI 1 #define M4_STATE_CRUN 2 #define M4_STATE_CSTOP 3 #define M4_STATE_STANDBY 4 #define M4_STATE_CRASH 5 struct stm32_syscon { struct regmap *map; u32 reg; u32 mask; }; struct stm32_rproc_mem { char name[20]; void __iomem *cpu_addr; phys_addr_t bus_addr; u32 dev_addr; size_t size; }; struct stm32_rproc_mem_ranges { u32 dev_addr; u32 bus_addr; u32 size; }; struct stm32_mbox { const unsigned char name[10]; struct mbox_chan *chan; struct mbox_client client; struct work_struct vq_work; int vq_id; }; struct stm32_rproc { struct reset_control *rst; struct stm32_syscon hold_boot; struct stm32_syscon pdds; struct stm32_syscon m4_state; struct stm32_syscon rsctbl; int wdg_irq; u32 nb_rmems; struct stm32_rproc_mem *rmems; struct stm32_mbox mb[MBOX_NB_MBX]; struct workqueue_struct *workqueue; bool secured_soc; void __iomem *rsc_va; }; static int stm32_rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da) { unsigned int i; struct stm32_rproc *ddata = rproc->priv; struct stm32_rproc_mem *p_mem; for (i = 0; i < ddata->nb_rmems; i++) { p_mem = &ddata->rmems[i]; if (pa < p_mem->bus_addr || pa >= p_mem->bus_addr + p_mem->size) continue; *da = pa - p_mem->bus_addr + p_mem->dev_addr; dev_dbg(rproc->dev.parent, "pa %pa to da %llx\n", &pa, *da); return 0; } return -EINVAL; } static int stm32_rproc_mem_alloc(struct rproc *rproc, struct rproc_mem_entry *mem) { struct device *dev = rproc->dev.parent; void *va; dev_dbg(dev, "map memory: %pad+%zx\n", &mem->dma, mem->len); va = (__force void *)ioremap_wc(mem->dma, mem->len); if (IS_ERR_OR_NULL(va)) { dev_err(dev, "Unable to map memory region: %pad+0x%zx\n", &mem->dma, mem->len); return -ENOMEM; } /* Update memory entry va */ mem->va = va; return 0; } static int stm32_rproc_mem_release(struct rproc *rproc, struct rproc_mem_entry *mem) { dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma); iounmap((__force __iomem void *)mem->va); return 0; } static int stm32_rproc_of_memory_translations(struct platform_device *pdev, struct stm32_rproc *ddata) { struct device *parent, *dev = &pdev->dev; struct device_node *np; struct stm32_rproc_mem *p_mems; struct stm32_rproc_mem_ranges *mem_range; int cnt, array_size, i, ret = 0; parent = dev->parent; np = parent->of_node; cnt = of_property_count_elems_of_size(np, "dma-ranges", sizeof(*mem_range)); if (cnt <= 0) { dev_err(dev, "%s: dma-ranges property not defined\n", __func__); return -EINVAL; } p_mems = devm_kcalloc(dev, cnt, sizeof(*p_mems), GFP_KERNEL); if (!p_mems) return -ENOMEM; mem_range = kcalloc(cnt, sizeof(*mem_range), GFP_KERNEL); if (!mem_range) return -ENOMEM; array_size = cnt * sizeof(struct stm32_rproc_mem_ranges) / sizeof(u32); ret = of_property_read_u32_array(np, "dma-ranges", (u32 *)mem_range, array_size); if (ret) { dev_err(dev, "error while get dma-ranges property: %x\n", ret); goto free_mem; } for (i = 0; i < cnt; i++) { p_mems[i].bus_addr = mem_range[i].bus_addr; p_mems[i].dev_addr = mem_range[i].dev_addr; p_mems[i].size = mem_range[i].size; dev_dbg(dev, "memory range[%i]: da %#x, pa %pa, size %#zx:\n", i, p_mems[i].dev_addr, &p_mems[i].bus_addr, p_mems[i].size); } ddata->rmems = p_mems; ddata->nb_rmems = cnt; free_mem: kfree(mem_range); return ret; } static int stm32_rproc_mbox_idx(struct rproc *rproc, const unsigned char *name) { struct stm32_rproc *ddata = rproc->priv; int i; for (i = 0; i < ARRAY_SIZE(ddata->mb); i++) { if (!strncmp(ddata->mb[i].name, name, strlen(name))) return i; } dev_err(&rproc->dev, "mailbox %s not found\n", name); return -EINVAL; } static int stm32_rproc_prepare(struct rproc *rproc) { struct device *dev = rproc->dev.parent; struct device_node *np = dev->of_node; struct of_phandle_iterator it; struct rproc_mem_entry *mem; struct reserved_mem *rmem; u64 da; int index = 0; /* Register associated reserved memory regions */ of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); while (of_phandle_iterator_next(&it) == 0) { rmem = of_reserved_mem_lookup(it.node); if (!rmem) { of_node_put(it.node); dev_err(dev, "unable to acquire memory-region\n"); return -EINVAL; } if (stm32_rproc_pa_to_da(rproc, rmem->base, &da) < 0) { of_node_put(it.node); dev_err(dev, "memory region not valid %pa\n", &rmem->base); return -EINVAL; } /* No need to map vdev buffer */ if (strcmp(it.node->name, "vdev0buffer")) { /* Register memory region */ mem = rproc_mem_entry_init(dev, NULL, (dma_addr_t)rmem->base, rmem->size, da, stm32_rproc_mem_alloc, stm32_rproc_mem_release, it.node->name); if (mem) rproc_coredump_add_segment(rproc, da, rmem->size); } else { /* Register reserved memory for vdev buffer alloc */ mem = rproc_of_resm_mem_entry_init(dev, index, rmem->size, rmem->base, it.node->name); } if (!mem) { of_node_put(it.node); return -ENOMEM; } rproc_add_carveout(rproc, mem); index++; } return 0; } static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { if (rproc_elf_load_rsc_table(rproc, fw)) dev_warn(&rproc->dev, "no resource table found for this firmware\n"); return 0; } static irqreturn_t stm32_rproc_wdg(int irq, void *data) { struct platform_device *pdev = data; struct rproc *rproc = platform_get_drvdata(pdev); rproc_report_crash(rproc, RPROC_WATCHDOG); return IRQ_HANDLED; } static void stm32_rproc_mb_vq_work(struct work_struct *work) { struct stm32_mbox *mb = container_of(work, struct stm32_mbox, vq_work); struct rproc *rproc = dev_get_drvdata(mb->client.dev); mutex_lock(&rproc->lock); if (rproc->state != RPROC_RUNNING) goto unlock_mutex; if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) dev_dbg(&rproc->dev, "no message found in vq%d\n", mb->vq_id); unlock_mutex: mutex_unlock(&rproc->lock); } static void stm32_rproc_mb_callback(struct mbox_client *cl, void *data) { struct rproc *rproc = dev_get_drvdata(cl->dev); struct stm32_mbox *mb = container_of(cl, struct stm32_mbox, client); struct stm32_rproc *ddata = rproc->priv; queue_work(ddata->workqueue, &mb->vq_work); } static void stm32_rproc_free_mbox(struct rproc *rproc) { struct stm32_rproc *ddata = rproc->priv; unsigned int i; for (i = 0; i < ARRAY_SIZE(ddata->mb); i++) { if (ddata->mb[i].chan) mbox_free_channel(ddata->mb[i].chan); ddata->mb[i].chan = NULL; } } static const struct stm32_mbox stm32_rproc_mbox[MBOX_NB_MBX] = { { .name = STM32_MBX_VQ0, .vq_id = STM32_MBX_VQ0_ID, .client = { .rx_callback = stm32_rproc_mb_callback, .tx_block = false, }, }, { .name = STM32_MBX_VQ1, .vq_id = STM32_MBX_VQ1_ID, .client = { .rx_callback = stm32_rproc_mb_callback, .tx_block = false, }, }, { .name = STM32_MBX_SHUTDOWN, .vq_id = -1, .client = { .tx_block = true, .tx_done = NULL, .tx_tout = 500, /* 500 ms time out */ }, }, { .name = STM32_MBX_DETACH, .vq_id = -1, .client = { .tx_block = true, .tx_done = NULL, .tx_tout = 200, /* 200 ms time out to detach should be fair enough */ }, } }; static int stm32_rproc_request_mbox(struct rproc *rproc) { struct stm32_rproc *ddata = rproc->priv; struct device *dev = &rproc->dev; unsigned int i; int j; const unsigned char *name; struct mbox_client *cl; /* Initialise mailbox structure table */ memcpy(ddata->mb, stm32_rproc_mbox, sizeof(stm32_rproc_mbox)); for (i = 0; i < MBOX_NB_MBX; i++) { name = ddata->mb[i].name; cl = &ddata->mb[i].client; cl->dev = dev->parent; ddata->mb[i].chan = mbox_request_channel_byname(cl, name); if (IS_ERR(ddata->mb[i].chan)) { if (PTR_ERR(ddata->mb[i].chan) == -EPROBE_DEFER) { dev_err_probe(dev->parent, PTR_ERR(ddata->mb[i].chan), "failed to request mailbox %s\n", name); goto err_probe; } dev_warn(dev, "cannot get %s mbox\n", name); ddata->mb[i].chan = NULL; } if (ddata->mb[i].vq_id >= 0) { INIT_WORK(&ddata->mb[i].vq_work, stm32_rproc_mb_vq_work); } } return 0; err_probe: for (j = i - 1; j >= 0; j--) if (ddata->mb[j].chan) mbox_free_channel(ddata->mb[j].chan); return -EPROBE_DEFER; } static int stm32_rproc_set_hold_boot(struct rproc *rproc, bool hold) { struct stm32_rproc *ddata = rproc->priv; struct stm32_syscon hold_boot = ddata->hold_boot; struct arm_smccc_res smc_res; int val, err; val = hold ? HOLD_BOOT : RELEASE_BOOT; if (IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) && ddata->secured_soc) { arm_smccc_smc(STM32_SMC_RCC, STM32_SMC_REG_WRITE, hold_boot.reg, val, 0, 0, 0, 0, &smc_res); err = smc_res.a0; } else { err = regmap_update_bits(hold_boot.map, hold_boot.reg, hold_boot.mask, val); } if (err) dev_err(&rproc->dev, "failed to set hold boot\n"); return err; } static void stm32_rproc_add_coredump_trace(struct rproc *rproc) { struct rproc_debug_trace *trace; struct rproc_dump_segment *segment; bool already_added; list_for_each_entry(trace, &rproc->traces, node) { already_added = false; list_for_each_entry(segment, &rproc->dump_segments, node) { if (segment->da == trace->trace_mem.da) { already_added = true; break; } } if (!already_added) rproc_coredump_add_segment(rproc, trace->trace_mem.da, trace->trace_mem.len); } } static int stm32_rproc_start(struct rproc *rproc) { struct stm32_rproc *ddata = rproc->priv; int err; stm32_rproc_add_coredump_trace(rproc); /* clear remote proc Deep Sleep */ if (ddata->pdds.map) { err = regmap_update_bits(ddata->pdds.map, ddata->pdds.reg, ddata->pdds.mask, 0); if (err) { dev_err(&rproc->dev, "failed to clear pdds\n"); return err; } } err = stm32_rproc_set_hold_boot(rproc, false); if (err) return err; return stm32_rproc_set_hold_boot(rproc, true); } static int stm32_rproc_attach(struct rproc *rproc) { stm32_rproc_add_coredump_trace(rproc); return stm32_rproc_set_hold_boot(rproc, true); } static int stm32_rproc_detach(struct rproc *rproc) { struct stm32_rproc *ddata = rproc->priv; int err, idx; /* Inform the remote processor of the detach */ idx = stm32_rproc_mbox_idx(rproc, STM32_MBX_DETACH); if (idx >= 0 && ddata->mb[idx].chan) { err = mbox_send_message(ddata->mb[idx].chan, "stop"); if (err < 0) dev_warn(&rproc->dev, "warning: remote FW detach without ack\n"); } /* Allow remote processor to auto-reboot */ return stm32_rproc_set_hold_boot(rproc, false); } static int stm32_rproc_stop(struct rproc *rproc) { struct stm32_rproc *ddata = rproc->priv; int err, idx; /* request shutdown of the remote processor */ if (rproc->state != RPROC_OFFLINE && rproc->state != RPROC_CRASHED) { idx = stm32_rproc_mbox_idx(rproc, STM32_MBX_SHUTDOWN); if (idx >= 0 && ddata->mb[idx].chan) { err = mbox_send_message(ddata->mb[idx].chan, "detach"); if (err < 0) dev_warn(&rproc->dev, "warning: remote FW shutdown without ack\n"); } } err = stm32_rproc_set_hold_boot(rproc, true); if (err) return err; err = reset_control_assert(ddata->rst); if (err) { dev_err(&rproc->dev, "failed to assert the reset\n"); return err; } /* to allow platform Standby power mode, set remote proc Deep Sleep */ if (ddata->pdds.map) { err = regmap_update_bits(ddata->pdds.map, ddata->pdds.reg, ddata->pdds.mask, 1); if (err) { dev_err(&rproc->dev, "failed to set pdds\n"); return err; } } /* update coprocessor state to OFF if available */ if (ddata->m4_state.map) { err = regmap_update_bits(ddata->m4_state.map, ddata->m4_state.reg, ddata->m4_state.mask, M4_STATE_OFF); if (err) { dev_err(&rproc->dev, "failed to set copro state\n"); return err; } } return 0; } static void stm32_rproc_kick(struct rproc *rproc, int vqid) { struct stm32_rproc *ddata = rproc->priv; unsigned int i; int err; if (WARN_ON(vqid >= MBOX_NB_VQ)) return; for (i = 0; i < MBOX_NB_MBX; i++) { if (vqid != ddata->mb[i].vq_id) continue; if (!ddata->mb[i].chan) return; err = mbox_send_message(ddata->mb[i].chan, "kick"); if (err < 0) dev_err(&rproc->dev, "%s: failed (%s, err:%d)\n", __func__, ddata->mb[i].name, err); return; } } static int stm32_rproc_da_to_pa(struct rproc *rproc, u64 da, phys_addr_t *pa) { struct stm32_rproc *ddata = rproc->priv; struct device *dev = rproc->dev.parent; struct stm32_rproc_mem *p_mem; unsigned int i; for (i = 0; i < ddata->nb_rmems; i++) { p_mem = &ddata->rmems[i]; if (da < p_mem->dev_addr || da >= p_mem->dev_addr + p_mem->size) continue; *pa = da - p_mem->dev_addr + p_mem->bus_addr; dev_dbg(dev, "da %llx to pa %pap\n", da, pa); return 0; } dev_err(dev, "can't translate da %llx\n", da); return -EINVAL; } static struct resource_table * stm32_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz) { struct stm32_rproc *ddata = rproc->priv; struct device *dev = rproc->dev.parent; phys_addr_t rsc_pa; u32 rsc_da; int err; /* The resource table has already been mapped, nothing to do */ if (ddata->rsc_va) goto done; err = regmap_read(ddata->rsctbl.map, ddata->rsctbl.reg, &rsc_da); if (err) { dev_err(dev, "failed to read rsc tbl addr\n"); return ERR_PTR(-EINVAL); } if (!rsc_da) /* no rsc table */ return ERR_PTR(-ENOENT); err = stm32_rproc_da_to_pa(rproc, rsc_da, &rsc_pa); if (err) return ERR_PTR(err); ddata->rsc_va = devm_ioremap_wc(dev, rsc_pa, RSC_TBL_SIZE); if (IS_ERR_OR_NULL(ddata->rsc_va)) { dev_err(dev, "Unable to map memory region: %pa+%x\n", &rsc_pa, RSC_TBL_SIZE); ddata->rsc_va = NULL; return ERR_PTR(-ENOMEM); } done: /* * Assuming the resource table fits in 1kB is fair. * Notice for the detach, that this 1 kB memory area has to be reserved in the coprocessor * firmware for the resource table. On detach, the remoteproc core re-initializes this * entire area by overwriting it with the initial values stored in rproc->clean_table. */ *table_sz = RSC_TBL_SIZE; return (__force struct resource_table *)ddata->rsc_va; } static const struct rproc_ops st_rproc_ops = { .prepare = stm32_rproc_prepare, .start = stm32_rproc_start, .stop = stm32_rproc_stop, .attach = stm32_rproc_attach, .detach = stm32_rproc_detach, .kick = stm32_rproc_kick, .load = rproc_elf_load_segments, .parse_fw = stm32_rproc_parse_fw, .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table, .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, }; static const struct of_device_id stm32_rproc_match[] = { { .compatible = "st,stm32mp1-m4" }, {}, }; MODULE_DEVICE_TABLE(of, stm32_rproc_match); static int stm32_rproc_get_syscon(struct device_node *np, const char *prop, struct stm32_syscon *syscon) { int err = 0; syscon->map = syscon_regmap_lookup_by_phandle(np, prop); if (IS_ERR(syscon->map)) { err = PTR_ERR(syscon->map); syscon->map = NULL; goto out; } err = of_property_read_u32_index(np, prop, 1, &syscon->reg); if (err) goto out; err = of_property_read_u32_index(np, prop, 2, &syscon->mask); out: return err; } static int stm32_rproc_parse_dt(struct platform_device *pdev, struct stm32_rproc *ddata, bool *auto_boot) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct stm32_syscon tz; unsigned int tzen; int err, irq; irq = platform_get_irq(pdev, 0); if (irq == -EPROBE_DEFER) return dev_err_probe(dev, irq, "failed to get interrupt\n"); if (irq > 0) { err = devm_request_irq(dev, irq, stm32_rproc_wdg, 0, dev_name(dev), pdev); if (err) return dev_err_probe(dev, err, "failed to request wdg irq\n"); ddata->wdg_irq = irq; if (of_property_read_bool(np, "wakeup-source")) { device_init_wakeup(dev, true); dev_pm_set_wake_irq(dev, irq); } dev_info(dev, "wdg irq registered\n"); } ddata->rst = devm_reset_control_get_by_index(dev, 0); if (IS_ERR(ddata->rst)) return dev_err_probe(dev, PTR_ERR(ddata->rst), "failed to get mcu_reset\n"); /* * if platform is secured the hold boot bit must be written by * smc call and read normally. * if not secure the hold boot bit could be read/write normally */ err = stm32_rproc_get_syscon(np, "st,syscfg-tz", &tz); if (err) { dev_err(dev, "failed to get tz syscfg\n"); return err; } err = regmap_read(tz.map, tz.reg, &tzen); if (err) { dev_err(dev, "failed to read tzen\n"); return err; } ddata->secured_soc = tzen & tz.mask; err = stm32_rproc_get_syscon(np, "st,syscfg-holdboot", &ddata->hold_boot); if (err) { dev_err(dev, "failed to get hold boot\n"); return err; } err = stm32_rproc_get_syscon(np, "st,syscfg-pdds", &ddata->pdds); if (err) dev_info(dev, "failed to get pdds\n"); *auto_boot = of_property_read_bool(np, "st,auto-boot"); /* * See if we can check the M4 status, i.e if it was started * from the boot loader or not. */ err = stm32_rproc_get_syscon(np, "st,syscfg-m4-state", &ddata->m4_state); if (err) { /* remember this */ ddata->m4_state.map = NULL; /* no coprocessor state syscon (optional) */ dev_warn(dev, "m4 state not supported\n"); /* no need to go further */ return 0; } /* See if we can get the resource table */ err = stm32_rproc_get_syscon(np, "st,syscfg-rsc-tbl", &ddata->rsctbl); if (err) { /* no rsc table syscon (optional) */ dev_warn(dev, "rsc tbl syscon not supported\n"); } return 0; } static int stm32_rproc_get_m4_status(struct stm32_rproc *ddata, unsigned int *state) { /* See stm32_rproc_parse_dt() */ if (!ddata->m4_state.map) { /* * We couldn't get the coprocessor's state, assume * it is not running. */ *state = M4_STATE_OFF; return 0; } return regmap_read(ddata->m4_state.map, ddata->m4_state.reg, state); } static int stm32_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stm32_rproc *ddata; struct device_node *np = dev->of_node; struct rproc *rproc; unsigned int state; int ret; ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) return ret; rproc = rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata)); if (!rproc) return -ENOMEM; ddata = rproc->priv; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); ret = stm32_rproc_parse_dt(pdev, ddata, &rproc->auto_boot); if (ret) goto free_rproc; ret = stm32_rproc_of_memory_translations(pdev, ddata); if (ret) goto free_rproc; ret = stm32_rproc_get_m4_status(ddata, &state); if (ret) goto free_rproc; if (state == M4_STATE_CRUN) rproc->state = RPROC_DETACHED; rproc->has_iommu = false; ddata->workqueue = create_workqueue(dev_name(dev)); if (!ddata->workqueue) { dev_err(dev, "cannot create workqueue\n"); ret = -ENOMEM; goto free_resources; } platform_set_drvdata(pdev, rproc); ret = stm32_rproc_request_mbox(rproc); if (ret) goto free_wkq; ret = rproc_add(rproc); if (ret) goto free_mb; return 0; free_mb: stm32_rproc_free_mbox(rproc); free_wkq: destroy_workqueue(ddata->workqueue); free_resources: rproc_resource_cleanup(rproc); free_rproc: if (device_may_wakeup(dev)) { dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); } rproc_free(rproc); return ret; } static int stm32_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); struct stm32_rproc *ddata = rproc->priv; struct device *dev = &pdev->dev; if (atomic_read(&rproc->power) > 0) rproc_shutdown(rproc); rproc_del(rproc); stm32_rproc_free_mbox(rproc); destroy_workqueue(ddata->workqueue); if (device_may_wakeup(dev)) { dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); } rproc_free(rproc); return 0; } static int stm32_rproc_suspend(struct device *dev) { struct rproc *rproc = dev_get_drvdata(dev); struct stm32_rproc *ddata = rproc->priv; if (device_may_wakeup(dev)) return enable_irq_wake(ddata->wdg_irq); return 0; } static int stm32_rproc_resume(struct device *dev) { struct rproc *rproc = dev_get_drvdata(dev); struct stm32_rproc *ddata = rproc->priv; if (device_may_wakeup(dev)) return disable_irq_wake(ddata->wdg_irq); return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(stm32_rproc_pm_ops, stm32_rproc_suspend, stm32_rproc_resume); static struct platform_driver stm32_rproc_driver = { .probe = stm32_rproc_probe, .remove = stm32_rproc_remove, .driver = { .name = "stm32-rproc", .pm = pm_ptr(&stm32_rproc_pm_ops), .of_match_table = stm32_rproc_match, }, }; module_platform_driver(stm32_rproc_driver); MODULE_DESCRIPTION("STM32 Remote Processor Control Driver"); MODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>"); MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); MODULE_LICENSE("GPL v2");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
You can’t perform that action at this time.