| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296 |
- From b37b26232ebb6c0a61b530f11ccd6eefdf782c04 Mon Sep 17 00:00:00 2001
- From: "shanlong.li" <[email protected]>
- Date: Fri, 16 Jun 2023 03:02:14 -0700
- Subject: [PATCH 18/55] driver:e24: add e24 driver
- add e24 driver
- Signed-off-by: shanlong.li <[email protected]>
- ---
- drivers/Kconfig | 1 +
- drivers/Makefile | 1 +
- drivers/e24/Kconfig | 5 +
- drivers/e24/Makefile | 12 +
- drivers/e24/e24_alloc.c | 241 ++++++
- drivers/e24/e24_alloc.h | 59 ++
- drivers/e24/starfive_e24.c | 1524 +++++++++++++++++++++++++++++++++
- drivers/e24/starfive_e24.h | 159 ++++
- drivers/e24/starfive_e24_hw.c | 134 +++
- drivers/e24/starfive_e24_hw.h | 94 ++
- 10 files changed, 2230 insertions(+)
- create mode 100644 drivers/e24/Kconfig
- create mode 100644 drivers/e24/Makefile
- create mode 100644 drivers/e24/e24_alloc.c
- create mode 100644 drivers/e24/e24_alloc.h
- create mode 100644 drivers/e24/starfive_e24.c
- create mode 100644 drivers/e24/starfive_e24.h
- create mode 100644 drivers/e24/starfive_e24_hw.c
- create mode 100644 drivers/e24/starfive_e24_hw.h
- --- a/drivers/Kconfig
- +++ b/drivers/Kconfig
- @@ -245,4 +245,5 @@ source "drivers/cdx/Kconfig"
-
- source "drivers/dpll/Kconfig"
-
- +source "drivers/e24/Kconfig"
- endmenu
- --- a/drivers/Makefile
- +++ b/drivers/Makefile
- @@ -195,3 +195,4 @@ obj-$(CONFIG_CDX_BUS) += cdx/
- obj-$(CONFIG_DPLL) += dpll/
-
- obj-$(CONFIG_S390) += s390/
- +obj-$(CONFIG_E24) += e24/
- --- /dev/null
- +++ b/drivers/e24/Kconfig
- @@ -0,0 +1,5 @@
- +config E24
- + tristate "E24 support"
- + default m
- + help
- + This module provides the function of E24 device.
- --- /dev/null
- +++ b/drivers/e24/Makefile
- @@ -0,0 +1,12 @@
- +# SPDX-License-Identifier: GPL-2.0
- +# Copyright(c) 1999 - 2018 Intel Corporation.
- +#
- +# Makefile for the E24 driver
- +#
- +ccflags-y += -I$(srctree)/drivers/e24
- +#ccflags-y += -DDEBUG
- +ccflags-y += -Wunused-variable -Wno-error=missing-prototypes
- +
- +obj-$(CONFIG_E24) += e24.o
- +
- +e24-y := starfive_e24.o starfive_e24_hw.o e24_alloc.o
- --- /dev/null
- +++ b/drivers/e24/e24_alloc.c
- @@ -0,0 +1,241 @@
- +// SPDX-License-Identifier: GPL-2.0
- +#include <linux/atomic.h>
- +#include <linux/kernel.h>
- +#include <linux/mutex.h>
- +#include <linux/printk.h>
- +#include <linux/slab.h>
- +#include "e24_alloc.h"
- +
- +struct e24_private_pool {
- + struct e24_allocation_pool pool;
- + struct mutex free_list_lock;
- + phys_addr_t start;
- + u32 size;
- + struct e24_allocation *free_list;
- +};
- +
- +static void e24_private_free(struct e24_allocation *e24_allocation)
- +{
- + struct e24_private_pool *pool = container_of(e24_allocation->pool,
- + struct e24_private_pool,
- + pool);
- + struct e24_allocation **pcur;
- +
- + pr_debug("%s: %pap x %d\n", __func__,
- + &e24_allocation->start, e24_allocation->size);
- +
- + mutex_lock(&pool->free_list_lock);
- +
- + for (pcur = &pool->free_list; ; pcur = &(*pcur)->next) {
- + struct e24_allocation *cur = *pcur;
- +
- + if (cur && cur->start + cur->size == e24_allocation->start) {
- + struct e24_allocation *next = cur->next;
- +
- + pr_debug("merging block tail: %pap x 0x%x ->\n",
- + &cur->start, cur->size);
- + cur->size += e24_allocation->size;
- + pr_debug("... -> %pap x 0x%x\n",
- + &cur->start, cur->size);
- + kfree(e24_allocation);
- +
- + if (next && cur->start + cur->size == next->start) {
- + pr_debug("merging with next block: %pap x 0x%x ->\n",
- + &cur->start, cur->size);
- + cur->size += next->size;
- + cur->next = next->next;
- + pr_debug("... -> %pap x 0x%x\n",
- + &cur->start, cur->size);
- + kfree(next);
- + }
- + break;
- + }
- +
- + if (!cur || e24_allocation->start < cur->start) {
- + if (cur && e24_allocation->start + e24_allocation->size ==
- + cur->start) {
- + pr_debug("merging block head: %pap x 0x%x ->\n",
- + &cur->start, cur->size);
- + cur->size += e24_allocation->size;
- + cur->start = e24_allocation->start;
- + pr_debug("... -> %pap x 0x%x\n",
- + &cur->start, cur->size);
- + kfree(e24_allocation);
- + } else {
- + pr_debug("inserting new free block\n");
- + e24_allocation->next = cur;
- + *pcur = e24_allocation;
- + }
- + break;
- + }
- + }
- +
- + mutex_unlock(&pool->free_list_lock);
- +}
- +
- +static long e24_private_alloc(struct e24_allocation_pool *pool,
- + u32 size, u32 align,
- + struct e24_allocation **alloc)
- +{
- + struct e24_private_pool *ppool = container_of(pool,
- + struct e24_private_pool,
- + pool);
- + struct e24_allocation **pcur;
- + struct e24_allocation *cur = NULL;
- + struct e24_allocation *new;
- + phys_addr_t aligned_start = 0;
- + bool found = false;
- +
- + if (!size || (align & (align - 1)))
- + return -EINVAL;
- + if (!align)
- + align = 1;
- +
- + new = kzalloc(sizeof(struct e24_allocation), GFP_KERNEL);
- + if (!new)
- + return -ENOMEM;
- +
- + align = ALIGN(align, PAGE_SIZE);
- + size = ALIGN(size, PAGE_SIZE);
- +
- + mutex_lock(&ppool->free_list_lock);
- +
- + /* on exit free list is fixed */
- + for (pcur = &ppool->free_list; *pcur; pcur = &(*pcur)->next) {
- + cur = *pcur;
- + aligned_start = ALIGN(cur->start, align);
- +
- + if (aligned_start >= cur->start &&
- + aligned_start - cur->start + size <= cur->size) {
- + if (aligned_start == cur->start) {
- + if (aligned_start + size == cur->start + cur->size) {
- + pr_debug("reusing complete block: %pap x %x\n",
- + &cur->start, cur->size);
- + *pcur = cur->next;
- + } else {
- + pr_debug("cutting block head: %pap x %x ->\n",
- + &cur->start, cur->size);
- + cur->size -= aligned_start + size - cur->start;
- + cur->start = aligned_start + size;
- + pr_debug("... -> %pap x %x\n",
- + &cur->start, cur->size);
- + cur = NULL;
- + }
- + } else {
- + if (aligned_start + size == cur->start + cur->size) {
- + pr_debug("cutting block tail: %pap x %x ->\n",
- + &cur->start, cur->size);
- + cur->size = aligned_start - cur->start;
- + pr_debug("... -> %pap x %x\n",
- + &cur->start, cur->size);
- + cur = NULL;
- + } else {
- + pr_debug("splitting block into two: %pap x %x ->\n",
- + &cur->start, cur->size);
- + new->start = aligned_start + size;
- + new->size = cur->start +
- + cur->size - new->start;
- +
- + cur->size = aligned_start - cur->start;
- +
- + new->next = cur->next;
- + cur->next = new;
- + pr_debug("... -> %pap x %x + %pap x %x\n",
- + &cur->start, cur->size,
- + &new->start, new->size);
- +
- + cur = NULL;
- + new = NULL;
- + }
- + }
- + found = true;
- + break;
- + } else {
- + cur = NULL;
- + }
- + }
- +
- + mutex_unlock(&ppool->free_list_lock);
- +
- + if (!found) {
- + kfree(cur);
- + kfree(new);
- + return -ENOMEM;
- + }
- +
- + if (!cur) {
- + cur = new;
- + new = NULL;
- + }
- + if (!cur) {
- + cur = kzalloc(sizeof(struct e24_allocation), GFP_KERNEL);
- + if (!cur)
- + return -ENOMEM;
- + }
- +
- + kfree(new);
- + pr_debug("returning: %pap x %x\n", &aligned_start, size);
- + cur->start = aligned_start;
- + cur->size = size;
- + cur->pool = pool;
- + atomic_set(&cur->ref, 0);
- + atomic_inc(&cur->ref);
- + *alloc = cur;
- +
- + return 0;
- +}
- +
- +static void e24_private_free_pool(struct e24_allocation_pool *pool)
- +{
- + struct e24_private_pool *ppool = container_of(pool,
- + struct e24_private_pool,
- + pool);
- + kfree(ppool->free_list);
- + kfree(ppool);
- +}
- +
- +static phys_addr_t e24_private_offset(const struct e24_allocation *allocation)
- +{
- + struct e24_private_pool *ppool = container_of(allocation->pool,
- + struct e24_private_pool,
- + pool);
- + return allocation->start - ppool->start;
- +}
- +
- +static const struct e24_allocation_ops e24_private_pool_ops = {
- + .alloc = e24_private_alloc,
- + .free = e24_private_free,
- + .free_pool = e24_private_free_pool,
- + .offset = e24_private_offset,
- +};
- +
- +long e24_init_private_pool(struct e24_allocation_pool **ppool,
- + phys_addr_t start, u32 size)
- +{
- + struct e24_private_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
- + struct e24_allocation *allocation = kmalloc(sizeof(*allocation),
- + GFP_KERNEL);
- +
- + if (!pool || !allocation) {
- + kfree(pool);
- + kfree(allocation);
- + return -ENOMEM;
- + }
- +
- + *allocation = (struct e24_allocation){
- + .pool = &pool->pool,
- + .start = start,
- + .size = size,
- + };
- + *pool = (struct e24_private_pool){
- + .pool = {
- + .ops = &e24_private_pool_ops,
- + },
- + .start = start,
- + .size = size,
- + .free_list = allocation,
- + };
- + mutex_init(&pool->free_list_lock);
- + *ppool = &pool->pool;
- + return 0;
- +}
- --- /dev/null
- +++ b/drivers/e24/e24_alloc.h
- @@ -0,0 +1,59 @@
- +/* SPDX-License-Identifier: GPL-2.0 */
- +#ifndef E24_ALLOC_H
- +#define E24_ALLOC_H
- +
- +struct e24_allocation_pool;
- +struct e24_allocation;
- +
- +struct e24_allocation_ops {
- + long (*alloc)(struct e24_allocation_pool *allocation_pool,
- + u32 size, u32 align, struct e24_allocation **alloc);
- + void (*free)(struct e24_allocation *allocation);
- + void (*free_pool)(struct e24_allocation_pool *allocation_pool);
- + phys_addr_t (*offset)(const struct e24_allocation *allocation);
- +};
- +
- +struct e24_allocation_pool {
- + const struct e24_allocation_ops *ops;
- +};
- +
- +struct e24_allocation {
- + struct e24_allocation_pool *pool;
- + struct e24_allocation *next;
- + phys_addr_t start;
- + u32 size;
- + atomic_t ref;
- +};
- +
- +static inline void e24_free_pool(struct e24_allocation_pool *allocation_pool)
- +{
- + allocation_pool->ops->free_pool(allocation_pool);
- +}
- +
- +static inline void e24_free(struct e24_allocation *allocation)
- +{
- + return allocation->pool->ops->free(allocation);
- +}
- +
- +static inline long e24_allocate(struct e24_allocation_pool *allocation_pool,
- + u32 size, u32 align,
- + struct e24_allocation **alloc)
- +{
- + return allocation_pool->ops->alloc(allocation_pool,
- + size, align, alloc);
- +}
- +
- +static inline void e24_allocation_put(struct e24_allocation *e24_allocation)
- +{
- + if (atomic_dec_and_test(&e24_allocation->ref))
- + e24_allocation->pool->ops->free(e24_allocation);
- +}
- +
- +static inline phys_addr_t e24_allocation_offset(const struct e24_allocation *allocation)
- +{
- + return allocation->pool->ops->offset(allocation);
- +}
- +
- +long e24_init_private_pool(struct e24_allocation_pool **ppool, phys_addr_t start, u32 size);
- +
- +#endif
- --- /dev/null
- +++ b/drivers/e24/starfive_e24.c
- @@ -0,0 +1,1524 @@
- +// SPDX-License-Identifier: GPL-2.0
- +/*
- + * e24 driver for StarFive JH7110 SoC
- + *
- + * Copyright (c) 2021 StarFive Technology Co., Ltd.
- + * Author: Shanlong Li <[email protected]>
- + */
- +#include <linux/version.h>
- +#include <linux/atomic.h>
- +#include <linux/acpi.h>
- +#include <linux/completion.h>
- +#include <linux/delay.h>
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)
- +#include <linux/dma-mapping.h>
- +#else
- +#include <linux/dma-direct.h>
- +#endif
- +#include <linux/firmware.h>
- +#include <linux/fs.h>
- +#include <linux/hashtable.h>
- +#include <linux/highmem.h>
- +#include <linux/idr.h>
- +#include <linux/interrupt.h>
- +#include <linux/io.h>
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/of.h>
- +#include <linux/of_address.h>
- +#include <linux/of_device.h>
- +#include <linux/of_reserved_mem.h>
- +#include <linux/platform_device.h>
- +#include <linux/pm_runtime.h>
- +#include <linux/property.h>
- +#include <linux/sched.h>
- +#include <linux/slab.h>
- +#include <linux/sort.h>
- +#include <linux/mman.h>
- +#include <linux/uaccess.h>
- +#include <linux/mailbox_controller.h>
- +#include <linux/mailbox_client.h>
- +
- +#include <linux/bsearch.h>
- +
- +#include "e24_alloc.h"
- +#include "starfive_e24.h"
- +#include "starfive_e24_hw.h"
- +
- +#define EMBOX_MAX_MSG_LEN 4
- +
- +static DEFINE_IDA(e24_nodeid);
- +
- +struct e24_dsp_cmd {
- + __u32 flags;
- + __u32 in_data_size;
- + __u32 out_data_size;
- + union {
- + __u32 in_data_addr;
- + __u8 in_data[E24_DSP_CMD_INLINE_DATA_SIZE];
- + };
- + union {
- + __u32 out_data_addr;
- + __u8 out_data[E24_DSP_CMD_INLINE_DATA_SIZE];
- + };
- +};
- +
- +struct e24_ioctl_user {
- + u32 flags;
- + u32 in_data_size;
- + u32 out_data_size;
- + u64 in_data_addr;
- + u64 out_data_addr;
- +};
- +
- +struct e24_ioctl_request {
- + struct e24_ioctl_user ioctl_data;
- + phys_addr_t in_data_phys;
- + phys_addr_t out_data_phys;
- + struct e24_mapping *buffer_mapping;
- +
- + union {
- + struct e24_mapping in_data_mapping;
- + u8 in_data[E24_DSP_CMD_INLINE_DATA_SIZE];
- + };
- + union {
- + struct e24_mapping out_data_mapping;
- + u8 out_data[E24_DSP_CMD_INLINE_DATA_SIZE];
- + };
- +};
- +
- +static int firmware_command_timeout = 10;
- +
- +static inline void e24_comm_read(volatile void __iomem *addr, void *p,
- + size_t sz)
- +{
- + size_t sz32 = sz & ~3;
- + u32 v;
- +
- + while (sz32) {
- + v = __raw_readl(addr);
- + memcpy(p, &v, sizeof(v));
- + p += 4;
- + addr += 4;
- + sz32 -= 4;
- + }
- + sz &= 3;
- + if (sz) {
- + v = __raw_readl(addr);
- + memcpy(p, &v, sz);
- + }
- +}
- +
- +static inline void e24_comm_write(volatile void __iomem *addr, const void *p,
- + size_t sz)
- +{
- + size_t sz32 = sz & ~3;
- + u32 v;
- +
- + while (sz32) {
- + memcpy(&v, p, sizeof(v));
- + __raw_writel(v, addr);
- + p += 4;
- + addr += 4;
- + sz32 -= 4;
- + }
- + sz &= 3;
- + if (sz) {
- + v = 0;
- + memcpy(&v, p, sz);
- + __raw_writel(v, addr);
- + }
- +}
- +
- +static bool e24_cacheable(struct e24_device *e24_dat, unsigned long pfn,
- + unsigned long n_pages)
- +{
- + if (e24_dat->hw_ops->cacheable) {
- + return e24_dat->hw_ops->cacheable(e24_dat->hw_arg, pfn, n_pages);
- + } else {
- + unsigned long i;
- +
- + for (i = 0; i < n_pages; ++i)
- + if (!pfn_valid(pfn + i))
- + return false;
- + return true;
- + }
- +}
- +
- +static int e24_compare_address_sort(const void *a, const void *b)
- +{
- + const struct e24_address_map_entry *pa = a;
- + const struct e24_address_map_entry *pb = b;
- +
- + if (pa->src_addr < pb->src_addr &&
- + pb->src_addr - pa->src_addr >= pa->size)
- + return -1;
- + if (pa->src_addr > pb->src_addr &&
- + pa->src_addr - pb->src_addr >= pb->size)
- + return 1;
- +
- + return 0;
- +}
- +
- +static int e24_compare_address_search(const void *a, const void *b)
- +{
- + const phys_addr_t *pa = a;
- +
- + return e24_compare_address(*pa, b);
- +}
- +
- +struct e24_address_map_entry *
- +e24_get_address_mapping(const struct e24_address_map *map, phys_addr_t addr)
- +{
- + return bsearch(&addr, map->entry, map->n, sizeof(*map->entry),
- + e24_compare_address_search);
- +}
- +
- +u32 e24_translate_to_dsp(const struct e24_address_map *map, phys_addr_t addr)
- +{
- +#ifdef E24_MEM_MAP
- + return addr;
- +#else
- + struct e24_address_map_entry *entry = e24_get_address_mapping(map, addr);
- +
- + if (!entry)
- + return E24_NO_TRANSLATION;
- + return entry->dst_addr + addr - entry->src_addr;
- +#endif
- +}
- +
- +static int e24_dma_direction(unsigned int flags)
- +{
- + static const enum dma_data_direction e24_dma_direction[] = {
- + [0] = DMA_NONE,
- + [E24_FLAG_READ] = DMA_TO_DEVICE,
- + [E24_FLAG_WRITE] = DMA_FROM_DEVICE,
- + [E24_FLAG_READ_WRITE] = DMA_BIDIRECTIONAL,
- + };
- + return e24_dma_direction[flags & E24_FLAG_READ_WRITE];
- +}
- +
- +static void e24_dma_sync_for_cpu(struct e24_device *e24_dat,
- + unsigned long virt,
- + phys_addr_t phys,
- + unsigned long size,
- + unsigned long flags)
- +{
- + if (e24_dat->hw_ops->dma_sync_for_cpu)
- + e24_dat->hw_ops->dma_sync_for_cpu(e24_dat->hw_arg,
- + (void *)virt, phys, size,
- + flags);
- + else
- + dma_sync_single_for_cpu(e24_dat->dev, phys_to_dma(e24_dat->dev, phys), size,
- + e24_dma_direction(flags));
- +}
- +
- +static void starfive_mbox_receive_message(struct mbox_client *client, void *message)
- +{
- + struct e24_device *e24_dat = dev_get_drvdata(client->dev);
- +
- + complete(&e24_dat->tx_channel->tx_complete);
- +}
- +
- +static struct mbox_chan *
- +starfive_mbox_request_channel(struct device *dev, const char *name)
- +{
- + struct mbox_client *client;
- + struct mbox_chan *channel;
- +
- + client = devm_kzalloc(dev, sizeof(*client), GFP_KERNEL);
- + if (!client)
- + return ERR_PTR(-ENOMEM);
- +
- + client->dev = dev;
- + client->rx_callback = starfive_mbox_receive_message;
- + client->tx_prepare = NULL;
- + client->tx_done = NULL;
- + client->tx_block = true;
- + client->knows_txdone = false;
- + client->tx_tout = 3000;
- +
- + channel = mbox_request_channel_byname(client, name);
- + if (IS_ERR(channel)) {
- + dev_warn(dev, "Failed to request %s channel\n", name);
- + return NULL;
- + }
- +
- + return channel;
- +}
- +
- +static void e24_vm_open(struct vm_area_struct *vma)
- +{
- + struct e24_allocation *cur = vma->vm_private_data;
- +
- + atomic_inc(&cur->ref);
- +}
- +
- +static void e24_vm_close(struct vm_area_struct *vma)
- +{
- + e24_allocation_put(vma->vm_private_data);
- +}
- +
- +static const struct vm_operations_struct e24_vm_ops = {
- + .open = e24_vm_open,
- + .close = e24_vm_close,
- +};
- +
- +static long e24_synchronize(struct e24_device *dev)
- +{
- +
- + struct e24_comm *queue = dev->queue;
- + struct e24_dsp_cmd __iomem *cmd = queue->comm;
- + u32 flags;
- + unsigned long deadline = jiffies + 10 * HZ;
- +
- + do {
- + flags = __raw_readl(&cmd->flags);
- + /* memory barrier */
- + rmb();
- + if (flags == 0x104)
- + return 0;
- +
- + schedule();
- + } while (time_before(jiffies, deadline));
- +
- + return -1;
- +}
- +
- +static int e24_open(struct inode *inode, struct file *filp)
- +{
- + struct e24_device *e24_dev = container_of(filp->private_data,
- + struct e24_device, miscdev);
- + int rc = 0;
- +
- + rc = pm_runtime_get_sync(e24_dev->dev);
- + if (rc < 0)
- + return rc;
- +
- + spin_lock_init(&e24_dev->busy_list_lock);
- + filp->private_data = e24_dev;
- + mdelay(1);
- +
- + return 0;
- +}
- +
- +int e24_release(struct inode *inode, struct file *filp)
- +{
- + struct e24_device *e24_dev = (struct e24_device *)filp->private_data;
- + int rc = 0;
- +
- + rc = pm_runtime_put_sync(e24_dev->dev);
- + if (rc < 0)
- + return rc;
- +
- + return 0;
- +}
- +
- +static ssize_t mbox_e24_message_write(struct file *filp,
- + const char __user *userbuf,
- + size_t count, loff_t *ppos)
- +{
- + struct e24_device *edev = filp->private_data;
- + void *data;
- + int ret;
- +
- + if (!edev->tx_channel) {
- + dev_err(edev->dev, "Channel cannot do Tx\n");
- + return -EINVAL;
- + }
- +
- + if (count > EMBOX_MAX_MSG_LEN) {
- + dev_err(edev->dev,
- + "Message length %zd greater than max allowed %d\n",
- + count, EMBOX_MAX_MSG_LEN);
- + return -EINVAL;
- + }
- +
- + edev->message = kzalloc(EMBOX_MAX_MSG_LEN, GFP_KERNEL);
- + if (!edev->message)
- + return -ENOMEM;
- +
- + ret = copy_from_user(edev->message, userbuf, count);
- + if (ret) {
- + ret = -EFAULT;
- + goto out;
- + }
- +
- + print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS,
- + edev->message, EMBOX_MAX_MSG_LEN);
- + data = edev->message;
- + pr_debug("%s:%d, %d\n", __func__, __LINE__, *((int *)data));
- + ret = mbox_send_message(edev->tx_channel, data);
- +
- + if (ret < 0 || !edev->tx_channel->active_req)
- + dev_err(edev->dev, "Failed to send message via mailbox:%d\n", ret);
- +
- +out:
- + kfree(edev->message);
- + edev->tx_channel->active_req = NULL;
- +
- + return ret < 0 ? ret : count;
- +}
- +
- +static long _e24_copy_user_phys(struct e24_device *edev,
- + unsigned long vaddr, unsigned long size,
- + phys_addr_t paddr, unsigned long flags,
- + bool to_phys)
- +{
- + void __iomem *p = ioremap(paddr, size);
- + unsigned long rc;
- +
- + if (!p) {
- + dev_err(edev->dev,
- + "couldn't ioremap %pap x 0x%08x\n",
- + &paddr, (u32)size);
- + return -EINVAL;
- + }
- + if (to_phys)
- + rc = raw_copy_from_user(__io_virt(p),
- + (void __user *)vaddr, size);
- + else
- + rc = copy_to_user((void __user *)vaddr,
- + __io_virt(p), size);
- + iounmap(p);
- + if (rc)
- + return -EFAULT;
- + return 0;
- +}
- +
- +static long e24_copy_user_to_phys(struct e24_device *edev,
- + unsigned long vaddr, unsigned long size,
- + phys_addr_t paddr, unsigned long flags)
- +{
- + return _e24_copy_user_phys(edev, vaddr, size, paddr, flags, true);
- +}
- +
- +static long e24_copy_user_from_phys(struct e24_device *edev,
- + unsigned long vaddr, unsigned long size,
- + phys_addr_t paddr, unsigned long flags)
- +{
- + return _e24_copy_user_phys(edev, vaddr, size, paddr, flags, false);
- +}
- +
- +static long e24_copy_virt_to_phys(struct e24_device *edev,
- + unsigned long flags,
- + unsigned long vaddr, unsigned long size,
- + phys_addr_t *paddr,
- + struct e24_alien_mapping *mapping)
- +{
- + phys_addr_t phys;
- + unsigned long align = clamp(vaddr & -vaddr, 16ul, PAGE_SIZE);
- + unsigned long offset = vaddr & (align - 1);
- + struct e24_allocation *allocation;
- + long rc;
- +
- + rc = e24_allocate(edev->pool,
- + size + align, align, &allocation);
- + if (rc < 0)
- + return rc;
- +
- + phys = (allocation->start & -align) | offset;
- + if (phys < allocation->start)
- + phys += align;
- +
- + if (flags & E24_FLAG_READ) {
- + if (e24_copy_user_to_phys(edev, vaddr,
- + size, phys, flags)) {
- + e24_allocation_put(allocation);
- + return -EFAULT;
- + }
- + }
- +
- + *paddr = phys;
- + *mapping = (struct e24_alien_mapping){
- + .vaddr = vaddr,
- + .size = size,
- + .paddr = *paddr,
- + .allocation = allocation,
- + .type = ALIEN_COPY,
- + };
- + pr_debug("%s: copying to pa: %pap\n", __func__, paddr);
- +
- + return 0;
- +}
- +
- +static long e24_writeback_alien_mapping(struct e24_device *edev,
- + struct e24_alien_mapping *alien_mapping,
- + unsigned long flags)
- +{
- + struct page *page;
- + size_t nr_pages;
- + size_t i;
- + long ret = 0;
- +
- + switch (alien_mapping->type) {
- + case ALIEN_GUP:
- + e24_dma_sync_for_cpu(edev,
- + alien_mapping->vaddr,
- + alien_mapping->paddr,
- + alien_mapping->size,
- + flags);
- + pr_debug("%s: dirtying alien GUP @va = %p, pa = %pap\n",
- + __func__, (void __user *)alien_mapping->vaddr,
- + &alien_mapping->paddr);
- + page = pfn_to_page(__phys_to_pfn(alien_mapping->paddr));
- + nr_pages = PFN_UP(alien_mapping->vaddr + alien_mapping->size) -
- + PFN_DOWN(alien_mapping->vaddr);
- + for (i = 0; i < nr_pages; ++i)
- + SetPageDirty(page + i);
- + break;
- +
- + case ALIEN_COPY:
- + pr_debug("%s: synchronizing alien copy @pa = %pap back to %p\n",
- + __func__, &alien_mapping->paddr,
- + (void __user *)alien_mapping->vaddr);
- + if (e24_copy_user_from_phys(edev,
- + alien_mapping->vaddr,
- + alien_mapping->size,
- + alien_mapping->paddr,
- + flags))
- + ret = -EINVAL;
- + break;
- +
- + default:
- + break;
- + }
- + return ret;
- +}
- +
- +static bool vma_needs_cache_ops(struct vm_area_struct *vma)
- +{
- + pgprot_t prot = vma->vm_page_prot;
- +
- + return pgprot_val(prot) != pgprot_val(pgprot_noncached(prot)) &&
- + pgprot_val(prot) != pgprot_val(pgprot_writecombine(prot));
- +}
- +
- +static void e24_alien_mapping_destroy(struct e24_alien_mapping *alien_mapping)
- +{
- + switch (alien_mapping->type) {
- + case ALIEN_COPY:
- + e24_allocation_put(alien_mapping->allocation);
- + break;
- + default:
- + break;
- + }
- +}
- +
- +static long __e24_unshare_block(struct file *filp, struct e24_mapping *mapping,
- + unsigned long flags)
- +{
- + long ret = 0;
- + struct e24_device *edev = filp->private_data;
- +
- + switch (mapping->type & ~E24_MAPPING_KERNEL) {
- + case E24_MAPPING_NATIVE:
- + if (flags & E24_FLAG_WRITE) {
- + e24_dma_sync_for_cpu(edev,
- + mapping->native.vaddr,
- + mapping->native.m_allocation->start,
- + mapping->native.m_allocation->size,
- + flags);
- + }
- + e24_allocation_put(mapping->native.m_allocation);
- + break;
- +
- + case E24_MAPPING_ALIEN:
- + if (flags & E24_FLAG_WRITE) {
- + ret = e24_writeback_alien_mapping(edev,
- + &mapping->alien_mapping,
- + flags);
- + }
- + e24_alien_mapping_destroy(&mapping->alien_mapping);
- + break;
- +
- + case E24_MAPPING_KERNEL:
- + break;
- +
- + default:
- + break;
- + }
- +
- + mapping->type = E24_MAPPING_NONE;
- +
- + return ret;
- +}
- +
- +static long __e24_share_block(struct file *filp,
- + unsigned long virt, unsigned long size,
- + unsigned long flags, phys_addr_t *paddr,
- + struct e24_mapping *mapping)
- +{
- + phys_addr_t phys = ~0ul;
- + struct e24_device *edev = filp->private_data;
- + struct mm_struct *mm = current->mm;
- + struct vm_area_struct *vma = find_vma(mm, virt);
- + bool do_cache = true;
- + long rc = -EINVAL;
- +
- + if (!vma) {
- + pr_debug("%s: no vma for vaddr/size = 0x%08lx/0x%08lx\n",
- + __func__, virt, size);
- + return -EINVAL;
- + }
- +
- + if (virt + size < virt || vma->vm_start > virt)
- + return -EINVAL;
- +
- + if (vma && (vma->vm_file == filp)) {
- + struct e24_device *vm_file = vma->vm_file->private_data;
- + struct e24_allocation *e24_user_allocation = vma->vm_private_data;
- +
- + phys = vm_file->shared_mem + (vma->vm_pgoff << PAGE_SHIFT) +
- + virt - vma->vm_start;
- + pr_debug("%s: E24 allocation at 0x%08lx, paddr: %pap\n",
- + __func__, virt, &phys);
- +
- + rc = 0;
- + mapping->type = E24_MAPPING_NATIVE;
- + mapping->native.m_allocation = e24_user_allocation;
- + mapping->native.vaddr = virt;
- + atomic_inc(&e24_user_allocation->ref);
- + do_cache = vma_needs_cache_ops(vma);
- + }
- + if (rc < 0) {
- + struct e24_alien_mapping *alien_mapping =
- + &mapping->alien_mapping;
- +
- + /* Otherwise this is alien allocation. */
- + pr_debug("%s: non-E24 allocation at 0x%08lx\n",
- + __func__, virt);
- +
- + rc = e24_copy_virt_to_phys(edev, flags,
- + virt, size, &phys,
- + alien_mapping);
- +
- + if (rc < 0) {
- + pr_debug("%s: couldn't map virt to phys\n",
- + __func__);
- + return -EINVAL;
- + }
- +
- + phys = alien_mapping->paddr +
- + virt - alien_mapping->vaddr;
- +
- + mapping->type = E24_MAPPING_ALIEN;
- + }
- +
- + *paddr = phys;
- + pr_debug("%s: mapping = %p, mapping->type = %d\n",
- + __func__, mapping, mapping->type);
- +
- + return 0;
- +}
- +
- +
- +static void e24_unmap_request_nowb(struct file *filp, struct e24_ioctl_request *rq)
- +{
- + if (rq->ioctl_data.in_data_size > E24_DSP_CMD_INLINE_DATA_SIZE)
- + __e24_unshare_block(filp, &rq->in_data_mapping, 0);
- + if (rq->ioctl_data.out_data_size > E24_DSP_CMD_INLINE_DATA_SIZE)
- + __e24_unshare_block(filp, &rq->out_data_mapping, 0);
- +}
- +
- +static long e24_unmap_request(struct file *filp, struct e24_ioctl_request *rq)
- +{
- + long ret = 0;
- +
- + if (rq->ioctl_data.in_data_size > E24_DSP_CMD_INLINE_DATA_SIZE)
- + __e24_unshare_block(filp, &rq->in_data_mapping, E24_FLAG_READ);
- +
- + if (rq->ioctl_data.out_data_size > E24_DSP_CMD_INLINE_DATA_SIZE) {
- + ret = __e24_unshare_block(filp, &rq->out_data_mapping,
- + E24_FLAG_WRITE);
- + if (ret < 0)
- + pr_debug("%s: out_data could not be unshared\n", __func__);
- +
- + } else {
- + if (copy_to_user((void __user *)(unsigned long)rq->ioctl_data.out_data_addr,
- + rq->out_data,
- + rq->ioctl_data.out_data_size)) {
- + pr_debug("%s: out_data could not be copied\n", __func__);
- + ret = -EFAULT;
- + }
- + }
- +
- + return ret;
- +}
- +
- +static bool e24_cmd_complete(struct e24_comm *share_com)
- +{
- + struct e24_dsp_cmd __iomem *cmd = share_com->comm;
- + u32 flags = __raw_readl(&cmd->flags);
- +
- + rmb();
- + return (flags & (E24_CMD_FLAG_REQUEST_VALID |
- + E24_CMD_FLAG_RESPONSE_VALID)) ==
- + (E24_CMD_FLAG_REQUEST_VALID |
- + E24_CMD_FLAG_RESPONSE_VALID);
- +}
- +
- +static long e24_complete_poll(struct e24_device *edev, struct e24_comm *comm,
- + bool (*cmd_complete)(struct e24_comm *p), struct e24_ioctl_request *rq)
- +{
- + unsigned long deadline = jiffies + firmware_command_timeout * HZ;
- +
- + do {
- + if (cmd_complete(comm)) {
- + pr_debug("%s: poll complete.\n", __func__);
- + return 0;
- + }
- + schedule();
- + } while (time_before(jiffies, deadline));
- +
- + pr_debug("%s: poll complete cmd timeout.\n", __func__);
- +
- + return -EBUSY;
- +}
- +
- +static long e24_map_request(struct file *filp, struct e24_ioctl_request *rq, struct mm_struct *mm)
- +{
- + long ret = 0;
- +
- + mmap_read_lock(mm);
- + if (rq->ioctl_data.in_data_size > E24_DSP_CMD_INLINE_DATA_SIZE) {
- + ret = __e24_share_block(filp, rq->ioctl_data.in_data_addr,
- + rq->ioctl_data.in_data_size,
- + E24_FLAG_READ, &rq->in_data_phys,
- + &rq->in_data_mapping);
- + if (ret < 0) {
- + pr_debug("%s: in_data could not be shared\n", __func__);
- + goto share_err;
- + }
- + } else {
- + if (copy_from_user(rq->in_data,
- + (void __user *)(unsigned long)rq->ioctl_data.in_data_addr,
- + rq->ioctl_data.in_data_size)) {
- + pr_debug("%s: in_data could not be copied\n",
- + __func__);
- + ret = -EFAULT;
- + goto share_err;
- + }
- + }
- +
- + if (rq->ioctl_data.out_data_size > E24_DSP_CMD_INLINE_DATA_SIZE) {
- + ret = __e24_share_block(filp, rq->ioctl_data.out_data_addr,
- + rq->ioctl_data.out_data_size,
- + E24_FLAG_WRITE, &rq->out_data_phys,
- + &rq->out_data_mapping);
- + if (ret < 0) {
- + pr_debug("%s: out_data could not be shared\n",
- + __func__);
- + goto share_err;
- + }
- + }
- +share_err:
- + mmap_read_unlock(mm);
- + if (ret < 0)
- + e24_unmap_request_nowb(filp, rq);
- + return ret;
- +
- +}
- +
- +static void e24_fill_hw_request(struct e24_dsp_cmd __iomem *cmd,
- + struct e24_ioctl_request *rq,
- + const struct e24_address_map *map)
- +{
- + __raw_writel(rq->ioctl_data.in_data_size, &cmd->in_data_size);
- + __raw_writel(rq->ioctl_data.out_data_size, &cmd->out_data_size);
- +
- + if (rq->ioctl_data.in_data_size > E24_DSP_CMD_INLINE_DATA_SIZE)
- + __raw_writel(e24_translate_to_dsp(map, rq->in_data_phys),
- + &cmd->in_data_addr);
- + else
- + e24_comm_write(&cmd->in_data, rq->in_data,
- + rq->ioctl_data.in_data_size);
- +
- + if (rq->ioctl_data.out_data_size > E24_DSP_CMD_INLINE_DATA_SIZE)
- + __raw_writel(e24_translate_to_dsp(map, rq->out_data_phys),
- + &cmd->out_data_addr);
- +
- + wmb();
- + /* update flags */
- + __raw_writel(rq->ioctl_data.flags, &cmd->flags);
- +}
- +
- +static long e24_complete_hw_request(struct e24_dsp_cmd __iomem *cmd,
- + struct e24_ioctl_request *rq)
- +{
- + u32 flags = __raw_readl(&cmd->flags);
- +
- + if (rq->ioctl_data.out_data_size <= E24_DSP_CMD_INLINE_DATA_SIZE)
- + e24_comm_read(&cmd->out_data, rq->out_data,
- + rq->ioctl_data.out_data_size);
- +
- + __raw_writel(0, &cmd->flags);
- +
- + return (flags & E24_QUEUE_VALID_FLAGS) ? -ENXIO : 0;
- +}
- +
- +static long e24_ioctl_submit_task(struct file *filp,
- + struct e24_ioctl_user __user *msg)
- +{
- + struct e24_device *edev = filp->private_data;
- + struct e24_comm *queue = edev->queue;
- + struct e24_ioctl_request rq_data, *vrq = &rq_data;
- + void *data = &edev->mbox_data;
- + int ret = -ENOMEM;
- + int irq_mode = edev->irq_mode;
- +
- + if (copy_from_user(&vrq->ioctl_data, msg, sizeof(*msg)))
- + return -EFAULT;
- +
- + if (vrq->ioctl_data.flags & ~E24_QUEUE_VALID_FLAGS) {
- + dev_dbg(edev->dev, "%s: invalid flags 0x%08x\n",
- + __func__, vrq->ioctl_data.flags);
- + return -EINVAL;
- + }
- +
- + ret = e24_map_request(filp, vrq, current->mm);
- + if (ret < 0)
- + return ret;
- +
- + mutex_lock(&queue->lock);
- + e24_fill_hw_request(queue->comm, vrq, &edev->address_map);
- +
- + if (irq_mode) {
- + ret = mbox_send_message(edev->tx_channel, data);
- + mbox_chan_txdone(edev->tx_channel, ret);
- + } else {
- + ret = e24_complete_poll(edev, queue, e24_cmd_complete, vrq);
- + }
- +
- + ret = e24_complete_hw_request(queue->comm, vrq);
- + mutex_unlock(&queue->lock);
- +
- + if (ret == 0)
- + ret = e24_unmap_request(filp, vrq);
- +
- + return ret;
- +}
- +
- +static long e24_ioctl_get_channel(struct file *filp,
- + void __user *msg)
- +{
- + struct e24_device *edev = filp->private_data;
- +
- + if (edev->tx_channel == NULL)
- + edev->tx_channel = starfive_mbox_request_channel(edev->dev, "tx");
- + if (edev->rx_channel == NULL)
- + edev->rx_channel = starfive_mbox_request_channel(edev->dev, "rx");
- +
- + return 0;
- +}
- +
- +static long e24_ioctl_free_channel(struct file *filp,
- + void __user *msg)
- +{
- + struct e24_device *edev = filp->private_data;
- +
- + if (edev->rx_channel)
- + mbox_free_channel(edev->rx_channel);
- + if (edev->tx_channel)
- + mbox_free_channel(edev->tx_channel);
- +
- + edev->rx_channel = NULL;
- + edev->tx_channel = NULL;
- + return 0;
- +}
- +
- +static void e24_allocation_queue(struct e24_device *edev,
- + struct e24_allocation *e24_pool_allocation)
- +{
- + spin_lock(&edev->busy_list_lock);
- +
- + e24_pool_allocation->next = edev->busy_list;
- + edev->busy_list = e24_pool_allocation;
- +
- + spin_unlock(&edev->busy_list_lock);
- +}
- +
- +static struct e24_allocation *e24_allocation_dequeue(struct e24_device *edev,
- + phys_addr_t paddr, u32 size)
- +{
- + struct e24_allocation **pcur;
- + struct e24_allocation *cur;
- +
- + spin_lock(&edev->busy_list_lock);
- +
- + for (pcur = &edev->busy_list; (cur = *pcur); pcur = &((*pcur)->next)) {
- + pr_debug("%s: %pap / %pap x %d\n", __func__, &paddr, &cur->start, cur->size);
- + if (paddr >= cur->start && paddr + size - cur->start <= cur->size) {
- + *pcur = cur->next;
- + break;
- + }
- + }
- +
- + spin_unlock(&edev->busy_list_lock);
- + return cur;
- +}
- +
- +static int e24_mmap(struct file *filp, struct vm_area_struct *vma)
- +{
- + int err;
- + struct e24_device *edev = filp->private_data;
- + unsigned long pfn = vma->vm_pgoff + PFN_DOWN(edev->shared_mem);
- + struct e24_allocation *e24_user_allocation;
- +
- + e24_user_allocation = e24_allocation_dequeue(filp->private_data,
- + pfn << PAGE_SHIFT,
- + vma->vm_end - vma->vm_start);
- + if (e24_user_allocation) {
- + pgprot_t prot = vma->vm_page_prot;
- +
- + if (!e24_cacheable(edev, pfn,
- + PFN_DOWN(vma->vm_end - vma->vm_start))) {
- + prot = pgprot_writecombine(prot);
- + vma->vm_page_prot = prot;
- + }
- +
- + err = remap_pfn_range(vma, vma->vm_start, pfn,
- + vma->vm_end - vma->vm_start,
- + prot);
- +
- + vma->vm_private_data = e24_user_allocation;
- + vma->vm_ops = &e24_vm_ops;
- + } else {
- + err = -EINVAL;
- + }
- +
- + return err;
- +}
- +
- +static long e24_ioctl_free(struct file *filp,
- + struct e24_ioctl_alloc __user *p)
- +{
- + struct mm_struct *mm = current->mm;
- + struct e24_ioctl_alloc user_ioctl_alloc;
- + struct vm_area_struct *vma;
- + unsigned long start;
- +
- + if (copy_from_user(&user_ioctl_alloc, p, sizeof(*p)))
- + return -EFAULT;
- +
- + start = user_ioctl_alloc.addr;
- + pr_debug("%s: virt_addr = 0x%08lx\n", __func__, start);
- +
- + mmap_read_lock(mm);
- + vma = find_vma(mm, start);
- +
- + if (vma && vma->vm_file == filp &&
- + vma->vm_start <= start && start < vma->vm_end) {
- + size_t size;
- +
- + start = vma->vm_start;
- + size = vma->vm_end - vma->vm_start;
- + mmap_read_unlock(mm);
- + pr_debug("%s: 0x%lx x %zu\n", __func__, start, size);
- + return vm_munmap(start, size);
- + }
- + pr_debug("%s: no vma/bad vma for vaddr = 0x%08lx\n", __func__, start);
- + mmap_read_unlock(mm);
- +
- + return -EINVAL;
- +}
- +
- +static long e24_ioctl_alloc(struct file *filp,
- + struct e24_ioctl_alloc __user *p)
- +{
- + struct e24_device *edev = filp->private_data;
- + struct e24_allocation *e24_pool_allocation;
- + unsigned long vaddr;
- + struct e24_ioctl_alloc user_ioctl_alloc;
- + long err;
- +
- + if (copy_from_user(&user_ioctl_alloc, p, sizeof(*p)))
- + return -EFAULT;
- +
- + pr_debug("%s: size = %d, align = %x\n", __func__,
- + user_ioctl_alloc.size, user_ioctl_alloc.align);
- +
- + err = e24_allocate(edev->pool,
- + user_ioctl_alloc.size,
- + user_ioctl_alloc.align,
- + &e24_pool_allocation);
- + if (err)
- + return err;
- +
- + e24_allocation_queue(edev, e24_pool_allocation);
- +
- + vaddr = vm_mmap(filp, 0, e24_pool_allocation->size,
- + PROT_READ | PROT_WRITE, MAP_SHARED,
- + e24_allocation_offset(e24_pool_allocation));
- +
- + user_ioctl_alloc.addr = vaddr;
- +
- + if (copy_to_user(p, &user_ioctl_alloc, sizeof(*p))) {
- + vm_munmap(vaddr, user_ioctl_alloc.size);
- + return -EFAULT;
- + }
- + return 0;
- +}
- +
- +static long e24_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- +{
- + long retval;
- +
- + switch (cmd) {
- + case E24_IOCTL_SEND:
- + retval = e24_ioctl_submit_task(filp, (struct e24_ioctl_user *)arg);
- + break;
- + case E24_IOCTL_GET_CHANNEL:
- + retval = e24_ioctl_get_channel(filp, NULL);
- + break;
- + case E24_IOCTL_FREE_CHANNEL:
- + retval = e24_ioctl_free_channel(filp, NULL);
- + break;
- + case E24_IOCTL_ALLOC:
- + retval = e24_ioctl_alloc(filp, (struct e24_ioctl_alloc __user *)arg);
- + break;
- + case E24_IOCTL_FREE:
- + retval = e24_ioctl_free(filp, (struct e24_ioctl_alloc __user *)arg);
- + break;
- + default:
- + retval = -EINVAL;
- + break;
- + }
- + return retval;
- +}
- +
- +static const struct file_operations e24_fops = {
- + .owner = THIS_MODULE,
- + .unlocked_ioctl = e24_ioctl,
- +#ifdef CONFIG_COMPAT
- + .compat_ioctl = e24_ioctl,
- +#endif
- + .open = e24_open,
- + .release = e24_release,
- + .write = mbox_e24_message_write,
- + .mmap = e24_mmap,
- +};
- +
- +void mailbox_task(struct platform_device *pdev)
- +{
- + struct e24_device *e24_dev = platform_get_drvdata(pdev);
- +
- + e24_dev->tx_channel = starfive_mbox_request_channel(e24_dev->dev, "tx");
- + e24_dev->rx_channel = starfive_mbox_request_channel(e24_dev->dev, "rx");
- + pr_debug("%s:%d.%#llx\n", __func__, __LINE__, (u64)e24_dev->rx_channel);
- +}
- +
- +static long e24_init_mem_pool(struct platform_device *pdev, struct e24_device *devs)
- +{
- + struct resource *mem;
- +
- + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecmd");
- + if (!mem)
- + return -ENODEV;
- +
- + devs->comm_phys = mem->start;
- + devs->comm = devm_ioremap(&pdev->dev, mem->start, mem->end - mem->start);
- + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "espace");
- + if (!mem)
- + return -ENODEV;
- +
- + devs->shared_mem = mem->start;
- + devs->shared_size = resource_size(mem);
- + pr_debug("%s:%d.%llx,%llx\n", __func__, __LINE__, devs->comm_phys, devs->shared_mem);
- + return e24_init_private_pool(&devs->pool, devs->shared_mem, devs->shared_size);
- +}
- +
- +static int e24_init_address_map(struct device *dev,
- + struct e24_address_map *map)
- +{
- +#if IS_ENABLED(CONFIG_OF)
- + struct device_node *pnode = dev->of_node;
- + struct device_node *node;
- + int rlen, off;
- + const __be32 *ranges = of_get_property(pnode, "ranges", &rlen);
- + int na, pna, ns;
- + int i;
- +
- + if (!ranges) {
- + dev_dbg(dev, "%s: no 'ranges' property in the device tree, no translation at that level\n",
- + __func__);
- + goto empty;
- + }
- +
- + node = of_get_next_child(pnode, NULL);
- + if (!node) {
- + dev_warn(dev, "%s: no child node found in the device tree, no translation at that level\n",
- + __func__);
- + goto empty;
- + }
- +
- + na = of_n_addr_cells(node);
- + ns = of_n_size_cells(node);
- + pna = of_n_addr_cells(pnode);
- +
- + rlen /= 4;
- + map->n = rlen / (na + pna + ns);
- + map->entry = kmalloc_array(map->n, sizeof(*map->entry), GFP_KERNEL);
- + if (!map->entry)
- + return -ENOMEM;
- +
- + dev_dbg(dev,
- + "%s: na = %d, pna = %d, ns = %d, rlen = %d cells, n = %d\n",
- + __func__, na, pna, ns, rlen, map->n);
- +
- + for (off = 0, i = 0; off < rlen; off += na + pna + ns, ++i) {
- + map->entry[i].src_addr = of_translate_address(node,
- + ranges + off);
- + map->entry[i].dst_addr = of_read_number(ranges + off, na);
- + map->entry[i].size = of_read_number(ranges + off + na + pna, ns);
- + dev_dbg(dev,
- + " src_addr = 0x%llx, dst_addr = 0x%lx, size = 0x%lx\n",
- + (unsigned long long)map->entry[i].src_addr,
- + (unsigned long)map->entry[i].dst_addr,
- + (unsigned long)map->entry[i].size);
- + }
- + sort(map->entry, map->n, sizeof(*map->entry), e24_compare_address_sort, NULL);
- +
- + of_node_put(node);
- + return 0;
- +
- +empty:
- +#endif
- + map->n = 1;
- + map->entry = kmalloc(sizeof(*map->entry), GFP_KERNEL);
- + map->entry->src_addr = 0;
- + map->entry->dst_addr = 0;
- + map->entry->size = ~0u;
- + return -ENOMEM;
- +}
- +
- +typedef long e24_init_function(struct platform_device *pdev);
- +
- +static inline void e24_init(struct e24_device *e24_hw)
- +{
- + if (e24_hw->hw_ops->init)
- + e24_hw->hw_ops->init(e24_hw->hw_arg);
- +}
- +
- +static inline void e24_release_e24(struct e24_device *e24_hw)
- +{
- + if (e24_hw->hw_ops->reset)
- + e24_hw->hw_ops->release(e24_hw->hw_arg);
- +}
- +
- +static inline void e24_halt_e24(struct e24_device *e24_hw)
- +{
- + if (e24_hw->hw_ops->halt)
- + e24_hw->hw_ops->halt(e24_hw->hw_arg);
- +}
- +
- +static inline int e24_enable_e24(struct e24_device *e24_hw)
- +{
- + if (e24_hw->hw_ops->enable)
- + return e24_hw->hw_ops->enable(e24_hw->hw_arg);
- + else
- + return -EINVAL;
- +}
- +
- +static inline void e24_reset_e24(struct e24_device *e24_hw)
- +{
- + if (e24_hw->hw_ops->reset)
- + e24_hw->hw_ops->reset(e24_hw->hw_arg);
- +}
- +
- +static inline void e24_disable_e24(struct e24_device *e24_hw)
- +{
- + if (e24_hw->hw_ops->disable)
- + e24_hw->hw_ops->disable(e24_hw->hw_arg);
- +}
- +
- +static inline void e24_sendirq_e24(struct e24_device *e24_hw)
- +{
- + if (e24_hw->hw_ops->send_irq)
- + e24_hw->hw_ops->send_irq(e24_hw->hw_arg);
- +}
- +
- +irqreturn_t e24_irq_handler(int irq, struct e24_device *e24_hw)
- +{
- + dev_dbg(e24_hw->dev, "%s\n", __func__);
- + complete(&e24_hw->completion);
- +
- + return IRQ_HANDLED;
- +}
- +EXPORT_SYMBOL(e24_irq_handler);
- +
- +static phys_addr_t e24_translate_to_cpu(struct e24_device *mail, Elf32_Phdr *phdr)
- +{
- + phys_addr_t res;
- + __be32 addr = cpu_to_be32((u32)phdr->p_paddr);
- + struct device_node *node =
- + of_get_next_child(mail->dev->of_node, NULL);
- +
- + if (!node)
- + node = mail->dev->of_node;
- +
- + res = of_translate_address(node, &addr);
- +
- + if (node != mail->dev->of_node)
- + of_node_put(node);
- + return res;
- +}
- +
- +static int e24_load_segment_to_sysmem(struct e24_device *e24, Elf32_Phdr *phdr)
- +{
- + phys_addr_t pa = e24_translate_to_cpu(e24, phdr);
- + struct page *page = pfn_to_page(__phys_to_pfn(pa));
- + size_t page_offs = pa & ~PAGE_MASK;
- + size_t offs;
- +
- + for (offs = 0; offs < phdr->p_memsz; ++page) {
- + void *p = kmap(page);
- + size_t sz;
- +
- + if (!p)
- + return -ENOMEM;
- +
- + page_offs &= ~PAGE_MASK;
- + sz = PAGE_SIZE - page_offs;
- +
- + if (offs < phdr->p_filesz) {
- + size_t copy_sz = sz;
- +
- + if (phdr->p_filesz - offs < copy_sz)
- + copy_sz = phdr->p_filesz - offs;
- +
- + copy_sz = ALIGN(copy_sz, 4);
- + memcpy(p + page_offs,
- + (void *)e24->firmware->data +
- + phdr->p_offset + offs,
- + copy_sz);
- + page_offs += copy_sz;
- + offs += copy_sz;
- + sz -= copy_sz;
- + }
- +
- + if (offs < phdr->p_memsz && sz) {
- + if (phdr->p_memsz - offs < sz)
- + sz = phdr->p_memsz - offs;
- +
- + sz = ALIGN(sz, 4);
- + memset(p + page_offs, 0, sz);
- + page_offs += sz;
- + offs += sz;
- + }
- + kunmap(page);
- + }
- + dma_sync_single_for_device(e24->dev, pa, phdr->p_memsz, DMA_TO_DEVICE);
- + return 0;
- +}
- +
- +static int e24_load_segment_to_iomem(struct e24_device *e24, Elf32_Phdr *phdr)
- +{
- + phys_addr_t pa = e24_translate_to_cpu(e24, phdr);
- + void __iomem *p = ioremap(pa, phdr->p_memsz);
- +
- + if (!p) {
- + dev_err(e24->dev, "couldn't ioremap %pap x 0x%08x\n",
- + &pa, (u32)phdr->p_memsz);
- + return -EINVAL;
- + }
- + if (e24->hw_ops->memcpy_tohw)
- + e24->hw_ops->memcpy_tohw(p, (void *)e24->firmware->data +
- + phdr->p_offset, phdr->p_filesz);
- + else
- + memcpy_toio(p, (void *)e24->firmware->data + phdr->p_offset,
- + ALIGN(phdr->p_filesz, 4));
- +
- + if (e24->hw_ops->memset_hw)
- + e24->hw_ops->memset_hw(p + phdr->p_filesz, 0,
- + phdr->p_memsz - phdr->p_filesz);
- + else
- + memset_io(p + ALIGN(phdr->p_filesz, 4), 0,
- + ALIGN(phdr->p_memsz - ALIGN(phdr->p_filesz, 4), 4));
- +
- + iounmap(p);
- + return 0;
- +}
- +
- +static int e24_load_firmware(struct e24_device *e24_dev)
- +{
- + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)e24_dev->firmware->data;
- + u32 *dai = (u32 *)e24_dev->firmware->data;
- + int i;
- +
- + pr_debug("elf size:%ld,%x,%x\n", e24_dev->firmware->size, dai[0], dai[1]);
- + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
- + dev_err(e24_dev->dev, "bad firmware ELF magic\n");
- + return -EINVAL;
- + }
- +
- + if (ehdr->e_type != ET_EXEC) {
- + dev_err(e24_dev->dev, "bad firmware ELF type\n");
- + return -EINVAL;
- + }
- +
- + if (ehdr->e_machine != EM_RISCV) {
- + dev_err(e24_dev->dev, "bad firmware ELF machine\n");
- + return -EINVAL;
- + }
- +
- + if (ehdr->e_phoff >= e24_dev->firmware->size ||
- + ehdr->e_phoff +
- + ehdr->e_phentsize * ehdr->e_phnum > e24_dev->firmware->size) {
- + dev_err(e24_dev->dev, "bad firmware ELF PHDR information\n");
- + return -EINVAL;
- + }
- +
- + for (i = ehdr->e_phnum; i >= 0 ; i--) {
- + Elf32_Phdr *phdr = (void *)e24_dev->firmware->data +
- + ehdr->e_phoff + i * ehdr->e_phentsize;
- + phys_addr_t pa;
- + int rc;
- +
- + /* Only load non-empty loadable segments, R/W/X */
- + if (!(phdr->p_type == PT_LOAD &&
- + (phdr->p_flags & (PF_X | PF_R | PF_W)) &&
- + phdr->p_memsz > 0))
- + continue;
- +
- + if (phdr->p_offset >= e24_dev->firmware->size ||
- + phdr->p_offset + phdr->p_filesz > e24_dev->firmware->size) {
- + dev_err(e24_dev->dev, "bad firmware ELF program header entry %d\n", i);
- + return -EINVAL;
- + }
- +
- + pa = e24_translate_to_cpu(e24_dev, phdr);
- + if (pa == (phys_addr_t)OF_BAD_ADDR) {
- + dev_err(e24_dev->dev,
- + "device address 0x%08x could not be mapped to host physical address",
- + (u32)phdr->p_paddr);
- + return -EINVAL;
- + }
- + dev_dbg(e24_dev->dev, "loading segment %d (device 0x%08x) to physical %pap\n",
- + i, (u32)phdr->p_paddr, &pa);
- +
- + if (pfn_valid(__phys_to_pfn(pa)))
- + rc = e24_load_segment_to_sysmem(e24_dev, phdr);
- + else
- + rc = e24_load_segment_to_iomem(e24_dev, phdr);
- +
- + if (rc < 0)
- + return rc;
- + }
- + return 0;
- +}
- +
- +static int e24_boot_firmware(struct device *dev)
- +{
- + int ret;
- + struct e24_device *e24_dev = dev_get_drvdata(dev);
- +
- + if (e24_dev->firmware_name) {
- + ret = request_firmware(&e24_dev->firmware, e24_dev->firmware_name, e24_dev->dev);
- +
- + if (ret < 0)
- + return ret;
- +
- + ret = e24_load_firmware(e24_dev);
- + if (ret < 0) {
- + release_firmware(e24_dev->firmware);
- + return ret;
- + }
- +
- + }
- +
- + release_firmware(e24_dev->firmware);
- + ret = e24_enable_e24(e24_dev);
- + if (ret < 0)
- + return ret;
- +
- + e24_reset_e24(e24_dev);
- + e24_release_e24(e24_dev);
- + e24_synchronize(e24_dev);
- +
- + return ret;
- +}
- +
- +int e24_runtime_suspend(struct device *dev)
- +{
- + struct e24_device *e24_dev = dev_get_drvdata(dev);
- +
- + e24_halt_e24(e24_dev);
- + e24_disable_e24(e24_dev);
- +
- + return 0;
- +}
- +
- +long e24_init_v0(struct platform_device *pdev)
- +{
- + long ret = -EINVAL;
- + int nodeid, i;
- + struct e24_hw_arg *hw_arg;
- + struct e24_device *e24_dev;
- + char nodename[sizeof("eboot") + 2 * sizeof(int)];
- +
- + e24_dev = devm_kzalloc(&pdev->dev, sizeof(*e24_dev), GFP_KERNEL);
- + if (e24_dev == NULL) {
- + ret = -ENOMEM;
- + return ret;
- + }
- +
- + hw_arg = devm_kzalloc(&pdev->dev, sizeof(*hw_arg), GFP_KERNEL);
- + if (hw_arg == NULL)
- + return ret;
- +
- + platform_set_drvdata(pdev, e24_dev);
- + e24_dev->dev = &pdev->dev;
- + e24_dev->hw_arg = hw_arg;
- + e24_dev->hw_ops = e24_get_hw_ops();
- + e24_dev->nodeid = -1;
- + hw_arg->e24 = e24_dev;
- +
- + ret = e24_init_mem_pool(pdev, e24_dev);
- + if (ret < 0)
- + goto err;
- +
- + ret = e24_init_address_map(e24_dev->dev, &e24_dev->address_map);
- + if (ret < 0)
- + goto err_free_pool;
- +
- + e24_dev->n_queues = 1;
- + e24_dev->queue = devm_kmalloc(&pdev->dev,
- + e24_dev->n_queues * sizeof(*e24_dev->queue),
- + GFP_KERNEL);
- + if (e24_dev->queue == NULL)
- + goto err_free_map;
- +
- + for (i = 0; i < e24_dev->n_queues; i++) {
- + mutex_init(&e24_dev->queue[i].lock);
- + e24_dev->queue[i].comm = e24_dev->comm + E24_CMD_STRIDE * i;
- + }
- +
- + ret = of_property_read_u32(pdev->dev.of_node, "irq-mode",
- + &hw_arg->irq_mode);
- + e24_dev->irq_mode = hw_arg->irq_mode;
- + if (hw_arg->irq_mode == 0)
- + dev_info(&pdev->dev, "using polling mode on the device side\n");
- +
- + e24_dev->mbox_data = e24_translate_to_dsp(&e24_dev->address_map, e24_dev->comm_phys);
- + ret = device_property_read_string(e24_dev->dev, "firmware-name",
- + &e24_dev->firmware_name);
- + if (ret == -EINVAL || ret == -ENODATA) {
- + dev_dbg(e24_dev->dev,
- + "no firmware-name property, not loading firmware");
- + } else if (ret < 0) {
- + dev_err(e24_dev->dev, "invalid firmware name (%ld)", ret);
- + goto err_free_map;
- + }
- +
- + e24_init(e24_dev);
- + pm_runtime_set_active(e24_dev->dev);
- + pm_runtime_enable(e24_dev->dev);
- + if (!pm_runtime_enabled(e24_dev->dev)) {
- + ret = e24_boot_firmware(e24_dev->dev);
- + if (ret)
- + goto err_pm_disable;
- + }
- + nodeid = ida_simple_get(&e24_nodeid, 0, 0, GFP_KERNEL);
- + if (nodeid < 0) {
- + ret = nodeid;
- + goto err_pm_disable;
- + }
- +
- + e24_dev->nodeid = nodeid;
- + sprintf(nodename, "eboot%u", nodeid);
- +
- + e24_dev->miscdev = (struct miscdevice){
- + .minor = MISC_DYNAMIC_MINOR,
- + .name = devm_kstrdup(&pdev->dev, nodename, GFP_KERNEL),
- + .nodename = devm_kstrdup(&pdev->dev, nodename, GFP_KERNEL),
- + .fops = &e24_fops,
- + };
- +
- + ret = misc_register(&e24_dev->miscdev);
- + if (ret < 0)
- + goto err_free_id;
- +
- + return PTR_ERR(e24_dev);
- +err_free_id:
- + ida_simple_remove(&e24_nodeid, nodeid);
- +
- +err_pm_disable:
- + pm_runtime_disable(e24_dev->dev);
- +err_free_map:
- + kfree(e24_dev->address_map.entry);
- +err_free_pool:
- + e24_free_pool(e24_dev->pool);
- +err:
- + dev_err(&pdev->dev, "%s: ret = %ld\n", __func__, ret);
- + return ret;
- +}
- +
- +static const struct of_device_id e24_of_match[] = {
- + {
- + .compatible = "starfive,e24",
- + .data = e24_init_v0,
- + },
- + {
- + },
- +};
- +MODULE_DEVICE_TABLE(of, e24_of_match);
- +
- +int e24_deinit(struct platform_device *pdev)
- +{
- + struct e24_device *e24_dev = platform_get_drvdata(pdev);
- +
- + pm_runtime_disable(&pdev->dev);
- + if (!pm_runtime_status_suspended(e24_dev->dev))
- + e24_runtime_suspend(e24_dev->dev);
- +
- + misc_deregister(&e24_dev->miscdev);
- + e24_free_pool(e24_dev->pool);
- + kfree(e24_dev->address_map.entry);
- + ida_simple_remove(&e24_nodeid, e24_dev->nodeid);
- +
- + if (e24_dev->rx_channel)
- + mbox_free_channel(e24_dev->rx_channel);
- + if (e24_dev->tx_channel)
- + mbox_free_channel(e24_dev->tx_channel);
- + return 0;
- +}
- +
- +static int e24_probe(struct platform_device *pdev)
- +{
- + long ret = -EINVAL;
- + const struct of_device_id *match;
- + e24_init_function *init;
- +
- + match = of_match_device(e24_of_match, &pdev->dev);
- + init = match->data;
- + ret = init(pdev);
- +
- + return IS_ERR_VALUE(ret) ? ret : 0;
- +}
- +
- +static void e24_remove(struct platform_device *pdev)
- +{
- + e24_deinit(pdev);
- +}
- +
- +static const struct dev_pm_ops e24_runtime_pm_ops = {
- + SET_RUNTIME_PM_OPS(e24_runtime_suspend,
- + e24_boot_firmware, NULL)
- +};
- +
- +static struct platform_driver e24_driver = {
- + .probe = e24_probe,
- + .remove = e24_remove,
- + .driver = {
- + .name = "e24_boot",
- + .of_match_table = of_match_ptr(e24_of_match),
- + .pm = &e24_runtime_pm_ops,
- + },
- +};
- +
- +module_platform_driver(e24_driver);
- +
- +MODULE_DESCRIPTION("StarFive e24 driver");
- +MODULE_AUTHOR("Shanlong Li <[email protected]>");
- +MODULE_LICENSE("GPL");
- --- /dev/null
- +++ b/drivers/e24/starfive_e24.h
- @@ -0,0 +1,159 @@
- +/* SPDX-License-Identifier: GPL-2.0 */
- +#ifndef __STARFIVE_E24_H__
- +#define __STARFIVE_E24_H__
- +
- +#include <linux/types.h>
- +#include <linux/completion.h>
- +#include <linux/miscdevice.h>
- +#include <linux/mutex.h>
- +#include <linux/irqreturn.h>
- +#include <linux/platform_device.h>
- +
- +#define E24_IOCTL_MAGIC 'e'
- +#define E24_IOCTL_SEND _IO(E24_IOCTL_MAGIC, 1)
- +#define E24_IOCTL_RECV _IO(E24_IOCTL_MAGIC, 2)
- +#define E24_IOCTL_GET_CHANNEL _IO(E24_IOCTL_MAGIC, 3)
- +#define E24_IOCTL_FREE_CHANNEL _IO(E24_IOCTL_MAGIC, 4)
- +#define E24_IOCTL_ALLOC _IO(E24_IOCTL_MAGIC, 5)
- +#define E24_IOCTL_FREE _IO(E24_IOCTL_MAGIC, 6)
- +
- +#define E24_DSP_CMD_INLINE_DATA_SIZE 16
- +#define E24_NO_TRANSLATION ((u32)~0ul)
- +#define E24_CMD_STRIDE 256
- +
- +#define E24_MEM_MAP
- +enum e24_irq_mode {
- + MAIL_IRQ_NONE,
- + MAIL_IRQ_LEVEL,
- + MAIL_IRQ_MAX
- +};
- +
- +enum {
- + E24_FLAG_READ = 0x1,
- + E24_FLAG_WRITE = 0x2,
- + E24_FLAG_READ_WRITE = 0x3,
- +};
- +
- +enum {
- + E24_QUEUE_FLAG_VALID = 0x4,
- + E24_QUEUE_FLAG_PRIO = 0xff00,
- + E24_QUEUE_FLAG_PRIO_SHIFT = 8,
- +
- + E24_QUEUE_VALID_FLAGS =
- + E24_QUEUE_FLAG_VALID |
- + E24_QUEUE_FLAG_PRIO,
- +};
- +
- +enum {
- + E24_CMD_FLAG_REQUEST_VALID = 0x00000001,
- + E24_CMD_FLAG_RESPONSE_VALID = 0x00000002,
- + E24_CMD_FLAG_REQUEST_NSID = 0x00000004,
- + E24_CMD_FLAG_RESPONSE_DELIVERY_FAIL = 0x00000008,
- +};
- +
- +struct e24_address_map_entry {
- + phys_addr_t src_addr;
- + u32 dst_addr;
- + u32 size;
- +};
- +
- +struct e24_address_map {
- + unsigned int n;
- + struct e24_address_map_entry *entry;
- +};
- +
- +struct e24_alien_mapping {
- + unsigned long vaddr;
- + unsigned long size;
- + phys_addr_t paddr;
- + void *allocation;
- + enum {
- + ALIEN_GUP,
- + ALIEN_PFN_MAP,
- + ALIEN_COPY,
- + } type;
- +};
- +
- +struct e24_mapping {
- + enum {
- + E24_MAPPING_NONE,
- + E24_MAPPING_NATIVE,
- + E24_MAPPING_ALIEN,
- + E24_MAPPING_KERNEL = 0x4,
- + } type;
- + union {
- + struct {
- + struct e24_allocation *m_allocation;
- + unsigned long vaddr;
- + } native;
- + struct e24_alien_mapping alien_mapping;
- + };
- +};
- +
- +struct e24_ioctl_alloc {
- + u32 size;
- + u32 align;
- + u64 addr;
- +};
- +
- +struct e24_comm {
- + struct mutex lock;
- + void __iomem *comm;
- + struct completion completion;
- + u32 priority;
- +};
- +
- +struct e24_device {
- + struct device *dev;
- + const char *firmware_name;
- + const struct firmware *firmware;
- + struct miscdevice miscdev;
- + const struct e24_hw_ops *hw_ops;
- + void *hw_arg;
- + int irq_mode;
- +
- + u32 n_queues;
- + struct completion completion;
- + struct e24_address_map address_map;
- + struct e24_comm *queue;
- + void __iomem *comm;
- + phys_addr_t comm_phys;
- + phys_addr_t shared_mem;
- + phys_addr_t shared_size;
- +
- + u32 mbox_data;
- + int nodeid;
- + spinlock_t busy_list_lock;
- +
- + struct mbox_chan *tx_channel;
- + struct mbox_chan *rx_channel;
- + void *rx_buffer;
- + void *message;
- + struct e24_allocation_pool *pool;
- + struct e24_allocation *busy_list;
- +};
- +
- +struct e24_hw_arg {
- + struct e24_device *e24;
- + phys_addr_t regs_phys;
- + struct clk *clk_rtc;
- + struct clk *clk_core;
- + struct clk *clk_dbg;
- + struct reset_control *rst_core;
- + struct regmap *reg_syscon;
- + enum e24_irq_mode irq_mode;
- +};
- +
- +static inline int e24_compare_address(phys_addr_t addr,
- + const struct e24_address_map_entry *entry)
- +{
- + if (addr < entry->src_addr)
- + return -1;
- + if (addr - entry->src_addr < entry->size)
- + return 0;
- + return 1;
- +}
- +
- +irqreturn_t e24_irq_handler(int irq, struct e24_device *e24_hw);
- +
- +#endif
- --- /dev/null
- +++ b/drivers/e24/starfive_e24_hw.c
- @@ -0,0 +1,134 @@
- +// SPDX-License-Identifier: GPL-2.0
- +#include <linux/delay.h>
- +#include <linux/interrupt.h>
- +#include <linux/idr.h>
- +#include <linux/io.h>
- +#include <linux/slab.h>
- +#include <linux/of.h>
- +#include <linux/clk.h>
- +#include <linux/reset.h>
- +#include <linux/module.h>
- +#include <linux/mfd/syscon.h>
- +#include <linux/regmap.h>
- +#include "starfive_e24.h"
- +#include "starfive_e24_hw.h"
- +
- +#define RET_E24_VECTOR_ADDR 0x6CE00000
- +
- +static void halt(void *hw_arg)
- +{
- + struct e24_hw_arg *mail_arg = hw_arg;
- +
- + reset_control_assert(mail_arg->rst_core);
- + pr_debug("e24 halt.\n");
- +}
- +
- +static void release(void *hw_arg)
- +{
- + struct e24_hw_arg *mail_arg = hw_arg;
- +
- + reset_control_deassert(mail_arg->rst_core);
- + pr_debug("e24 begin run.\n");
- +}
- +
- +static void reset(void *hw_arg)
- +{
- + struct e24_hw_arg *mail_arg = hw_arg;
- +
- + regmap_update_bits(mail_arg->reg_syscon, 0x24, 0xFFFFFFFF, RET_E24_VECTOR_ADDR);
- + pr_debug("e24 reset vector.\n");
- +}
- +
- +static void disable(void *hw_arg)
- +{
- + struct e24_hw_arg *mail_arg = hw_arg;
- +
- + clk_disable_unprepare(mail_arg->clk_core);
- + clk_disable_unprepare(mail_arg->clk_dbg);
- + clk_disable_unprepare(mail_arg->clk_rtc);
- +
- + pr_debug("e24 disable ...\n");
- +
- +}
- +
- +static int enable(void *hw_arg)
- +{
- + struct e24_hw_arg *mail_arg = hw_arg;
- + int ret = 0;
- +
- + ret = clk_prepare_enable(mail_arg->clk_core);
- + if (ret)
- + return -EAGAIN;
- +
- + ret = clk_prepare_enable(mail_arg->clk_dbg);
- + if (ret) {
- + clk_disable_unprepare(mail_arg->clk_core);
- + return -EAGAIN;
- + }
- +
- + ret = clk_prepare_enable(mail_arg->clk_rtc);
- + if (ret) {
- + clk_disable_unprepare(mail_arg->clk_core);
- + clk_disable_unprepare(mail_arg->clk_dbg);
- + return -EAGAIN;
- + }
- +
- + pr_debug("e24_enable clk ...\n");
- + return 0;
- +}
- +
- +
- +static int init(void *hw_arg)
- +{
- + struct e24_hw_arg *mail_arg = hw_arg;
- +
- + mail_arg->reg_syscon = syscon_regmap_lookup_by_phandle(
- + mail_arg->e24->dev->of_node,
- + "starfive,stg-syscon");
- + if (IS_ERR(mail_arg->reg_syscon)) {
- + dev_err(mail_arg->e24->dev, "No starfive,stg-syscon\n");
- + return PTR_ERR(mail_arg->reg_syscon);
- + }
- +
- + mail_arg->clk_core = devm_clk_get_optional(mail_arg->e24->dev, "clk_core");
- + if (IS_ERR(mail_arg->clk_core)) {
- + dev_err(mail_arg->e24->dev, "failed to get e24 clk core\n");
- + return -ENOMEM;
- + }
- +
- + mail_arg->clk_dbg = devm_clk_get_optional(mail_arg->e24->dev, "clk_dbg");
- + if (IS_ERR(mail_arg->clk_dbg)) {
- + dev_err(mail_arg->e24->dev, "failed to get e24 clk dbg\n");
- + return -ENOMEM;
- + }
- +
- + mail_arg->clk_rtc = devm_clk_get_optional(mail_arg->e24->dev, "clk_rtc");
- + if (IS_ERR(mail_arg->clk_rtc)) {
- + dev_err(mail_arg->e24->dev, "failed to get e24 clk rtc\n");
- + return -ENOMEM;
- + }
- +
- + mail_arg->rst_core = devm_reset_control_get_exclusive(mail_arg->e24->dev, "e24_core");
- + if (IS_ERR(mail_arg->rst_core)) {
- + dev_err(mail_arg->e24->dev, "failed to get e24 reset\n");
- + return -ENOMEM;
- + }
- +
- + enable(hw_arg);
- +
- + return 0;
- +}
- +
- +static struct e24_hw_ops e24_hw_ops = {
- + .init = init,
- + .enable = enable,
- + .reset = reset,
- + .halt = halt,
- + .release = release,
- + .disable = disable,
- +};
- +
- +struct e24_hw_ops *e24_get_hw_ops(void)
- +{
- + return &e24_hw_ops;
- +}
- --- /dev/null
- +++ b/drivers/e24/starfive_e24_hw.h
- @@ -0,0 +1,94 @@
- +/* SPDX-License-Identifier: GPL-2.0 */
- +#ifndef __STARFIVE_E24_HW_H__
- +#define __STARFIVE_E24_HW_H__
- +/*
- + * Hardware-specific operation entry points.
- + */
- +struct e24_hw_ops {
- + /*
- + * Gets the clock for E24.
- + */
- + int (*init)(void *hw_arg);
- + /*
- + * Enable power/clock, but keep the core stalled.
- + */
- + int (*enable)(void *hw_arg);
- + /*
- + * Diable power/clock.
- + */
- + void (*disable)(void *hw_arg);
- + /*
- + * Reset the core.
- + */
- + void (*reset)(void *hw_arg);
- + /*
- + * Unstall the core.
- + */
- + void (*release)(void *hw_arg);
- + /*
- + * Stall the core.
- + */
- + void (*halt)(void *hw_arg);
- +
- + /* Get HW-specific data to pass to the DSP on synchronization
- + *
- + * param hw_arg: opaque parameter passed to DSP at initialization
- + * param sz: return size of sync data here
- + * return a buffer allocated with kmalloc that the caller will free
- + */
- + void *(*get_hw_sync_data)(void *hw_arg, size_t *sz);
- +
- + /*
- + * Send IRQ to the core.
- + */
- + void (*send_irq)(void *hw_arg);
- +
- + /*
- + * Check whether region of physical memory may be handled by
- + * dma_sync_* operations
- + *
- + * \param hw_arg: opaque parameter passed to DSP at initialization
- + * time
- + */
- + bool (*cacheable)(void *hw_arg, unsigned long pfn, unsigned long n_pages);
- + /*
- + * Synchronize region of memory for DSP access.
- + *
- + * \param hw_arg: opaque parameter passed to DSP at initialization
- + * time
- + */
- + void (*dma_sync_for_device)(void *hw_arg,
- + void *vaddr, phys_addr_t paddr,
- + unsigned long sz, unsigned int flags);
- + /*
- + * Synchronize region of memory for host access.
- + *
- + * \param hw_arg: opaque parameter passed to DSP at initialization
- + * time
- + */
- + void (*dma_sync_for_cpu)(void *hw_arg,
- + void *vaddr, phys_addr_t paddr,
- + unsigned long sz, unsigned int flags);
- +
- + /*
- + * memcpy data/code to device-specific memory.
- + */
- + void (*memcpy_tohw)(void __iomem *dst, const void *src, size_t sz);
- + /*
- + * memset device-specific memory.
- + */
- + void (*memset_hw)(void __iomem *dst, int c, size_t sz);
- +
- + /*
- + * Check DSP status.
- + *
- + * \param hw_arg: opaque parameter passed to DSP at initialization
- + * time
- + * \return whether the core has crashed and needs to be restarted
- + */
- + bool (*panic_check)(void *hw_arg);
- +};
- +
- +long e24_init_hw(struct platform_device *pdev, void *hw_arg);
- +struct e24_hw_ops *e24_get_hw_ops(void);
- +#endif
|