summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWhi copybara merger <whitechapel-automerger@google.com>2021-09-24 17:07:54 +0800
committerNrithya Kanakasabapathy <nrithya@google.com>2021-09-28 16:18:15 +0000
commit4c7500762b4905e1d245e76684fa1511108900d5 (patch)
tree4068c9628eeb2448a88702fb2dc5e73da700d956
parent5e99acd1273d60b1755d9af1a89f705ee82599ee (diff)
downloadjaneiro-4c7500762b4905e1d245e76684fa1511108900d5.tar.gz
Merge branch 'pro' into android13-gs-pixel-5.10
edgetpu: janeiro remove compat. code for shareability Bug: 193593081 edgetpu: janeiro set shareability in after probe Bug: 200923214 edgetpu: janeiro: Add TPU_OFF Bug: 199365799 edgetpu: set first_open to false for external mailbox Bug: 200507456 edgetpu: Put sscoredump features behind config flag Bug: 197672212 edgetpu: rio import drivers Bug: 198718365 Revert "edgetpu: remove edgetpu_unregister_irq" Bug: 200183185 edgetpu: implement commands for interop Bug: 199355448 edgetpu: fix up header inclusions edgetpu: janeiro use common mobile interface Bug: 199717700 edgetpu: add edgetpu-mobile-platform.c Bug: 199717700 edgetpu: janeiro use IRQ array in mobile dev Bug: 199717700 edgetpu: remove edgetpu_unregister_irq edgetpu: mobile add IRQ fields to platform_dev Bug: 199717700 edgetpu: support predefined KCI timeout Bug: 197068927 edgetpu: mobile cpu reset according to #cores Bug: 199571462 Bug: 198718365 edgetpu: config add EDGETPU_NUM_CORES Bug: 199571462 edgetpu: clearer config selection Bug: 198718365 edgetpu: mobile firmware fix typos edgetpu: fix firmware load at different priv level edgetpu: fix returning/check correct status of errors Bug: 197919435 edgetpu: janeiro: add GSA device Bug: 194054265 edgetpu: janeiro: setup SSMT if GSA is available Bug: 197301774 edgetpu: merge mobile firmware code Bug: 176881607 edgetpu: merge mobile PM code Bug: 176881607 edgetpu: move number of contexts to chip configs Bug: 194989925 GitOrigin-RevId: 96ee87151266f7bd4f96e5e3c4ae4cf1580bc8d2 Change-Id: If9c902d6d17b00502e01b0ba4f801985912640f6
-rw-r--r--drivers/edgetpu/BUILD.bazel20
-rw-r--r--drivers/edgetpu/Kbuild4
-rw-r--r--drivers/edgetpu/Makefile8
-rw-r--r--drivers/edgetpu/edgetpu-config.h15
-rw-r--r--drivers/edgetpu/edgetpu-external.c134
-rw-r--r--drivers/edgetpu/edgetpu-fs.c2
-rw-r--r--drivers/edgetpu/edgetpu-google-iommu.c1
-rw-r--r--drivers/edgetpu/edgetpu-internal.h15
-rw-r--r--drivers/edgetpu/edgetpu-kci.c6
-rw-r--r--drivers/edgetpu/edgetpu-mailbox.c8
-rw-r--r--drivers/edgetpu/edgetpu-mailbox.h19
-rw-r--r--drivers/edgetpu/edgetpu-mobile-firmware.c63
-rw-r--r--drivers/edgetpu/edgetpu-mobile-platform.c340
-rw-r--r--drivers/edgetpu/edgetpu-mobile-platform.h120
-rw-r--r--drivers/edgetpu/janeiro-debug-dump.c2
-rw-r--r--drivers/edgetpu/janeiro-device.c23
-rw-r--r--drivers/edgetpu/janeiro-external.c2
-rw-r--r--drivers/edgetpu/janeiro-firmware.c282
-rw-r--r--drivers/edgetpu/janeiro-platform.c335
-rw-r--r--drivers/edgetpu/janeiro-platform.h42
-rw-r--r--drivers/edgetpu/janeiro-pm.c431
-rw-r--r--drivers/edgetpu/janeiro/config-pwr-state.h2
-rw-r--r--drivers/edgetpu/janeiro/config.h2
-rw-r--r--drivers/edgetpu/janeiro/csrs.h1
-rw-r--r--drivers/edgetpu/mobile-debug-dump.c1
-rw-r--r--drivers/edgetpu/mobile-debug-dump.h5
-rw-r--r--drivers/edgetpu/mobile-firmware.c490
-rw-r--r--drivers/edgetpu/mobile-firmware.h36
-rw-r--r--drivers/edgetpu/mobile-pm.c570
-rw-r--r--drivers/edgetpu/mobile-pm.h7
30 files changed, 1856 insertions, 1130 deletions
diff --git a/drivers/edgetpu/BUILD.bazel b/drivers/edgetpu/BUILD.bazel
deleted file mode 100644
index 83d0c84..0000000
--- a/drivers/edgetpu/BUILD.bazel
+++ /dev/null
@@ -1,20 +0,0 @@
-# NOTE: THIS FILE IS EXPERIMENTAL FOR THE BAZEL MIGRATION AND NOT USED FOR
-# YOUR BUILDS CURRENTLY.
-#
-# It is not yet the source of truth for your build. If you're looking to modify
-# the build file, modify the Android.bp file instead. Do *not* modify this file
-# unless you have coordinated with the team managing the Soong to Bazel
-# migration.
-
-load("//build/kleaf:kernel.bzl", "kernel_module")
-
-kernel_module(
- name = "edgetpu.cloudripper",
- outs = [
- "janeiro.ko",
- ],
- kernel_build = "//private/gs-google:cloudripper",
- visibility = [
- "//private/gs-google:__pkg__",
- ],
-)
diff --git a/drivers/edgetpu/Kbuild b/drivers/edgetpu/Kbuild
index 88d8163..3479ad2 100644
--- a/drivers/edgetpu/Kbuild
+++ b/drivers/edgetpu/Kbuild
@@ -12,10 +12,9 @@ endif
edgetpu-objs := edgetpu-mailbox.o edgetpu-kci.o edgetpu-telemetry.o edgetpu-mapping.o edgetpu-dmabuf.o edgetpu-async.o edgetpu-iremap-pool.o edgetpu-sw-watchdog.o edgetpu-firmware.o edgetpu-firmware-util.o
-edgetpu-mobile-objs := edgetpu-mobile-firmware.o
+janeiro-y := janeiro-device.o janeiro-device-group.o janeiro-fs.o janeiro-core.o janeiro-platform.o janeiro-firmware.o janeiro-pm.o janeiro-debug-dump.o janeiro-usage-stats.o janeiro-iommu.o janeiro-wakelock.o janeiro-external.o $(edgetpu-objs)
-janeiro-y := janeiro-device.o janeiro-device-group.o janeiro-fs.o janeiro-core.o janeiro-platform.o janeiro-firmware.o janeiro-pm.o janeiro-debug-dump.o janeiro-usage-stats.o janeiro-iommu.o janeiro-wakelock.o $(edgetpu-mobile-objs) $(edgetpu-objs)
CFLAGS_janeiro-fs.o := -DCONFIG_JANEIRO=1
CFLAGS_janeiro-core.o := -DCONFIG_JANEIRO=1
@@ -29,3 +28,4 @@ CFLAGS_janeiro-thermal.o := -DCONFIG_JANEIRO=1
CFLAGS_janeiro-debug-dump.o := -DCONFIG_JANEIRO=1
CFLAGS_janeiro-usage-stats.o := -DCONFIG_JANEIRO=1
CFLAGS_janeiro-wakelock.o := -DCONFIG_JANEIRO=1
+CFLAGS_janeiro-external.o := -DCONFIG_JANEIRO=1
diff --git a/drivers/edgetpu/Makefile b/drivers/edgetpu/Makefile
index 3bbfaaa..f21f1dc 100644
--- a/drivers/edgetpu/Makefile
+++ b/drivers/edgetpu/Makefile
@@ -18,14 +18,12 @@ edgetpu-objs := edgetpu-async.o edgetpu-dmabuf.o edgetpu-iremap-pool.o \
edgetpu-sw-watchdog.o edgetpu-telemetry.o \
edgetpu-firmware-util.o edgetpu-firmware.o
-edgetpu-mobile-objs := edgetpu-mobile-firmware.o
-
-
janeiro-objs := janeiro-core.o janeiro-debug-dump.o janeiro-device-group.o \
janeiro-device.o janeiro-firmware.o janeiro-fs.o \
janeiro-iommu.o janeiro-platform.o janeiro-pm.o \
- janeiro-usage-stats.o janeiro-wakelock.o \
- $(edgetpu-objs) $(edgetpu-mobile-objs)
+ janeiro-usage-stats.o janeiro-wakelock.o janeiro-external.o \
+ $(edgetpu-objs)
+
KBUILD_OPTIONS += CONFIG_JANEIRO=m
diff --git a/drivers/edgetpu/edgetpu-config.h b/drivers/edgetpu/edgetpu-config.h
index 9306fac..0626ba0 100644
--- a/drivers/edgetpu/edgetpu-config.h
+++ b/drivers/edgetpu/edgetpu-config.h
@@ -8,11 +8,20 @@
#ifndef __EDGETPU_CONFIG_H__
#define __EDGETPU_CONFIG_H__
-#ifdef CONFIG_JANEIRO
-
+#if IS_ENABLED(CONFIG_JANEIRO)
#include "janeiro/config.h"
-#endif /* CONFIG_JANEIRO */
+
+#else /* unknown */
+
+#error "Unknown EdgeTPU config"
+
+#endif /* unknown */
+
#define EDGETPU_DEFAULT_FIRMWARE_NAME "google/edgetpu-" DRIVER_NAME ".fw"
#define EDGETPU_TEST_FIRMWARE_NAME "google/edgetpu-" DRIVER_NAME "-test.fw"
+#ifndef EDGETPU_NUM_CORES
+#define EDGETPU_NUM_CORES 1
+#endif
+
#endif /* __EDGETPU_CONFIG_H__ */
diff --git a/drivers/edgetpu/edgetpu-external.c b/drivers/edgetpu/edgetpu-external.c
new file mode 100644
index 0000000..bbbea8c
--- /dev/null
+++ b/drivers/edgetpu/edgetpu-external.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Utility functions for interfacing other modules with Edge TPU ML accelerator.
+ *
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include <soc/google/tpu-ext.h>
+
+#include "edgetpu-config.h"
+#include "edgetpu-device-group.h"
+#include "edgetpu-internal.h"
+#include "edgetpu-mailbox.h"
+
+static int edgetpu_external_mailbox_info_get(struct edgetpu_external_mailbox_info *info,
+ struct edgetpu_external_mailbox *ext_mailbox)
+{
+ int i;
+ u32 count = ext_mailbox->count;
+ struct edgetpu_mailbox_descriptor *desc;
+
+ if (!info)
+ return -EINVAL;
+
+ if (info->count < count) {
+ etdev_err(ext_mailbox->etdev,
+ "Insufficient space in provided buffer expected: %d received: %d\n",
+ count, info->count);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < count; i++) {
+ desc = &ext_mailbox->descriptors[i];
+ info->mailboxes[i].cmdq_pa = desc->cmd_queue_mem.phys_addr;
+ info->mailboxes[i].respq_pa = desc->resp_queue_mem.phys_addr;
+ info->mailboxes[i].mailbox_id =
+ desc->mailbox->mailbox_id - (EDGETPU_NUM_VII_MAILBOXES + 1);
+ }
+
+ info->cmdq_size = ext_mailbox->attr.cmd_queue_size;
+ info->respq_size = ext_mailbox->attr.resp_queue_size;
+ info->count = count;
+
+ return 0;
+}
+
+static bool is_edgetpu_valid_client(struct edgetpu_external_mailbox *ext_mailbox,
+ enum edgetpu_external_client_type client_type)
+{
+ switch (client_type) {
+ case EDGETPU_EXTERNAL_CLIENT_TYPE_DSP:
+ return ext_mailbox->client_type == EDGETPU_EXT_MAILBOX_TYPE_DSP;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+static int edgetpu_mailbox_external_info_get_cmd(struct device *edgetpu_dev,
+ enum edgetpu_external_client_type client_type,
+ int *client_fd,
+ struct edgetpu_external_mailbox_info *info)
+{
+ struct edgetpu_client *client;
+ struct edgetpu_device_group *group;
+ struct edgetpu_external_mailbox *ext_mailbox;
+ int ret = 0;
+ struct fd f = fdget(*client_fd);
+ struct file *file = f.file;
+
+ if (!file)
+ return -ENOENT;
+
+ if (!is_edgetpu_file(file)) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ client = file->private_data;
+ if (!client) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&client->group_lock);
+ if (!client->group || client->etdev->dev != edgetpu_dev) {
+ ret = -EINVAL;
+ mutex_unlock(&client->group_lock);
+ goto out;
+ }
+ group = edgetpu_device_group_get(client->group);
+ mutex_unlock(&client->group_lock);
+
+ mutex_lock(&group->lock);
+ ext_mailbox = group->ext_mailbox;
+ if (!ext_mailbox) {
+ ret = -ENOENT;
+ goto unlock;
+ }
+ if (!is_edgetpu_valid_client(ext_mailbox, client_type)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = edgetpu_external_mailbox_info_get(info, ext_mailbox);
+unlock:
+ mutex_unlock(&group->lock);
+ edgetpu_device_group_put(group);
+out:
+ fdput(f);
+ return ret;
+}
+
+int edgetpu_ext_driver_cmd(struct device *edgetpu_dev,
+ enum edgetpu_external_client_type client_type,
+ enum edgetpu_external_commands cmd_id, void *in_data, void *out_data)
+{
+ int ret = 0;
+
+ switch (cmd_id) {
+ case MAILBOX_EXTERNAL_INFO_GET:
+ ret = edgetpu_mailbox_external_info_get_cmd(edgetpu_dev, client_type, in_data,
+ out_data);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(edgetpu_ext_driver_cmd);
diff --git a/drivers/edgetpu/edgetpu-fs.c b/drivers/edgetpu/edgetpu-fs.c
index 8b5a782..46de18f 100644
--- a/drivers/edgetpu/edgetpu-fs.c
+++ b/drivers/edgetpu/edgetpu-fs.c
@@ -66,7 +66,7 @@ static struct dentry *edgetpu_debugfs_dir;
} \
} while (0)
-static bool is_edgetpu_file(struct file *file)
+bool is_edgetpu_file(struct file *file)
{
if (edgetpu_is_external_wrapper_class_file(file))
return true;
diff --git a/drivers/edgetpu/edgetpu-google-iommu.c b/drivers/edgetpu/edgetpu-google-iommu.c
index f15b61a..eae1730 100644
--- a/drivers/edgetpu/edgetpu-google-iommu.c
+++ b/drivers/edgetpu/edgetpu-google-iommu.c
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/version.h>
+#include "edgetpu-config.h"
#include "edgetpu-internal.h"
#include "edgetpu-mapping.h"
#include "edgetpu-mmu.h"
diff --git a/drivers/edgetpu/edgetpu-internal.h b/drivers/edgetpu/edgetpu-internal.h
index f223dcb..6642e5f 100644
--- a/drivers/edgetpu/edgetpu-internal.h
+++ b/drivers/edgetpu/edgetpu-internal.h
@@ -58,12 +58,6 @@
/* The number of TPU tiles in an edgetpu chip */
#define EDGETPU_NTILES 16
-/* Up to 7 concurrent device groups / workloads per device. */
-#define EDGETPU_NGROUPS 7
-
-/* 1 context per VII/group plus 1 for KCI */
-#define EDGETPU_NCONTEXTS (EDGETPU_NGROUPS + 1)
-
/*
* Common-layer context IDs for non-secure TPU access, translated to chip-
* specific values in the mmu driver.
@@ -71,8 +65,7 @@
enum edgetpu_context_id {
EDGETPU_CONTEXT_INVALID = -1,
EDGETPU_CONTEXT_KCI = 0, /* TPU firmware/kernel ID 0 */
- EDGETPU_CONTEXT_VII_BASE = 1, /* groups 0-6 IDs 1-7 */
- /* contexts 8 and above not yet allocated */
+ EDGETPU_CONTEXT_VII_BASE = 1, /* groups IDs starts from 1 to (EDGETPU_CONTEXTS - 1) */
/* A bit mask to mark the context is an IOMMU domain token */
EDGETPU_CONTEXT_DOMAIN_TOKEN = 1 << 30,
};
@@ -232,8 +225,6 @@ struct edgetpu_dev {
/* debug dump handlers */
edgetpu_debug_dump_handlers *debug_dump_handlers;
struct work_struct debug_dump_work;
- /* status about if device going to be removed from system */
- bool on_exit;
};
struct edgetpu_dev_iface {
@@ -358,6 +349,8 @@ void edgetpu_free_coherent(struct edgetpu_dev *etdev,
struct edgetpu_coherent_mem *mem,
enum edgetpu_context_id context_id);
+/* Checks if @file belongs to edgetpu driver */
+bool is_edgetpu_file(struct file *file);
/* External drivers can hook up to edgetpu driver using these calls. */
int edgetpu_open(struct edgetpu_dev_iface *etiface, struct file *file);
@@ -388,7 +381,9 @@ int edgetpu_device_add(struct edgetpu_dev *etdev,
const struct edgetpu_iface_params *iface_params,
uint num_ifaces);
void edgetpu_device_remove(struct edgetpu_dev *etdev);
+/* Registers IRQ. */
int edgetpu_register_irq(struct edgetpu_dev *etdev, int irq);
+/* Reverts edgetpu_register_irq */
void edgetpu_unregister_irq(struct edgetpu_dev *etdev, int irq);
/* Core -> Device FS API */
diff --git a/drivers/edgetpu/edgetpu-kci.c b/drivers/edgetpu/edgetpu-kci.c
index d262bba..17d92ab 100644
--- a/drivers/edgetpu/edgetpu-kci.c
+++ b/drivers/edgetpu/edgetpu-kci.c
@@ -30,7 +30,11 @@
#define QUEUE_SIZE MAX_QUEUE_SIZE
/* Timeout for KCI responses from the firmware (milliseconds) */
-#if IS_ENABLED(CONFIG_EDGETPU_TEST)
+#ifdef EDGETPU_KCI_TIMEOUT
+
+#define KCI_TIMEOUT EDGETPU_KCI_TIMEOUT
+
+#elif IS_ENABLED(CONFIG_EDGETPU_TEST)
/* fake-firmware could respond in a short time */
#define KCI_TIMEOUT (200)
#else
diff --git a/drivers/edgetpu/edgetpu-mailbox.c b/drivers/edgetpu/edgetpu-mailbox.c
index 56acd72..f4a3e4e 100644
--- a/drivers/edgetpu/edgetpu-mailbox.c
+++ b/drivers/edgetpu/edgetpu-mailbox.c
@@ -853,6 +853,10 @@ static int edgetpu_mailbox_external_alloc(struct edgetpu_device_group *group,
if (!ext_mailbox_req)
return -EINVAL;
+ ret = edgetpu_mailbox_validate_attr(&ext_mailbox_req->attr);
+ if (ret)
+ return ret;
+
count = ext_mailbox_req->count;
attr = ext_mailbox_req->attr;
@@ -874,6 +878,7 @@ static int edgetpu_mailbox_external_alloc(struct edgetpu_device_group *group,
ext_mailbox->attr = attr;
ext_mailbox->count = count;
ext_mailbox->etdev = group->etdev;
+ ext_mailbox->client_type = ext_mailbox_req->client_type;
write_lock_irqsave(&mgr->mailboxes_lock, flags);
for (i = ext_mailbox_req->start; i <= ext_mailbox_req->end; i++) {
@@ -892,6 +897,7 @@ static int edgetpu_mailbox_external_alloc(struct edgetpu_device_group *group,
mgr->mailboxes[i] = mailbox;
ext_mailbox->descriptors[j++].mailbox = mailbox;
} else {
+ ret = PTR_ERR(mailbox);
goto release;
}
}
@@ -989,7 +995,7 @@ static int edgetpu_mailbox_external_alloc_enable(struct edgetpu_client *client,
for (i = 0; i < ext_mailbox->count; i++) {
id = ext_mailbox->descriptors[i].mailbox->mailbox_id;
etdev_dbg(group->etdev, "Enabling mailbox: %d\n", id);
- ret = edgetpu_mailbox_activate(group->etdev, id, vcid, true);
+ ret = edgetpu_mailbox_activate(group->etdev, id, vcid, false);
if (ret) {
etdev_err(group->etdev, "Activate mailbox %d failed: %d", id, ret);
break;
diff --git a/drivers/edgetpu/edgetpu-mailbox.h b/drivers/edgetpu/edgetpu-mailbox.h
index d76534a..3098760 100644
--- a/drivers/edgetpu/edgetpu-mailbox.h
+++ b/drivers/edgetpu/edgetpu-mailbox.h
@@ -91,7 +91,12 @@ struct edgetpu_mailbox_descriptor {
/* Structure to hold multiple external mailboxes allocated for a device group. */
struct edgetpu_external_mailbox {
/* Number of external mailboxes allocated for a device group. */
- int count;
+ u32 count;
+ /*
+ * Client type of external mailboxes requester, it belongs to
+ * EDGETPU_EXT_MAILBOX_TYPE_*
+ */
+ u64 client_type;
/* Leader of device group. */
struct edgetpu_dev *etdev;
/* Array of external mailboxes info with length @count. */
@@ -106,6 +111,11 @@ struct edgetpu_external_mailbox_req {
uint end; /* end index of external mailbox in mailbox_manager */
/* number of mailboxes to be allocated, should be less or equal to (end - start + 1) */
uint count;
+ /*
+ * Client type of external mailboxes requester, it belongs to
+ * EDGETPU_EXT_MAILBOX_TYPE_*
+ */
+ u64 client_type;
struct edgetpu_mailbox_attr attr; /* mailbox attribute for allocation */
};
@@ -487,12 +497,7 @@ static inline void edgetpu_mailbox_enable(struct edgetpu_mailbox *mailbox)
/* Disables a mailbox by setting CSR. */
static inline void edgetpu_mailbox_disable(struct edgetpu_mailbox *mailbox)
{
- /*
- * if device is being removed, no need to access device CSR since it
- * is going to be powered-off anyway
- */
- if (!mailbox->etdev->on_exit)
- EDGETPU_MAILBOX_CONTEXT_WRITE(mailbox, context_enable, 0);
+ EDGETPU_MAILBOX_CONTEXT_WRITE(mailbox, context_enable, 0);
}
#endif /* __EDGETPU_MAILBOX_H__ */
diff --git a/drivers/edgetpu/edgetpu-mobile-firmware.c b/drivers/edgetpu/edgetpu-mobile-firmware.c
deleted file mode 100644
index dedac0f..0000000
--- a/drivers/edgetpu/edgetpu-mobile-firmware.c
+++ /dev/null
@@ -1,63 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Edge TPU firmware management for mobile chipsets.
- *
- * Copyright (C) 2021 Google, Inc.
- */
-
-#include <linux/device.h>
-#include <linux/firmware.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/types.h>
-
-#include "edgetpu-firmware.h"
-#include "edgetpu-firmware-util.h"
-#include "edgetpu-internal.h"
-#include "mobile-firmware.h"
-
-/* Load firmware for chips that use carveout memory for a single chip. */
-int edgetpu_firmware_chip_load_locked(
- struct edgetpu_firmware *et_fw,
- struct edgetpu_firmware_desc *fw_desc, const char *name)
-{
- int ret;
- struct edgetpu_dev *etdev = et_fw->etdev;
- struct device *dev = etdev->dev;
- const struct firmware *fw;
- size_t aligned_size;
-
- ret = request_firmware(&fw, name, dev);
- if (ret) {
- etdev_dbg(etdev,
- "%s: request '%s' failed: %d\n", __func__, name, ret);
- return ret;
- }
-
- aligned_size = ALIGN(fw->size, fw_desc->buf.used_size_align);
- if (aligned_size > fw_desc->buf.alloc_size) {
- etdev_dbg(etdev,
- "%s: firmware buffer too small: alloc size=%#zx, required size=%#zx\n",
- __func__, fw_desc->buf.alloc_size, aligned_size);
- ret = -ENOSPC;
- goto out_release_firmware;
- }
-
- memcpy(fw_desc->buf.vaddr, fw->data, fw->size);
- fw_desc->buf.used_size = aligned_size;
- /* May return NULL on out of memory, driver must handle properly */
- fw_desc->buf.name = kstrdup(name, GFP_KERNEL);
-
-out_release_firmware:
- release_firmware(fw);
- return ret;
-}
-
-void edgetpu_firmware_chip_unload_locked(
- struct edgetpu_firmware *et_fw,
- struct edgetpu_firmware_desc *fw_desc)
-{
- kfree(fw_desc->buf.name);
- fw_desc->buf.name = NULL;
- fw_desc->buf.used_size = 0;
-}
diff --git a/drivers/edgetpu/edgetpu-mobile-platform.c b/drivers/edgetpu/edgetpu-mobile-platform.c
new file mode 100644
index 0000000..7b9abec
--- /dev/null
+++ b/drivers/edgetpu/edgetpu-mobile-platform.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common platform interfaces for mobile TPU chips.
+ *
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/gsa/gsa_tpu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "edgetpu-config.h"
+#include "edgetpu-internal.h"
+#include "edgetpu-iremap-pool.h"
+#include "edgetpu-mmu.h"
+#include "edgetpu-mobile-platform.h"
+#include "edgetpu-telemetry.h"
+#include "mobile-firmware.h"
+#include "mobile-pm.h"
+
+/*
+ * Log and trace buffers at the beginning of the remapped region,
+ * pool memory afterwards.
+ */
+#define EDGETPU_POOL_MEM_OFFSET (EDGETPU_TELEMETRY_BUFFER_SIZE * 2)
+
+static void get_telemetry_mem(struct edgetpu_mobile_platform_dev *etmdev,
+ enum edgetpu_telemetry_type type, struct edgetpu_coherent_mem *mem)
+{
+ int offset = type == EDGETPU_TELEMETRY_TRACE ? EDGETPU_TELEMETRY_BUFFER_SIZE : 0;
+
+ mem->vaddr = etmdev->shared_mem_vaddr + offset;
+ mem->dma_addr = EDGETPU_REMAPPED_DATA_ADDR + offset;
+ mem->tpu_addr = EDGETPU_REMAPPED_DATA_ADDR + offset;
+ mem->host_addr = 0;
+ mem->size = EDGETPU_TELEMETRY_BUFFER_SIZE;
+}
+
+static void edgetpu_mobile_get_telemetry_mem(struct edgetpu_mobile_platform_dev *etmdev)
+{
+ get_telemetry_mem(etmdev, EDGETPU_TELEMETRY_LOG, &etmdev->log_mem);
+ get_telemetry_mem(etmdev, EDGETPU_TELEMETRY_TRACE, &etmdev->trace_mem);
+}
+
+static int edgetpu_platform_setup_fw_region(struct edgetpu_mobile_platform_dev *etmdev)
+{
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
+ struct platform_device *gsa_pdev;
+ struct device *dev = etdev->dev;
+ struct resource r;
+ struct device_node *np;
+ int err;
+ size_t region_map_size = EDGETPU_FW_SIZE_MAX + EDGETPU_REMAPPED_DATA_SIZE;
+
+ np = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!np) {
+ dev_err(dev, "No memory region for firmware");
+ return -ENODEV;
+ }
+
+ err = of_address_to_resource(np, 0, &r);
+ of_node_put(np);
+ if (err) {
+ dev_err(dev, "No memory address assigned to firmware region");
+ return err;
+ }
+
+ if (resource_size(&r) < region_map_size) {
+ dev_err(dev, "Memory region for firmware too small (%zu bytes needed, got %llu)",
+ region_map_size, resource_size(&r));
+ return -ENOSPC;
+ }
+
+ /* Get GSA device from device tree */
+ np = of_parse_phandle(dev->of_node, "gsa-device", 0);
+ if (!np) {
+ dev_warn(dev, "No gsa-device in device tree. Authentication not available");
+ } else {
+ gsa_pdev = of_find_device_by_node(np);
+ if (!gsa_pdev) {
+ dev_err(dev, "GSA device not found");
+ of_node_put(np);
+ return -ENODEV;
+ }
+ etmdev->gsa_dev = get_device(&gsa_pdev->dev);
+ of_node_put(np);
+ }
+
+ etmdev->fw_region_paddr = r.start;
+ etmdev->fw_region_size = EDGETPU_FW_SIZE_MAX;
+
+ etmdev->shared_mem_vaddr = memremap(r.start + EDGETPU_REMAPPED_DATA_OFFSET,
+ EDGETPU_REMAPPED_DATA_SIZE, MEMREMAP_WC);
+ if (!etmdev->shared_mem_vaddr) {
+ dev_err(dev, "Shared memory remap failed");
+ if (etmdev->gsa_dev)
+ put_device(etmdev->gsa_dev);
+ return -EINVAL;
+ }
+ etmdev->shared_mem_paddr = r.start + EDGETPU_REMAPPED_DATA_OFFSET;
+
+ return 0;
+}
+
+static void edgetpu_platform_cleanup_fw_region(struct edgetpu_mobile_platform_dev *etmdev)
+{
+ if (etmdev->gsa_dev) {
+ gsa_unload_tpu_fw_image(etmdev->gsa_dev);
+ put_device(etmdev->gsa_dev);
+ }
+ if (!etmdev->shared_mem_vaddr)
+ return;
+ memunmap(etmdev->shared_mem_vaddr);
+ etmdev->shared_mem_vaddr = NULL;
+}
+
+int edgetpu_chip_setup_mmu(struct edgetpu_dev *etdev)
+{
+ int ret;
+
+ ret = edgetpu_mmu_attach(etdev, NULL);
+ if (ret)
+ dev_err(etdev->dev, "failed to attach IOMMU: %d", ret);
+ return ret;
+}
+
+void edgetpu_chip_remove_mmu(struct edgetpu_dev *etdev)
+{
+ edgetpu_mmu_detach(etdev);
+}
+
+static int edgetpu_platform_parse_ssmt(struct edgetpu_mobile_platform_dev *etmdev)
+{
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
+ struct platform_device *pdev = to_platform_device(etdev->dev);
+ struct resource *res;
+ int ret;
+ void __iomem *ssmt_base;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ssmt");
+ if (!res) {
+ etdev_warn(etdev, "Failed to find SSMT register base");
+ return -EINVAL;
+ }
+ ssmt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ssmt_base)) {
+ ret = PTR_ERR(ssmt_base);
+ etdev_warn(etdev, "Failed to map SSMT register base: %d", ret);
+ return ret;
+ }
+ etmdev->ssmt_base = ssmt_base;
+ return 0;
+}
+
+static int edgetpu_platform_setup_irq(struct edgetpu_mobile_platform_dev *etmdev)
+{
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
+ struct platform_device *pdev = to_platform_device(etdev->dev);
+ int n = platform_irq_count(pdev);
+ int ret;
+ int i;
+
+ etmdev->irq = devm_kmalloc_array(etdev->dev, n, sizeof(*etmdev->irq), GFP_KERNEL);
+ if (!etmdev->irq)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ etmdev->irq[i] = platform_get_irq(pdev, i);
+ ret = edgetpu_register_irq(etdev, etmdev->irq[i]);
+ if (ret)
+ goto rollback;
+ }
+ etmdev->n_irq = n;
+ return 0;
+
+rollback:
+ while (i--)
+ edgetpu_unregister_irq(etdev, etmdev->irq[i]);
+ return ret;
+}
+
+static void edgetpu_platform_remove_irq(struct edgetpu_mobile_platform_dev *etmdev)
+{
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
+ int i;
+
+ for (i = 0; i < etmdev->n_irq; i++)
+ edgetpu_unregister_irq(etdev, etmdev->irq[i]);
+}
+
+static int edgetpu_mobile_platform_probe(struct platform_device *pdev,
+ struct edgetpu_mobile_platform_dev *etmdev)
+{
+ struct device *dev = &pdev->dev;
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
+ struct resource *r;
+ struct edgetpu_mapped_resource regs;
+ int ret;
+ struct edgetpu_iface_params iface_params[] = {
+ /* Default interface */
+ { .name = NULL },
+ /* Common name for embedded SoC devices */
+ { .name = "edgetpu-soc" },
+ };
+
+ mutex_init(&etmdev->tz_mailbox_lock);
+
+ platform_set_drvdata(pdev, etdev);
+ etdev->dev = dev;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR_OR_NULL(r)) {
+ dev_err(dev, "failed to get memory resource");
+ return -ENODEV;
+ }
+
+ regs.phys = r->start;
+ regs.size = resource_size(r);
+ regs.mem = devm_ioremap_resource(dev, r);
+ if (IS_ERR_OR_NULL(regs.mem)) {
+ dev_err(dev, "failed to map registers");
+ return -ENODEV;
+ }
+
+ mutex_init(&etmdev->platform_pwr.policy_lock);
+ etmdev->platform_pwr.curr_policy = TPU_POLICY_MAX;
+
+ ret = edgetpu_chip_pm_create(etdev);
+ if (ret) {
+ dev_err(dev, "Failed to initialize PM interface: %d", ret);
+ return ret;
+ }
+
+ ret = edgetpu_platform_setup_fw_region(etmdev);
+ if (ret) {
+ dev_err(dev, "setup fw regions failed: %d", ret);
+ goto out_shutdown;
+ }
+
+ ret = edgetpu_iremap_pool_create(etdev,
+ /* Base virtual address (kernel address space) */
+ etmdev->shared_mem_vaddr + EDGETPU_POOL_MEM_OFFSET,
+ /* Base DMA address */
+ EDGETPU_REMAPPED_DATA_ADDR + EDGETPU_POOL_MEM_OFFSET,
+ /* Base TPU address */
+ EDGETPU_REMAPPED_DATA_ADDR + EDGETPU_POOL_MEM_OFFSET,
+ /* Base physical address */
+ etmdev->shared_mem_paddr + EDGETPU_POOL_MEM_OFFSET,
+ /* Size */
+ EDGETPU_REMAPPED_DATA_SIZE - EDGETPU_POOL_MEM_OFFSET,
+ /* Granularity */
+ PAGE_SIZE);
+ if (ret) {
+ dev_err(dev, "failed to initialize remapped memory pool: %d", ret);
+ goto out_cleanup_fw;
+ }
+
+ etdev->mcp_id = -1;
+ etdev->mcp_die_index = 0;
+ ret = edgetpu_device_add(etdev, &regs, iface_params, ARRAY_SIZE(iface_params));
+ if (ret) {
+ dev_err(dev, "edgetpu setup failed: %d", ret);
+ goto out_destroy_iremap;
+ }
+
+ ret = edgetpu_platform_setup_irq(etmdev);
+ if (ret) {
+ dev_err(dev, "IRQ setup failed: %d", ret);
+ goto out_remove_device;
+ }
+
+ ret = edgetpu_platform_parse_ssmt(etmdev);
+ if (ret)
+ dev_warn(dev, "SSMT setup failed (%d). Context isolation not enforced", ret);
+
+ edgetpu_mobile_get_telemetry_mem(etmdev);
+ ret = edgetpu_telemetry_init(etdev, &etmdev->log_mem, &etmdev->trace_mem);
+ if (ret)
+ goto out_remove_irq;
+
+ ret = edgetpu_mobile_firmware_create(etdev);
+ if (ret) {
+ dev_err(dev, "initialize firmware downloader failed: %d", ret);
+ goto out_tel_exit;
+ }
+
+ if (etmdev->after_probe) {
+ ret = etmdev->after_probe(etmdev);
+ if (ret) {
+ dev_err(dev, "after_probe callback failed: %d", ret);
+ goto out_destroy_fw;
+ }
+ }
+
+ dev_info(dev, "%s edgetpu initialized. Build: %s", etdev->dev_name, GIT_REPO_TAG);
+ /* Turn the device off unless a client request is already received. */
+ edgetpu_pm_shutdown(etdev, false);
+
+ return 0;
+out_destroy_fw:
+ edgetpu_mobile_firmware_destroy(etdev);
+out_tel_exit:
+ edgetpu_telemetry_exit(etdev);
+out_remove_irq:
+ edgetpu_platform_remove_irq(etmdev);
+out_remove_device:
+ edgetpu_device_remove(etdev);
+out_destroy_iremap:
+ edgetpu_iremap_pool_destroy(etdev);
+out_cleanup_fw:
+ edgetpu_platform_cleanup_fw_region(etmdev);
+out_shutdown:
+ dev_dbg(dev, "Probe finished with error %d, powering down", ret);
+ edgetpu_pm_shutdown(etdev, true);
+ return ret;
+}
+
+static int edgetpu_mobile_platform_remove(struct platform_device *pdev)
+{
+ struct edgetpu_dev *etdev = platform_get_drvdata(pdev);
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+
+ if (etmdev->before_remove)
+ etmdev->before_remove(etmdev);
+ edgetpu_mobile_firmware_destroy(etdev);
+ edgetpu_platform_remove_irq(etmdev);
+ edgetpu_pm_get(etdev->pm);
+ edgetpu_telemetry_exit(etdev);
+ edgetpu_device_remove(etdev);
+ edgetpu_iremap_pool_destroy(etdev);
+ edgetpu_platform_cleanup_fw_region(etmdev);
+ edgetpu_pm_put(etdev->pm);
+ edgetpu_pm_shutdown(etdev, true);
+ mobile_pm_destroy(etdev);
+ return 0;
+}
diff --git a/drivers/edgetpu/edgetpu-mobile-platform.h b/drivers/edgetpu/edgetpu-mobile-platform.h
new file mode 100644
index 0000000..8b3a25f
--- /dev/null
+++ b/drivers/edgetpu/edgetpu-mobile-platform.h
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Common platform interfaces for mobile TPU chips.
+ *
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#ifndef __EDGETPU_MOBILE_PLATFORM_H__
+#define __EDGETPU_MOBILE_PLATFORM_H__
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <soc/google/exynos_pm_qos.h>
+
+#if IS_ENABLED(CONFIG_GOOGLE_BCL)
+#include <soc/google/bcl.h>
+#endif
+
+#include "edgetpu-config.h"
+#include "edgetpu-internal.h"
+
+#define to_mobile_dev(etdev) container_of(etdev, struct edgetpu_mobile_platform_dev, edgetpu_dev)
+
+struct edgetpu_mobile_platform_pwr {
+ struct dentry *debugfs_dir;
+ struct mutex policy_lock;
+ enum edgetpu_pwr_state curr_policy;
+ struct mutex state_lock;
+ u64 min_state;
+ u64 requested_state;
+ /* INT/MIF requests for memory bandwidth */
+ struct exynos_pm_qos_request int_min;
+ struct exynos_pm_qos_request mif_min;
+ /* BTS */
+ unsigned int performance_scenario;
+ int scenario_count;
+ struct mutex scenario_lock;
+
+ /* LPM callbacks, NULL for chips without LPM */
+ int (*lpm_up)(struct edgetpu_dev *etdev);
+ void (*lpm_down)(struct edgetpu_dev *etdev);
+
+ /* Block shutdown callback, may be NULL */
+ void (*block_down)(struct edgetpu_dev *etdev);
+
+ /* Firmware shutdown callback. Must be implemented */
+ void (*firmware_down)(struct edgetpu_dev *etdev);
+
+ /* Chip-specific setup after the PM interface is created */
+ int (*after_create)(struct edgetpu_dev *etdev);
+
+ /* Chip-specific cleanup before the PM interface is destroyed */
+ int (*before_destroy)(struct edgetpu_dev *etdev);
+
+ /* ACPM set rate callback. Must be implemented */
+ int (*acpm_set_rate)(unsigned int id, unsigned long rate);
+};
+
+struct edgetpu_mobile_platform_dev {
+ /* Generic edgetpu device */
+ struct edgetpu_dev edgetpu_dev;
+ /* Common mobile platform power interface */
+ struct edgetpu_mobile_platform_pwr platform_pwr;
+ /* Physical address of the firmware image */
+ phys_addr_t fw_region_paddr;
+ /* Size of the firmware region */
+ size_t fw_region_size;
+ /* Virtual address of the memory region shared with firmware */
+ void *shared_mem_vaddr;
+ /* Physical address of the memory region shared with firmware */
+ phys_addr_t shared_mem_paddr;
+ /* Size of the shared memory region size */
+ size_t shared_mem_size;
+ /*
+ * Pointer to GSA device for firmware authentication.
+ * May be NULL if the chip does not support firmware authentication
+ */
+ struct device *gsa_dev;
+ /* Virtual address of the SSMT block for this chip. */
+ void __iomem *ssmt_base;
+ /* Coherent log buffer */
+ struct edgetpu_coherent_mem log_mem;
+ /* Coherent trace buffer */
+ struct edgetpu_coherent_mem trace_mem;
+#if IS_ENABLED(CONFIG_GOOGLE_BCL)
+ struct bcl_device *bcl_dev;
+#endif
+ /* Protects TZ Mailbox client pointer */
+ struct mutex tz_mailbox_lock;
+ /* TZ mailbox client */
+ struct edgetpu_client *secure_client;
+
+ /* Length of @irq */
+ int n_irq;
+ /* Array of IRQ numbers */
+ int *irq;
+
+ /* callbacks for chip-dependent implementations */
+
+ /*
+ * Called when common device probing procedure is done.
+ *
+ * Return a non-zero value can fail the probe procedure.
+ *
+ * This callback is optional.
+ */
+ int (*after_probe)(struct edgetpu_mobile_platform_dev *etmdev);
+ /*
+ * Called before common device removal procedure.
+ *
+ * This callback is optional.
+ */
+ void (*before_remove)(struct edgetpu_mobile_platform_dev *etmdev);
+};
+
+#endif /* __EDGETPU_MOBILE_PLATFORM_H__ */
diff --git a/drivers/edgetpu/janeiro-debug-dump.c b/drivers/edgetpu/janeiro-debug-dump.c
index 394ec08..4314abe 100644
--- a/drivers/edgetpu/janeiro-debug-dump.c
+++ b/drivers/edgetpu/janeiro-debug-dump.c
@@ -5,7 +5,7 @@
* Copyright (C) 2021 Google, Inc.
*/
-#include "mobile-debug-dump.c"
+#include "edgetpu-debug-dump.c"
int edgetpu_debug_dump_init(struct edgetpu_dev *etdev)
{
diff --git a/drivers/edgetpu/janeiro-device.c b/drivers/edgetpu/janeiro-device.c
index 36dbe76..cc4c975 100644
--- a/drivers/edgetpu/janeiro-device.c
+++ b/drivers/edgetpu/janeiro-device.c
@@ -11,8 +11,8 @@
#include "edgetpu-config.h"
#include "edgetpu-internal.h"
#include "edgetpu-mailbox.h"
+#include "edgetpu-mobile-platform.h"
#include "edgetpu-telemetry.h"
-#include "janeiro-platform.h"
#include "mobile-pm.h"
#define SSMT_NS_READ_STREAM_VID_OFFSET(n) (0x1000u + (0x4u * (n)))
@@ -27,16 +27,16 @@ static irqreturn_t janeiro_mailbox_handle_irq(struct edgetpu_dev *etdev,
int irq)
{
struct edgetpu_mailbox *mailbox;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
struct edgetpu_mailbox_manager *mgr = etdev->mailbox_manager;
uint i;
- struct janeiro_platform_dev *jpdev = to_janeiro_dev(etdev);
if (!mgr)
return IRQ_NONE;
- for (i = 0; i < EDGETPU_NCONTEXTS; i++)
- if (jpdev->irq[i] == irq)
+ for (i = 0; i < etmdev->n_irq; i++)
+ if (etmdev->irq[i] == irq)
break;
- if (i == EDGETPU_NCONTEXTS)
+ if (i == etmdev->n_irq)
return IRQ_NONE;
read_lock(&mgr->mailboxes_lock);
mailbox = mgr->mailboxes[i];
@@ -70,15 +70,19 @@ u64 edgetpu_chip_tpu_timestamp(struct edgetpu_dev *etdev)
void edgetpu_chip_init(struct edgetpu_dev *etdev)
{
int i;
- struct janeiro_platform_dev *jpdev = to_janeiro_dev(etdev);
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
- if (!jpdev->ssmt_base)
+ /*
+ * This only works if the SSMT is set to client-driven mode, which only GSA can do.
+ * Skip if GSA is not available
+ */
+ if (!etmdev->ssmt_base || !etmdev->gsa_dev)
return;
/* Setup non-secure SCIDs, assume VID = SCID */
for (i = 0; i < EDGETPU_NCONTEXTS; i++) {
- writel(i, SSMT_NS_READ_STREAM_VID_REG(jpdev->ssmt_base, i));
- writel(i, SSMT_NS_WRITE_STREAM_VID_REG(jpdev->ssmt_base, i));
+ writel(i, SSMT_NS_READ_STREAM_VID_REG(etmdev->ssmt_base, i));
+ writel(i, SSMT_NS_WRITE_STREAM_VID_REG(etmdev->ssmt_base, i));
}
}
@@ -120,6 +124,7 @@ int edgetpu_chip_acquire_ext_mailbox(struct edgetpu_client *client,
req.count = args->count;
req.start = JANEIRO_EXT_DSP_MAILBOX_START;
req.end = JANEIRO_EXT_DSP_MAILBOX_END;
+ req.client_type = EDGETPU_EXT_MAILBOX_TYPE_DSP;
return edgetpu_mailbox_enable_ext(client, EDGETPU_MAILBOX_ID_USE_ASSOC, &req);
}
return -ENODEV;
diff --git a/drivers/edgetpu/janeiro-external.c b/drivers/edgetpu/janeiro-external.c
new file mode 100644
index 0000000..c9c3494
--- /dev/null
+++ b/drivers/edgetpu/janeiro-external.c
@@ -0,0 +1,2 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "edgetpu-external.c"
diff --git a/drivers/edgetpu/janeiro-firmware.c b/drivers/edgetpu/janeiro-firmware.c
index ef43931..0339c16 100644
--- a/drivers/edgetpu/janeiro-firmware.c
+++ b/drivers/edgetpu/janeiro-firmware.c
@@ -5,284 +5,4 @@
* Copyright (C) 2020 Google, Inc.
*/
-#include <linux/dma-mapping.h>
-#include <linux/sizes.h>
-#include <linux/slab.h>
-
-#include <linux/iommu.h>
-
-#include "edgetpu.h"
-#include "edgetpu-config.h"
-#include "edgetpu-firmware.h"
-#include "edgetpu-internal.h"
-#include "edgetpu-mmu.h"
-#include "edgetpu-kci.h"
-#include "edgetpu-mailbox.h"
-#include "janeiro-platform.h"
-#include "mobile-firmware.h"
-
-#define MAX_IOMMU_MAPPINGS 26
-
-#define CONFIG_TO_SIZE(a) ((1 << ((a) & 0xFFF)) << 12)
-
-struct iommu_mapping {
- /* TPU virt address */
- __u32 virt_address;
- /*
- * contains a 12-bit aligned address and a page-order size into a
- * 32-bit value i.e. a physical address and size in page order.
- */
- __u32 image_config_value;
-};
-
-struct janeiro_image_config {
- __u32 carveout_base;
- __u32 firmware_base;
- __u32 firmware_size;
- struct edgetpu_fw_version firmware_versions;
- __u32 config_version;
- __u32 privilege_level;
- __u32 remapped_region_start;
- __u32 remapped_region_end;
- __u32 num_iommu_mapping;
- struct iommu_mapping mappings[MAX_IOMMU_MAPPINGS];
-} __packed;
-
-struct janeiro_firmware_data {
- __u32 num_mapping;
- struct iommu_mapping mappings[MAX_IOMMU_MAPPINGS];
-};
-/*
- * Sets the reset state of the TPU CPU.
- * @val: 1 to put the core in reset state, 0 to release core from reset state.
- */
-static void tpu_cpu_reset(struct edgetpu_dev *etdev, u64 val)
-{
- edgetpu_dev_write_32_sync(etdev, EDGETPU_REG_RESET_CONTROL, val);
-}
-
-static int janeiro_firmware_after_create(struct edgetpu_firmware *et_fw)
-{
- struct janeiro_firmware_data *data;
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- edgetpu_firmware_set_data(et_fw, data);
- return 0;
-}
-
-static void janeiro_firmware_before_destroy(struct edgetpu_firmware *et_fw)
-{
- struct janeiro_firmware_data *data;
- u32 i, tpu_addr, size;
- struct edgetpu_dev *etdev = et_fw->etdev;
-
- if (!etdev->on_exit)
- tpu_cpu_reset(et_fw->etdev, 1);
- /* TODO(b/189906347): Remove when GSA/TZ support is available. */
- /* Remove mappings created by setup_buffer() */
- data = edgetpu_firmware_get_data(et_fw);
-
- if (data) {
- for (i = 0; i < data->num_mapping; i++) {
- tpu_addr = data->mappings[i].virt_address;
- size = CONFIG_TO_SIZE(data->mappings[i].image_config_value);
- edgetpu_mmu_remove_translation(etdev, tpu_addr, size,
- EDGETPU_CONTEXT_KCI);
- }
- edgetpu_firmware_set_data(et_fw, NULL);
- kfree(data);
- }
-}
-
-static int janeiro_firmware_alloc_buffer(struct edgetpu_firmware *et_fw,
- struct edgetpu_firmware_buffer *fw_buf)
-{
- struct edgetpu_dev *etdev = et_fw->etdev;
- struct janeiro_platform_dev *edgetpu_pdev =
- container_of(etdev, struct janeiro_platform_dev, edgetpu_dev);
- /* Allocate extra space for the image header */
- size_t buffer_size =
- edgetpu_pdev->fw_region_size + MOBILE_FW_HEADER_SIZE;
-
- fw_buf->vaddr = kzalloc(buffer_size, GFP_KERNEL);
- if (!fw_buf->vaddr) {
- etdev_err(etdev, "%s: failed to allocate buffer (%zu bytes)\n",
- __func__, buffer_size);
- return -ENOMEM;
- }
- fw_buf->dma_addr = 0;
- fw_buf->alloc_size = buffer_size;
- fw_buf->used_size_align = 16;
- return 0;
-}
-
-static void janeiro_firmware_free_buffer(struct edgetpu_firmware *et_fw,
- struct edgetpu_firmware_buffer *fw_buf)
-{
- kfree(fw_buf->vaddr);
- fw_buf->vaddr = NULL;
- fw_buf->alloc_size = 0;
- fw_buf->used_size_align = 0;
-}
-
-static int janeiro_firmware_setup_buffer(struct edgetpu_firmware *et_fw,
- struct edgetpu_firmware_buffer *fw_buf)
-{
- int ret = 0;
- void *image_vaddr;
- u32 tpu_addr, phys_addr, size, i, j;
- struct janeiro_image_config *image_config;
- struct janeiro_firmware_data *data;
- struct edgetpu_dev *etdev = et_fw->etdev;
- struct janeiro_platform_dev *edgetpu_pdev =
- container_of(etdev, struct janeiro_platform_dev, edgetpu_dev);
-
- if (fw_buf->used_size < MOBILE_FW_HEADER_SIZE) {
- etdev_err(etdev, "Invalid buffer size: %zu < %d\n",
- fw_buf->used_size, MOBILE_FW_HEADER_SIZE);
- return -EINVAL;
- }
-
- image_vaddr = memremap(edgetpu_pdev->fw_region_paddr,
- edgetpu_pdev->fw_region_size, MEMREMAP_WC);
- if (!image_vaddr) {
- etdev_err(etdev, "memremap failed\n");
- return -ENOMEM;
- }
-
- /* fetch the firmware versions */
- image_config = fw_buf->vaddr + MOBILE_IMAGE_CONFIG_OFFSET;
- memcpy(&etdev->fw_version, &image_config->firmware_versions,
- sizeof(etdev->fw_version));
-
- /* TODO(b/189906347): Remove when GSA/TZ support is available. */
- data = edgetpu_firmware_get_data(et_fw);
- /* Remove old mappings created for previous firmware. */
- for (i = 0; i < data->num_mapping; i++) {
- tpu_addr = data->mappings[i].virt_address;
- size = CONFIG_TO_SIZE(data->mappings[i].image_config_value);
- phys_addr = (data->mappings[i].image_config_value & ~(0xFFF));
-
- edgetpu_mmu_remove_translation(etdev, tpu_addr, size, EDGETPU_CONTEXT_KCI);
- }
- for (i = 0, j = 0; i < MAX_IOMMU_MAPPINGS; i++) {
- tpu_addr = image_config->mappings[i].virt_address;
- if (!tpu_addr)
- continue;
- size = CONFIG_TO_SIZE(image_config->mappings[i].image_config_value);
- phys_addr = (image_config->mappings[i].image_config_value & ~(0xFFF));
-
- ret = edgetpu_mmu_add_translation(etdev, tpu_addr, phys_addr, size,
- IOMMU_READ | IOMMU_WRITE, EDGETPU_CONTEXT_KCI);
- if (ret) {
- etdev_dbg(etdev,
- "Unable to map: %d tpu_addr: %#x phys_addr: %pap size: %#x\n",
- ret, tpu_addr, &phys_addr, size);
- goto err;
- }
- data->mappings[j].virt_address = tpu_addr;
- data->mappings[j++].image_config_value =
- image_config->mappings[i].image_config_value;
- }
-
- if (image_config->num_iommu_mapping != j) {
- etdev_err(etdev, "Invalid firmware header\n");
- ret = -EINVAL;
- goto err;
- }
- data->num_mapping = j;
-
- /* Skip the header */
- memcpy(image_vaddr, fw_buf->vaddr + MOBILE_FW_HEADER_SIZE,
- fw_buf->used_size - MOBILE_FW_HEADER_SIZE);
- memunmap(image_vaddr);
- return 0;
-err:
- while (j--) {
- tpu_addr = data->mappings[j].virt_address;
- size = CONFIG_TO_SIZE(data->mappings[j].image_config_value);
- edgetpu_mmu_remove_translation(etdev, tpu_addr, size, EDGETPU_CONTEXT_KCI);
- }
- data->num_mapping = 0;
- memunmap(image_vaddr);
- return ret;
-}
-
-static void
-janeiro_firmware_teardown_buffer(struct edgetpu_firmware *et_fw,
- struct edgetpu_firmware_buffer *fw_buf)
-{
-}
-
-static int janeiro_firmware_prepare_run(struct edgetpu_firmware *et_fw,
- struct edgetpu_firmware_buffer *fw_buf)
-{
- struct edgetpu_dev *etdev = et_fw->etdev;
- struct janeiro_platform_dev *edgetpu_pdev =
- container_of(etdev, struct janeiro_platform_dev, edgetpu_dev);
- dma_addr_t fw_dma_addr;
- int ret = 0;
-
- fw_dma_addr = edgetpu_pdev->fw_region_paddr;
-
- //TODO: enable as and when sysmmu started working correctly on hybrid
- //platform
-#if 0
- /* Clear Substream ID (aka SCID) for instruction remapped addresses */
- u32 sec_reg = edgetpu_dev_read_32(
- etdev, EDGETPU_REG_INSTRUCTION_REMAP_SECURITY);
- sec_reg &= ~(0x0F << 16);
- edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_SECURITY,
- sec_reg);
-
- /* Clear Substream ID (aka SCID) for all other addresses */
- sec_reg = edgetpu_dev_read_32(etdev, EDGETPU_REG_SECURITY);
- sec_reg &= ~(0x0F << 16);
- edgetpu_dev_write_32(etdev, EDGETPU_REG_SECURITY, sec_reg);
-// dma_sync_single_for_device(etdev->dev, fw_dma_addr,
-// fw_buf->used_size - JANEIRO_FW_HEADER_SIZE,
-// DMA_TO_DEVICE);
-#endif
- tpu_cpu_reset(etdev, 1);
-
- /* Reset KCI mailbox before starting f/w, don't process anything old.*/
- edgetpu_mailbox_reset(etdev->kci->mailbox);
-
- tpu_cpu_reset(etdev, 0);
- //TODO: cleanup
- return ret;
-}
-
-static const struct edgetpu_firmware_chip_data janeiro_firmware_chip_data = {
- .default_firmware_name = EDGETPU_DEFAULT_FIRMWARE_NAME,
- .after_create = janeiro_firmware_after_create,
- .before_destroy = janeiro_firmware_before_destroy,
- .alloc_buffer = janeiro_firmware_alloc_buffer,
- .free_buffer = janeiro_firmware_free_buffer,
- .setup_buffer = janeiro_firmware_setup_buffer,
- .teardown_buffer = janeiro_firmware_teardown_buffer,
- .prepare_run = janeiro_firmware_prepare_run,
-};
-
-int mobile_edgetpu_firmware_create(struct edgetpu_dev *etdev)
-{
- return edgetpu_firmware_create(etdev, &janeiro_firmware_chip_data);
-}
-
-void mobile_edgetpu_firmware_destroy(struct edgetpu_dev *etdev)
-{
- edgetpu_firmware_destroy(etdev);
-}
-
-unsigned long edgetpu_chip_firmware_iova(struct edgetpu_dev *etdev)
-{
- /*
- * There is no IOVA in Janeiro, since firmware the IOMMU is
- * bypassed and the only translation in effect is the one
- * done by instruction remap registers
- */
- return EDGETPU_INSTRUCTION_REMAP_BASE;
-}
+#include "mobile-firmware.c"
diff --git a/drivers/edgetpu/janeiro-platform.c b/drivers/edgetpu/janeiro-platform.c
index 5708a5d..9ca3ff3 100644
--- a/drivers/edgetpu/janeiro-platform.c
+++ b/drivers/edgetpu/janeiro-platform.c
@@ -6,22 +6,18 @@
*/
#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/iopoll.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/platform_device.h>
#include "edgetpu-config.h"
-#include "edgetpu-debug-dump.h"
-#include "edgetpu-firmware.h"
#include "edgetpu-internal.h"
-#include "edgetpu-iremap-pool.h"
-#include "edgetpu-mmu.h"
-#include "edgetpu-telemetry.h"
+#include "edgetpu-mobile-platform.h"
#include "janeiro-platform.h"
-#include "mobile-firmware.h"
-#include "mobile-pm.h"
+
+#include "edgetpu-mobile-platform.c"
static const struct of_device_id edgetpu_of_match[] = {
/* TODO(b/190677977): remove */
@@ -32,117 +28,6 @@ static const struct of_device_id edgetpu_of_match[] = {
MODULE_DEVICE_TABLE(of, edgetpu_of_match);
-#define EDGETPU_POOL_MEM_OFFSET (EDGETPU_TELEMETRY_BUFFER_SIZE * 2)
-
-static void janeiro_get_telemetry_mem(struct janeiro_platform_dev *etpdev,
- enum edgetpu_telemetry_type type,
- struct edgetpu_coherent_mem *mem)
-{
- int offset = type == EDGETPU_TELEMETRY_TRACE ?
- EDGETPU_TELEMETRY_BUFFER_SIZE :
- 0;
- mem->vaddr = etpdev->shared_mem_vaddr + offset;
- mem->dma_addr = EDGETPU_REMAPPED_DATA_ADDR + offset;
- mem->tpu_addr = EDGETPU_REMAPPED_DATA_ADDR + offset;
- mem->host_addr = 0;
- mem->size = EDGETPU_TELEMETRY_BUFFER_SIZE;
-}
-
-static int janeiro_platform_setup_fw_region(struct janeiro_platform_dev *etpdev)
-{
- struct edgetpu_dev *etdev = &etpdev->edgetpu_dev;
- struct device *dev = etdev->dev;
- struct resource r;
- struct device_node *np;
- int err;
- size_t region_map_size =
- EDGETPU_FW_SIZE_MAX + EDGETPU_REMAPPED_DATA_SIZE;
-
- np = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (!np) {
- dev_err(dev, "No memory region for firmware\n");
- return -ENODEV;
- }
-
- err = of_address_to_resource(np, 0, &r);
- of_node_put(np);
- if (err) {
- dev_err(dev, "No memory address assigned to firmware region\n");
- return err;
- }
-
- if (resource_size(&r) < region_map_size) {
- dev_err(dev,
- "Memory region for firmware too small (%zu bytes needed, got %llu)\n",
- region_map_size, resource_size(&r));
- return -ENOSPC;
- }
-
- etpdev->fw_region_paddr = r.start;
- etpdev->fw_region_size = EDGETPU_FW_SIZE_MAX;
-
- etpdev->shared_mem_vaddr =
- memremap(r.start + EDGETPU_REMAPPED_DATA_OFFSET,
- EDGETPU_REMAPPED_DATA_SIZE, MEMREMAP_WC);
- if (!etpdev->shared_mem_vaddr) {
- dev_err(dev, "Shared memory remap failed\n");
- return -EINVAL;
- }
- etpdev->shared_mem_paddr = r.start + EDGETPU_REMAPPED_DATA_OFFSET;
-
- return 0;
-}
-
-static void
-janeiro_platform_cleanup_fw_region(struct janeiro_platform_dev *etpdev)
-{
- if (!etpdev->shared_mem_vaddr)
- return;
- memunmap(etpdev->shared_mem_vaddr);
- etpdev->shared_mem_vaddr = NULL;
-}
-
-int edgetpu_chip_setup_mmu(struct edgetpu_dev *etdev)
-{
- int ret;
-
- ret = edgetpu_mmu_attach(etdev, NULL);
- if (ret)
- dev_err(etdev->dev, "failed to attach IOMMU: %d\n", ret);
- return ret;
-}
-
-void edgetpu_chip_remove_mmu(struct edgetpu_dev *etdev)
-{
- edgetpu_mmu_detach(etdev);
-}
-
-static int janeiro_parse_ssmt(struct janeiro_platform_dev *etpdev)
-{
- struct edgetpu_dev *etdev = &etpdev->edgetpu_dev;
- struct platform_device *pdev = to_platform_device(etdev->dev);
- struct resource *res;
- int rc;
- void __iomem *ssmt_base;
-
- /* TODO(b/197301774): Remove when GSA configure SSMT */
- return -EINVAL;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ssmt");
- if (!res) {
- etdev_warn(etdev, "Failed to find SSMT register base");
- return -EINVAL;
- }
- ssmt_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(ssmt_base)) {
- rc = PTR_ERR(ssmt_base);
- etdev_warn(etdev, "Failed to map SSMT register base: %d\n", rc);
- return rc;
- }
- etpdev->ssmt_base = ssmt_base;
- return 0;
-}
-
/*
* Set shareability for enabling IO coherency in Janeiro
*/
@@ -162,207 +47,49 @@ static int janeiro_mmu_set_shareability(struct device *dev, u32 reg_base)
return 0;
}
-static int janeiro_parse_dt(struct device *dev)
+static int janeiro_parse_set_dt_property(struct device *dev)
{
int ret;
u32 reg;
- if (of_find_property(dev->of_node, "edgetpu,shareability", NULL)) {
- ret = of_property_read_u32_index(dev->of_node, "edgetpu,shareability", 0, &reg);
- if (ret)
- return ret;
- } else {
- /*
- * TODO(b/193593081): Remove compatibility code
- * Fallback for older driver versions until DT property support is
- * widely adopted.
- */
- reg = EDGETPU_SYSREG_TPU_BASE;
- }
-
+ if (!of_find_property(dev->of_node, "edgetpu,shareability", NULL))
+ return -ENODEV;
+ ret = of_property_read_u32_index(dev->of_node, "edgetpu,shareability", 0, &reg);
+ if (ret)
+ return ret;
return janeiro_mmu_set_shareability(dev, reg);
}
-static int edgetpu_platform_probe(struct platform_device *pdev)
+static int janeiro_platform_after_probe(struct edgetpu_mobile_platform_dev *etmdev)
{
- struct device *dev = &pdev->dev;
- struct janeiro_platform_dev *edgetpu_pdev;
- struct resource *r;
- struct edgetpu_mapped_resource regs;
- int ret, i;
- struct edgetpu_iface_params iface_params[] = {
- /* Default interface */
- { .name = NULL },
- /* Common name for SoC embedded devices */
- { .name = "edgetpu-soc" },
- };
-
- edgetpu_pdev = devm_kzalloc(dev, sizeof(*edgetpu_pdev), GFP_KERNEL);
- if (!edgetpu_pdev)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, &edgetpu_pdev->edgetpu_dev);
- edgetpu_pdev->edgetpu_dev.dev = dev;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (IS_ERR_OR_NULL(r)) {
- dev_err(dev, "failed to get memory resource\n");
- return -ENODEV;
- }
-
- regs.phys = r->start;
- regs.size = resource_size(r);
-
- regs.mem = devm_ioremap_resource(dev, r);
- if (IS_ERR_OR_NULL(regs.mem)) {
- dev_err(dev, "failed to map registers\n");
- return -ENODEV;
- }
-
- ret = mobile_pm_create(&edgetpu_pdev->edgetpu_dev);
- if (ret) {
- dev_err(dev, "Failed to initialize PM interface (%d)\n", ret);
- return ret;
- }
-
- ret = janeiro_platform_setup_fw_region(edgetpu_pdev);
- if (ret) {
- dev_err(dev, "%s setup fw regions failed: %d\n", DRIVER_NAME,
- ret);
- goto out;
- }
-
- ret = edgetpu_iremap_pool_create(
- &edgetpu_pdev->edgetpu_dev,
- /* Base virtual address (kernel address space) */
- edgetpu_pdev->shared_mem_vaddr + EDGETPU_POOL_MEM_OFFSET,
- /* Base DMA address */
- EDGETPU_REMAPPED_DATA_ADDR + EDGETPU_POOL_MEM_OFFSET,
- /* Base TPU address */
- EDGETPU_REMAPPED_DATA_ADDR + EDGETPU_POOL_MEM_OFFSET,
- /* Base physical address */
- edgetpu_pdev->shared_mem_paddr + EDGETPU_POOL_MEM_OFFSET,
- /* Size */
- EDGETPU_REMAPPED_DATA_SIZE - EDGETPU_POOL_MEM_OFFSET,
- /* Granularity */
- PAGE_SIZE);
- if (ret) {
- dev_err(dev,
- "%s failed to initialize remapped memory pool: %d\n",
- DRIVER_NAME, ret);
- goto out_cleanup_fw;
- }
-
- edgetpu_pdev->edgetpu_dev.mcp_id = -1;
- edgetpu_pdev->edgetpu_dev.mcp_die_index = 0;
-
- for (i = 0; i < EDGETPU_NCONTEXTS; i++)
- edgetpu_pdev->irq[i] = platform_get_irq(pdev, i);
-
- ret = janeiro_parse_dt(dev);
-
- if (ret)
- dev_warn(dev, "%s failed to enable shareability: %d\n",
- DRIVER_NAME, ret);
-
- ret = edgetpu_device_add(&edgetpu_pdev->edgetpu_dev, &regs, iface_params,
- ARRAY_SIZE(iface_params));
-
- if (ret) {
- dev_err(dev, "%s edgetpu setup failed: %d\n", DRIVER_NAME, ret);
- goto out_destroy_iremap;
- }
- for (i = 0; i < EDGETPU_NCONTEXTS; i++) {
- if (edgetpu_pdev->irq[i] >= 0)
- ret = edgetpu_register_irq(&edgetpu_pdev->edgetpu_dev,
- edgetpu_pdev->irq[i]);
- if (ret)
- break;
- }
- if (ret) {
- while (i-- > 0) {
- edgetpu_unregister_irq(&edgetpu_pdev->edgetpu_dev,
- edgetpu_pdev->irq[i]);
- }
- dev_err(dev, "%s edgetpu irq registration failed: %d\n",
- DRIVER_NAME, ret);
- goto out_remove_device;
- }
-
- ret = janeiro_parse_ssmt(edgetpu_pdev);
- if (ret)
- dev_warn(
- dev,
- "SSMT setup failed (%d). Context isolation not enforced\n",
- ret);
-
- janeiro_get_telemetry_mem(edgetpu_pdev, EDGETPU_TELEMETRY_LOG,
- &edgetpu_pdev->log_mem);
- janeiro_get_telemetry_mem(edgetpu_pdev, EDGETPU_TELEMETRY_TRACE,
- &edgetpu_pdev->trace_mem);
+ int ret;
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
- ret = edgetpu_telemetry_init(&edgetpu_pdev->edgetpu_dev,
- &edgetpu_pdev->log_mem,
- &edgetpu_pdev->trace_mem);
+ ret = janeiro_parse_set_dt_property(etdev->dev);
if (ret)
- goto out_remove_device;
+ dev_warn(etdev->dev, "failed to enable shareability: %d", ret);
- ret = mobile_edgetpu_firmware_create(&edgetpu_pdev->edgetpu_dev);
- if (ret) {
- dev_err(dev, "%s initialize firmware downloader failed: %d\n",
- DRIVER_NAME, ret);
- goto out_tel_exit;
- }
+ return 0;
+}
- dev_info(dev, "%s edgetpu initialized. Build: %s\n",
- edgetpu_pdev->edgetpu_dev.dev_name, GIT_REPO_TAG);
+static int edgetpu_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct janeiro_platform_dev *jpdev;
+ struct edgetpu_mobile_platform_dev *etmdev;
- edgetpu_pm_shutdown(&edgetpu_pdev->edgetpu_dev, false);
-out:
- dev_dbg(dev, "Probe finished\n");
+ jpdev = devm_kzalloc(dev, sizeof(*jpdev), GFP_KERNEL);
+ if (!jpdev)
+ return -ENOMEM;
- return 0;
-out_tel_exit:
- edgetpu_telemetry_exit(&edgetpu_pdev->edgetpu_dev);
-out_remove_device:
- edgetpu_device_remove(&edgetpu_pdev->edgetpu_dev);
-out_destroy_iremap:
- edgetpu_iremap_pool_destroy(&edgetpu_pdev->edgetpu_dev);
-out_cleanup_fw:
- janeiro_platform_cleanup_fw_region(edgetpu_pdev);
- dev_dbg(dev, "Probe finished with error %d, powering down\n", ret);
- return ret;
+ etmdev = &jpdev->mobile_dev;
+ etmdev->after_probe = janeiro_platform_after_probe;
+ return edgetpu_mobile_platform_probe(pdev, etmdev);
}
static int edgetpu_platform_remove(struct platform_device *pdev)
{
- int i;
- struct edgetpu_dev *etdev = platform_get_drvdata(pdev);
- struct janeiro_platform_dev *janeiro_pdev = to_janeiro_dev(etdev);
-
- /* TODO(b/189906347): Use edgetpu_device_remove() for cleanup after
- * having GSA/TZ support.
- */
- etdev->on_exit = true;
- edgetpu_pm_get(etdev->pm);
- for (i = 0; i < EDGETPU_NCONTEXTS; i++) {
- if (janeiro_pdev->irq[i] >= 0)
- edgetpu_unregister_irq(etdev, janeiro_pdev->irq[i]);
- }
- mobile_edgetpu_firmware_destroy(etdev);
- edgetpu_telemetry_exit(etdev);
- edgetpu_chip_exit(etdev);
- edgetpu_debug_dump_exit(etdev);
- edgetpu_mailbox_remove_all(etdev->mailbox_manager);
- edgetpu_pm_put(etdev->pm);
- edgetpu_pm_shutdown(etdev, true);
- edgetpu_usage_stats_exit(etdev);
- edgetpu_chip_remove_mmu(etdev);
- edgetpu_fs_remove(etdev);
- edgetpu_iremap_pool_destroy(etdev);
- janeiro_platform_cleanup_fw_region(janeiro_pdev);
- mobile_pm_destroy(etdev);
- return 0;
+ return edgetpu_mobile_platform_remove(pdev);
}
static struct platform_driver edgetpu_platform_driver = {
@@ -390,7 +117,7 @@ static void __exit edgetpu_platform_exit(void)
edgetpu_exit();
}
-MODULE_DESCRIPTION("Janeiro Edge TPU platform driver");
+MODULE_DESCRIPTION("Google Edge TPU platform driver");
MODULE_LICENSE("GPL v2");
module_init(edgetpu_platform_init);
module_exit(edgetpu_platform_exit);
diff --git a/drivers/edgetpu/janeiro-platform.h b/drivers/edgetpu/janeiro-platform.h
index 51eb8d6..5ffe734 100644
--- a/drivers/edgetpu/janeiro-platform.h
+++ b/drivers/edgetpu/janeiro-platform.h
@@ -4,50 +4,18 @@
*
* Copyright (C) 2020 Google, Inc.
*/
+
#ifndef __JANEIRO_PLATFORM_H__
#define __JANEIRO_PLATFORM_H__
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-#include <linux/types.h>
-
-#if IS_ENABLED(CONFIG_GOOGLE_BCL)
-#include <soc/google/bcl.h>
-#endif
-
#include "edgetpu-internal.h"
+#include "edgetpu-mobile-platform.h"
-#define to_janeiro_dev(etdev) \
- container_of(etdev, struct janeiro_platform_dev, edgetpu_dev)
-
-// TODO(b/176881607): merge with abrolhos
-struct janeiro_platform_pwr {
- struct mutex state_lock;
- u64 min_state;
- u64 requested_state;
-};
+#define to_janeiro_dev(etdev) \
+ container_of((to_mobile_dev(etdev)), struct janeiro_platform_dev, mobile_dev)
struct janeiro_platform_dev {
- struct edgetpu_dev edgetpu_dev;
- struct janeiro_platform_pwr platform_pwr;
- int irq[EDGETPU_NCONTEXTS];
- phys_addr_t fw_region_paddr;
- void *fw_region_vaddr;
- size_t fw_region_size;
- void *shared_mem_vaddr;
- phys_addr_t shared_mem_paddr;
- size_t shared_mem_size;
- phys_addr_t csr_paddr;
- dma_addr_t csr_iova;
- size_t csr_size;
- struct device *gsa_dev;
- void __iomem *ssmt_base;
- struct edgetpu_coherent_mem log_mem;
- struct edgetpu_coherent_mem trace_mem;
-#if IS_ENABLED(CONFIG_GOOGLE_BCL)
- struct bcl_device *bcl_dev;
-#endif
+ struct edgetpu_mobile_platform_dev mobile_dev;
};
#endif /* __JANEIRO_PLATFORM_H__ */
diff --git a/drivers/edgetpu/janeiro-pm.c b/drivers/edgetpu/janeiro-pm.c
index 700601e..681f58d 100644
--- a/drivers/edgetpu/janeiro-pm.c
+++ b/drivers/edgetpu/janeiro-pm.c
@@ -5,26 +5,17 @@
* Copyright (C) 2020 Google, Inc.
*/
-#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/pm_runtime.h>
-#include <linux/version.h>
-#if IS_ENABLED(CONFIG_GOOGLE_BCL)
-#include <soc/google/bcl.h>
-#endif
-
-#include "edgetpu-firmware.h"
+#include "edgetpu-config.h"
#include "edgetpu-internal.h"
-#include "edgetpu-kci.h"
-#include "edgetpu-mailbox.h"
-#include "edgetpu-pm.h"
-#include "janeiro-platform.h"
+#include "edgetpu-mobile-platform.h"
#include "mobile-pm.h"
-#include "edgetpu-pm.c"
+#define TPU_DEFAULT_POWER_STATE TPU_ACTIVE_NOM
+
+#include "mobile-pm.c"
#define SHUTDOWN_DELAY_US_MIN 20
#define SHUTDOWN_DELAY_US_MAX 20
@@ -32,200 +23,6 @@
#define BOOTUP_DELAY_US_MAX 250
#define SHUTDOWN_MAX_DELAY_COUNT 50
-/* Default power state */
-static int power_state = TPU_ACTIVE_NOM;
-
-module_param(power_state, int, 0660);
-
-static struct dentry *janeiro_pwr_debugfs_dir;
-
-enum edgetpu_pwr_state edgetpu_active_states[EDGETPU_NUM_STATES] = {
- TPU_ACTIVE_UUD,
- TPU_ACTIVE_SUD,
- TPU_ACTIVE_UD,
- TPU_ACTIVE_NOM,
-};
-
-uint32_t *edgetpu_states_display = edgetpu_active_states;
-
-static int janeiro_pwr_state_init(struct device *dev)
-{
- int ret;
- int curr_state;
-
- pm_runtime_enable(dev);
- curr_state = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 0);
-
- if (curr_state > TPU_OFF) {
- ret = pm_runtime_get_sync(dev);
- if (ret) {
- dev_err(dev, "%s: pm_runtime_get_sync err: %d\n",
- __func__, ret);
- return ret;
- }
- }
- ret = exynos_acpm_set_init_freq(TPU_ACPM_DOMAIN, curr_state);
- if (ret) {
- dev_err(dev, "error initializing tpu ACPM freq: %d\n", ret);
- if (curr_state > TPU_OFF)
- pm_runtime_put_sync(dev);
- return ret;
- }
- return ret;
-}
-
-static int janeiro_pwr_state_set_locked(void *data, u64 val)
-{
- int ret;
- int curr_state;
- int timeout_cnt = 0;
- struct edgetpu_dev *etdev = (typeof(etdev))data;
- struct device *dev = etdev->dev;
-
- curr_state = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 0);
-
- dev_dbg(dev, "Power state %d -> %llu\n", curr_state, val);
-
- if (curr_state == TPU_OFF && val > TPU_OFF) {
- ret = pm_runtime_get_sync(dev);
- if (ret) {
- dev_err(dev, "%s: pm_runtime_get_sync err: %d\n",
- __func__, ret);
- return ret;
- }
- }
-
- /* TPU_OFF is invalid state */
- if (val != TPU_OFF) {
- ret = exynos_acpm_set_rate(TPU_ACPM_DOMAIN, (unsigned long)val);
- if (ret) {
- dev_err(dev, "error setting tpu power state: %d\n", ret);
- pm_runtime_put_sync(dev);
- return ret;
- }
- }
-
- if (curr_state != TPU_OFF && val == TPU_OFF) {
- ret = pm_runtime_put_sync(dev);
- if (ret) {
- dev_err(dev, "%s: pm_runtime_put_sync returned %d\n",
- __func__, ret);
- return ret;
- }
- do {
- /* Delay 20us per retry till blk shutdown finished */
- usleep_range(SHUTDOWN_DELAY_US_MIN, SHUTDOWN_DELAY_US_MAX);
- /* Only poll for BLK status instead of CLK rate */
- curr_state = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 1);
- if (!curr_state)
- break;
- timeout_cnt++;
- } while (timeout_cnt < SHUTDOWN_MAX_DELAY_COUNT);
- if (timeout_cnt == SHUTDOWN_MAX_DELAY_COUNT)
- dev_warn(dev, "%s: blk_shutdown timeout\n", __func__);
- }
-
- return ret;
-}
-
-static int janeiro_pwr_state_get_locked(void *data, u64 *val)
-{
- struct edgetpu_dev *etdev = (typeof(etdev))data;
- struct device *dev = etdev->dev;
-
- *val = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 0);
- dev_dbg(dev, "current tpu power state: %llu\n", *val);
-
- return 0;
-}
-
-static int janeiro_pwr_state_set(void *data, u64 val)
-{
- struct edgetpu_dev *etdev = (typeof(etdev))data;
- struct janeiro_platform_dev *edgetpu_pdev = to_janeiro_dev(etdev);
- struct janeiro_platform_pwr *platform_pwr = &edgetpu_pdev->platform_pwr;
- int ret = 0;
-
- mutex_lock(&platform_pwr->state_lock);
- platform_pwr->requested_state = val;
- if (val >= platform_pwr->min_state)
- ret = janeiro_pwr_state_set_locked(etdev, val);
- mutex_unlock(&platform_pwr->state_lock);
- return ret;
-}
-
-static int janeiro_pwr_state_get(void *data, u64 *val)
-{
- struct edgetpu_dev *etdev = (typeof(etdev))data;
- struct janeiro_platform_dev *edgetpu_pdev = to_janeiro_dev(etdev);
- struct janeiro_platform_pwr *platform_pwr = &edgetpu_pdev->platform_pwr;
- int ret;
-
- mutex_lock(&platform_pwr->state_lock);
- ret = janeiro_pwr_state_get_locked(etdev, val);
- mutex_unlock(&platform_pwr->state_lock);
- return ret;
-}
-
-static int janeiro_min_pwr_state_set(void *data, u64 val)
-{
- struct edgetpu_dev *etdev = (typeof(etdev))data;
- struct janeiro_platform_dev *edgetpu_pdev = to_janeiro_dev(etdev);
- struct janeiro_platform_pwr *platform_pwr = &edgetpu_pdev->platform_pwr;
- int ret = 0;
-
- mutex_lock(&platform_pwr->state_lock);
- platform_pwr->min_state = val;
- if (val >= platform_pwr->requested_state)
- ret = janeiro_pwr_state_set_locked(etdev, val);
- mutex_unlock(&platform_pwr->state_lock);
- return ret;
-}
-
-static int janeiro_min_pwr_state_get(void *data, u64 *val)
-{
- struct edgetpu_dev *etdev = (typeof(etdev))data;
- struct janeiro_platform_dev *edgetpu_pdev = to_janeiro_dev(etdev);
- struct janeiro_platform_pwr *platform_pwr = &edgetpu_pdev->platform_pwr;
-
- mutex_lock(&platform_pwr->state_lock);
- *val = platform_pwr->min_state;
- mutex_unlock(&platform_pwr->state_lock);
- return 0;
-}
-
-DEFINE_DEBUGFS_ATTRIBUTE(fops_tpu_pwr_state, janeiro_pwr_state_get,
- janeiro_pwr_state_set, "%llu\n");
-
-DEFINE_DEBUGFS_ATTRIBUTE(fops_tpu_min_pwr_state, janeiro_min_pwr_state_get,
- janeiro_min_pwr_state_set, "%llu\n");
-
-static int janeiro_get_initial_pwr_state(struct device *dev)
-{
- switch (power_state) {
- case TPU_ACTIVE_UUD:
- case TPU_ACTIVE_SUD:
- case TPU_ACTIVE_UD:
- case TPU_ACTIVE_NOM:
- dev_info(dev, "Initial power state: %d\n", power_state);
- break;
- case TPU_OFF:
- dev_warn(dev, "Power state %d prevents control core booting",
- power_state);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
- fallthrough;
-#endif
- default:
- dev_warn(dev, "Power state %d is invalid\n", power_state);
- dev_warn(dev, "defaulting to active nominal\n");
- power_state = TPU_ACTIVE_NOM;
- break;
- }
- return power_state;
-}
-
-static void janeiro_power_down(struct edgetpu_pm *etpm);
-
#define EDGETPU_PSM0_CFG 0x1c1880
#define EDGETPU_PSM0_START 0x1c1884
#define EDGETPU_PSM0_STATUS 0x1c1888
@@ -234,21 +31,39 @@ static void janeiro_power_down(struct edgetpu_pm *etpm);
#define EDGETPU_PSM1_STATUS 0x1c2888
#define EDGETPU_LPM_CHANGE_TIMEOUT 30000
-static int janeiro_set_lpm(struct edgetpu_dev *etdev)
+static void janeiro_lpm_down(struct edgetpu_dev *etdev)
+{
+ int timeout_cnt = 0;
+ u32 val;
+
+ do {
+ /* Manually delay 20us per retry till LPM shutdown finished */
+ usleep_range(SHUTDOWN_DELAY_US_MIN, SHUTDOWN_DELAY_US_MAX);
+ val = edgetpu_dev_read_32_sync(etdev, EDGETPU_REG_LPM_CONTROL);
+ if ((val & 0x1000) || (val == 0))
+ break;
+ timeout_cnt++;
+ } while (timeout_cnt < SHUTDOWN_MAX_DELAY_COUNT);
+ if (timeout_cnt == SHUTDOWN_MAX_DELAY_COUNT)
+ // Log the issue then continue to perform the shutdown forcefully.
+ etdev_warn(etdev, "LPM shutdown failure, continuing BLK shutdown\n");
+}
+
+static int janeiro_lpm_up(struct edgetpu_dev *etdev)
{
int ret;
u32 val;
edgetpu_dev_write_32_sync(etdev, EDGETPU_PSM0_START, 1);
- ret = readl_poll_timeout(etdev->regs.mem + EDGETPU_PSM0_STATUS, val,
- val & 0x80, 20, EDGETPU_LPM_CHANGE_TIMEOUT);
+ ret = readl_poll_timeout(etdev->regs.mem + EDGETPU_PSM0_STATUS, val, val & 0x80, 20,
+ EDGETPU_LPM_CHANGE_TIMEOUT);
if (ret) {
etdev_err(etdev, "Set LPM0 failed: %d\n", ret);
return ret;
}
edgetpu_dev_write_32_sync(etdev, EDGETPU_PSM1_START, 1);
- ret = readl_poll_timeout(etdev->regs.mem + EDGETPU_PSM1_STATUS, val,
- val & 0x80, 20, EDGETPU_LPM_CHANGE_TIMEOUT);
+ ret = readl_poll_timeout(etdev->regs.mem + EDGETPU_PSM1_STATUS, val, val & 0x80, 20,
+ EDGETPU_LPM_CHANGE_TIMEOUT);
if (ret) {
etdev_err(etdev, "Set LPM1 failed: %d\n", ret);
return ret;
@@ -260,194 +75,50 @@ static int janeiro_set_lpm(struct edgetpu_dev *etdev)
return 0;
}
-static int janeiro_power_up(struct edgetpu_pm *etpm)
-{
- struct edgetpu_dev *etdev = etpm->etdev;
-#if IS_ENABLED(CONFIG_GOOGLE_BCL)
- struct janeiro_platform_dev *edgetpu_pdev = to_janeiro_dev(etdev);
-#endif
- int ret = 0;
-
- ret = janeiro_pwr_state_set(
- etpm->etdev, janeiro_get_initial_pwr_state(etdev->dev));
-
- etdev_info(etpm->etdev, "Powering up\n");
-
- if (ret)
- return ret;
-
- /* Delay 100us to make sure LPM is accessible */
- usleep_range(BOOTUP_DELAY_US_MIN, BOOTUP_DELAY_US_MAX);
- janeiro_set_lpm(etdev);
-
- edgetpu_chip_init(etdev);
-
- if (etdev->kci) {
- etdev_dbg(etdev, "Resetting KCI\n");
- edgetpu_kci_reinit(etdev->kci);
- }
- if (etdev->mailbox_manager) {
- etdev_dbg(etdev, "Resetting VII mailboxes\n");
- edgetpu_mailbox_reset_vii(etdev->mailbox_manager);
- }
-
- if (!etdev->firmware || etdev->on_exit)
- return 0;
-
- /*
- * Why this function uses edgetpu_firmware_*_locked functions without explicitly holding
- * edgetpu_firmware_lock:
- *
- * edgetpu_pm_get() is called in two scenarios - one is when the firmware loading is
- * attempting, another one is when the user-space clients need the device be powered
- * (usually through acquiring the wakelock).
- *
- * For the first scenario edgetpu_firmware_is_loading() below shall return true.
- * For the second scenario we are indeed called without holding the firmware lock, but the
- * firmware loading procedures (i.e. the first scenario) always call edgetpu_pm_get() before
- * changing the firmware state, and edgetpu_pm_get() is blocked until this function
- * finishes. In short, we are protected by the PM lock.
- */
-
- if (edgetpu_firmware_is_loading(etdev))
- return 0;
-
- /* attempt firmware run */
- switch (edgetpu_firmware_status_locked(etdev)) {
- case FW_VALID:
- ret = edgetpu_firmware_restart_locked(etdev, false);
- break;
- case FW_INVALID:
- ret = edgetpu_firmware_run_default_locked(etdev);
- break;
- default:
- break;
- }
-
- if (ret) {
- janeiro_power_down(etpm);
- } else {
-#if IS_ENABLED(CONFIG_GOOGLE_BCL)
- if (!edgetpu_pdev->bcl_dev)
- edgetpu_pdev->bcl_dev = google_retrieve_bcl_handle();
- if (edgetpu_pdev->bcl_dev)
- google_init_tpu_ratio(edgetpu_pdev->bcl_dev);
-#endif
- }
-
- return ret;
-}
-
-static void
-janeiro_pm_shutdown_firmware(struct janeiro_platform_dev *etpdev,
- struct edgetpu_dev *etdev)
-{
- int ret;
-
- ret = edgetpu_kci_shutdown(etdev->kci);
- if (ret) {
- etdev_err(etdev, "firmware shutdown failed: %d",
- ret);
- return;
- }
-}
-
-static void janeiro_power_down(struct edgetpu_pm *etpm)
+static void janeiro_block_down(struct edgetpu_dev *etdev)
{
- struct edgetpu_dev *etdev = etpm->etdev;
- struct janeiro_platform_dev *edgetpu_pdev = to_janeiro_dev(etdev);
- u64 val;
int timeout_cnt = 0;
+ int curr_state;
- etdev_info(etdev, "Powering down\n");
-
- if (janeiro_pwr_state_get(etdev, &val)) {
- etdev_warn(etdev, "Failed to read current power state\n");
- val = TPU_ACTIVE_NOM;
- }
- if (val == TPU_OFF) {
- etdev_dbg(etdev, "Device already off, skipping shutdown\n");
- return;
- }
-
- if (etdev->kci && edgetpu_firmware_status_locked(etdev) == FW_VALID) {
- /* Update usage stats before we power off fw. */
- edgetpu_kci_update_usage_locked(etdev);
- janeiro_pm_shutdown_firmware(edgetpu_pdev, etdev);
- edgetpu_kci_cancel_work_queues(etdev->kci);
- }
do {
- /* Manually delay 20us per retry till LPM shutdown finished */
+ /* Delay 20us per retry till blk shutdown finished */
usleep_range(SHUTDOWN_DELAY_US_MIN, SHUTDOWN_DELAY_US_MAX);
- val = edgetpu_dev_read_32_sync(etdev, EDGETPU_REG_LPM_CONTROL);
- if ((val & 0x1000) || (val == 0))
+ /* Only poll for BLK status instead of CLK rate */
+ curr_state = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 1);
+ if (!curr_state)
break;
timeout_cnt++;
} while (timeout_cnt < SHUTDOWN_MAX_DELAY_COUNT);
if (timeout_cnt == SHUTDOWN_MAX_DELAY_COUNT)
- // Log the issue then continue to perform the shutdown forcefully.
- etdev_err(etdev, "LPM shutdown failure, performing BLK shutdown\n");
- janeiro_pwr_state_set(etdev, TPU_OFF);
+ etdev_warn(etdev, "%s: blk_shutdown timeout\n", __func__);
}
-static int janeiro_pm_after_create(struct edgetpu_pm *etpm)
+static void janeiro_firmware_down(struct edgetpu_dev *etdev)
{
int ret;
- struct edgetpu_dev *etdev = etpm->etdev;
- struct janeiro_platform_dev *edgetpu_pdev = to_janeiro_dev(etdev);
- struct device *dev = etdev->dev;
-
- ret = janeiro_pwr_state_init(dev);
- if (ret)
- return ret;
- mutex_init(&edgetpu_pdev->platform_pwr.state_lock);
-
- ret = janeiro_pwr_state_set(etdev,
- janeiro_get_initial_pwr_state(dev));
- if (ret)
- return ret;
- janeiro_pwr_debugfs_dir =
- debugfs_create_dir("power", edgetpu_fs_debugfs_dir());
- if (IS_ERR_OR_NULL(janeiro_pwr_debugfs_dir)) {
- etdev_warn(etdev, "Failed to create debug FS power");
- /* don't fail the procedure on debug FS creation fails */
- return 0;
+ ret = edgetpu_kci_shutdown(etdev->kci);
+ if (ret) {
+ etdev_err(etdev, "firmware shutdown failed: %d", ret);
+ return;
}
- debugfs_create_file("state", 0660, janeiro_pwr_debugfs_dir, etdev,
- &fops_tpu_pwr_state);
- debugfs_create_file("min_state", 0660, janeiro_pwr_debugfs_dir, etdev,
- &fops_tpu_min_pwr_state);
- return 0;
}
-static void janeiro_pm_before_destroy(struct edgetpu_pm *etpm)
+static int janeiro_acpm_set_rate(unsigned int id, unsigned long rate)
{
- debugfs_remove_recursive(janeiro_pwr_debugfs_dir);
- pm_runtime_disable(etpm->etdev->dev);
+ return exynos_acpm_set_rate(id, rate);
}
-static struct edgetpu_pm_handlers janeiro_pm_handlers = {
- .after_create = janeiro_pm_after_create,
- .before_destroy = janeiro_pm_before_destroy,
- .power_up = janeiro_power_up,
- .power_down = janeiro_power_down,
-};
-
-int mobile_pm_create(struct edgetpu_dev *etdev)
-{
- return edgetpu_pm_create(etdev, &janeiro_pm_handlers);
-}
-
-void mobile_pm_destroy(struct edgetpu_dev *etdev)
+int edgetpu_chip_pm_create(struct edgetpu_dev *etdev)
{
- edgetpu_pm_destroy(etdev);
-}
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
-void mobile_pm_set_pm_qos(struct edgetpu_dev *etdev, u32 pm_qos_val)
-{
-}
+ platform_pwr->lpm_up = janeiro_lpm_up;
+ platform_pwr->lpm_down = janeiro_lpm_down;
+ platform_pwr->block_down = janeiro_block_down;
+ platform_pwr->firmware_down = janeiro_firmware_down;
+ platform_pwr->acpm_set_rate = janeiro_acpm_set_rate;
-void mobile_pm_set_bts(struct edgetpu_dev *etdev, u32 bts_val)
-{
+ return mobile_pm_create(etdev);
}
diff --git a/drivers/edgetpu/janeiro/config-pwr-state.h b/drivers/edgetpu/janeiro/config-pwr-state.h
index cdea172..f30d82e 100644
--- a/drivers/edgetpu/janeiro/config-pwr-state.h
+++ b/drivers/edgetpu/janeiro/config-pwr-state.h
@@ -24,6 +24,8 @@ enum edgetpu_pwr_state {
TPU_ACTIVE_NOM = 1066000,
};
+#define MIN_ACTIVE_STATE TPU_ACTIVE_UUD
+
#define EDGETPU_NUM_STATES 4
extern enum edgetpu_pwr_state edgetpu_active_states[];
diff --git a/drivers/edgetpu/janeiro/config.h b/drivers/edgetpu/janeiro/config.h
index add13dc..a5adbef 100644
--- a/drivers/edgetpu/janeiro/config.h
+++ b/drivers/edgetpu/janeiro/config.h
@@ -13,6 +13,8 @@
#define EDGETPU_DEV_MAX 1
#define EDGETPU_HAS_MULTI_GROUPS
+/* 1 context per VII/group plus 1 for KCI */
+#define EDGETPU_NCONTEXTS 8
/* Max number of virtual context IDs that can be allocated for one device. */
#define EDGETPU_NUM_VCIDS 16
/* Reserved VCID that uses the extra partition. */
diff --git a/drivers/edgetpu/janeiro/csrs.h b/drivers/edgetpu/janeiro/csrs.h
index ec9598d..385a220 100644
--- a/drivers/edgetpu/janeiro/csrs.h
+++ b/drivers/edgetpu/janeiro/csrs.h
@@ -38,7 +38,6 @@ enum edgetpu_csrs {
};
/* SYSREG TPU */
-#define EDGETPU_SYSREG_TPU_BASE 0x1cc20000
#define EDGETPU_SYSREG_TPU_SHAREABILITY 0x700
#define SHAREABLE_WRITE (1 << 13)
#define SHAREABLE_READ (1 << 12)
diff --git a/drivers/edgetpu/mobile-debug-dump.c b/drivers/edgetpu/mobile-debug-dump.c
index 82dc5a2..3732fbb 100644
--- a/drivers/edgetpu/mobile-debug-dump.c
+++ b/drivers/edgetpu/mobile-debug-dump.c
@@ -7,6 +7,7 @@
*/
#include <linux/mutex.h>
+#include <linux/platform_data/sscoredump.h>
#include <linux/rbtree.h>
#include <linux/slab.h>
diff --git a/drivers/edgetpu/mobile-debug-dump.h b/drivers/edgetpu/mobile-debug-dump.h
index 105bd70..9eaf069 100644
--- a/drivers/edgetpu/mobile-debug-dump.h
+++ b/drivers/edgetpu/mobile-debug-dump.h
@@ -5,11 +5,10 @@
*
* Copyright (C) 2021 Google, Inc.
*/
+
#ifndef __MOBILE_DEBUG_DUMP_H__
#define __MOBILE_DEBUG_DUMP_H__
-#include <linux/platform_data/sscoredump.h>
-
#include "edgetpu-debug-dump.h"
struct mobile_sscd_info {
@@ -24,6 +23,8 @@ struct mobile_sscd_mappings_dump {
u64 size;
};
+struct sscd_segment;
+
/*
* Collects the mapping information of all the host mapping and dmabuf mapping buffers of all
* @groups as an array of struct mobile_sscd_mappings_dump and populates the @sscd_seg.
diff --git a/drivers/edgetpu/mobile-firmware.c b/drivers/edgetpu/mobile-firmware.c
new file mode 100644
index 0000000..601ecfc
--- /dev/null
+++ b/drivers/edgetpu/mobile-firmware.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Edge TPU firmware management for mobile chipsets.
+ *
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/gsa/gsa_tpu.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "edgetpu.h"
+#include "edgetpu-config.h"
+#include "edgetpu-firmware.h"
+#include "edgetpu-firmware-util.h"
+#include "edgetpu-internal.h"
+#include "edgetpu-kci.h"
+#include "edgetpu-mailbox.h"
+#include "edgetpu-mmu.h"
+#include "edgetpu-mobile-platform.h"
+#include "mobile-firmware.h"
+
+static int mobile_firmware_after_create(struct edgetpu_firmware *et_fw)
+{
+ /*
+ * Use firmware data to keep a copy of the image config in order
+ * to avoid re-doing IOMMU mapping on each firmware run
+ */
+ struct mobile_image_config *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ edgetpu_firmware_set_data(et_fw, data);
+ return 0;
+}
+
+static void mobile_firmware_before_destroy(struct edgetpu_firmware *et_fw)
+{
+ struct mobile_image_config *image_config;
+ u32 i, tpu_addr, size;
+ struct edgetpu_dev *etdev = et_fw->etdev;
+
+ image_config = edgetpu_firmware_get_data(et_fw);
+
+ if (image_config && image_config->privilege_level == FW_PRIV_LEVEL_NS) {
+ for (i = 0; i < image_config->num_iommu_mapping; i++) {
+ tpu_addr = image_config->mappings[i].virt_address;
+ if (!tpu_addr)
+ continue;
+ size = CONFIG_TO_SIZE(image_config->mappings[i].image_config_value);
+ edgetpu_mmu_remove_translation(etdev, tpu_addr, size, EDGETPU_CONTEXT_KCI);
+ }
+ edgetpu_firmware_set_data(et_fw, NULL);
+ kfree(image_config);
+ }
+}
+
+static int mobile_firmware_alloc_buffer(
+ struct edgetpu_firmware *et_fw,
+ struct edgetpu_firmware_buffer *fw_buf)
+{
+ struct edgetpu_dev *etdev = et_fw->etdev;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+
+ /* Allocate extra space the image header */
+ size_t buffer_size =
+ etmdev->fw_region_size + MOBILE_FW_HEADER_SIZE;
+
+ fw_buf->vaddr = vmalloc(buffer_size);
+ if (!fw_buf->vaddr) {
+ etdev_err(etdev, "%s: failed to allocate buffer (%zu bytes)\n",
+ __func__, buffer_size);
+ return -ENOMEM;
+ }
+ fw_buf->dma_addr = 0;
+ fw_buf->alloc_size = buffer_size;
+ fw_buf->used_size_align = 16;
+ return 0;
+}
+
+static void mobile_firmware_free_buffer(
+ struct edgetpu_firmware *et_fw,
+ struct edgetpu_firmware_buffer *fw_buf)
+{
+ vfree(fw_buf->vaddr);
+ fw_buf->vaddr = NULL;
+ fw_buf->dma_addr = 0;
+ fw_buf->alloc_size = 0;
+ fw_buf->used_size_align = 0;
+}
+
+static struct mobile_image_config *mobile_firmware_get_image_config(struct edgetpu_dev *etdev)
+{
+ return (struct mobile_image_config *) edgetpu_firmware_get_data(etdev->firmware);
+}
+
+static void mobile_firmware_save_image_config(struct edgetpu_dev *etdev,
+ struct mobile_image_config *image_config)
+{
+ struct mobile_image_config *saved_image_config = mobile_firmware_get_image_config(etdev);
+
+ memcpy(saved_image_config, image_config, sizeof(*saved_image_config));
+}
+
+static void mobile_firmware_clear_image_config(struct edgetpu_dev *etdev)
+{
+ struct mobile_image_config *saved_image_config = mobile_firmware_get_image_config(etdev);
+
+ memset(saved_image_config, 0, sizeof(*saved_image_config));
+}
+
+static void mobile_firmware_clear_ns_mappings(struct edgetpu_dev *etdev,
+ struct mobile_image_config *image_config)
+{
+ unsigned int tpu_addr, size;
+ int i;
+
+ etdev_dbg(etdev, "Firmware image config changed, removing previous mappings\n");
+ for (i = 0; i < image_config->num_iommu_mapping; i++) {
+ tpu_addr = image_config->mappings[i].virt_address;
+ size = CONFIG_TO_SIZE(image_config->mappings[i].image_config_value);
+ edgetpu_mmu_remove_translation(etdev, tpu_addr, size, EDGETPU_CONTEXT_KCI);
+ }
+}
+
+static int mobile_firmware_setup_ns_mappings(struct edgetpu_firmware *et_fw,
+ struct mobile_image_config *image_config)
+{
+ struct edgetpu_dev *etdev = et_fw->etdev;
+ struct mobile_image_config *last_image_config = mobile_firmware_get_image_config(etdev);
+ int i, ret;
+ unsigned int tpu_addr, size;
+ phys_addr_t phys_addr;
+
+ if (memcmp(image_config, last_image_config, sizeof(*image_config))) {
+ mobile_firmware_clear_ns_mappings(etdev, last_image_config);
+ } else {
+ return 0;
+ }
+
+ /* Clear saved image config. It will be updated once the new config is confirmed valid */
+ mobile_firmware_clear_image_config(etdev);
+
+ for (i = 0; i < image_config->num_iommu_mapping; i++) {
+ tpu_addr = image_config->mappings[i].virt_address;
+ if (!tpu_addr) {
+ etdev_warn(etdev, "Invalid firmware header\n");
+ goto err;
+ }
+ size = CONFIG_TO_SIZE(image_config->mappings[i].image_config_value);
+ phys_addr = (image_config->mappings[i].image_config_value & ~(0xFFF));
+
+ etdev_dbg(etdev, "Adding IOMMU mapping for firmware : %08X -> %08llX", tpu_addr,
+ phys_addr);
+
+ ret = edgetpu_mmu_add_translation(etdev, tpu_addr, phys_addr, size,
+ IOMMU_READ | IOMMU_WRITE, EDGETPU_CONTEXT_KCI);
+ if (ret) {
+ etdev_err(etdev,
+ "Unable to Map: %d tpu_addr: %#x phys_addr: %#llx size: %#x\n",
+ ret, tpu_addr, phys_addr, size);
+ goto err;
+ }
+ }
+
+ mobile_firmware_save_image_config(etdev, image_config);
+ return 0;
+
+err:
+ while (i--) {
+ tpu_addr = image_config->mappings[i].virt_address;
+ size = CONFIG_TO_SIZE(image_config->mappings[i].image_config_value);
+ edgetpu_mmu_remove_translation(etdev, tpu_addr, size, EDGETPU_CONTEXT_KCI);
+ }
+ return ret;
+}
+
+static int mobile_firmware_gsa_authenticate(struct edgetpu_mobile_platform_dev *etmdev,
+ struct edgetpu_firmware_buffer *fw_buf,
+ struct mobile_image_config *image_config,
+ void *image_vaddr)
+{
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
+ struct mobile_image_config *saved_image_config = mobile_firmware_get_image_config(etdev);
+ struct mobile_image_config last_image_config;
+ void *header_vaddr;
+ dma_addr_t header_dma_addr;
+ int tpu_state;
+ int ret = 0;
+ bool image_config_changed = memcmp(image_config, saved_image_config, sizeof(*image_config));
+
+ /* Need a local copy of the saved image in case we need to undo NS mappings */
+ memcpy(&last_image_config, saved_image_config, sizeof(last_image_config));
+
+ /* Clear saved image config. It will be updated once the new config is confirmed valid */
+ mobile_firmware_clear_image_config(etdev);
+
+ tpu_state = gsa_send_tpu_cmd(etmdev->gsa_dev, GSA_TPU_GET_STATE);
+
+ if (tpu_state < GSA_TPU_STATE_INACTIVE) {
+ etdev_err(etdev, "GSA failed to retrieve current status: %d\n", tpu_state);
+ return tpu_state;
+ }
+
+ etdev_dbg(etdev, "GSA Reports TPU state: %d\n", tpu_state);
+
+ if (tpu_state > GSA_TPU_STATE_INACTIVE) {
+ ret = gsa_unload_tpu_fw_image(etmdev->gsa_dev);
+ if (ret) {
+ etdev_warn(etdev, "GSA release failed: %d\n", ret);
+ return -EIO;
+ }
+ }
+
+ /* Copy the firmware image to the target location, skipping the header */
+ memcpy(image_vaddr, fw_buf->vaddr + MOBILE_FW_HEADER_SIZE,
+ fw_buf->used_size - MOBILE_FW_HEADER_SIZE);
+
+ /* Allocate coherent memory for the image header */
+ header_vaddr = dma_alloc_coherent(etmdev->gsa_dev,
+ MOBILE_FW_HEADER_SIZE,
+ &header_dma_addr, GFP_KERNEL);
+ if (!header_vaddr) {
+ etdev_err(etdev,
+ "Failed to allocate coherent memory for header\n");
+ return -ENOMEM;
+ }
+
+ memcpy(header_vaddr, fw_buf->vaddr, MOBILE_FW_HEADER_SIZE);
+ etdev_dbg(etdev, "Requesting GSA image load. meta = %llX payload = %llX", header_dma_addr,
+ (u64)etmdev->fw_region_paddr);
+
+ ret = gsa_load_tpu_fw_image(etmdev->gsa_dev, header_dma_addr,
+ etmdev->fw_region_paddr);
+ if (ret) {
+ etdev_err(etdev, "GSA authentication failed: %d\n", ret);
+ } else {
+ /* Transitioning from NS to something else, clear NS mappings */
+ if (last_image_config.privilege_level == FW_PRIV_LEVEL_NS &&
+ image_config->privilege_level != FW_PRIV_LEVEL_NS)
+ mobile_firmware_clear_ns_mappings(etdev, &last_image_config);
+
+ /*
+ * No change in image config, save it here so mobile_firmware_setup_ns_mappings()
+ * can re-use existing mappings
+ */
+ if (!image_config_changed)
+ mobile_firmware_save_image_config(etdev, image_config);
+
+ if (image_config->privilege_level == FW_PRIV_LEVEL_NS)
+ ret = mobile_firmware_setup_ns_mappings(etdev->firmware, image_config);
+ }
+
+ dma_free_coherent(etmdev->gsa_dev, MOBILE_FW_HEADER_SIZE, header_vaddr, header_dma_addr);
+
+ return ret;
+}
+
+/* TODO(b/197074886): remove once rio has GSA support */
+static void program_iremap_csr(struct edgetpu_dev *etdev)
+{
+ const int ctx_id = 0, sid0 = 0x30, sid1 = 0x34;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ phys_addr_t fw_paddr = etmdev->fw_region_paddr;
+
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_SECURITY, (ctx_id << 16) | sid0);
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_SECURITY + 8,
+ (ctx_id << 16) | sid1);
+#if defined(ZEBU_SYSMMU_WORKAROUND)
+ /*
+ * This is required on ZeBu after b/197718405 is fixed, which forwards all transactions to
+ * the non-secure SysMMU.
+ */
+ fw_paddr = EDGETPU_INSTRUCTION_REMAP_BASE;
+#endif
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_NEW_BASE, fw_paddr);
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_NEW_BASE + 8, fw_paddr);
+
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_LIMIT,
+ EDGETPU_INSTRUCTION_REMAP_BASE + SZ_16M);
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_LIMIT + 8,
+ EDGETPU_INSTRUCTION_REMAP_BASE + SZ_16M);
+
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_CONTROL, 1);
+ edgetpu_dev_write_32(etdev, EDGETPU_REG_INSTRUCTION_REMAP_CONTROL + 8, 1);
+}
+
+static int mobile_firmware_prepare_run(struct edgetpu_firmware *et_fw,
+ struct edgetpu_firmware_buffer *fw_buf)
+{
+ struct edgetpu_dev *etdev = et_fw->etdev;
+
+ /* Reset KCI mailbox before starting f/w, don't process anything old.*/
+ edgetpu_mailbox_reset(etdev->kci->mailbox);
+
+ if (IS_ENABLED(CONFIG_RIO))
+ program_iremap_csr(etdev);
+
+ return edgetpu_mobile_firmware_reset_cpu(etdev, false);
+}
+
+static int mobile_firmware_restart(struct edgetpu_firmware *et_fw, bool force_reset)
+{
+ struct edgetpu_dev *etdev = et_fw->etdev;
+
+ /*
+ * We are in a bad state, reset the CPU and hope the device recovers.
+ * Ignore failures in the reset assert request and proceed to reset release.
+ */
+ if (force_reset)
+ edgetpu_mobile_firmware_reset_cpu(etdev, true);
+
+ return edgetpu_mobile_firmware_reset_cpu(etdev, false);
+}
+
+static int mobile_firmware_setup_buffer(struct edgetpu_firmware *et_fw,
+ struct edgetpu_firmware_buffer *fw_buf)
+{
+ int ret = 0;
+ void *image_vaddr;
+ struct mobile_image_config *image_config;
+ struct edgetpu_dev *etdev = et_fw->etdev;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ phys_addr_t image_start, image_end, carveout_start, carveout_end;
+
+ if (fw_buf->used_size < MOBILE_FW_HEADER_SIZE) {
+ etdev_err(etdev, "Invalid buffer size: %zu < %d\n",
+ fw_buf->used_size, MOBILE_FW_HEADER_SIZE);
+ return -EINVAL;
+ }
+
+ image_vaddr = memremap(etmdev->fw_region_paddr,
+ etmdev->fw_region_size, MEMREMAP_WC);
+ if (!image_vaddr) {
+ etdev_err(etdev, "memremap failed\n");
+ return -ENOMEM;
+ }
+
+ /* fetch the firmware versions */
+ image_config = fw_buf->vaddr + MOBILE_IMAGE_CONFIG_OFFSET;
+ memcpy(&etdev->fw_version, &image_config->firmware_versions,
+ sizeof(etdev->fw_version));
+
+ if (etmdev->gsa_dev) {
+ ret = mobile_firmware_gsa_authenticate(etmdev, fw_buf, image_config, image_vaddr);
+ } else if (image_config->privilege_level == FW_PRIV_LEVEL_NS) {
+ etdev_dbg(etdev, "Loading unauthenticated non-secure firmware\n");
+ /* Copy the firmware image to the target location, skipping the header */
+ memcpy(image_vaddr, fw_buf->vaddr + MOBILE_FW_HEADER_SIZE,
+ fw_buf->used_size - MOBILE_FW_HEADER_SIZE);
+ ret = mobile_firmware_setup_ns_mappings(et_fw, image_config);
+ } else {
+ etdev_err(etdev,
+ "Cannot load firmware at privilege level %d with no authentication\n",
+ image_config->privilege_level);
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ goto out;
+
+ image_start = (phys_addr_t)image_config->carveout_base;
+ image_end = (phys_addr_t)(image_config->firmware_base + image_config->firmware_size - 1);
+ carveout_start = etmdev->fw_region_paddr;
+ carveout_end = carveout_start + etmdev->fw_region_size - 1;
+
+ /* Image must fit within the carveout */
+ if (image_start < carveout_start || image_end > carveout_end) {
+ etdev_err(etdev, "Firmware image doesn't fit in carveout\n");
+ etdev_err(etdev, "Image config: %pap - %pap\n", &image_start, &image_end);
+ etdev_err(etdev, "Carveout: %pap - %pap\n", &carveout_start, &carveout_end);
+ ret = -ERANGE;
+ }
+
+out:
+ memunmap(image_vaddr);
+ return ret;
+}
+
+/* Load firmware for chips that use carveout memory for a single chip. */
+int edgetpu_firmware_chip_load_locked(
+ struct edgetpu_firmware *et_fw,
+ struct edgetpu_firmware_desc *fw_desc, const char *name)
+{
+ int ret;
+ struct edgetpu_dev *etdev = et_fw->etdev;
+ struct device *dev = etdev->dev;
+ const struct firmware *fw;
+ size_t aligned_size;
+
+ ret = request_firmware(&fw, name, dev);
+ if (ret) {
+ etdev_dbg(etdev,
+ "%s: request '%s' failed: %d\n", __func__, name, ret);
+ return ret;
+ }
+
+ aligned_size = ALIGN(fw->size, fw_desc->buf.used_size_align);
+ if (aligned_size > fw_desc->buf.alloc_size) {
+ etdev_dbg(etdev,
+ "%s: firmware buffer too small: alloc size=%#zx, required size=%#zx\n",
+ __func__, fw_desc->buf.alloc_size, aligned_size);
+ ret = -ENOSPC;
+ goto out_release_firmware;
+ }
+
+ memcpy(fw_desc->buf.vaddr, fw->data, fw->size);
+ fw_desc->buf.used_size = aligned_size;
+ /* May return NULL on out of memory, driver must handle properly */
+ fw_desc->buf.name = kstrdup(name, GFP_KERNEL);
+
+out_release_firmware:
+ release_firmware(fw);
+ return ret;
+}
+
+void edgetpu_firmware_chip_unload_locked(
+ struct edgetpu_firmware *et_fw,
+ struct edgetpu_firmware_desc *fw_desc)
+{
+ kfree(fw_desc->buf.name);
+ fw_desc->buf.name = NULL;
+ fw_desc->buf.used_size = 0;
+}
+
+int edgetpu_mobile_firmware_reset_cpu(struct edgetpu_dev *etdev, bool assert_reset)
+{
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct mobile_image_config *image_config = mobile_firmware_get_image_config(etdev);
+ int ret = 0;
+
+ if (image_config->privilege_level == FW_PRIV_LEVEL_NS) {
+ int i;
+
+ for (i = 0; i < EDGETPU_NUM_CORES; i++)
+ edgetpu_dev_write_32_sync(etdev, EDGETPU_REG_RESET_CONTROL + i * 8,
+ assert_reset ? 1 : 0);
+ }
+ else if (etmdev->gsa_dev)
+ ret = gsa_send_tpu_cmd(etmdev->gsa_dev,
+ assert_reset ? GSA_TPU_SHUTDOWN : GSA_TPU_START);
+ else
+ ret = -ENODEV;
+
+ etdev_dbg(etdev, "%s CPU reset result = %d", assert_reset ? "assert" : "release", ret);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct edgetpu_firmware_chip_data mobile_firmware_chip_data = {
+ .default_firmware_name = EDGETPU_DEFAULT_FIRMWARE_NAME,
+ .after_create = mobile_firmware_after_create,
+ .before_destroy = mobile_firmware_before_destroy,
+ .alloc_buffer = mobile_firmware_alloc_buffer,
+ .free_buffer = mobile_firmware_free_buffer,
+ .setup_buffer = mobile_firmware_setup_buffer,
+ .prepare_run = mobile_firmware_prepare_run,
+ .restart = mobile_firmware_restart,
+};
+
+int edgetpu_mobile_firmware_create(struct edgetpu_dev *etdev)
+{
+ return edgetpu_firmware_create(etdev, &mobile_firmware_chip_data);
+}
+
+void edgetpu_mobile_firmware_destroy(struct edgetpu_dev *etdev)
+{
+ edgetpu_firmware_destroy(etdev);
+}
+
+unsigned long edgetpu_chip_firmware_iova(struct edgetpu_dev *etdev)
+{
+ /*
+ * On mobile platforms, firmware address translation may happen in 1 or 2 stages:
+ * 1.- Instruction remap registers.
+ * 2.- IOMMU translation (when not running in GSA privilege).
+ * In either case, the address seen by the TPU's CPU core will remain constant, and
+ * equal to the macro below.
+ */
+ return EDGETPU_INSTRUCTION_REMAP_BASE;
+}
diff --git a/drivers/edgetpu/mobile-firmware.h b/drivers/edgetpu/mobile-firmware.h
index 691eaf5..1250d7e 100644
--- a/drivers/edgetpu/mobile-firmware.h
+++ b/drivers/edgetpu/mobile-firmware.h
@@ -12,6 +12,12 @@
#include "edgetpu-internal.h"
#include "edgetpu.h"
+#define MAX_IOMMU_MAPPINGS 26
+
+#define FW_PRIV_LEVEL_GSA (0)
+#define FW_PRIV_LEVEL_TZ (1)
+#define FW_PRIV_LEVEL_NS (2)
+
/* mobile FW header size */
#define MOBILE_FW_HEADER_SIZE SZ_4K
/* The offset to the signed firmware header. */
@@ -19,6 +25,18 @@
/* The offset to image configuration. */
#define MOBILE_IMAGE_CONFIG_OFFSET (MOBILE_HEADER_OFFSET + 0x160)
+#define CONFIG_TO_SIZE(a) ((1 << ((a) & 0xFFF)) << 12)
+
+struct iommu_mapping {
+ /* TPU virt address */
+ __u32 virt_address;
+ /*
+ * contains a 12-bit aligned address and a page-order size into a
+ * 32-bit value i.e. a physical address and size in page order.
+ */
+ __u32 image_config_value;
+};
+
/*
* The image configuration attached to the signed firmware.
*/
@@ -27,6 +45,12 @@ struct mobile_image_config {
__u32 firmware_base;
__u32 firmware_size;
struct edgetpu_fw_version firmware_versions;
+ __u32 config_version;
+ __u32 privilege_level;
+ __u32 remapped_region_start;
+ __u32 remapped_region_end;
+ __u32 num_iommu_mapping;
+ struct iommu_mapping mappings[MAX_IOMMU_MAPPINGS];
} __packed;
/*
@@ -46,7 +70,15 @@ struct mobile_image_header {
struct mobile_image_config ImageConfig;
};
-int mobile_edgetpu_firmware_create(struct edgetpu_dev *etdev);
-void mobile_edgetpu_firmware_destroy(struct edgetpu_dev *etdev);
+int edgetpu_mobile_firmware_create(struct edgetpu_dev *etdev);
+void edgetpu_mobile_firmware_destroy(struct edgetpu_dev *etdev);
+
+/*
+ * Assert or release the reset signal of the TPU's CPU
+ * Depending on privilege level, this may be by a direct register write
+ * or a call into GSA.
+ */
+int edgetpu_mobile_firmware_reset_cpu(struct edgetpu_dev *etdev, bool assert_reset);
+
#endif /* __MOBILE_FIRMWARE_H__ */
diff --git a/drivers/edgetpu/mobile-pm.c b/drivers/edgetpu/mobile-pm.c
new file mode 100644
index 0000000..de21bda
--- /dev/null
+++ b/drivers/edgetpu/mobile-pm.c
@@ -0,0 +1,570 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common EdgeTPU mobile power management support
+ *
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/gsa/gsa_tpu.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <soc/google/bcl.h>
+#include <soc/google/bts.h>
+#include <soc/google/exynos_pm_qos.h>
+
+#include "edgetpu-config.h"
+#include "edgetpu-firmware.h"
+#include "edgetpu-internal.h"
+#include "edgetpu-kci.h"
+#include "edgetpu-mailbox.h"
+#include "edgetpu-mobile-platform.h"
+#include "edgetpu-pm.h"
+#include "mobile-firmware.h"
+#include "mobile-pm.h"
+
+#include "edgetpu-pm.c"
+
+/*
+ * Encode INT/MIF values as a 16 bit pair in the 32-bit return value
+ * (in units of MHz, to provide enough range)
+ */
+#define PM_QOS_INT_SHIFT (16)
+#define PM_QOS_MIF_MASK (0xFFFF)
+#define PM_QOS_FACTOR (1000)
+
+static int power_state = TPU_DEFAULT_POWER_STATE;
+
+module_param(power_state, int, 0660);
+
+#define MAX_VOLTAGE_VAL 1250000
+
+enum edgetpu_pwr_state edgetpu_active_states[EDGETPU_NUM_STATES] = {
+ TPU_ACTIVE_UUD,
+ TPU_ACTIVE_SUD,
+ TPU_ACTIVE_UD,
+ TPU_ACTIVE_NOM,
+#if IS_ENABLED(CONFIG_ABROLHOS)
+ TPU_ACTIVE_OD,
+#endif /* IS_ENABLED(CONFIG_ABROLHOS) */
+};
+
+uint32_t *edgetpu_states_display = edgetpu_active_states;
+
+static int mobile_pwr_state_init(struct device *dev)
+{
+ int ret;
+ int curr_state;
+
+ pm_runtime_enable(dev);
+ curr_state = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 0);
+
+ if (curr_state > TPU_OFF) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret) {
+ dev_err(dev, "pm_runtime_get_sync returned %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = exynos_acpm_set_init_freq(TPU_ACPM_DOMAIN, curr_state);
+ if (ret) {
+ dev_err(dev, "error initializing tpu state: %d\n", ret);
+ if (curr_state > TPU_OFF)
+ pm_runtime_put_sync(dev);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mobile_pwr_state_set_locked(struct edgetpu_mobile_platform_dev *etmdev, u64 val)
+{
+ int ret;
+ int curr_state;
+ struct edgetpu_dev *etdev = &etmdev->edgetpu_dev;
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ struct device *dev = etdev->dev;
+
+ curr_state = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 0);
+
+ dev_dbg(dev, "Power state %d -> %llu\n", curr_state, val);
+
+ if (curr_state == TPU_OFF && val > TPU_OFF) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret) {
+ dev_err(dev, "pm_runtime_get_sync returned %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = platform_pwr->acpm_set_rate(TPU_ACPM_DOMAIN, (unsigned long)val);
+ if (ret) {
+ dev_err(dev, "error setting tpu state: %d\n", ret);
+ pm_runtime_put_sync(dev);
+ return ret;
+ }
+
+ if (curr_state != TPU_OFF && val == TPU_OFF) {
+ ret = pm_runtime_put_sync(dev);
+ if (ret) {
+ dev_err(dev, "%s: pm_runtime_put_sync returned %d\n", __func__, ret);
+ return ret;
+ }
+ if (platform_pwr->block_down)
+ platform_pwr->block_down(etdev);
+ }
+
+ return ret;
+}
+
+static int mobile_pwr_state_get_locked(void *data, u64 *val)
+{
+ struct edgetpu_dev *etdev = (typeof(etdev))data;
+ struct device *dev = etdev->dev;
+
+ *val = exynos_acpm_get_rate(TPU_ACPM_DOMAIN, 0);
+ dev_dbg(dev, "current tpu state: %llu\n", *val);
+
+ return 0;
+}
+
+static int mobile_pwr_state_set(void *data, u64 val)
+{
+ struct edgetpu_dev *etdev = (typeof(etdev))data;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int ret = 0;
+
+ mutex_lock(&platform_pwr->state_lock);
+ platform_pwr->requested_state = val;
+ if (val >= platform_pwr->min_state)
+ ret = mobile_pwr_state_set_locked(etmdev, val);
+ mutex_unlock(&platform_pwr->state_lock);
+ return ret;
+}
+
+static int mobile_pwr_state_get(void *data, u64 *val)
+{
+ struct edgetpu_dev *etdev = (typeof(etdev))data;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int ret;
+
+ mutex_lock(&platform_pwr->state_lock);
+ ret = mobile_pwr_state_get_locked(etdev, val);
+ mutex_unlock(&platform_pwr->state_lock);
+ return ret;
+}
+
+static int mobile_min_pwr_state_set(void *data, u64 val)
+{
+ struct edgetpu_dev *etdev = (typeof(etdev))data;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int ret = 0;
+
+ mutex_lock(&platform_pwr->state_lock);
+ platform_pwr->min_state = val;
+ if (val >= platform_pwr->requested_state)
+ ret = mobile_pwr_state_set_locked(etmdev, val);
+ mutex_unlock(&platform_pwr->state_lock);
+ return ret;
+}
+
+static int mobile_min_pwr_state_get(void *data, u64 *val)
+{
+ struct edgetpu_dev *etdev = (typeof(etdev))data;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+
+ mutex_lock(&platform_pwr->state_lock);
+ *val = platform_pwr->min_state;
+ mutex_unlock(&platform_pwr->state_lock);
+ return 0;
+}
+
+static int mobile_pwr_policy_set(void *data, u64 val)
+{
+ struct edgetpu_dev *etdev = (typeof(etdev))data;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int ret;
+
+ mutex_lock(&platform_pwr->policy_lock);
+ ret = exynos_acpm_set_policy(TPU_ACPM_DOMAIN, val);
+
+ if (ret) {
+ dev_err(etmdev->edgetpu_dev.dev,
+ "unable to set policy %lld (ret %d)\n", val, ret);
+ mutex_unlock(&platform_pwr->policy_lock);
+ return ret;
+ }
+
+ platform_pwr->curr_policy = val;
+ mutex_unlock(&platform_pwr->policy_lock);
+ return 0;
+}
+
+static int mobile_pwr_policy_get(void *data, u64 *val)
+{
+ struct edgetpu_dev *etdev = (typeof(etdev))data;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+
+ mutex_lock(&platform_pwr->policy_lock);
+ *val = platform_pwr->curr_policy;
+ mutex_unlock(&platform_pwr->policy_lock);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_tpu_pwr_policy, mobile_pwr_policy_get, mobile_pwr_policy_set,
+ "%llu\n");
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_tpu_pwr_state, mobile_pwr_state_get, mobile_pwr_state_set, "%llu\n");
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_tpu_min_pwr_state, mobile_min_pwr_state_get, mobile_min_pwr_state_set,
+ "%llu\n");
+
+static int mobile_get_initial_pwr_state(struct device *dev)
+{
+ switch (power_state) {
+#if IS_ENABLED(CONFIG_ABROLHOS)
+ case TPU_ACTIVE_OD:
+ case TPU_DEEP_SLEEP_CLOCKS_SLOW:
+ case TPU_DEEP_SLEEP_CLOCKS_FAST:
+ case TPU_RETENTION_CLOCKS_SLOW:
+#endif /* IS_ENABLED(CONFIG_ABROLHOS) */
+ case TPU_ACTIVE_UUD:
+ case TPU_ACTIVE_SUD:
+ case TPU_ACTIVE_UD:
+ case TPU_ACTIVE_NOM:
+ dev_info(dev, "Initial power state: %d\n", power_state);
+ break;
+ case TPU_OFF:
+#if IS_ENABLED(CONFIG_ABROLHOS)
+ case TPU_DEEP_SLEEP_CLOCKS_OFF:
+ case TPU_SLEEP_CLOCKS_OFF:
+#endif /* IS_ENABLED(CONFIG_ABROLHOS) */
+ dev_warn(dev, "Power state %d prevents control core booting", power_state);
+ fallthrough;
+ default:
+ dev_warn(dev, "Power state %d is invalid\n", power_state);
+ dev_warn(dev, "defaulting to active nominal\n");
+ power_state = TPU_ACTIVE_NOM;
+ break;
+ }
+ return power_state;
+}
+
+static void mobile_power_down(struct edgetpu_pm *etpm);
+
+static int mobile_power_up(struct edgetpu_pm *etpm)
+{
+ struct edgetpu_dev *etdev = etpm->etdev;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int ret = mobile_pwr_state_set(etpm->etdev, mobile_get_initial_pwr_state(etdev->dev));
+
+ etdev_info(etpm->etdev, "Powering up\n");
+
+ if (ret)
+ return ret;
+
+ if (platform_pwr->lpm_up)
+ platform_pwr->lpm_up(etdev);
+
+ edgetpu_chip_init(etdev);
+
+ if (etdev->kci) {
+ etdev_dbg(etdev, "Resetting KCI\n");
+ edgetpu_kci_reinit(etdev->kci);
+ }
+ if (etdev->mailbox_manager) {
+ etdev_dbg(etdev, "Resetting VII mailboxes\n");
+ edgetpu_mailbox_reset_vii(etdev->mailbox_manager);
+ }
+
+ if (!etdev->firmware)
+ return 0;
+
+ /*
+ * Why this function uses edgetpu_firmware_*_locked functions without explicitly holding
+ * edgetpu_firmware_lock:
+ *
+ * edgetpu_pm_get() is called in two scenarios - one is when the firmware loading is
+ * attempting, another one is when the user-space clients need the device be powered
+ * (usually through acquiring the wakelock).
+ *
+ * For the first scenario edgetpu_firmware_is_loading() below shall return true.
+ * For the second scenario we are indeed called without holding the firmware lock, but the
+ * firmware loading procedures (i.e. the first scenario) always call edgetpu_pm_get() before
+ * changing the firmware state, and edgetpu_pm_get() is blocked until this function
+ * finishes. In short, we are protected by the PM lock.
+ */
+
+ if (edgetpu_firmware_is_loading(etdev))
+ return 0;
+
+ /* attempt firmware run */
+ switch (edgetpu_firmware_status_locked(etdev)) {
+ case FW_VALID:
+ ret = edgetpu_firmware_restart_locked(etdev, false);
+ break;
+ case FW_INVALID:
+ ret = edgetpu_firmware_run_default_locked(etdev);
+ break;
+ default:
+ break;
+ }
+ if (ret) {
+ mobile_power_down(etpm);
+ } else {
+#if IS_ENABLED(CONFIG_GOOGLE_BCL)
+ if (!etmdev->bcl_dev)
+ etmdev->bcl_dev = google_retrieve_bcl_handle();
+ if (etmdev->bcl_dev)
+ google_init_tpu_ratio(etmdev->bcl_dev);
+#endif
+ }
+
+ return ret;
+}
+
+static void mobile_pm_cleanup_bts_scenario(struct edgetpu_dev *etdev)
+{
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int performance_scenario = platform_pwr->performance_scenario;
+
+ if (!performance_scenario)
+ return;
+
+ mutex_lock(&platform_pwr->scenario_lock);
+ while (platform_pwr->scenario_count) {
+ int ret = bts_del_scenario(performance_scenario);
+
+ if (ret) {
+ platform_pwr->scenario_count = 0;
+ etdev_warn_once(etdev, "error %d in cleaning up BTS scenario %u\n", ret,
+ performance_scenario);
+ break;
+ }
+ platform_pwr->scenario_count--;
+ }
+ mutex_unlock(&platform_pwr->scenario_lock);
+}
+
+static void mobile_power_down(struct edgetpu_pm *etpm)
+{
+ struct edgetpu_dev *etdev = etpm->etdev;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ u64 val;
+ int res = 0;
+ int min_state = platform_pwr->min_state;
+
+ etdev_info(etdev, "Powering down\n");
+
+ if (min_state >= MIN_ACTIVE_STATE) {
+ etdev_info(etdev, "Power down skipped due to min state = %d\n", min_state);
+ return;
+ }
+
+ if (mobile_pwr_state_get(etdev, &val)) {
+ etdev_warn(etdev, "Failed to read current power state\n");
+ val = TPU_ACTIVE_NOM;
+ }
+ if (val == TPU_OFF) {
+ etdev_dbg(etdev, "Device already off, skipping shutdown\n");
+ return;
+ }
+
+ if (edgetpu_firmware_status_locked(etdev) == FW_VALID) {
+ /* Update usage stats before we power off fw. */
+ edgetpu_kci_update_usage_locked(etdev);
+ platform_pwr->firmware_down(etdev);
+ edgetpu_kci_cancel_work_queues(etdev->kci);
+ res = edgetpu_mobile_firmware_reset_cpu(etdev, true);
+ if (res < 0)
+ etdev_warn(etdev, "CPU reset request failed (%d)\n", res);
+ }
+
+ if (platform_pwr->lpm_down)
+ platform_pwr->lpm_down(etdev);
+
+ mobile_pwr_state_set(etdev, TPU_OFF);
+
+ /* Remove our vote for INT/MIF state (if any) */
+ exynos_pm_qos_update_request(&platform_pwr->int_min, 0);
+ exynos_pm_qos_update_request(&platform_pwr->mif_min, 0);
+
+ mobile_pm_cleanup_bts_scenario(etdev);
+
+ /*
+ * It should be impossible that power_down() is called when secure_client is set.
+ * Non-null secure_client implies ext mailbox is acquired, which implies wakelock is
+ * acquired.
+ * Clear the state here just in case.
+ */
+ etmdev->secure_client = NULL;
+}
+
+static int mobile_pm_after_create(struct edgetpu_pm *etpm)
+{
+ int ret;
+ struct edgetpu_dev *etdev = etpm->etdev;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct device *dev = etdev->dev;
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+
+ ret = mobile_pwr_state_init(dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&platform_pwr->policy_lock);
+ mutex_init(&platform_pwr->state_lock);
+ mutex_init(&platform_pwr->scenario_lock);
+
+ exynos_pm_qos_add_request(&platform_pwr->int_min, PM_QOS_DEVICE_THROUGHPUT, 0);
+ exynos_pm_qos_add_request(&platform_pwr->mif_min, PM_QOS_BUS_THROUGHPUT, 0);
+
+ platform_pwr->performance_scenario = bts_get_scenindex("tpu_performance");
+ if (!platform_pwr->performance_scenario)
+ dev_warn(etdev->dev, "tpu_performance BTS scenario not found\n");
+ platform_pwr->scenario_count = 0;
+
+ ret = mobile_pwr_state_set(etdev, mobile_get_initial_pwr_state(dev));
+ if (ret)
+ return ret;
+ platform_pwr->debugfs_dir = debugfs_create_dir("power", edgetpu_fs_debugfs_dir());
+ if (IS_ERR_OR_NULL(platform_pwr->debugfs_dir)) {
+ dev_warn(etdev->dev, "Failed to create debug FS power");
+ /* don't fail the procedure on debug FS creation fails */
+ return 0;
+ }
+ debugfs_create_file("state", 0660, platform_pwr->debugfs_dir, etdev, &fops_tpu_pwr_state);
+ debugfs_create_file("min_state", 0660, platform_pwr->debugfs_dir, etdev,
+ &fops_tpu_min_pwr_state);
+ debugfs_create_file("policy", 0660, platform_pwr->debugfs_dir, etdev, &fops_tpu_pwr_policy);
+
+ if (platform_pwr->after_create)
+ ret = platform_pwr->after_create(etdev);
+
+ return ret;
+}
+
+static void mobile_pm_before_destroy(struct edgetpu_pm *etpm)
+{
+ struct edgetpu_dev *etdev = etpm->etdev;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+
+ if (platform_pwr->before_destroy)
+ platform_pwr->before_destroy(etdev);
+
+ debugfs_remove_recursive(platform_pwr->debugfs_dir);
+ pm_runtime_disable(etpm->etdev->dev);
+ mobile_pm_cleanup_bts_scenario(etdev);
+ exynos_pm_qos_remove_request(&platform_pwr->int_min);
+ exynos_pm_qos_remove_request(&platform_pwr->mif_min);
+}
+
+static struct edgetpu_pm_handlers mobile_pm_handlers = {
+ .after_create = mobile_pm_after_create,
+ .before_destroy = mobile_pm_before_destroy,
+ .power_up = mobile_power_up,
+ .power_down = mobile_power_down,
+};
+
+int mobile_pm_create(struct edgetpu_dev *etdev)
+{
+ return edgetpu_pm_create(etdev, &mobile_pm_handlers);
+}
+
+void mobile_pm_destroy(struct edgetpu_dev *etdev)
+{
+ edgetpu_pm_destroy(etdev);
+}
+
+void mobile_pm_set_pm_qos(struct edgetpu_dev *etdev, u32 pm_qos_val)
+{
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ s32 int_val = (pm_qos_val >> PM_QOS_INT_SHIFT) * PM_QOS_FACTOR;
+ s32 mif_val = (pm_qos_val & PM_QOS_MIF_MASK) * PM_QOS_FACTOR;
+
+ etdev_dbg(etdev, "%s: pm_qos request - int = %d mif = %d\n", __func__, int_val, mif_val);
+
+ exynos_pm_qos_update_request(&platform_pwr->int_min, int_val);
+ exynos_pm_qos_update_request(&platform_pwr->mif_min, mif_val);
+}
+
+static void mobile_pm_activate_bts_scenario(struct edgetpu_dev *etdev)
+{
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int performance_scenario = platform_pwr->performance_scenario;
+
+ /* bts_add_scenario() keeps track of reference count internally.*/
+ int ret;
+
+ if (!performance_scenario)
+ return;
+ mutex_lock(&platform_pwr->scenario_lock);
+ ret = bts_add_scenario(performance_scenario);
+ if (ret)
+ etdev_warn_once(etdev, "error %d adding BTS scenario %u\n", ret,
+ performance_scenario);
+ else
+ platform_pwr->scenario_count++;
+
+ etdev_dbg(etdev, "BTS Scenario activated: %d\n", platform_pwr->scenario_count);
+ mutex_unlock(&platform_pwr->scenario_lock);
+}
+
+static void mobile_pm_deactivate_bts_scenario(struct edgetpu_dev *etdev)
+{
+ /* bts_del_scenario() keeps track of reference count internally.*/
+ int ret;
+ struct edgetpu_mobile_platform_dev *etmdev = to_mobile_dev(etdev);
+ struct edgetpu_mobile_platform_pwr *platform_pwr = &etmdev->platform_pwr;
+ int performance_scenario = platform_pwr->performance_scenario;
+
+ if (!performance_scenario)
+ return;
+ mutex_lock(&platform_pwr->scenario_lock);
+ if (!platform_pwr->scenario_count) {
+ etdev_warn(etdev, "Unbalanced bts deactivate\n");
+ mutex_unlock(&platform_pwr->scenario_lock);
+ return;
+ }
+ ret = bts_del_scenario(performance_scenario);
+ if (ret)
+ etdev_warn_once(etdev, "error %d deleting BTS scenario %u\n", ret,
+ performance_scenario);
+ else
+ platform_pwr->scenario_count--;
+
+ etdev_dbg(etdev, "BTS Scenario deactivated: %d\n", platform_pwr->scenario_count);
+ mutex_unlock(&platform_pwr->scenario_lock);
+}
+
+void mobile_pm_set_bts(struct edgetpu_dev *etdev, u32 bts_val)
+{
+ etdev_dbg(etdev, "%s: bts request - val = %u\n", __func__, bts_val);
+
+ switch (bts_val) {
+ case 0:
+ mobile_pm_deactivate_bts_scenario(etdev);
+ break;
+ case 1:
+ mobile_pm_activate_bts_scenario(etdev);
+ break;
+ default:
+ etdev_warn(etdev, "%s: invalid BTS request value: %u\n", __func__, bts_val);
+ break;
+ }
+}
diff --git a/drivers/edgetpu/mobile-pm.h b/drivers/edgetpu/mobile-pm.h
index a04b6bb..e5fafb6 100644
--- a/drivers/edgetpu/mobile-pm.h
+++ b/drivers/edgetpu/mobile-pm.h
@@ -52,10 +52,17 @@ enum mobile_reverse_kci_code {
/*
* Initialize a power management interface for an edgetpu device on mobile
* chipsets.
+ * Needs to be called after the devices's platform_pwr struct has been initialized.
*/
int mobile_pm_create(struct edgetpu_dev *etdev);
/*
+ * Wrapper for chip-specific implementation.
+ * Typically calls mobile_pm_create after initializing the platform_pwr struct.
+ */
+int edgetpu_chip_pm_create(struct edgetpu_dev *etdev);
+
+/*
* Destroy power management interface for an edgetpu device on mobile chipsets.
*/
void mobile_pm_destroy(struct edgetpu_dev *etdev);