- refresh, rebase and reorder patches - JH7110 media drivers have been dropped for now - JH7110 E24 and mailbox drivers were added - JH7100 DMA- and errata-patches have been dropped as they were upstreamed Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
2296 lines
57 KiB
Diff
2296 lines
57 KiB
Diff
From b37b26232ebb6c0a61b530f11ccd6eefdf782c04 Mon Sep 17 00:00:00 2001
|
|
From: "shanlong.li" <shanlong.li@starfivetech.com>
|
|
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 <shanlong.li@starfivetech.com>
|
|
---
|
|
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 <shanlong.li@starfivetech.com>
|
|
+ */
|
|
+#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 <shanlong.li@starfivetech.com>");
|
|
+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
|