summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--alsa/aoc_alsa.h6
-rw-r--r--alsa/aoc_alsa_card.c9
-rw-r--r--alsa/aoc_alsa_ctl.c48
-rw-r--r--alsa/aoc_alsa_drv.h3
-rw-r--r--alsa/aoc_alsa_hw.c117
-rw-r--r--alsa/aoc_alsa_path.c1
-rw-r--r--aoc-interface-gs101.h68
-rw-r--r--aoc.c1794
-rw-r--r--aoc.h215
-rw-r--r--aoc_channel_dev.c25
-rw-r--r--aoc_control_dev.c19
-rw-r--r--aoc_firmware.c14
-rw-r--r--aoc_firmware.h4
-rw-r--r--aoc_ramdump_regions.h5
-rw-r--r--aoc_service_core.c563
-rw-r--r--aoc_v1.c498
17 files changed, 1910 insertions, 1481 deletions
diff --git a/Makefile b/Makefile
index 10a1b3a..93bbe0c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
obj-$(CONFIG_WC_MBOX) += mailbox-wc.o
obj-$(CONFIG_AOC_DRIVER) += aoc_core.o
-aoc_core-objs := aoc.o ../aoc_ipc/aoc_ipc_core.o aoc_firmware.o ion_physical_heap.o
+aoc_core-objs := aoc.o aoc_v1.o aoc_service_core.o ../aoc_ipc/aoc_ipc_core.o aoc_firmware.o ion_physical_heap.o
obj-$(CONFIG_AOC_CHAR_DRIVER) += aoc_char_dev.o
obj-$(CONFIG_AOC_CONTROL_DRIVER) += aoc_control_dev.o
diff --git a/alsa/aoc_alsa.h b/alsa/aoc_alsa.h
index 9272bbb..2ac015c 100644
--- a/alsa/aoc_alsa.h
+++ b/alsa/aoc_alsa.h
@@ -267,6 +267,9 @@ struct aoc_chip {
long pcm_wait_time_in_ms;
long voice_pcm_wait_time_in_ms;
+ bool hotword_supported;
+ bool chre_supported;
+
struct AUDIO_OUTPUT_BT_A2DP_ENC_CFG a2dp_encoder_cfg;
struct CMD_AUDIO_OUTPUT_USB_CONFIG usb_sink_cfg;
struct CMD_AUDIO_OUTPUT_USB_CONFIG_V2 usb_sink_cfg_v2;
@@ -384,11 +387,8 @@ int ap_record_stop(struct aoc_chip *chip, struct aoc_alsa_stream *alsa_stream);
int aoc_capture_filter_runtime_control(struct aoc_chip *chip, uint32_t port_id, bool on);
int aoc_audio_capture_runtime_trigger(struct aoc_chip *chip, int ep_id, int dst, bool on);
int aoc_audio_capture_eraser_enable(struct aoc_chip *chip, long enable);
-#if ! IS_ENABLED(CONFIG_SOC_GS101)
int aoc_hotword_tap_enable(struct aoc_chip *chip, long enable);
-#endif
int aoc_eraser_aec_reference_set(struct aoc_chip *chip, long ref_source);
-
int aoc_load_cca_module(struct aoc_chip *chip, long load);
int aoc_voice_call_mic_mute(struct aoc_chip *chip, int mute);
diff --git a/alsa/aoc_alsa_card.c b/alsa/aoc_alsa_card.c
index 09be87e..0ba6b6b 100644
--- a/alsa/aoc_alsa_card.c
+++ b/alsa/aoc_alsa_card.c
@@ -1806,6 +1806,7 @@ static int aoc_card_late_probe(struct snd_soc_card *card)
static int snd_aoc_init(struct aoc_chip *chip)
{
int i;
+ struct device_node *aoc_node;
chip->mic_loopback_enabled = 0;
@@ -1852,6 +1853,14 @@ static int snd_aoc_init(struct aoc_chip *chip)
mutex_init(&chip->audio_cmd_chan_mutex);
spin_lock_init(&chip->audio_lock);
+ aoc_node = of_find_compatible_node(NULL, NULL, "google,aoc");
+ if (!aoc_node)
+ pr_err("Cannot find aoc device node\n");
+
+ chip->hotword_supported = of_property_read_bool(aoc_node, "hotword-supported");
+ chip->chre_supported = of_property_read_bool(aoc_node, "chre-supported");
+
+ of_node_put(aoc_node);
return 0;
}
diff --git a/alsa/aoc_alsa_ctl.c b/alsa/aoc_alsa_ctl.c
index fa840e4..d06a9a6 100644
--- a/alsa/aoc_alsa_ctl.c
+++ b/alsa/aoc_alsa_ctl.c
@@ -656,41 +656,49 @@ static int audio_capture_eraser_enable_ctl_set(struct snd_kcontrol *kcontrol,
return err;
}
-#if ! IS_ENABLED(CONFIG_SOC_GS101)
static int hotword_tap_enable_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct aoc_chip *chip = snd_kcontrol_chip(kcontrol);
+ if (chip->hotword_supported) {
+ if (mutex_lock_interruptible(&chip->audio_mutex))
+ return -EINTR;
- if (mutex_lock_interruptible(&chip->audio_mutex))
- return -EINTR;
+ ucontrol->value.integer.value[0] = chip->hotword_tap_enable;
- ucontrol->value.integer.value[0] = chip->hotword_tap_enable;
+ mutex_unlock(&chip->audio_mutex);
- mutex_unlock(&chip->audio_mutex);
-
- return 0;
+ return 0;
+ } else {
+ pr_err("WARN:hotword is not supported on this device\n");
+ return 0;
+ }
}
static int hotword_tap_enable_ctl_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct aoc_chip *chip = snd_kcontrol_chip(kcontrol);
- int err = 0;
+ if (chip->hotword_supported) {
+ int err = 0;
- if (mutex_lock_interruptible(&chip->audio_mutex))
- return -EINTR;
+ if (mutex_lock_interruptible(&chip->audio_mutex))
+ return -EINTR;
- chip->hotword_tap_enable = ucontrol->value.integer.value[0];
- err = aoc_hotword_tap_enable(chip, chip->hotword_tap_enable);
- if (err < 0)
- pr_err("ERR:%d hotword_tap %s fail\n", err,
- (chip->hotword_tap_enable) ? "Enable" : "Disable");
+ chip->hotword_tap_enable = ucontrol->value.integer.value[0];
+ err = aoc_hotword_tap_enable(chip, chip->hotword_tap_enable);
+ if (err < 0)
+ pr_err("ERR:%d hotword_tap %s fail\n", err,
+ (chip->hotword_tap_enable) ? "Enable" : "Disable");
- mutex_unlock(&chip->audio_mutex);
- return err;
+ mutex_unlock(&chip->audio_mutex);
+ return err;
+ }
+ else {
+ pr_err("WARN:hotword is not supported on this device\n");
+ return 0;
+ }
}
-#endif
static int audio_cca_module_load_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2119,12 +2127,8 @@ static struct snd_kcontrol_new snd_aoc_ctl[] = {
SOC_SINGLE_EXT("Audio Capture Eraser Enable", SND_SOC_NOPM, 0, 1, 0,
audio_capture_eraser_enable_ctl_get, audio_capture_eraser_enable_ctl_set),
-
-#if ! IS_ENABLED(CONFIG_SOC_GS101)
SOC_SINGLE_EXT("Hotword Tap Enable", SND_SOC_NOPM, 0, 1, 0,
hotword_tap_enable_ctl_get, hotword_tap_enable_ctl_set),
-#endif
-
SOC_ENUM_EXT("Audio Capture Mic Source", audio_capture_mic_source_enum,
audio_capture_mic_source_get, audio_capture_mic_source_set),
diff --git a/alsa/aoc_alsa_drv.h b/alsa/aoc_alsa_drv.h
index 0587c89..c45b681 100644
--- a/alsa/aoc_alsa_drv.h
+++ b/alsa/aoc_alsa_drv.h
@@ -18,9 +18,6 @@
#include <linux/poll.h>
#include "aoc.h"
-/* TODO: it may not be needed later, depending on aoc_ipc header revision */
-#include "aoc_ipc_core_internal.h"
-
typedef enum {
AOC_SERVICE_EVENT_DOWN = 0,
AOC_SERVICE_EVENT_MAX,
diff --git a/alsa/aoc_alsa_hw.c b/alsa/aoc_alsa_hw.c
index 45e632e..8da92c6 100644
--- a/alsa/aoc_alsa_hw.c
+++ b/alsa/aoc_alsa_hw.c
@@ -2164,22 +2164,25 @@ int aoc_audio_capture_eraser_enable(struct aoc_chip *chip, long enable)
return 0;
}
-#if ! IS_ENABLED(CONFIG_SOC_GS101)
int aoc_hotword_tap_enable(struct aoc_chip *chip, long enable)
{
- int cmd_id, err = 0;
+ if (chip->hotword_supported) {
+ int cmd_id, err = 0;
- cmd_id = (enable == 1) ? CMD_AUDIO_INPUT_HOTWORD_ENABLE_HOTWORD_TAP_ID :
- CMD_AUDIO_INPUT_HOTWORD_DISABLE_HOTWORD_TAP_ID;
- err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
- if (err < 0) {
- pr_err("ERR:%d in hotword tap %s\n", err, (enable) ? "enable" : "disable");
- return err;
- }
+ cmd_id = (enable == 1) ? CMD_AUDIO_INPUT_HOTWORD_ENABLE_HOTWORD_TAP_ID :
+ CMD_AUDIO_INPUT_HOTWORD_DISABLE_HOTWORD_TAP_ID;
+ err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
+ if (err < 0) {
+ pr_err("ERR:%d in hotword tap %s\n", err, (enable) ? "enable" : "disable");
+ return err;
+ }
- return 0;
+ return 0;
+ } else {
+ pr_err("WARN:hotword is not supported on this device\n");
+ return 0;
+ }
}
-#endif
int aoc_load_cca_module(struct aoc_chip *chip, long load)
{
@@ -3723,66 +3726,66 @@ int aoc_audio_set_chirp_parameter(struct aoc_chip *chip, int key, int value)
int aoc_audio_set_chre_src_pdm_gain(struct aoc_chip *chip, int gain)
{
-#if ! IS_ENABLED(CONFIG_SOC_GS101)
- int err;
- struct CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN cmd;
+ if (chip->chre_supported) {
+ int err;
+ struct CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN cmd;
- AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN_ID,
- sizeof(cmd));
- cmd.gain_centibel = gain;
+ AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN_ID,
+ sizeof(cmd));
+ cmd.gain_centibel = gain;
- err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
- sizeof(cmd), (uint8_t *)&cmd, chip);
- if (err < 0)
- pr_err("ERR:%d in AoC Set CHRE PDM gain\n", err);
+ err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
+ sizeof(cmd), (uint8_t *)&cmd, chip);
+ if (err < 0)
+ pr_err("ERR:%d in AoC Set CHRE PDM gain\n", err);
- return err < 0 ? err : 0;
-#else
- pr_err("WARN: setting CHRE PDM gain is not supported\n");
- return 0;
-#endif
+ return err < 0 ? err : 0;
+ } else {
+ pr_err("WARN: setting CHRE PDM gain is not supported\n");
+ return 0;
+ }
}
int aoc_audio_set_chre_src_aec_gain(struct aoc_chip *chip, int gain)
{
-#if ! IS_ENABLED(CONFIG_SOC_GS101)
- int err;
- struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN cmd;
+ if (chip->chre_supported) {
+ int err;
+ struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN cmd;
- AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN_ID,
- sizeof(cmd));
- cmd.gain_centibel = gain;
+ AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN_ID,
+ sizeof(cmd));
+ cmd.gain_centibel = gain;
- err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
- sizeof(cmd), (uint8_t *)&cmd, chip);
- if (err < 0)
- pr_err("ERR:%d in AoC Set CHRE AEC gain\n", err);
+ err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
+ sizeof(cmd), (uint8_t *)&cmd, chip);
+ if (err < 0)
+ pr_err("ERR:%d in AoC Set CHRE AEC gain\n", err);
- return err < 0 ? err : 0;
-#else
- pr_err("WARN: setting CHRE AEC gain is not supported\n");
- return 0;
-#endif
+ return err < 0 ? err : 0;
+ } else {
+ pr_err("WARN: setting CHRE AEC gain is not supported\n");
+ return 0;
+ }
}
int aoc_audio_set_chre_src_aec_timeout(struct aoc_chip *chip, int timeout)
{
-#if ! IS_ENABLED(CONFIG_SOC_GS101)
- int err;
- struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT cmd;
+ if (chip->chre_supported) {
+ int err;
+ struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT cmd;
- AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT_ID,
- sizeof(cmd));
- cmd.timeout_ms = timeout;
+ AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT_ID,
+ sizeof(cmd));
+ cmd.timeout_ms = timeout;
- err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
- sizeof(cmd), (uint8_t *)&cmd, chip);
- if (err < 0)
- pr_err("ERR:%d in AoC Set CHRE timeout\n", err);
+ err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
+ sizeof(cmd), (uint8_t *)&cmd, chip);
+ if (err < 0)
+ pr_err("ERR:%d in AoC Set CHRE timeout\n", err);
- return err < 0 ? err : 0;
-#else
- pr_err("WARN: setting CHRE AEC gain is not supported\n");
- return 0;
-#endif
-} \ No newline at end of file
+ return err < 0 ? err : 0;
+ } else {
+ pr_err("WARN: setting CHRE AEC gain is not supported\n");
+ return 0;
+ }
+}
diff --git a/alsa/aoc_alsa_path.c b/alsa/aoc_alsa_path.c
index d2f8d86..9652cce 100644
--- a/alsa/aoc_alsa_path.c
+++ b/alsa/aoc_alsa_path.c
@@ -2335,6 +2335,7 @@ static int aoc_path_probe(struct platform_device *pdev)
pr_err("%s: fail to reigster aoc path compon %d", __func__,
ret);
}
+
return ret;
}
diff --git a/aoc-interface-gs101.h b/aoc-interface-gs101.h
index 7b71b07..d876c6c 100644
--- a/aoc-interface-gs101.h
+++ b/aoc-interface-gs101.h
@@ -18,7 +18,7 @@
* Autogenerated AoC interface matching AoC source code
* associated with the following source code:
*
- * hash: 57ceae13174538d1ee19a1aaba563ec44f89e900
+ * hash: 369ba3d57af1d698f311a3a33f876930b6eb2b3b
*
* DO NOT MODIFY THIS FILE
*
@@ -83,7 +83,8 @@ enum AOC_COMMAND {
CMD_DBG_MEM_DUMP_ID = 38, /* [0x0026] -> struct CMD_DBG_MEM_DUMP */
CMD_DBG_MEM_INVAL_ACCESS_ID = 45, /* [0x002d] -> struct CMD_CORE_GENERIC */
CMD_DBG_MCPS_ID = 47, /* [0x002f] -> struct CMD_DBG_MCPS */
- CMD_DBG_SCRIB_ID = 48, /* [0x0030] -> struct CMD_DBG_SCRIB */
+ CMD_DBG_SCRIB_ID = 50, /* [0x0032] -> struct CMD_DBG_SCRIB */
+ CMD_DBG_PRIVATE_ID = 51, /* [0x0033] -> struct CMD_DBG_PRIVATE */
CMD_IPC_NOTIF_ENABLE_ID = 100, /* [0x0064] -> struct CMD_IPC_NOTIF_ENABLE */
CMD_AUDIO_OUTPUT_SINK_ID = 200, /* [0x00c8] -> struct CMD_AUDIO_OUTPUT_SINK */
CMD_DIAG_CORE_POWER_ID = 200, /* [0x00c8] -> struct CMD_DIAG_CORE_POWER */
@@ -255,7 +256,11 @@ enum AOC_COMMAND {
CMD_USB_CONTROL_SEND_FEEDBACK_EP_INFO_ID = 287, /* [0x011f] -> struct CMD_USB_CONTROL_SEND_FEEDBACK_EP_INFO */
CMD_AUDIO_INPUT_AMBIENT_MUSIC_BREAK_LOAD_MODEL_ID = 288, /* [0x0120] -> struct CMD_HDR */
CMD_AUDIO_INPUT_AMBIENT_MUSIC_BREAK_UNLOAD_MODEL_ID = 289, /* [0x0121] -> struct CMD_HDR */
+ CMD_AUDIO_OUTPUT_TELEPHONY_RTP_START_ID = 289, /* [0x0121] -> struct CMD_HDR */
+ CMD_AUDIO_OUTPUT_TELEPHONY_RTP_STOP_ID = 290, /* [0x0122] -> struct CMD_HDR */
+ CMD_AUDIO_OUTPUT_VOICE_ENABLE_CCA_ON_VOIP_ID = 291, /* [0x0123] -> struct CMD_HDR */
CMD_AUDIO_INPUT_MIC_RECORD_AP_START_PREPARE_ID = 292, /* [0x0124] -> struct CMD_HDR */
+ CMD_AUDIO_OUTPUT_VOICE_DISABLE_CCA_ON_VOIP_ID = 292, /* [0x0124] -> struct CMD_HDR */
CMD_AUDIO_INPUT_MIC_RECORD_AP_START_DATA_ID = 293, /* [0x0125] -> struct CMD_HDR */
CMD_AUDIO_INPUT_MIC_MMAP_ENABLE_ID = 294, /* [0x0126] -> struct CMD_AUDIO_INPUT_AP_INPUT_START */
CMD_AUDIO_INPUT_MIC_MMAP_DISABLE_ID = 295, /* [0x0127] -> struct CMD_HDR */
@@ -298,6 +303,13 @@ enum AOC_COMMAND {
CMD_AUDIO_INPUT_CAPTURE_INJECTION_STOP_ID = 341, /* [0x0155] -> struct CMD_HDR */
CMD_AUDIO_INPUT_HOTWORD_ENABLE_PRIMARY_MIC_SWAP_ID = 342, /* [0x0156] -> struct CMD_HDR */
CMD_AUDIO_INPUT_HOTWORD_DISABLE_PRIMARY_MIC_SWAP_ID = 343, /* [0x0157] -> struct CMD_HDR */
+ CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT_ID = 345, /* [0x0159] -> struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT */
+ CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN_ID = 346, /* [0x015a] -> struct CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN */
+ CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN_ID = 347, /* [0x015b] -> struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN */
+ CMD_AUDIO_INPUT_HOTWORD_ENABLE_NEURAL_AEC_ID = 350, /* [0x015e] -> struct CMD_HDR */
+ CMD_AUDIO_INPUT_HOTWORD_DISABLE_NEURAL_AEC_ID = 351, /* [0x015f] -> struct CMD_HDR */
+ CMD_AUDIO_INPUT_HOTWORD_ENABLE_HOTWORD_TAP_ID = 352, /* [0x0160] -> struct CMD_HDR */
+ CMD_AUDIO_INPUT_HOTWORD_DISABLE_HOTWORD_TAP_ID = 353, /* [0x0161] -> struct CMD_HDR */
};
/**
@@ -370,7 +382,11 @@ struct CONTAINER_HDR {
* CMD_AUDIO_OUTPUT_DECODER_GAPLESS_DEINIT_ID, UUID: a77ecb0deb43d8b2cff697ec83bd8571
* CMD_AUDIO_INPUT_AMBIENT_MUSIC_BREAK_LOAD_MODEL_ID, UUID: 3667c2affee42fed78892d05626d0a88
* CMD_AUDIO_INPUT_AMBIENT_MUSIC_BREAK_UNLOAD_MODEL_ID, UUID: d3b7cbecbc7f3c607bf090245f691002
+ * CMD_AUDIO_OUTPUT_TELEPHONY_RTP_START_ID, UUID: 9d12f53f498b427930257a6d8010478c
+ * CMD_AUDIO_OUTPUT_TELEPHONY_RTP_STOP_ID, UUID: cc499dc1692b5fe55df8fc02f9809c7f
+ * CMD_AUDIO_OUTPUT_VOICE_ENABLE_CCA_ON_VOIP_ID, UUID: 514450e55854eed2a15651067d6a3e55
* CMD_AUDIO_INPUT_MIC_RECORD_AP_START_PREPARE_ID, UUID: a2d983137ee5c50405f719a310c20971
+ * CMD_AUDIO_OUTPUT_VOICE_DISABLE_CCA_ON_VOIP_ID, UUID: 67226e15aef11c25557ca1130234a756
* CMD_AUDIO_INPUT_MIC_RECORD_AP_START_DATA_ID, UUID: eb4ca3ecc1e61d8abbdc79671dc0edbb
* CMD_AUDIO_INPUT_MIC_MMAP_DISABLE_ID, UUID: 02aa59769391dbf4e0bae82fbcaebb3c
* CMD_AUDIO_INPUT_WNR_START_ID, UUID: cdfe4c6b0cbbebb53cd4f1de4cf7d10a
@@ -395,6 +411,10 @@ struct CONTAINER_HDR {
* CMD_AUDIO_INPUT_CAPTURE_INJECTION_STOP_ID, UUID: 59bd69e4f8da57ee844043c426239ba0
* CMD_AUDIO_INPUT_HOTWORD_ENABLE_PRIMARY_MIC_SWAP_ID, UUID: 14406b26352156fc3fd45dbd2afe8f21
* CMD_AUDIO_INPUT_HOTWORD_DISABLE_PRIMARY_MIC_SWAP_ID, UUID: 8dcc7c59a45658053cffdef0ac6d079e
+ * CMD_AUDIO_INPUT_HOTWORD_ENABLE_NEURAL_AEC_ID, UUID: 9d485929dc61d3babd81ddff05cf9392
+ * CMD_AUDIO_INPUT_HOTWORD_DISABLE_NEURAL_AEC_ID, UUID: c8a19d5b447ce1fae9a8666883c723d7
+ * CMD_AUDIO_INPUT_HOTWORD_ENABLE_HOTWORD_TAP_ID, UUID: 26bde63caffd544e581a7293f1cf793c
+ * CMD_AUDIO_INPUT_HOTWORD_DISABLE_HOTWORD_TAP_ID, UUID: 228b2b4c01e7c126658056a596ac4e5c
*/
struct CMD_HDR {
struct CONTAINER_HDR parent;
@@ -2266,7 +2286,8 @@ struct CMD_AUDIO_INPUT_FEEDBACK_SRC_SELECT_REF {
} __attribute__((packed));
enum UDFPS_INPUT_SOURCE {
- SOURCE_OSC = 0,
+ SOURCE_NONE = 0,
+ SOURCE_OSC,
SOURCE_DISP,
SOURCE_TOT,
};
@@ -2389,6 +2410,17 @@ struct CMD_DBG_SCRIB {
int32_t free_after_free;
} __attribute__((packed));
+/**
+ * Structure associated with the following commands:
+ *
+ * CMD_DBG_PRIVATE_ID, UUID: e338efdfe6222a0d2dd95100233654c1
+ */
+struct CMD_DBG_PRIVATE {
+ struct CMD_CORE_GENERIC parent;
+ uint8_t sub_cmd;
+ uint8_t payload_size;
+} __attribute__((packed));
+
enum ApStateTransition {
ENTER_SLEEP = 0,
EXIT_SLEEP,
@@ -2407,6 +2439,36 @@ struct CMD_AP_STATE_TRANSITION {
uint8_t transition; /* For valid values, refer to "enum ApStateTransition" */
} __attribute__((packed));
+/**
+ * Structure associated with the following commands:
+ *
+ * CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT_ID, UUID: cf9af404b8238148390eb250b535c2a9
+ */
+struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT {
+ struct CMD_HDR parent;
+ uint32_t timeout_ms;
+} __attribute__((packed));
+
+/**
+ * Structure associated with the following commands:
+ *
+ * CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN_ID, UUID: 70ad1f841ce46465e2389e08938835c9
+ */
+struct CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN {
+ struct CMD_HDR parent;
+ int32_t gain_centibel;
+} __attribute__((packed));
+
+/**
+ * Structure associated with the following commands:
+ *
+ * CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN_ID, UUID: fde782e5f1431573bd867d163fbf43a1
+ */
+struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN {
+ struct CMD_HDR parent;
+ int32_t gain_centibel;
+} __attribute__((packed));
+
struct NOTIF_HDR {
struct CONTAINER_HDR parent;
uint32_t id;
diff --git a/aoc.c b/aoc.c
index c2f2003..8a0daae 100644
--- a/aoc.c
+++ b/aoc.c
@@ -14,20 +14,15 @@
#include "aoc.h"
#include <linux/atomic.h>
-#include <linux/cdev.h>
-#include <linux/delay.h>
#include <linux/dma-map-ops.h>
#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/glob.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/jiffies.h>
-#include <linux/kernel.h>
#include <linux/list.h>
-#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
@@ -35,12 +30,10 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_data/sscoredump.h>
-#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/string.h>
-#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <linux/wait.h>
@@ -53,174 +46,30 @@
#include <linux/gsa/gsa_aoc.h>
-#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
-#include <soc/google/exynos-itmon.h>
-#endif
-
-#include "ion_physical_heap.h"
-
#include "aoc_firmware.h"
-#include "aoc_ipc_core.h"
#include "aoc_ramdump_regions.h"
-/* TODO: Remove internal calls, or promote to "public" */
-#include "aoc_ipc_core_internal.h"
-
-/* This should not be required, as we expect only one of the two to be defined */
-#if IS_ENABLED(CONFIG_SOC_GS201)
- #undef CONFIG_SOC_GS101
-#endif
-
-#if IS_ENABLED(CONFIG_SOC_GS201) && IS_ENABLED(CONFIG_SOC_GS101)
- #error "GS201 and GS101 are mutually exclusive"
-#endif
-
-#define MAX_FIRMWARE_LENGTH 128
-#define AP_RESET_REASON_LENGTH 32
-#define AOC_S2MPU_CTRL0 0x0
-
#define AOC_MAX_MINOR (1U)
-#if IS_ENABLED(CONFIG_SOC_GS101)
- #define AOC_MBOX_CHANNELS 16 /* AP-A32 mbox */
-#else
- #define AOC_MBOX_CHANNELS (16 * 3) /* AP-A32, AP-F1 and AP-P6 mbox */
-#endif
#define AOC_FWDATA_ENTRIES 10
#define AOC_FWDATA_BOARDID_DFL 0x20202
#define AOC_FWDATA_BOARDREV_DFL 0x10000
-#define SENSOR_DIRECT_HEAP_SIZE SZ_4M
-#define PLAYBACK_HEAP_SIZE SZ_16K
-#define CAPTURE_HEAP_SIZE SZ_64K
-
#define MAX_RESET_REASON_STRING_LEN 128UL
-#define MAX_SENSOR_POWER_NUM 5
-
-#if IS_ENABLED(CONFIG_SOC_GS201)
- #define AOC_PCU_BASE AOC_PCU_BASE_PRO
- #define AOC_GPIO_BASE AOC_GPIO_BASE_PRO
- #define AOC_CP_APERTURE_START_OFFSET 0x7FDF80
- #define AOC_CP_APERTURE_END_OFFSET 0x7FFFFF
- #define AOC_CLOCK_DIVIDER 1
-#elif IS_ENABLED(CONFIG_SOC_GS101)
- #define AOC_PCU_BASE AOC_PCU_BASE_WC
- #define AOC_GPIO_BASE AOC_GPIO_BASE_WC
- #define AOC_CP_APERTURE_START_OFFSET 0x5FDF80
- #define AOC_CP_APERTURE_END_OFFSET 0x5FFFFF
- #define GPIO_INTERRUPT 93
- #define AOC_CLOCK_DIVIDER 6
-#endif
-
-#define MAX_SENSOR_POWER_NUM 5
-
#define RESET_WAIT_TIMES_NUM 3
#define RESET_WAIT_TIME_MS 3000
#define RESET_WAIT_TIME_INCREMENT_MS 2048
-static DEFINE_MUTEX(aoc_service_lock);
-
-enum AOC_FW_STATE {
- AOC_STATE_OFFLINE = 0,
- AOC_STATE_FIRMWARE_LOADED,
- AOC_STATE_STARTING,
- AOC_STATE_ONLINE
-};
-static enum AOC_FW_STATE aoc_state;
-
-static struct platform_device *aoc_platform_device;
+DEFINE_MUTEX(aoc_service_lock);
-struct mbox_slot {
- struct mbox_client client;
- struct mbox_chan *channel;
- void *prvdata;
- int index;
-};
-
-struct aoc_prvdata {
- struct mbox_slot mbox_channels[AOC_MBOX_CHANNELS];
- struct aoc_service_dev **services;
-
- unsigned long *read_blocked_mask;
- unsigned long *write_blocked_mask;
-
- struct work_struct online_work;
- struct resource dram_resource;
- aoc_map_handler map_handler;
- void *map_handler_ctx;
-
- struct delayed_work monitor_work;
- atomic_t aoc_process_active;
-
- struct device *dev;
- struct iommu_domain *domain;
- void *ipc_base;
-
- void *sram_virt;
- void *dram_virt;
- void *aoc_req_virt;
- void *aoc_s2mpu_virt;
- size_t sram_size;
- size_t dram_size;
- size_t aoc_req_size;
- u32 aoc_s2mpu_saved_value;
-
- struct dma_heap *sensor_heap;
- struct dma_heap *audio_playback_heap;
- struct dma_heap *audio_capture_heap;
- phys_addr_t sensor_heap_base;
- phys_addr_t audio_playback_heap_base;
- phys_addr_t audio_capture_heap_base;
-
- int watchdog_irq;
- struct work_struct watchdog_work;
- bool aoc_reset_done;
- bool ap_triggered_reset;
- char ap_reset_reason[AP_RESET_REASON_LENGTH];
- wait_queue_head_t aoc_reset_wait_queue;
- unsigned int acpm_async_id;
- int total_services;
-
- char firmware_name[MAX_FIRMWARE_LENGTH];
- char *firmware_version;
-
- struct cdev cdev;
- dev_t aoc_devt;
- struct class *_class;
- struct device *_device;
-
- u32 disable_monitor_mode;
- u32 enable_uart_tx;
- u32 force_voltage_nominal;
- u32 no_ap_resets;
- u32 force_speaker_ultrasonic;
-
- u32 total_coredumps;
- u32 total_restarts;
- unsigned int sysmmu_nonsecure_irq;
- unsigned int sysmmu_secure_irq;
-
-#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
- struct notifier_block itmon_nb;
-#endif
- struct device *gsa_dev;
- bool protected_by_gsa;
-
- int sensor_power_count;
- const char *sensor_power_list[MAX_SENSOR_POWER_NUM];
- struct regulator *sensor_regulator[MAX_SENSOR_POWER_NUM];
-
- int reset_hysteresis_trigger_ms;
- u64 last_reset_time_ns;
- int reset_wait_time_index;
-};
+enum AOC_FW_STATE aoc_state;
-struct aoc_prvdata *aoc_prvdata_copy;
+struct platform_device *aoc_platform_device;
/* TODO: Reduce the global variables (move into a driver structure) */
/* Resources found from the device tree */
-static struct resource *aoc_sram_resource;
+struct resource *aoc_sram_resource;
struct sscd_info {
char *name;
@@ -228,7 +77,6 @@ struct sscd_info {
u16 seg_count;
};
-static void trigger_aoc_ramdump(struct aoc_prvdata *prvdata);
static void sscd_release(struct device *dev);
static struct sscd_info sscd_info;
@@ -246,15 +94,25 @@ static void *aoc_dram_virt_mapping;
static int aoc_irq;
-static struct aoc_control_block *aoc_control;
+struct aoc_control_block *aoc_control;
static int aoc_major;
static const char *default_firmware = "aoc.bin";
-static bool aoc_autoload_firmware;
+static bool aoc_autoload_firmware = false;
module_param(aoc_autoload_firmware, bool, 0644);
MODULE_PARM_DESC(aoc_autoload_firmware, "Automatically load firmware if true");
+static bool aoc_disable_restart = false;
+module_param(aoc_disable_restart, bool, 0644);
+MODULE_PARM_DESC(aoc_disable_restart, "Prevent AoC from restarting after crashing.");
+
+static bool aoc_panic_on_req_timeout = true;
+module_param(aoc_panic_on_req_timeout, bool, 0644);
+MODULE_PARM_DESC(aoc_panic_on_req_timeout, "Enable kernel panic when aoc_req times out.");
+
+static struct aoc_module_parameters *aoc_module_params;
+
static int aoc_core_suspend(struct device *dev);
static int aoc_core_resume(struct device *dev);
@@ -267,8 +125,8 @@ static int aoc_bus_match(struct device *dev, struct device_driver *drv);
static int aoc_bus_probe(struct device *dev);
static int aoc_bus_remove(struct device *dev);
+static void aoc_configure_sysmmu_fault_handler(struct aoc_prvdata *p);
static void aoc_configure_sysmmu(struct aoc_prvdata *p, const struct firmware *fw);
-static void aoc_configure_sysmmu_manual(struct aoc_prvdata *p);
static struct bus_type aoc_bus_type = {
.name = "aoc",
@@ -282,52 +140,17 @@ struct aoc_client {
int endpoint;
};
-static bool aoc_fpga_reset(struct aoc_prvdata *prvdata);
-static bool write_reset_trampoline(u32 addr);
-static bool aoc_a32_reset(void);
+static bool write_reset_trampoline(const struct firmware *fw);
+static bool configure_dmic_regulator(struct aoc_prvdata *prvdata, bool enable);
static bool configure_sensor_regulator(struct aoc_prvdata *prvdata, bool enable);
-static int aoc_watchdog_restart(struct aoc_prvdata *prvdata);
-static void acpm_aoc_reset_callback(unsigned int *cmd, unsigned int size);
-static int start_firmware_load(struct device *dev);
static void aoc_take_offline(struct aoc_prvdata *prvdata);
-static void signal_aoc(struct mbox_chan *channel);
-static void reset_sensor_power(struct aoc_prvdata *prvdata, bool is_init);
static void aoc_process_services(struct aoc_prvdata *prvdata, int offset);
-static irqreturn_t watchdog_int_handler(int irq, void *dev);
static void aoc_watchdog(struct work_struct *work);
-#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
-static int aoc_itmon_notifier(struct notifier_block *nb, unsigned long action,
- void *nb_data)
-{
- struct aoc_prvdata *prvdata;
- struct itmon_notifier *itmon_info = nb_data;
-
- prvdata = container_of(nb, struct aoc_prvdata, itmon_nb);
- if (itmon_info->port && (strncmp(itmon_info->port, "AOC", sizeof("AOC") - 1) == 0))
- return NOTIFY_STOP;
-
- if (itmon_info->target_addr == 0) {
- dev_err(prvdata->dev,
- "Possible repro of b/174577569, please upload a bugreport and /data/vendor/ssrdump to that bug\n");
- return NOTIFY_STOP;
- }
-
- if ((itmon_info->target_addr >= aoc_sram_resource->start + AOC_CP_APERTURE_START_OFFSET) &&
- (itmon_info->target_addr <= aoc_sram_resource->start + AOC_CP_APERTURE_END_OFFSET)) {
- dev_err(prvdata->dev,
- "Valid memory access triggered ITMON error. Please file a bug with bugreport and contents of /data/vendor/ssrdump\n");
- return NOTIFY_STOP;
- }
-
- return NOTIFY_OK;
-}
-#endif
-
-static inline void *aoc_sram_translate(u32 offset)
+void *aoc_sram_translate(u32 offset)
{
BUG_ON(aoc_sram_virt_mapping == NULL);
if (offset > resource_size(aoc_sram_resource))
@@ -345,7 +168,7 @@ static inline void *aoc_dram_translate(struct aoc_prvdata *p, u32 offset)
return p->dram_virt + offset;
}
-static bool aoc_is_valid_dram_address(struct aoc_prvdata *prv, void *addr)
+bool aoc_is_valid_dram_address(struct aoc_prvdata *prv, void *addr)
{
ptrdiff_t offset;
@@ -356,7 +179,7 @@ static bool aoc_is_valid_dram_address(struct aoc_prvdata *prv, void *addr)
return (offset < prv->dram_size);
}
-static inline phys_addr_t aoc_dram_translate_to_aoc(struct aoc_prvdata *p,
+phys_addr_t aoc_dram_translate_to_aoc(struct aoc_prvdata *p,
phys_addr_t addr)
{
phys_addr_t phys_start = p->dram_resource.start;
@@ -370,61 +193,11 @@ static inline phys_addr_t aoc_dram_translate_to_aoc(struct aoc_prvdata *p,
return AOC_BINARY_DRAM_BASE + offset;
}
-static inline bool aoc_fw_ready(void)
+bool aoc_fw_ready(void)
{
return aoc_control != NULL && aoc_control->magic == AOC_MAGIC;
}
-static inline int aoc_num_services(void)
-{
- return aoc_fw_ready() ? le32_to_cpu(aoc_control->services) : 0;
-}
-
-static inline aoc_service *service_at_index(struct aoc_prvdata *prvdata,
- unsigned index)
-{
- if (!aoc_fw_ready() || index > aoc_num_services())
- return NULL;
-
- return (((uint8_t *)prvdata->ipc_base) + aoc_control->services_offset +
- (le32_to_cpu(aoc_control->service_size) * index));
-}
-
-static inline struct aoc_service_dev *service_dev_at_index(struct aoc_prvdata *prvdata, unsigned index)
-{
- if (!aoc_fw_ready() || index > aoc_num_services() || aoc_state != AOC_STATE_ONLINE)
- return NULL;
-
- return prvdata->services[index];
-}
-
-static bool validate_service(struct aoc_prvdata *prv, int i)
-{
- struct aoc_ipc_service_header *hdr = service_at_index(prv, i);
- struct device *dev = prv->dev;
-
- if (!aoc_is_valid_dram_address(prv, hdr)) {
- dev_err(dev, "service %d is not in DRAM region\n", i);
- return false;
- }
-
- if (hdr->regions[0].slots == 0 && hdr->regions[1].slots == 0) {
- dev_err(dev, "service %d is not readable or writable\n", i);
-
- return false;
- }
-
- if (aoc_service_is_ring(hdr) &&
- (hdr->regions[0].slots > 1 || hdr->regions[1].slots > 1)) {
- dev_err(dev, "service %d has invalid ring slot configuration\n",
- i);
-
- return false;
- }
-
- return true;
-}
-
static int driver_matches_service_by_name(struct device_driver *drv, void *name)
{
struct aoc_driver *aoc_drv = AOC_DRIVER(drv);
@@ -523,14 +296,13 @@ static int allocate_mailbox_channels(struct aoc_prvdata *prv)
struct mbox_slot *slot;
int i, rc = 0;
- for (i = 0; i < ARRAY_SIZE(prv->mbox_channels); i++) {
+ for (i = 0; i < prv->aoc_mbox_channels; i++) {
slot = &prv->mbox_channels[i];
slot->channel = mbox_request_channel(&slot->client, i);
if (IS_ERR(slot->channel)) {
- dev_err(dev, "failed to find mailbox interface %d : %ld\n", i,
- PTR_ERR(slot->channel));
+ rc = PTR_ERR(slot->channel);
+ dev_err(dev, "failed to find mailbox interface %d : %d\n", i, rc);
slot->channel = NULL;
- rc = -EIO;
goto err_mbox_req;
}
}
@@ -547,7 +319,7 @@ static void free_mailbox_channels(struct aoc_prvdata *prv)
struct mbox_slot *slot;
int i;
- for (i = 0; i < ARRAY_SIZE(prv->mbox_channels); i++) {
+ for (i = 0; i < prv->aoc_mbox_channels; i++) {
slot = &prv->mbox_channels[i];
if (slot->channel) {
mbox_free_channel(slot->channel);
@@ -584,67 +356,25 @@ static void aoc_mbox_tx_done(struct mbox_client *cl, void *mssg, int r)
{
}
-static void aoc_req_assert(struct aoc_prvdata *p, bool assert)
-{
- iowrite32(!!assert, p->aoc_req_virt);
-}
-
-static int aoc_req_wait(struct aoc_prvdata *p, bool assert)
-{
- unsigned long aoc_req_timeout;
-
- aoc_req_timeout = jiffies + (2 * HZ);
- while (time_before(jiffies, aoc_req_timeout)) {
- if (!!readl(p->aoc_req_virt + 0x40) == !!assert)
- return 0;
- msleep(100);
- }
-
- return -ETIMEDOUT;
-}
-
extern int gs_chipid_get_ap_hw_tune_array(const u8 **array);
-#if IS_ENABLED(CONFIG_SOC_GS101)
-static bool aoc_sram_was_repaired(struct aoc_prvdata *prvdata)
-{
- const u8 *array;
- struct device *dev = prvdata->dev;
- int ret;
-
- ret = gs_chipid_get_ap_hw_tune_array(&array);
-
- if (ret == -EPROBE_DEFER) {
- dev_err(dev, "Unable to determine SRAM repair state. Leaving monitor mode disabled\n");
- return false;
- }
-
- if (ret != 32) {
- dev_err(dev, "Unexpected hw_tune_array size. Leaving monitor mode disabled\n");
- return false;
- }
-
- /* Bit 65 says that AoC SRAM was repaired */
- return ((array[8] & 0x2) != 0);
-}
-#else
static inline bool aoc_sram_was_repaired(struct aoc_prvdata *prvdata) { return false; }
-#endif
struct aoc_fw_data {
u32 key;
u32 value;
};
-static u32 dt_property(struct device_node *node, const char *key)
+u32 dt_property(struct device_node *node, const char *key)
{
u32 ret;
if (of_property_read_u32(node, key, &ret))
- return 0xffffffff;
+ return DT_PROPERTY_NOT_FOUND;
return ret;
}
+EXPORT_SYMBOL_GPL(dt_property);
static void aoc_pass_fw_information(void *base, const struct aoc_fw_data *fwd,
size_t num)
@@ -675,7 +405,7 @@ static u32 aoc_board_config_parse(struct device_node *node, u32 *board_id, u32 *
pr_info("Assuming R4/O6 board configuration");
*board_id = AOC_FWDATA_BOARDID_DFL;
*board_rev = AOC_FWDATA_BOARDREV_DFL;
- return err;
+ return err;
}
/* Read board id from device tree */
@@ -736,6 +466,7 @@ err_alloc:
static void aoc_fw_callback(const struct firmware *fw, void *ctx)
{
+ static bool first_load_prevented = false;
struct device *dev = ctx;
struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
u32 sram_was_repaired = aoc_sram_was_repaired(prvdata);
@@ -748,6 +479,12 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx)
u32 force_speaker_ultrasonic = prvdata->force_speaker_ultrasonic;
u32 board_id = AOC_FWDATA_BOARDID_DFL;
u32 board_rev = AOC_FWDATA_BOARDREV_DFL;
+ u32 rand_seed = get_random_u32();
+ u32 chip_revision = gs_chipid_get_revision();
+ u32 chip_type = gs_chipid_get_type();
+ u32 dt_gnss_type = dt_property(prvdata->dev->of_node, "gnss-type");
+ u32 gnss_type = dt_gnss_type == 0xffffffff ? 0 : dt_gnss_type;
+ bool dt_prevent_aoc_load = (dt_property(prvdata->dev->of_node, "prevent-fw-load")==1);
phys_addr_t sensor_heap = aoc_dram_translate_to_aoc(prvdata, prvdata->sensor_heap_base);
phys_addr_t playback_heap = aoc_dram_translate_to_aoc(prvdata, prvdata->audio_playback_heap_base);
phys_addr_t capture_heap = aoc_dram_translate_to_aoc(prvdata, prvdata->audio_capture_heap_base);
@@ -769,19 +506,35 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx)
{ .key = kAOCForceVNOM, .value = force_vnom },
{ .key = kAOCDisableMM, .value = disable_mm },
{ .key = kAOCEnableUART, .value = enable_uart },
- { .key = kAOCForceSpeakerUltrasonic, .value = force_speaker_ultrasonic }
+ { .key = kAOCForceSpeakerUltrasonic, .value = force_speaker_ultrasonic },
+ { .key = kAOCRandSeed, .value = rand_seed },
+ { .key = kAOCChipRevision, .value = chip_revision },
+ { .key = kAOCChipType, .value = chip_type },
+ { .key = kAOCGnssType, .value = gnss_type }
};
+
const char *version;
u32 fw_data_entries = ARRAY_SIZE(fw_data);
- u32 ipc_offset, bootloader_offset;
+ u32 ipc_offset;
+
+ if ((dt_prevent_aoc_load) && (!first_load_prevented)) {
+ dev_err(dev, "DTS settings prevented AoC firmware from being loaded\n");
+ first_load_prevented = true;
+ return;
+ }
aoc_board_config_parse(prvdata->dev->of_node, &board_id, &board_rev);
if (!fw) {
- dev_err(dev, "failed to load firmware image\n");
+ dev_err(dev, "Failed to load AoC firmware image\n");
return;
}
+ if (prvdata->force_release_aoc) {
+ dev_info(dev, "Force Reload Trigger: Free current loaded\n");
+ goto free_fw;
+ }
+
for (i = 0; i < fw_data_entries; i++) {
if (fw_data[i].key == kAOCBoardID)
fw_data[i].value = board_id;
@@ -789,7 +542,7 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx)
fw_data[i].value = board_rev;
}
- aoc_req_assert(prvdata, true);
+ request_aoc_on(prvdata, true);
if (!fw->data) {
dev_err(dev, "firmware image contains no data\n");
@@ -802,7 +555,6 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx)
}
ipc_offset = _aoc_fw_ipc_offset(fw);
- bootloader_offset = _aoc_fw_bootloader_offset(fw);
version = _aoc_fw_version(fw);
prvdata->firmware_version = devm_kasprintf(dev, GFP_KERNEL, "%s", version);
@@ -834,33 +586,38 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx)
}
fw_signed = _aoc_fw_is_signed(fw);
- prvdata->protected_by_gsa = fw_signed;
+ if (!fw_signed) {
+ dev_err(dev, "Loading unsigned aoc image is unsupported\n");
+ goto free_fw;
+ }
- dev_info(dev, "Loading %s aoc image\n", fw_signed ? "signed" : "unsigned");
+ dev_info(dev, "Loading signed aoc image\n");
- aoc_control = aoc_dram_translate(prvdata, ipc_offset);
+ prvdata->protected_by_gsa = fw_signed;
- aoc_fpga_reset(prvdata);
+ aoc_control = aoc_dram_translate(prvdata, ipc_offset);
- _aoc_fw_commit(fw, aoc_dram_virt_mapping + AOC_BINARY_DRAM_OFFSET);
+ {
+ bool commit_rc = _aoc_fw_commit(fw, aoc_dram_virt_mapping + AOC_BINARY_DRAM_OFFSET);
+ if (!commit_rc) {
+ dev_err(dev, "FW commit failed!\n");
+ }
+ }
gsa_enabled = of_property_read_bool(prvdata->dev->of_node, "gsa-enabled");
- if (fw_signed) {
- if (gsa_enabled) {
- int rc = aoc_fw_authenticate(prvdata, fw);
+ if (gsa_enabled) {
+ int rc;
- if (rc) {
- dev_err(dev, "GSA: FW authentication failed: %d\n", rc);
- goto free_fw;
- }
- } else {
- aoc_configure_sysmmu(prvdata, fw);
- write_reset_trampoline(AOC_BINARY_LOAD_ADDRESS + bootloader_offset);
+ aoc_configure_sysmmu_fault_handler(prvdata);
+ rc = aoc_fw_authenticate(prvdata, fw);
+ if (rc) {
+ dev_err(dev, "GSA: FW authentication failed: %d\n", rc);
+ goto free_fw;
}
} else {
- aoc_configure_sysmmu_manual(prvdata);
- write_reset_trampoline(AOC_BINARY_LOAD_ADDRESS + bootloader_offset);
+ aoc_configure_sysmmu(prvdata, fw);
+ write_reset_trampoline(fw);
}
aoc_pass_fw_information(aoc_dram_translate(prvdata, ipc_offset),
@@ -868,19 +625,20 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx)
aoc_state = AOC_STATE_FIRMWARE_LOADED;
- dev_info(dev, "disabling SICD for 2 sec for aoc boot\n");
+ /* AOC needs DRAM while booting, so prevent AP from sleep. */
+ dev_info(dev, "preventing AP from sleep for 2 sec for aoc boot\n");
disable_power_mode(0, POWERMODE_TYPE_SYSTEM);
prvdata->ipc_base = aoc_dram_translate(prvdata, ipc_offset);
/* start AOC */
- if (fw_signed && gsa_enabled) {
+ if (gsa_enabled) {
int rc = gsa_send_aoc_cmd(prvdata->gsa_dev, GSA_AOC_START);
if (rc < 0) {
dev_err(dev, "GSA: Failed to start AOC: %d\n", rc);
goto free_fw;
}
} else {
- aoc_a32_reset();
+ aoc_release_from_reset(prvdata);
}
enable_irq(prvdata->watchdog_irq);
@@ -891,10 +649,15 @@ static void aoc_fw_callback(const struct firmware *fw, void *ctx)
msecs_to_jiffies(5 * 1000));
msleep(2000);
- dev_info(dev, "re-enabling SICD\n");
+ dev_info(dev, "re-enabling low power mode\n");
enable_power_mode(0, POWERMODE_TYPE_SYSTEM);
+ release_firmware(fw);
+ return;
+
free_fw:
+ /* Change aoc_state to offline due to abnormal firmware */
+ aoc_state = AOC_STATE_OFFLINE;
release_firmware(fw);
}
@@ -950,662 +713,31 @@ phys_addr_t aoc_get_heap_base_phys_addr(struct aoc_service_dev *dev, aoc_directi
else
audio_heap_base = prvdata->audio_capture_heap_base;
- pr_debug("Get heap address(phy):%llx\n", audio_heap_base);
+ pr_debug("Get heap address(phy):%pap\n", &audio_heap_base);
return audio_heap_base;
}
EXPORT_SYMBOL_GPL(aoc_get_heap_base_phys_addr);
-bool aoc_service_flush_read_data(struct aoc_service_dev *dev)
+static bool write_reset_trampoline(const struct firmware *fw)
{
- const struct device *parent;
- struct aoc_prvdata *prvdata;
- aoc_service *service;
- size_t slots;
-
- if (!dev)
- return false;
-
- parent = dev->dev.parent;
- prvdata = dev_get_drvdata(parent);
-
- service = service_at_index(prvdata, dev->service_index);
-
- slots = aoc_service_slots_available_to_read(service, AOC_UP);
- if (slots == 0)
- return false;
-
- aoc_service_advance_read_index(service, AOC_UP, slots);
- return true;
-}
-EXPORT_SYMBOL_GPL(aoc_service_flush_read_data);
-
-ssize_t aoc_service_read(struct aoc_service_dev *dev, uint8_t *buffer,
- size_t count, bool block)
-{
- const struct device *parent;
- struct aoc_prvdata *prvdata;
- aoc_service *service;
-
- size_t msg_size;
- int service_number;
- int ret = 0;
- bool was_full;
- int interrupt = dev->mbox_index;
-
- if (!dev || !buffer || !count)
- return -EINVAL;
-
- if (dev->dead)
- return -ENODEV;
-
- if (aoc_state != AOC_STATE_ONLINE)
- return -EBUSY;
-
- parent = dev->dev.parent;
- prvdata = dev_get_drvdata(parent);
-
- service_number = dev->service_index;
- service = service_at_index(prvdata, dev->service_index);
-
- BUG_ON(!aoc_is_valid_dram_address(prvdata, service));
-
- if (aoc_service_message_slots(service, AOC_UP) == 0)
- return -EBADF;
-
- if (!aoc_service_can_read_message(service, AOC_UP)) {
- if (!block)
- return -EAGAIN;
-
- set_bit(service_number, prvdata->read_blocked_mask);
- ret = wait_event_interruptible(dev->read_queue,
- aoc_state != AOC_STATE_ONLINE || dev->dead ||
- aoc_service_can_read_message(service, AOC_UP));
- clear_bit(service_number, prvdata->read_blocked_mask);
- }
-
- if (dev->dead)
- return -ENODEV;
-
- if (aoc_state != AOC_STATE_ONLINE)
- return -ENODEV;
-
- /*
- * The wait can fail if the AoC goes offline in the middle of a
- * blocking read, so check again after the wait
- */
- if (ret != 0)
- return -EAGAIN;
-
- if (!aoc_service_is_ring(service) &&
- count < aoc_service_current_message_size(service, prvdata->ipc_base,
- AOC_UP))
- return -EFBIG;
-
- msg_size = count;
- was_full = !aoc_service_can_write_message(service, AOC_UP);
-
- aoc_service_read_message(service, prvdata->ipc_base, AOC_UP, buffer,
- &msg_size);
-
- /*
- * If the service queue was full right before reading, signal AoC that
- * there is now space available to write.
- */
- if (was_full)
- signal_aoc(prvdata->mbox_channels[interrupt].channel);
-
- return msg_size;
-}
-EXPORT_SYMBOL_GPL(aoc_service_read);
-
-
-bool aoc_online_state(struct aoc_service_dev *dev) {
- struct aoc_prvdata *prvdata;
- if (!dev)
- return false;
-
- prvdata = dev_get_drvdata(dev->dev.parent);
- if (!prvdata)
- return false;
-
- if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work))
- return false;
- return true;
-}
-EXPORT_SYMBOL_GPL(aoc_online_state);
-
-ssize_t aoc_service_read_timeout(struct aoc_service_dev *dev, uint8_t *buffer,
- size_t count, long timeout)
-{
- struct aoc_prvdata *prvdata;
- aoc_service *service;
-
- size_t msg_size;
- int service_number;
- long ret = 1;
-
- if (!dev || !buffer || !count)
- return -EINVAL;
-
- if (dev->dead)
- return -ENODEV;
-
- prvdata = dev_get_drvdata(dev->dev.parent);
- if (!prvdata)
- return -ENODEV;
-
- atomic_inc(&prvdata->aoc_process_active);
- if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) {
- ret = -EBUSY;
- goto err;
- }
-
- service_number = dev->service_index;
- service = service_at_index(prvdata, dev->service_index);
-
- if (!aoc_is_valid_dram_address(prvdata, service)) {
- WARN_ONCE(1, "aoc service %d has invalid DRAM region", service_number);
- ret = -ENODEV;
- goto err;
- }
-
- if (aoc_service_message_slots(service, AOC_UP) == 0) {
- ret = -EBADF;
- goto err;
- }
-
- if (!aoc_service_can_read_message(service, AOC_UP)) {
- set_bit(service_number, prvdata->read_blocked_mask);
- ret = wait_event_interruptible_timeout(
- dev->read_queue,
- aoc_state != AOC_STATE_ONLINE || dev->dead ||
- aoc_service_can_read_message(service, AOC_UP),
- timeout);
- clear_bit(service_number, prvdata->read_blocked_mask);
- }
-
- if (dev->dead || (aoc_state != AOC_STATE_ONLINE)) {
- ret = -ENODEV;
- goto err;
- }
-
- if (ret < 0)
- goto err;
-
- /* AoC timed out */
- if (ret == 0) {
- ret = -ETIMEDOUT;
- goto err;
- }
-
- if (!aoc_service_is_ring(service) &&
- count < aoc_service_current_message_size(service, prvdata->ipc_base,
- AOC_UP)) {
- ret = -EFBIG;
- goto err;
- }
-
- msg_size = count;
- aoc_service_read_message(service, prvdata->ipc_base, AOC_UP, buffer,
- &msg_size);
-
-err:
- atomic_dec(&prvdata->aoc_process_active);
-
- if (ret < 0)
- return ret;
-
- return msg_size;
-}
-EXPORT_SYMBOL_GPL(aoc_service_read_timeout);
-
-ssize_t aoc_service_write(struct aoc_service_dev *dev, const uint8_t *buffer,
- size_t count, bool block)
-{
- const struct device *parent;
- struct aoc_prvdata *prvdata;
-
- aoc_service *service;
- int service_number;
- int interrupt = dev->mbox_index;
- int ret = 0;
-
- if (!dev || !buffer || !count)
- return -EINVAL;
-
- if (dev->dead)
- return -ENODEV;
-
- if (aoc_state != AOC_STATE_ONLINE)
- return -ENODEV;
-
- if (interrupt >= AOC_MBOX_CHANNELS)
- return -EINVAL;
-
- BUG_ON(!dev->dev.parent);
-
- parent = dev->dev.parent;
- prvdata = dev_get_drvdata(parent);
-
- service_number = dev->service_index;
- service = service_at_index(prvdata, service_number);
-
- BUG_ON(!aoc_is_valid_dram_address(prvdata, service));
-
- if (aoc_service_message_slots(service, AOC_DOWN) == 0)
- return -EBADF;
-
- if (count > aoc_service_message_size(service, AOC_DOWN))
- return -EFBIG;
-
- if (!aoc_service_can_write_message(service, AOC_DOWN)) {
- if (!block)
- return -EAGAIN;
-
- set_bit(service_number, prvdata->write_blocked_mask);
- ret = wait_event_interruptible(dev->write_queue,
- aoc_state != AOC_STATE_ONLINE || dev->dead ||
- aoc_service_can_write_message(service, AOC_DOWN));
- clear_bit(service_number, prvdata->write_blocked_mask);
- }
-
- if (dev->dead)
- return -ENODEV;
-
- if (aoc_state != AOC_STATE_ONLINE)
- return -ENODEV;
-
- /*
- * The wait can fail if the AoC goes offline in the middle of a
- * blocking write, so check again after the wait
- */
- if (ret != 0)
- return -EAGAIN;
-
- ret = aoc_service_write_message(service, prvdata->ipc_base, AOC_DOWN,
- buffer, count);
-
- if (!aoc_service_is_ring(service) || aoc_ring_is_push(service))
- signal_aoc(prvdata->mbox_channels[interrupt].channel);
-
- return count;
-}
-EXPORT_SYMBOL_GPL(aoc_service_write);
-
-ssize_t aoc_service_write_timeout(struct aoc_service_dev *dev, const uint8_t *buffer,
- size_t count, long timeout)
-{
- struct aoc_prvdata *prvdata;
-
- aoc_service *service;
- int service_number;
- int interrupt = dev->mbox_index;
- long ret = 1;
-
- if (!dev || !buffer || !count)
- return -EINVAL;
-
- if (dev->dead)
- return -ENODEV;
-
- prvdata = dev_get_drvdata(dev->dev.parent);
- if (!prvdata)
- return -ENODEV;
-
- atomic_inc(&prvdata->aoc_process_active);
- if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) {
- ret = -EBUSY;
- goto err;
- }
-
- service_number = dev->service_index;
- service = service_at_index(prvdata, service_number);
-
- if (!aoc_is_valid_dram_address(prvdata, service)) {
- WARN_ONCE(1, "aoc service %d has invalid DRAM region", service_number);
- ret = -ENODEV;
- goto err;
- }
-
- if (aoc_service_message_slots(service, AOC_DOWN) == 0) {
- ret = -EBADF;
- goto err;
- }
-
- if (count > aoc_service_message_size(service, AOC_DOWN)) {
- ret = -EFBIG;
- goto err;
- }
-
- if (!aoc_service_can_write_message(service, AOC_DOWN)) {
- set_bit(service_number, prvdata->write_blocked_mask);
- ret = wait_event_interruptible_timeout(
- dev->write_queue,
- aoc_state != AOC_STATE_ONLINE || dev->dead ||
- aoc_service_can_write_message(service, AOC_DOWN),
- timeout);
- clear_bit(service_number, prvdata->write_blocked_mask);
- }
-
- if (dev->dead || (aoc_state != AOC_STATE_ONLINE)) {
- ret = -ENODEV;
- goto err;
- }
-
- if (ret < 0)
- goto err;
-
- if (ret == 0) {
- ret = -ETIMEDOUT;
- goto err;
- }
-
- ret = aoc_service_write_message(service, prvdata->ipc_base, AOC_DOWN,
- buffer, count);
-
- if (!aoc_service_is_ring(service) || aoc_ring_is_push(service))
- signal_aoc(prvdata->mbox_channels[interrupt].channel);
-
-err:
- atomic_dec(&prvdata->aoc_process_active);
-
- if (ret < 0)
- return ret;
-
- return count;
-}
-EXPORT_SYMBOL_GPL(aoc_service_write_timeout);
-
-int aoc_service_can_read(struct aoc_service_dev *dev)
-{
- const struct device *parent;
- struct aoc_prvdata *prvdata;
- aoc_service *service;
-
- parent = dev->dev.parent;
- prvdata = dev_get_drvdata(parent);
- service = service_at_index(prvdata, dev->service_index);
-
- if (aoc_service_message_slots(service, AOC_UP) == 0)
- return 0;
-
- return aoc_service_can_read_message(service, AOC_UP);
-}
-EXPORT_SYMBOL_GPL(aoc_service_can_read);
-
-int aoc_service_can_write(struct aoc_service_dev *dev)
-{
- const struct device *parent;
- struct aoc_prvdata *prvdata;
- aoc_service *service;
-
- parent = dev->dev.parent;
- prvdata = dev_get_drvdata(parent);
- service = service_at_index(prvdata, dev->service_index);
-
- if (aoc_service_message_slots(service, AOC_DOWN) == 0)
- return 0;
-
- return aoc_service_can_write_message(service, AOC_DOWN);
-}
-EXPORT_SYMBOL_GPL(aoc_service_can_write);
-
-void aoc_service_set_read_blocked(struct aoc_service_dev *dev)
-{
- int service_number;
- struct device *parent = dev->dev.parent;
- struct aoc_prvdata *prvdata = dev_get_drvdata(parent);
-
- service_number = dev->service_index;
- set_bit(service_number, prvdata->read_blocked_mask);
-}
-EXPORT_SYMBOL_GPL(aoc_service_set_read_blocked);
-
-void aoc_service_set_write_blocked(struct aoc_service_dev *dev)
-{
- int service_number;
- struct device *parent = dev->dev.parent;
- struct aoc_prvdata *prvdata = dev_get_drvdata(parent);
-
- service_number = dev->service_index;
- set_bit(service_number, prvdata->write_blocked_mask);
-}
-EXPORT_SYMBOL_GPL(aoc_service_set_write_blocked);
-
-wait_queue_head_t *aoc_service_get_read_queue(struct aoc_service_dev *dev)
-{
- return &dev->read_queue;
-}
-EXPORT_SYMBOL_GPL(aoc_service_get_read_queue);
-
-wait_queue_head_t *aoc_service_get_write_queue(struct aoc_service_dev *dev)
-{
- return &dev->write_queue;
-}
-EXPORT_SYMBOL_GPL(aoc_service_get_write_queue);
-
-static bool write_reset_trampoline(u32 addr)
-{
- u32 *reset;
- u32 instructions[] = {
- /* <start>: */
- /* 0: */ 0xe59f004c, /* ldr r0, [pc, #76] ; 54 <.PCU_SLC_MIF_REQ_ADDR> */
- /* 4: */ 0xe59f104c, /* ldr r1, [pc, #76] ; 58 <.PCU_SLC_MIF_REQ_VALUE> */
- /* 8: */ 0xe5801000, /* str r1, [r0] */
- /* c: */ 0xe59f0048, /* ldr r0, [pc, #72] ; 5c <.PCU_SLC_MIF_ACK_ADDR> */
- /* 10: */ 0xe59f104c, /* ldr r1, [pc, #76] ; 64 <.PCU_SLC_MIF_ACK_VALUE> */
- /* 14: */ 0xe59f2044, /* ldr r2, [pc, #68] ; 60 <.PCU_SLC_MIF_ACK_MASK> */
-
- /* <mif_ack_loop>: */
- /* 18: */ 0xe5903000, /* ldr r3, [r0] */
- /* 1c: */ 0xe0033002, /* and r3, r3, r2 */
- /* 20: */ 0xe1530001, /* cmp r3, r1 */
- /* 24: */ 0x1afffffb, /* bne 18 <mif_ack_loop> */
-
- /* 28: */ 0xe59f0038, /* ldr r0, [pc, #56] ; 68 <.PCU_BLK_PWR_REQ_ADDR> */
- /* 2c: */ 0xe59f1038, /* ldr r1, [pc, #56] ; 6c <.PCU_BLK_PWR_REQ_VALUE> */
- /* 30: */ 0xe5801000, /* str r1, [r0] */
- /* 34: */ 0xe59f0034, /* ldr r0, [pc, #52] ; 70 <.PCU_BLK_PWR_ACK_ADDR> */
- /* 38: */ 0xe59f1038, /* ldr r1, [pc, #56] ; 78 <.PCU_BLK_PWR_ACK_VALUE> */
- /* 3c: */ 0xe59f2030, /* ldr r2, [pc, #48] ; 74 <.PCU_BLK_PWR_ACK_MASK> */
-
- /* <blk_aoc_on_loop>: */
- /* 40: */ 0xe5903000, /* ldr r3, [r0] */
- /* 44: */ 0xe0033002, /* and r3, r3, r2 */
- /* 48: */ 0xe1530001, /* cmp r3, r1 */
- /* 4c: */ 0x1afffffb, /* bne 40 <blk_aoc_on_loop> */
- /* 50: */ 0xe59ff024, /* ldr pc, [pc, #36] ; 7c <.BOOTLOADER_START_ADDR> */
-
-
- #if IS_ENABLED(CONFIG_SOC_GS201)
- /* .PCU_SLC_MIF_REQ_ADDR: */ 0xA08000,
- /* .PCU_SLC_MIF_REQ_VALUE: */ 0x000003, /* Set ACTIVE_REQUEST = 1, MIS_SLCn = 1 to request MIF access */
- /* .PCU_SLC_MIF_ACK_ADDR: */ 0xA08004,
- /* .PCU_SLC_MIF_ACK_MASK: */ 0x000002, /* MIF_ACK field is bit 1 */
- /* .PCU_SLC_MIF_ACK_VALUE: */ 0x000002, /* MIF_ACK = ACK, 0x1 (<< 1) */
-
- /* .PCU_BLK_PWR_REQ_ADDR: */ 0xA0103C,
- /* .PCU_BLK_PWR_REQ_VALUE: */ 0x000001, /* POWER_REQUEST = On, 0x1 (<< 0) */
- /* .PCU_BLK_PWR_ACK_ADDR: */ 0xA0103C,
- /* .PCU_BLK_PWR_ACK_MASK: */ 0x00000C, /* POWER_MODE field is bits 3:2 */
- /* .PCU_BLK_PWR_ACK_VALUE: */ 0x000004, /* POWER_MODE = On, 0x1 (<< 2) */
- #elif IS_ENABLED(CONFIG_SOC_GS101)
- /* .PCU_SLC_MIF_REQ_ADDR: */ 0xB0819C,
- /* .PCU_SLC_MIF_REQ_VALUE: */ 0x000003, /* Set ACTIVE_REQUEST = 1, MIS_SLCn = 1 to request MIF access */
- /* .PCU_SLC_MIF_ACK_ADDR: */ 0xB0819C,
- /* .PCU_SLC_MIF_ACK_MASK: */ 0x000002, /* MIF_ACK field is bit 1 */
- /* .PCU_SLC_MIF_ACK_VALUE: */ 0x000002, /* MIF_ACK = ACK, 0x1 (<< 1) */
-
- /* .PCU_BLK_PWR_REQ_ADDR: */ 0xB02004,
- /* .PCU_BLK_PWR_REQ_VALUE: */ 0x000004, /* BLK_AOC = Initiate Wakeup Sequence, 0x1 (<< 2) */
- /* .PCU_BLK_PWR_ACK_ADDR: */ 0xB02000,
- /* .PCU_BLK_PWR_ACK_MASK: */ 0x000004, /* BLK_AOC field is bit 2 */
- /* .PCU_BLK_PWR_ACK_VALUE: */ 0x000004, /* BLK_AOC = Active, 0x1 (<< 2) */
- #else
- #error "Unsupported silicon"
- #endif
- /* .BOOTLOADER_START_ADDR: */ addr,
- };
-
- pr_notice("writing reset trampoline to addr %#x\n", addr);
+ u32 *reset, bl_size;
+ u32 *bootloader;
reset = aoc_sram_translate(0);
if (!reset)
return false;
- memcpy_toio(reset, instructions, sizeof(instructions));
-
- return true;
-}
-
-static bool aoc_fpga_reset(struct aoc_prvdata *prvdata)
-{
-#ifdef AOC_JUNO
- u32 *reset = aoc_sram_translate(0x1000000);
-
- if (!reset)
- return false;
-
- aoc_take_offline(prvdata);
+ bl_size = _aoc_fw_bl_size(fw);
+ bootloader = _aoc_fw_bl(fw);
- /* Assert and deassert reset */
- iowrite32(0, reset);
- iowrite32(1, reset);
-#endif
+ pr_notice("writing reset trampoline to addr %#x\n",
+ bootloader[bl_size / sizeof(u32) - 1]);
+ memcpy_toio(reset, bootloader, bl_size);
return true;
}
-static bool aoc_a32_reset(void)
-{
- u32 pcu_value;
- void __iomem *pcu = aoc_sram_translate(AOC_PCU_BASE);
-
- if (!pcu)
- return false;
-
- pcu_value = ioread32(pcu);
-
- pcu_value |= 1;
- iowrite32(pcu_value, pcu);
-
- return true;
-}
-
-__attribute__((unused))
-static int aoc_watchdog_restart(struct aoc_prvdata *prvdata)
-{
- /* 4100 * 0.244 us * 100 = 100 ms */
- const int aoc_watchdog_value_ssr = 4100 * 100;
- const int aoc_reset_timeout_ms = 1000;
- const int aoc_reset_tries = 3;
- const u32 aoc_watchdog_control_ssr = 0x3F;
- const unsigned int custom_in_offset = 0x3AC4;
- const unsigned int custom_out_offset = 0x3AC0;
- int rc;
- void __iomem *pcu;
- unsigned int custom_in;
- unsigned int custom_out;
- int ret;
- bool aoc_reset_successful;
- int i;
-
- pcu = aoc_sram_translate(AOC_PCU_BASE);
- if (!pcu)
- return -ENODEV;
-
- dev_info(prvdata->dev, "asserting aoc_req\n");
- aoc_req_assert(prvdata, true);
- rc = aoc_req_wait(prvdata, true);
- if (rc) {
- dev_err(prvdata->dev, "timed out waiting for aoc_ack\n");
- return rc;
- }
-
- aoc_reset_successful = false;
- disable_irq_nosync(prvdata->sysmmu_nonsecure_irq);
- disable_irq_nosync(prvdata->sysmmu_secure_irq);
- for (i = 0; i < aoc_reset_tries; i++) {
- dev_info(prvdata->dev, "resetting aoc\n");
- writel(AOC_PCU_WATCHDOG_KEY_UNLOCK, pcu + AOC_PCU_WATCHDOG_KEY_OFFSET);
- if ((readl(pcu + AOC_PCU_WATCHDOG_CONTROL_OFFSET) &
- AOC_PCU_WATCHDOG_CONTROL_KEY_ENABLED_MASK) == 0) {
- dev_err(prvdata->dev, "unlock aoc watchdog failed\n");
- }
- writel(aoc_watchdog_value_ssr, pcu + AOC_PCU_WATCHDOG_VALUE_OFFSET);
- writel(aoc_watchdog_control_ssr, pcu + AOC_PCU_WATCHDOG_CONTROL_OFFSET);
-
- dev_info(prvdata->dev, "waiting for aoc reset to finish\n");
- if (wait_event_timeout(prvdata->aoc_reset_wait_queue, prvdata->aoc_reset_done,
- aoc_reset_timeout_ms) == 0) {
- ret = exynos_pmu_read(custom_out_offset, &custom_out);
- dev_err(prvdata->dev,
- "AoC reset timeout custom_out=%d, ret=%d\n", custom_out, ret);
- ret = exynos_pmu_read(custom_in_offset, &custom_in);
- dev_err(prvdata->dev,
- "AoC reset timeout custom_in=%d, ret=%d\n", custom_in, ret);
- dev_err(prvdata->dev, "PCU_WATCHDOG_CONTROL = 0x%x\n",
- readl(pcu + AOC_PCU_WATCHDOG_CONTROL_OFFSET));
- dev_err(prvdata->dev, "PCU_WATCHDOG_VALUE = 0x%x\n",
- readl(pcu + AOC_PCU_WATCHDOG_VALUE_OFFSET));
- } else {
- aoc_reset_successful = true;
- break;
- }
- }
- enable_irq(prvdata->sysmmu_nonsecure_irq);
- enable_irq(prvdata->sysmmu_secure_irq);
- if (!aoc_reset_successful) {
- /* Trigger acpm ramdump since we timed out the aoc reset request */
- dbg_snapshot_emergency_reboot("AoC Restart timed out");
- return -ETIMEDOUT;
- }
- reset_sensor_power(prvdata, false);
- dev_info(prvdata->dev, "aoc reset finished\n");
- prvdata->aoc_reset_done = false;
-
- /*
- * AOC_TZPC has been restored by ACPM, so we can access AOC_S2MPU.
- * Restore AOC_S2MPU.
- */
- writel(prvdata->aoc_s2mpu_saved_value, prvdata->aoc_s2mpu_virt + AOC_S2MPU_CTRL0);
-
- /* Restore SysMMU settings by briefly setting AoC to runtime active. Since SysMMU is a
- * supplier to AoC, it will be set to runtime active as a side effect. */
- rc = pm_runtime_set_active(prvdata->dev);
- if (rc < 0) {
- dev_err(prvdata->dev, "sysmmu restore failed: pm_runtime_resume rc = %d\n", rc);
- return rc;
- }
- rc = pm_runtime_set_suspended(prvdata->dev);
- if (rc < 0) {
- dev_err(prvdata->dev, "sysmmu restore failed: pm_runtime_suspend rc = %d\n", rc);
- return rc;
- }
-
- rc = start_firmware_load(prvdata->dev);
- if (rc) {
- dev_err(prvdata->dev, "load aoc firmware failed: rc = %d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-static void acpm_aoc_reset_callback(unsigned int *cmd, unsigned int size)
-{
- struct aoc_prvdata *prvdata;
-
- if (!aoc_platform_device)
- return;
-
- prvdata = platform_get_drvdata(aoc_platform_device);
- pr_info("AOC prvdata pointer is: %p (expected: %p)", prvdata, aoc_prvdata_copy);
- prvdata->aoc_reset_done = true;
- wake_up(&prvdata->aoc_reset_wait_queue);
-}
-
static ssize_t coredump_count_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
@@ -1656,7 +788,9 @@ static uint64_t clock_offset(void)
static inline u64 sys_tick_to_aoc_tick(u64 sys_tick)
{
- return (sys_tick - clock_offset()) / AOC_CLOCK_DIVIDER;
+ struct aoc_prvdata *prvdata = platform_get_drvdata(aoc_platform_device);
+
+ return (sys_tick - clock_offset()) / prvdata->aoc_clock_divider;
}
static ssize_t aoc_clock_show(struct device *dev, struct device_attribute *attr,
@@ -1713,6 +847,10 @@ static ssize_t services_show(struct device *dev, struct device_attribute *attr,
int ret = 0;
int i;
+ atomic_inc(&prvdata->aoc_process_active);
+ if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work))
+ goto exit;
+
ret += scnprintf(buf, PAGE_SIZE, "Services : %d\n", services);
for (i = 0; i < services && ret < (PAGE_SIZE - 1); i++) {
aoc_service *s = service_at_index(prvdata, i);
@@ -1737,13 +875,15 @@ static ssize_t services_show(struct device *dev, struct device_attribute *attr,
hdr->regions[1].tx, hdr->regions[1].rx);
}
}
+exit:
+ atomic_dec(&prvdata->aoc_process_active);
return ret;
}
static DEVICE_ATTR_RO(services);
-static int start_firmware_load(struct device *dev)
+int start_firmware_load(struct device *dev)
{
struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
@@ -1818,6 +958,44 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_WO(reset);
+static ssize_t force_reload_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
+
+ /* Force release current loaded AoC if watchdog already active */
+ prvdata->force_release_aoc = true;
+ while (work_busy(&prvdata->watchdog_work) || work_busy(&prvdata->monitor_work.work));
+ prvdata->force_release_aoc = false;
+
+ /* Disable IRQ if AoC is loaded for paired IRQ */
+ if (aoc_state != AOC_STATE_OFFLINE)
+ disable_irq_nosync(prvdata->watchdog_irq);
+
+ strlcpy(prvdata->ap_reset_reason, "Force Reload AoC", AP_RESET_REASON_LENGTH);
+ prvdata->ap_triggered_reset = true;
+
+ schedule_work(&prvdata->watchdog_work);
+
+ return count;
+}
+static DEVICE_ATTR_WO(force_reload);
+
+static ssize_t dmic_power_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
+ int val;
+
+ if (kstrtoint(buf, 10, &val) == 0) {
+ dev_info(prvdata->dev,"dmic_power_enable %d", val);
+ configure_dmic_regulator(prvdata, !!val);
+ }
+ return count;
+}
+static DEVICE_ATTR_WO(dmic_power_enable);
+
static ssize_t sensor_power_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -1845,6 +1023,8 @@ static struct attribute *aoc_attrs[] = {
&dev_attr_aoc_clock_and_kernel_boottime.attr,
&dev_attr_reset.attr,
&dev_attr_sensor_power_enable.attr,
+ &dev_attr_force_reload.attr,
+ &dev_attr_dmic_power_enable.attr,
NULL
};
@@ -1942,25 +1122,7 @@ void aoc_driver_unregister(struct aoc_driver *driver)
}
EXPORT_SYMBOL_GPL(aoc_driver_unregister);
-static void aoc_clear_gpio_interrupt(void)
-{
-#if defined(GPIO_INTERRUPT) && !defined(AOC_JUNO)
- int reg = GPIO_INTERRUPT, val;
- u32 *gpio_register =
- aoc_sram_translate(AOC_GPIO_BASE + ((reg / 32) * 12));
-
- val = ioread32(gpio_register);
- val &= ~(1 << (reg % 32));
- iowrite32(val, gpio_register);
-#endif
-}
-
-static void aoc_configure_interrupt(void)
-{
- aoc_clear_gpio_interrupt();
-}
-
-static int aoc_remove_device(struct device *dev, void *ctx)
+static int aoc_wakeup_queues(struct device *dev, void *ctx)
{
struct aoc_service_dev *the_dev = AOC_DEVICE(dev);
@@ -1974,8 +1136,12 @@ static int aoc_remove_device(struct device *dev, void *ctx)
wake_up(&the_dev->read_queue);
wake_up(&the_dev->write_queue);
- device_unregister(dev);
+ return 0;
+}
+static int aoc_remove_device(struct device *dev, void *ctx)
+{
+ device_unregister(dev);
return 0;
}
@@ -2032,35 +1198,6 @@ static struct aoc_service_dev *create_service_device(struct aoc_prvdata *prvdata
return dev;
}
-static void trigger_aoc_ramdump(struct aoc_prvdata *prvdata)
-{
- struct mbox_chan *channel = prvdata->mbox_channels[15].channel;
- static const uint32_t command[] = { 0, 0, 0, 0, 0x0deada0c, 0, 0, 0 };
-
- dev_notice(prvdata->dev, "Attempting to force AoC coredump\n");
-
- mbox_send_message(channel, (void *)&command);
-}
-
-static void signal_aoc(struct mbox_chan *channel)
-{
-#ifdef AOC_JUNO
- (void)channel;
-
- u32 mask = (1 << AOC_DOWNCALL_DOORBELL);
-
- /* The signal is called as directly after writing a message to shared
- * memory, so make sure all pending writes are flushed before actually
- * sending the signal
- */
- wmb();
- iowrite32(mask,
- aoc_sram_translate(AOC_PCU_BASE + AOC_PCU_DB_SET_OFFSET));
-#else
- mbox_send_message(channel, NULL);
-#endif
-}
-
static int aoc_iommu_fault_handler(struct iommu_fault *fault, void *token)
{
struct device *dev = token;
@@ -2075,118 +1212,107 @@ static int aoc_iommu_fault_handler(struct iommu_fault *fault, void *token)
return -EAGAIN;
}
-#define SSMT_BYPASS_VALUE 0x80000000U
-#define SSMT_NS_READ_PID(n) (0x4000 + 4 * (n))
-#define SSMT_NS_WRITE_PID(n) (0x4200 + 4 * (n))
-
-#if IS_ENABLED(CONFIG_SOC_GS101)
-static void aoc_configure_ssmt(struct platform_device *pdev)
+static void aoc_configure_sysmmu_fault_handler(struct aoc_prvdata *p)
{
- struct device *dev = &pdev->dev;
- int stream_id;
-
- void __iomem *ssmt_base = devm_platform_ioremap_resource_byname(pdev, "ssmt_aoc");
-
- if (IS_ERR(ssmt_base)) {
- dev_err(dev, "ssmt_aoc base address failure: %ld\n", PTR_ERR(ssmt_base));
- return;
- }
-
- /* Configure registers NS_READ_PID_<n>, NS_WRITE_PID_<n> for each stream id */
- for (stream_id = 0; stream_id <= 32; stream_id++) {
- /* Skip over stream id 31 */
- if (stream_id == 31)
- continue;
- writel_relaxed(SSMT_BYPASS_VALUE, ssmt_base + SSMT_NS_READ_PID(stream_id));
- writel_relaxed(SSMT_BYPASS_VALUE, ssmt_base + SSMT_NS_WRITE_PID(stream_id));
- }
+ struct device *dev = p->dev;
+ int rc = iommu_register_device_fault_handler(dev, aoc_iommu_fault_handler, dev);
- devm_iounmap(dev, ssmt_base);
+ if (rc)
+ dev_err(dev, "iommu_register_device_fault_handler failed: rc = %d\n", rc);
}
-#else
-static inline void aoc_configure_ssmt( struct platform_device *pdev
- __attribute__((unused))) { }
-#endif
static void aoc_configure_sysmmu(struct aoc_prvdata *p, const struct firmware *fw)
{
-#ifndef AOC_JUNO
int rc;
- size_t i, cnt;
+ size_t i, j, cnt;
struct sysmmu_entry *sysmmu;
struct iommu_domain *domain = p->domain;
struct device *dev = p->dev;
u16 sysmmu_offset, sysmmu_size;
- rc = iommu_register_device_fault_handler(dev, aoc_iommu_fault_handler, dev);
- if (rc)
- dev_err(dev, "iommu_register_device_fault_handler failed: rc = %d\n", rc);
+ if (p->sysmmu_configured && p->sysmmu_config_persistent) {
+ dev_info(dev, "SysMMU already configured skipping\n");
+ return;
+ }
+
+ aoc_configure_sysmmu_fault_handler(p);
sysmmu_offset = _aoc_fw_sysmmu_offset(fw);
sysmmu_size = _aoc_fw_sysmmu_size(fw);
if (!_aoc_fw_is_valid_sysmmu_size(fw)) {
- dev_info(dev, "Invalid sysmmu table (%u @ %u)\n", sysmmu_size, sysmmu_offset);
+ dev_warn(dev, "Invalid sysmmu table (0x%x @ 0x%x)\n", sysmmu_size, sysmmu_offset);
return;
}
cnt = sysmmu_size / sizeof(struct sysmmu_entry);
sysmmu = _aoc_fw_sysmmu_entry(fw);
- for (i = 0; i < cnt; i++, sysmmu++) {
- dev_info(dev, "Configuring sysmmu entry = 0x%llx 0x%llx 0x%llx\n",
- SYSMMU_VADDR(sysmmu->value), SYSMMU_PADDR(sysmmu->value),
- SYSMMU_SIZE(sysmmu->value));
- rc = iommu_map(domain, SYSMMU_VADDR(sysmmu->value),
- SYSMMU_PADDR(sysmmu->value),
- SYSMMU_SIZE(sysmmu->value),
+
+ p->sysmmu_size = sysmmu_size;
+ p->sysmmu = devm_kzalloc(dev, sysmmu_size, GFP_KERNEL);
+ if (!p->sysmmu)
+ return;
+
+ memcpy(p->sysmmu, sysmmu, sysmmu_size);
+
+ for (i = 0; i < cnt; i++) {
+ rc = iommu_map(domain, SYSMMU_VADDR(sysmmu[i].value),
+ SYSMMU_PADDR(sysmmu[i].value),
+ SYSMMU_SIZE(sysmmu[i].value),
IOMMU_READ | IOMMU_WRITE);
if (rc < 0) {
- dev_err(dev, "sysmmu mapping failed: %d\n", rc);
+ dev_err(
+ dev,
+ "Failed configuring sysmmu: [err=%d] [index:%zu, vaddr: 0x%llx, paddr: 0x%llx, size: 0x%llx]\n",
+ rc, i, SYSMMU_VADDR(sysmmu[i].value), SYSMMU_PADDR(sysmmu[i].value),
+ SYSMMU_SIZE(sysmmu[i].value));
+ for (j = 0; j < i; j++) {
+ rc = iommu_unmap(domain, SYSMMU_VADDR(sysmmu[j].value),
+ SYSMMU_SIZE(sysmmu[j].value));
+ if (rc < 0)
+ dev_err(dev, "Failed unmapping sysmmu: %d\n", rc);
+ }
return;
- }
+ }
}
-#endif
-}
-static void aoc_configure_sysmmu_manual(struct aoc_prvdata *p) {}
+ p->sysmmu_configured = true;
+}
static void aoc_clear_sysmmu(struct aoc_prvdata *p)
{
-#ifndef AOC_JUNO
+ int rc;
struct iommu_domain *domain = p->domain;
+ size_t i, cnt;
+ struct device *dev = p->dev;
- /* Memory carveout */
- iommu_unmap(domain, 0x98000000, p->dram_size);
- iommu_unmap(domain, 0x9B000000, SZ_4M);
-
- /* Device registers */
- iommu_unmap(domain, 0x9E000000, SZ_1M);
- iommu_unmap(domain, 0x9E100000, SZ_1M);
- iommu_unmap(domain, 0x9E200000, SZ_1M);
- iommu_unmap(domain, 0x9E300000, SZ_1M);
-#endif
+ if (p->sysmmu != NULL) {
+ cnt = p->sysmmu_size / sizeof(struct sysmmu_entry);
+ for (i = 0; i < cnt; i++) {
+ rc = iommu_unmap(domain, SYSMMU_VADDR(p->sysmmu[i].value),
+ SYSMMU_SIZE(p->sysmmu[i].value));
+ if (rc < 0)
+ dev_err(dev, "Failed umapping sysmmu: %d\n", rc);
+ }
+ }
}
static void aoc_monitor_online(struct work_struct *work)
{
struct aoc_prvdata *prvdata =
container_of(work, struct aoc_prvdata, monitor_work.work);
- int restart_rc;
-
+ bool skip_reset = of_property_read_bool(prvdata->dev->of_node, "skip-monitor-online-reset");
- mutex_lock(&aoc_service_lock);
if (aoc_state == AOC_STATE_FIRMWARE_LOADED) {
dev_err(prvdata->dev, "aoc init no respond, try restart\n");
+ if (skip_reset)
+ /* TODO: figure out if this still causes APC watchdogs on GS201 */
+ return;
+
disable_irq_nosync(prvdata->watchdog_irq);
- aoc_take_offline(prvdata);
- restart_rc = aoc_watchdog_restart(prvdata);
- if (restart_rc)
- dev_info(prvdata->dev,
- "aoc restart failed: rc = %d\n", restart_rc);
- else
- dev_info(prvdata->dev,
- "aoc restart succeeded\n");
+ strlcpy(prvdata->ap_reset_reason, "Monitor Reset", AP_RESET_REASON_LENGTH);
+ prvdata->ap_triggered_reset = true;
+ schedule_work(&prvdata->watchdog_work);
}
- mutex_unlock(&aoc_service_lock);
}
static void aoc_did_become_online(struct work_struct *work)
@@ -2194,7 +1320,7 @@ static void aoc_did_become_online(struct work_struct *work)
struct aoc_prvdata *prvdata =
container_of(work, struct aoc_prvdata, online_work);
struct device *dev = prvdata->dev;
- int i, s;
+ int i, s, ret;
cancel_delayed_work_sync(&prvdata->monitor_work);
@@ -2202,7 +1328,7 @@ static void aoc_did_become_online(struct work_struct *work)
s = aoc_num_services();
- aoc_req_assert(prvdata, false);
+ request_aoc_on(prvdata, false);
pr_notice("firmware version %s did become online with %d services\n",
prvdata->firmware_version ? prvdata->firmware_version : "0",
@@ -2256,8 +1382,12 @@ static void aoc_did_become_online(struct work_struct *work)
aoc_state = AOC_STATE_ONLINE;
- for (i = 0; i < s; i++)
- device_register(&prvdata->services[i]->dev);
+ for (i = 0; i < s; i++) {
+ ret = device_register(&prvdata->services[i]->dev);
+ if (ret)
+ dev_err(dev, "failed to register service device %s err=%d\n",
+ dev_name(&prvdata->services[i]->dev), ret);
+ }
err:
mutex_unlock(&aoc_service_lock);
@@ -2300,7 +1430,73 @@ static bool configure_sensor_regulator(struct aoc_prvdata *prvdata, bool enable)
return (check_enabled == enable);
}
-static void reset_sensor_power(struct aoc_prvdata *prvdata, bool is_init)
+static bool configure_dmic_regulator(struct aoc_prvdata *prvdata, bool enable)
+{
+ bool check_enabled;
+ int i;
+ if (enable) {
+ check_enabled = true;
+ for (i = 0; i < prvdata->dmic_power_count; i++) {
+ if (!prvdata->dmic_regulator[i] ||
+ regulator_is_enabled(prvdata->dmic_regulator[i])) {
+ continue;
+ }
+
+ if (regulator_enable(prvdata->dmic_regulator[i])) {
+ pr_warn("encountered error on enabling %s.",
+ prvdata->dmic_power_list[i]);
+ }
+ check_enabled &= regulator_is_enabled(prvdata->dmic_regulator[i]);
+ }
+ } else {
+ check_enabled = false;
+
+ for (i = prvdata->dmic_power_count - 1; i >= 0; i--) {
+ if (!prvdata->dmic_regulator[i] ||
+ !regulator_is_enabled(prvdata->dmic_regulator[i])) {
+ continue;
+ }
+
+ if (regulator_disable(prvdata->dmic_regulator[i])) {
+ pr_warn(" encountered error on disabling %s.",
+ prvdata->dmic_power_list[i]);
+ }
+ check_enabled |= regulator_is_enabled(prvdata->dmic_regulator[i]);
+ }
+ }
+
+ return (check_enabled == enable);
+}
+
+static void aoc_parse_dmic_power(struct aoc_prvdata *prvdata, struct device_node *node)
+{
+ int i;
+ prvdata->dmic_power_count = of_property_count_strings(node, "dmic_power_list");
+ if (prvdata->dmic_power_count > MAX_DMIC_POWER_NUM) {
+ pr_warn("dmic power count %i is larger than available number.",
+ prvdata->dmic_power_count);
+ prvdata->dmic_power_count = MAX_DMIC_POWER_NUM;
+ } else if (prvdata->dmic_power_count < 0) {
+ pr_err("unsupported dmic power list, err = %i.", prvdata->dmic_power_count);
+ prvdata->dmic_power_count = 0;
+ return;
+ }
+
+ of_property_read_string_array(node, "dmic_power_list",
+ (const char **)&prvdata->dmic_power_list,
+ prvdata->dmic_power_count);
+
+ for (i = 0; i < prvdata->dmic_power_count; i++) {
+ prvdata->dmic_regulator[i] =
+ devm_regulator_get_exclusive(prvdata->dev, prvdata->dmic_power_list[i]);
+ if (IS_ERR_OR_NULL(prvdata->dmic_regulator[i])) {
+ prvdata->dmic_regulator[i] = NULL;
+ pr_err("failed to get %s regulator.", prvdata->dmic_power_list[i]);
+ }
+ }
+}
+
+void reset_sensor_power(struct aoc_prvdata *prvdata, bool is_init)
{
const int max_retry = 5;
int count;
@@ -2349,7 +1545,9 @@ static void aoc_take_offline(struct aoc_prvdata *prvdata)
aoc_state = AOC_STATE_OFFLINE;
/* wait until aoc_process or service write/read finish */
- while (!!atomic_read(&prvdata->aoc_process_active));
+ while (!!atomic_read(&prvdata->aoc_process_active)) {
+ bus_for_each_dev(&aoc_bus_type, NULL, NULL, aoc_wakeup_queues);
+ }
bus_for_each_dev(&aoc_bus_type, NULL, NULL, aoc_remove_device);
@@ -2363,10 +1561,14 @@ static void aoc_take_offline(struct aoc_prvdata *prvdata)
}
/* wakeup AOC before calling GSA */
- aoc_req_assert(prvdata, true);
- rc = aoc_req_wait(prvdata, true);
- if (rc)
+ request_aoc_on(prvdata, true);
+ rc = wait_for_aoc_status(prvdata, true);
+ if (rc) {
dev_err(prvdata->dev, "timed out waiting for aoc_ack\n");
+ if (prvdata->protected_by_gsa)
+ dev_err(prvdata->dev, "skipping GSA commands");
+ return;
+ }
}
if(prvdata->protected_by_gsa) {
@@ -2452,88 +1654,6 @@ void aoc_remove_map_handler(struct aoc_service_dev *dev)
}
EXPORT_SYMBOL_GPL(aoc_remove_map_handler);
-static void aoc_pheap_alloc_cb(struct samsung_dma_buffer *buffer, void *ctx)
-{
- struct device *dev = ctx;
- struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
- struct sg_table *sg = &buffer->sg_table;
- phys_addr_t phys;
- size_t size;
-
- if (sg->nents != 1) {
- dev_warn(dev, "Unable to map sg_table with %d ents\n",
- sg->nents);
- return;
- }
-
- phys = sg_phys(&sg->sgl[0]);
- phys = aoc_dram_translate_to_aoc(prvdata, phys);
- size = sg->sgl[0].length;
-
- mutex_lock(&aoc_service_lock);
- if (prvdata->map_handler) {
- prvdata->map_handler((u64)buffer->priv, phys, size, true,
- prvdata->map_handler_ctx);
- }
- mutex_unlock(&aoc_service_lock);
-}
-
-static void aoc_pheap_free_cb(struct samsung_dma_buffer *buffer, void *ctx)
-{
- struct device *dev = ctx;
- struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
- struct sg_table *sg = &buffer->sg_table;
- phys_addr_t phys;
- size_t size;
-
- if (sg->nents != 1) {
- dev_warn(dev, "Unable to map sg_table with %d ents\n",
- sg->nents);
- return;
- }
-
- phys = sg_phys(&sg->sgl[0]);
- phys = aoc_dram_translate_to_aoc(prvdata, phys);
- size = sg->sgl[0].length;
-
- mutex_lock(&aoc_service_lock);
- if (prvdata->map_handler) {
- prvdata->map_handler((u64)buffer->priv, phys, size, false,
- prvdata->map_handler_ctx);
- }
- mutex_unlock(&aoc_service_lock);
-}
-
-#ifdef AOC_JUNO
-static irqreturn_t aoc_int_handler(int irq, void *dev)
-{
- aoc_clear_gpio_interrupt();
-
- /* Transitioning from offline to online */
- if (aoc_state == AOC_STATE_FIRMWARE_LOADED) {
- if (aoc_fw_ready())
- aoc_state = AOC_STATE_STARTING;
- schedule_work(&aoc_online_work);
- }
- } else if (aoc_state == AOC_STATE_ONLINE) {
- aoc_process_services(dev_get_drvdata(dev), 0);
- }
-
- return IRQ_HANDLED;
-}
-#else
-static irqreturn_t watchdog_int_handler(int irq, void *dev)
-{
- struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
-
- /* AP shouldn't access AoC registers to clear the IRQ. */
- /* Mask the IRQ until the IRQ gets cleared by AoC reset during SSR. */
- disable_irq_nosync(irq);
- schedule_work(&prvdata->watchdog_work);
-
- return IRQ_HANDLED;
-}
-
static struct aoc_section_header *find_ramdump_section(struct aoc_ramdump_header
*ramdump_header, int section_type)
{
@@ -2560,6 +1680,7 @@ static void aoc_watchdog(struct work_struct *work)
unsigned long carveout_paddr_from_aoc;
unsigned long carveout_vaddr_from_aoc;
size_t i;
+ bool skip_carveout_map = of_property_read_bool(prvdata->dev->of_node, "skip-carveout-map");
size_t num_pages;
struct page **dram_pages = NULL;
void *dram_cached = NULL;
@@ -2652,21 +1773,27 @@ static void aoc_watchdog(struct work_struct *work)
sizeof(crash_info));
}
- num_pages = DIV_ROUND_UP(prvdata->dram_size, PAGE_SIZE);
- dram_pages = vmalloc(num_pages * sizeof(*dram_pages));
- if (!dram_pages) {
- dev_err(prvdata->dev,
- "aoc coredump failed: alloc dram_pages failed\n");
- goto err_vmalloc;
- }
- for (i = 0; i < num_pages; i++)
- dram_pages[i] = phys_to_page(prvdata->dram_resource.start +
- (i * PAGE_SIZE));
- dram_cached = vmap(dram_pages, num_pages, VM_MAP, PAGE_KERNEL_RO);
- if (!dram_cached) {
- dev_err(prvdata->dev,
- "aoc coredump failed: vmap dram_pages failed\n");
- goto err_vmap;
+ if (!skip_carveout_map) {
+ /* In some cases, we don't map AoC carveout as cached due to b/240786634 */
+ num_pages = DIV_ROUND_UP(prvdata->dram_size, PAGE_SIZE);
+ dram_pages = vmalloc(num_pages * sizeof(*dram_pages));
+ if (!dram_pages) {
+ dev_err(prvdata->dev,
+ "aoc coredump failed: alloc dram_pages failed\n");
+ goto err_vmalloc;
+ }
+ for (i = 0; i < num_pages; i++)
+ dram_pages[i] = phys_to_page(prvdata->dram_resource.start +
+ (i * PAGE_SIZE));
+ dram_cached = vmap(dram_pages, num_pages, VM_MAP, PAGE_KERNEL_RO);
+ if (!dram_cached) {
+ dev_err(prvdata->dev,
+ "aoc coredump failed: vmap dram_pages failed\n");
+ goto err_vmap;
+ }
+ sscd_info.segs[0].addr = dram_cached;
+ } else {
+ sscd_info.segs[0].addr = prvdata->dram_virt;
}
if (ramdump_header->valid && !invalid_magic) {
@@ -2696,8 +1823,7 @@ static void aoc_watchdog(struct work_struct *work)
/* TODO(siqilin): Get paddr and vaddr base from firmware instead */
carveout_paddr_from_aoc = 0x98000000;
carveout_vaddr_from_aoc = 0x78000000;
- /* Entire AoC DRAM carveout, coredump is stored within the carveout */
- sscd_info.segs[0].addr = dram_cached;
+
sscd_info.segs[0].size = prvdata->dram_size;
sscd_info.segs[0].paddr = (void *)carveout_paddr_from_aoc;
sscd_info.segs[0].vaddr = (void *)carveout_vaddr_from_aoc;
@@ -2729,7 +1855,8 @@ static void aoc_watchdog(struct work_struct *work)
if (dram_cached)
vunmap(dram_cached);
err_vmap:
- vfree(dram_pages);
+ if (dram_pages)
+ vfree(dram_pages);
err_vmalloc:
err_coredump:
/* make sure there is no AoC startup work active */
@@ -2737,11 +1864,14 @@ err_coredump:
mutex_lock(&aoc_service_lock);
aoc_take_offline(prvdata);
- restart_rc = aoc_watchdog_restart(prvdata);
- if (restart_rc)
+ restart_rc = aoc_watchdog_restart(prvdata, aoc_module_params);
+ if (restart_rc == AOC_RESTART_DISABLED_RC) {
+ dev_info(prvdata->dev, "aoc subsystem restart is disabled\n");
+ } else if (restart_rc) {
dev_info(prvdata->dev, "aoc subsystem restart failed: rc = %d\n", restart_rc);
- else
+ } else {
dev_info(prvdata->dev, "aoc subsystem restart succeeded\n");
+ }
mutex_unlock(&aoc_service_lock);
}
@@ -2763,50 +1893,6 @@ void aoc_trigger_watchdog(const char *reason)
reset_store(prvdata->dev, NULL, reason, strlen(reason));
}
EXPORT_SYMBOL_GPL(aoc_trigger_watchdog);
-#endif
-
-static struct dma_heap *aoc_create_dma_buf_heap(struct aoc_prvdata *prvdata, const char *name,
- phys_addr_t base, size_t size)
-{
- struct device *dev = prvdata->dev;
- size_t align = SZ_16K;
- struct dma_heap *heap;
-
- heap = ion_physical_heap_create(base, size, align, name, aoc_pheap_alloc_cb,
- aoc_pheap_free_cb, dev);
- if (IS_ERR(heap))
- dev_err(dev, "heap \"%s\" creation failure: %ld\n", name, PTR_ERR(heap));
-
- return heap;
-}
-
-static bool aoc_create_dma_buf_heaps(struct aoc_prvdata *prvdata)
-{
- phys_addr_t base = prvdata->dram_resource.start + resource_size(&prvdata->dram_resource);
-
- base -= SENSOR_DIRECT_HEAP_SIZE;
- prvdata->sensor_heap = aoc_create_dma_buf_heap(prvdata, "sensor_direct_heap",
- base, SENSOR_DIRECT_HEAP_SIZE);
- prvdata->sensor_heap_base = base;
- if (IS_ERR(prvdata->sensor_heap))
- return false;
-
- base -= PLAYBACK_HEAP_SIZE;
- prvdata->audio_playback_heap = aoc_create_dma_buf_heap(prvdata, "aaudio_playback_heap",
- base, PLAYBACK_HEAP_SIZE);
- prvdata->audio_playback_heap_base = base;
- if (IS_ERR(prvdata->audio_playback_heap))
- return false;
-
- base -= CAPTURE_HEAP_SIZE;
- prvdata->audio_capture_heap = aoc_create_dma_buf_heap(prvdata, "aaudio_capture_heap",
- base, CAPTURE_HEAP_SIZE);
- prvdata->audio_capture_heap_base = base;
- if (IS_ERR(prvdata->audio_capture_heap))
- return false;
-
- return true;
-}
static int aoc_open(struct inode *inode, struct file *file)
{
@@ -2819,36 +1905,13 @@ static int aoc_open(struct inode *inode, struct file *file)
static long aoc_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct dma_buf *dmabuf;
struct aoc_prvdata *prvdata = file->private_data;
- struct samsung_dma_buffer *dma_heap_buf;
long ret = -EINVAL;
switch (cmd) {
case AOC_IOCTL_ION_FD_TO_HANDLE:
{
- struct aoc_ion_handle handle;
-
- BUILD_BUG_ON(sizeof(struct aoc_ion_handle) !=
- _IOC_SIZE(AOC_IOCTL_ION_FD_TO_HANDLE));
-
- if (copy_from_user(&handle, (struct aoc_ion_handle *)arg, _IOC_SIZE(cmd)))
- break;
-
- dmabuf = dma_buf_get(handle.fd);
- if (IS_ERR(dmabuf)) {
- pr_err("fd is not an ion buffer\n");
- ret = PTR_ERR(dmabuf);
- break;
- }
-
- dma_heap_buf = dmabuf->priv;
- handle.handle = (u64)dma_heap_buf->priv;
-
- dma_buf_put(dmabuf);
-
- if (!copy_to_user((struct aoc_ion_handle *)arg, &handle, _IOC_SIZE(cmd)))
- ret = 0;
+ ret = aoc_unlocked_ioctl_handle_ion_fd(cmd, arg);
}
break;
@@ -3057,20 +2120,14 @@ static void aoc_cleanup_resources(struct platform_device *pdev)
aoc_clear_sysmmu(prvdata);
prvdata->domain = NULL;
}
-
-#ifdef AOC_JUNO
- free_irq(aoc_irq, prvdata->dev);
- aoc_irq = -1;
-#endif
}
}
static void release_gsa_device(void *prv)
{
- struct aoc_prvdata *prvdata = prv;
-
- put_device(prvdata->gsa_dev);
+ struct device *gsa_device = (struct device *)prv;
+ put_device(gsa_device);
}
static int find_gsa_device(struct aoc_prvdata *prvdata)
@@ -3094,7 +2151,7 @@ static int find_gsa_device(struct aoc_prvdata *prvdata)
}
prvdata->gsa_dev = &gsa_pdev->dev;
return devm_add_action_or_reset(prvdata->dev, release_gsa_device,
- prvdata);
+ &gsa_pdev->dev);
}
static int aoc_core_suspend(struct device *dev)
@@ -3147,16 +2204,74 @@ exit:
return 0;
}
+static int platform_probe_parse_dt(struct device *dev, struct device_node *aoc_node)
+{
+ struct aoc_prvdata *prvdata = platform_get_drvdata(aoc_platform_device);
+
+ prvdata->aoc_pcu_base = dt_property(aoc_node, "pcu-base");
+ if (prvdata->aoc_pcu_base == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property pcu-base");
+ return -EINVAL;
+ }
+ prvdata->aoc_gpio_base = dt_property(aoc_node, "gpio-base");
+ if (prvdata->aoc_gpio_base == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property gpio-base");
+ return -EINVAL;
+ }
+ prvdata->aoc_pcu_db_set_offset = dt_property(aoc_node, "pcu-db-set-offset");
+ if (prvdata->aoc_pcu_db_set_offset == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property pcu-db-set-offset");
+ return -EINVAL;
+ }
+ prvdata->aoc_pcu_db_clr_offset = dt_property(aoc_node, "pcu-db-clr-offset");
+ if (prvdata->aoc_pcu_db_clr_offset == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property pcu-db-clr-offset");
+ return -EINVAL;
+ }
+ prvdata->aoc_cp_aperture_start_offset = dt_property(aoc_node,
+ "cp-aperture-start-offset");
+ if (prvdata->aoc_cp_aperture_start_offset == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property cp-aperture-start-offset");
+ return -EINVAL;
+ }
+ prvdata->aoc_cp_aperture_end_offset = dt_property(aoc_node,
+ "cp-aperture-end-offset");
+ if (prvdata->aoc_cp_aperture_end_offset == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property cp-aperture-end-offset");
+ return -EINVAL;
+ }
+ prvdata->aoc_clock_divider = dt_property(aoc_node, "clock-divider");
+ if (prvdata->aoc_clock_divider == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property clock-divider");
+ return -EINVAL;
+ }
+ prvdata->aoc_mbox_channels = dt_property(aoc_node, "mbox-channels");
+ if (prvdata->aoc_mbox_channels == DT_PROPERTY_NOT_FOUND) {
+ dev_err(dev, "AOC DT missing property mbox-channels");
+ return -EINVAL;
+ }
+ prvdata->sysmmu_config_persistent = of_property_read_bool(aoc_node,
+ "sysmmu-config-persistent");
+
+ return 0;
+}
+
static int aoc_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct aoc_prvdata *prvdata = NULL;
struct device_node *aoc_node, *mem_node, *sysmmu_node;
struct resource *rsrc;
- unsigned int acpm_async_size;
int ret;
int rc;
int i;
+ struct aoc_module_parameters module_params = {
+ .aoc_autoload_firmware = aoc_autoload_firmware,
+ .aoc_disable_restart = aoc_disable_restart,
+ .aoc_panic_on_req_timeout = aoc_panic_on_req_timeout
+ };
+
+ aoc_module_params = &module_params;
if (aoc_platform_device != NULL) {
dev_err(dev,
@@ -3164,6 +2279,7 @@ static int aoc_platform_probe(struct platform_device *pdev)
rc = -EEXIST;
goto err_platform_not_null;
}
+ aoc_platform_device = pdev;
aoc_node = dev->of_node;
mem_node = of_parse_phandle(aoc_node, "memory-region", 0);
@@ -3173,7 +2289,19 @@ static int aoc_platform_probe(struct platform_device *pdev)
rc = -ENOMEM;
goto err_failed_prvdata_alloc;
}
- aoc_prvdata_copy = prvdata;
+ platform_set_drvdata(pdev, prvdata);
+
+ if (platform_probe_parse_dt(dev, aoc_node) < 0) {
+ rc = -EINVAL;
+ goto err_invalid_dt;
+ }
+
+ prvdata->mbox_channels = devm_kzalloc(dev,
+ sizeof(struct mbox_slot) * prvdata->aoc_mbox_channels, GFP_KERNEL);
+ if (!prvdata->mbox_channels) {
+ rc = -ENOMEM;
+ goto err_failed_prvdata_alloc;
+ }
prvdata->dev = dev;
prvdata->disable_monitor_mode = 0;
@@ -3187,6 +2315,8 @@ static int aoc_platform_probe(struct platform_device *pdev)
rc = find_gsa_device(prvdata);
if (rc) {
dev_err(dev, "Failed to initialize gsa device: %d\n", rc);
+ rc = -EINVAL;
+ goto err_failed_prvdata_alloc;
}
ret = init_chardev(prvdata);
@@ -3217,15 +2347,7 @@ static int aoc_platform_probe(struct platform_device *pdev)
goto err_mem_resources;
}
-#ifdef AOC_JUNO
- aoc_irq = platform_get_irq(pdev, 0);
- if (aoc_irq < 1) {
- dev_err(dev, "failed to configure aoc interrupt\n");
- rc = aoc_irq;
- goto err_get_irq;
- }
-#else
- for (i = 0; i < ARRAY_SIZE(prvdata->mbox_channels); i++) {
+ for (i = 0; i < prvdata->aoc_mbox_channels; i++) {
prvdata->mbox_channels[i].client.dev = dev;
prvdata->mbox_channels[i].client.tx_block = false;
prvdata->mbox_channels[i].client.tx_tout = 100; /* 100ms timeout for tx */
@@ -3242,34 +2364,18 @@ static int aoc_platform_probe(struct platform_device *pdev)
strscpy(prvdata->firmware_name, default_firmware,
sizeof(prvdata->firmware_name));
- platform_set_drvdata(pdev, prvdata);
-
- ret = allocate_mailbox_channels(prvdata);
- if (ret) {
- dev_err(dev, "failed to allocate mailbox channels %d\n", ret);
- rc = -ENOMEM;
+ rc = allocate_mailbox_channels(prvdata);
+ if (rc) {
+ dev_err(dev, "failed to allocate mailbox channels %d\n", rc);
goto err_mem_resources;
}
init_waitqueue_head(&prvdata->aoc_reset_wait_queue);
INIT_WORK(&prvdata->watchdog_work, aoc_watchdog);
- prvdata->watchdog_irq = platform_get_irq_byname(pdev, "watchdog");
- if (prvdata->watchdog_irq < 0) {
- dev_err(dev, "failed to find watchdog irq\n");
- rc = -EIO;
- goto err_watchdog_irq_get;
- }
-
- irq_set_status_flags(prvdata->watchdog_irq, IRQ_NOAUTOEN);
- ret = devm_request_irq(dev, prvdata->watchdog_irq, watchdog_int_handler,
- IRQF_TRIGGER_HIGH, dev_name(dev), dev);
- if (ret != 0) {
- dev_err(dev, "failed to register watchdog irq handler: %d\n",
- ret);
- rc = -EIO;
- goto err_watchdog_irq_req;
- }
+ ret = configure_watchdog_interrupt(pdev, prvdata);
+ if (ret < 0)
+ goto err_watchdog_irq;
sysmmu_node = of_parse_phandle(aoc_node, "iommus", 0);
if (!sysmmu_node) {
@@ -3277,26 +2383,13 @@ static int aoc_platform_probe(struct platform_device *pdev)
rc = -ENODEV;
goto err_watchdog_sysmmu_irq;
}
- ret = of_irq_get(sysmmu_node, 0);
- if (ret < 0) {
- dev_err(dev, "failed to find sysmmu non-secure irq: %d\n", ret);
- rc = ret;
- goto err_watchdog_sysmmu_irq;
- }
- prvdata->sysmmu_nonsecure_irq = ret;
- ret = of_irq_get(sysmmu_node, 1);
- if (ret < 0) {
- dev_err(dev, "failed to find sysmmu secure irq: %d\n", ret);
- rc = ret;
+ ret = configure_sysmmu_interrupts(dev, sysmmu_node, prvdata);
+ if (ret < 0)
goto err_watchdog_sysmmu_irq;
- }
- prvdata->sysmmu_secure_irq = ret;
of_node_put(sysmmu_node);
-#endif
pr_notice("found aoc with interrupt:%d sram:%pR dram:%pR\n", aoc_irq,
aoc_sram_resource, &prvdata->dram_resource);
- aoc_platform_device = pdev;
aoc_sram_virt_mapping = devm_ioremap_resource(dev, aoc_sram_resource);
@@ -3335,7 +2428,6 @@ static int aoc_platform_probe(struct platform_device *pdev)
goto err_sram_dram_map;
}
-#ifndef AOC_JUNO
prvdata->aoc_s2mpu_virt = devm_platform_ioremap_resource_byname(pdev, "aoc_s2mpu");
if (IS_ERR(prvdata->aoc_s2mpu_virt)) {
dev_err(dev, "failed to map aoc_s2mpu: rc = %ld\n",
@@ -3349,6 +2441,7 @@ static int aoc_platform_probe(struct platform_device *pdev)
/* Leave AoC in suspended state. Otherwise, AoC SysMMU is set to active which results in the
* SysMMU driver trying to access SysMMU SFRs during device suspend/resume operations. The
* latter is problematic if AoC is in monitor mode and BLK_AOC is off. */
+
pm_runtime_set_suspended(dev);
prvdata->domain = iommu_get_domain_for_dev(dev);
@@ -3365,9 +2458,8 @@ static int aoc_platform_probe(struct platform_device *pdev)
aoc_cleanup_resources(pdev);
return -ENOMEM;
}
-#endif
- prvdata->sensor_power_count = of_property_count_strings(dev->of_node, "sensor_power_list");
+ prvdata->sensor_power_count = of_property_count_strings(aoc_node, "sensor_power_list");
if (prvdata->sensor_power_count > MAX_SENSOR_POWER_NUM) {
pr_warn("sensor power count %i is larger than available number.",
prvdata->sensor_power_count);
@@ -3377,7 +2469,7 @@ static int aoc_platform_probe(struct platform_device *pdev)
prvdata->sensor_power_count = 0;
}
- ret = of_property_read_string_array(dev->of_node, "sensor_power_list",
+ ret = of_property_read_string_array(aoc_node, "sensor_power_list",
(const char**)&prvdata->sensor_power_list,
prvdata->sensor_power_count);
@@ -3392,6 +2484,9 @@ static int aoc_platform_probe(struct platform_device *pdev)
reset_sensor_power(prvdata, true);
+ aoc_parse_dmic_power(prvdata, aoc_node);
+ configure_dmic_regulator(prvdata, true);
+
/* Default to 6MB if we are not loading the firmware (i.e. trace32) */
aoc_control = aoc_dram_translate(prvdata, 6 * SZ_1M);
@@ -3399,31 +2494,9 @@ static int aoc_platform_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&prvdata->monitor_work, aoc_monitor_online);
- aoc_configure_interrupt();
-
-#ifdef AOC_JUNO
- ret = request_irq(aoc_irq, aoc_int_handler, IRQF_TRIGGER_HIGH, "aoc",
- prvdata->_device);
- if (ret != 0) {
- pr_err("failed to register interrupt handler : %d\n", ret);
-
- rc = -ENXIO;
- goto err_aoc_irq_req;
- }
-#endif
-
- ret = acpm_ipc_request_channel(aoc_node, acpm_aoc_reset_callback,
- &prvdata->acpm_async_id, &acpm_async_size);
- if (ret < 0) {
- dev_err(dev, "failed to register acpm aoc reset callback\n");
- rc = -EIO;
- /* goto err_acmp_reset; */
- }
+ aoc_configure_hardware(prvdata);
-#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
- prvdata->itmon_nb.notifier_call = aoc_itmon_notifier;
- itmon_notifier_chain_register(&prvdata->itmon_nb);
-#endif
+ rc = platform_specific_probe(pdev, prvdata);
if (aoc_autoload_firmware) {
ret = start_firmware_load(dev);
@@ -3438,29 +2511,19 @@ static int aoc_platform_probe(struct platform_device *pdev)
return 0;
/* err_acmp_reset: */
-#ifdef AOC_JUNO
-err_aoc_irq_req:
-#endif
-#ifndef AOC_JUNO
err_find_iommu:
err_s2mpu_map:
-#endif
err_sram_dram_map:
-#ifndef AOC_JUNO
err_watchdog_sysmmu_irq:
-err_watchdog_irq_req:
-err_watchdog_irq_get:
-#else
-err_get_irq:
-#endif
+err_watchdog_irq:
err_mem_resources:
aoc_cleanup_resources(pdev);
err_memnode:
deinit_chardev(prvdata);
err_chardev:
- kfree(prvdata);
err_failed_prvdata_alloc:
+err_invalid_dt:
err_platform_not_null:
return rc;
}
@@ -3544,3 +2607,4 @@ module_init(aoc_init);
module_exit(aoc_exit);
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(DMA_BUF);
diff --git a/aoc.h b/aoc.h
index 36940bf..afa7fbc 100644
--- a/aoc.h
+++ b/aoc.h
@@ -9,16 +9,59 @@
* published by the Free Software Foundation.
*/
+#include <linux/cdev.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
#include <linux/sizes.h>
+#include <linux/timer.h>
+#include <soc/google/debug-snapshot.h>
#include "aoc_ipc_core.h"
+
+/* TODO: Remove internal calls, or promote to "public" */
+#include "aoc_ipc_core_internal.h"
+
#include "uapi/aoc.h"
#ifdef __KERNEL__
+#define MAX_SENSOR_POWER_NUM 5
+#define MAX_DMIC_POWER_NUM 4
+#define AP_RESET_REASON_LENGTH 32
+#define MAX_FIRMWARE_LENGTH 128
+
+#define AOC_S2MPU_CTRL0 0x0
+#define AOC_S2MPU_CTRL_PROTECTION_ENABLE_PER_VID_CLR 0x54
+#define AOC_S2MPU_CTRL_PROTECTION_ENABLE_VID_MASK_ALL 0xFF
+
+#define AOC_RESTART_DISABLED_RC (0xD15AB1ED)
+
+#define SENSOR_DIRECT_HEAP_SIZE SZ_4M
+#define PLAYBACK_HEAP_SIZE SZ_16K
+#define CAPTURE_HEAP_SIZE SZ_64K
+
+#define DT_PROPERTY_NOT_FOUND 0xffffffff
+
struct aoc_service_dev;
typedef void (*aoc_service_dev_handler)(struct aoc_service_dev *d);
+enum AOC_FW_STATE {
+ AOC_STATE_OFFLINE,
+ AOC_STATE_FIRMWARE_LOADED,
+ AOC_STATE_STARTING,
+ AOC_STATE_ONLINE
+};
+
+struct mbox_slot {
+ struct mbox_client client;
+ struct mbox_chan *channel;
+ void *prvdata;
+ int index;
+};
+
struct aoc_service_dev {
struct device dev;
wait_queue_head_t read_queue;
@@ -37,6 +80,112 @@ struct aoc_service_dev {
bool wake_capable;
};
+typedef int (*aoc_map_handler)(u32 handle, phys_addr_t p, size_t size,
+ bool mapped, void *ctx);
+
+struct aoc_prvdata {
+ struct mbox_slot *mbox_channels;
+ struct aoc_service_dev **services;
+
+ unsigned long *read_blocked_mask;
+ unsigned long *write_blocked_mask;
+
+ struct work_struct online_work;
+ struct resource dram_resource;
+ aoc_map_handler map_handler;
+ void *map_handler_ctx;
+
+ struct delayed_work monitor_work;
+ atomic_t aoc_process_active;
+
+ struct device *dev;
+ struct iommu_domain *domain;
+ void *ipc_base;
+
+ void *sram_virt;
+ void *dram_virt;
+ void *aoc_req_virt;
+ void *aoc_s2mpu_virt;
+ size_t sram_size;
+ size_t dram_size;
+ size_t aoc_req_size;
+ u32 aoc_s2mpu_saved_value;
+
+ struct dma_heap *sensor_heap;
+ struct dma_heap *audio_playback_heap;
+ struct dma_heap *audio_capture_heap;
+ phys_addr_t sensor_heap_base;
+ phys_addr_t audio_playback_heap_base;
+ phys_addr_t audio_capture_heap_base;
+
+ int watchdog_irq;
+ struct work_struct watchdog_work;
+ bool aoc_reset_done;
+ bool ap_triggered_reset;
+ bool force_release_aoc;
+ char ap_reset_reason[AP_RESET_REASON_LENGTH];
+ wait_queue_head_t aoc_reset_wait_queue;
+ unsigned int acpm_async_id;
+ int total_services;
+
+ char firmware_name[MAX_FIRMWARE_LENGTH];
+ char *firmware_version;
+
+ struct cdev cdev;
+ dev_t aoc_devt;
+ struct class *_class;
+ struct device *_device;
+
+ u32 disable_monitor_mode;
+ u32 enable_uart_tx;
+ u32 force_voltage_nominal;
+ u32 no_ap_resets;
+ u32 force_speaker_ultrasonic;
+
+ u32 total_coredumps;
+ u32 total_restarts;
+ unsigned int sysmmu_nonsecure_irq;
+ unsigned int sysmmu_secure_irq;
+
+#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
+ struct notifier_block itmon_nb;
+#endif
+ struct device *gsa_dev;
+ bool protected_by_gsa;
+
+ int sensor_power_count;
+ const char *sensor_power_list[MAX_SENSOR_POWER_NUM];
+ struct regulator *sensor_regulator[MAX_SENSOR_POWER_NUM];
+
+ int dmic_power_count;
+ const char *dmic_power_list[MAX_DMIC_POWER_NUM];
+ struct regulator *dmic_regulator[MAX_DMIC_POWER_NUM];
+
+ int reset_hysteresis_trigger_ms;
+ u64 last_reset_time_ns;
+ int reset_wait_time_index;
+
+ u32 aoc_pcu_base;
+ u32 aoc_gpio_base;
+ u32 aoc_pcu_db_set_offset;
+ u32 aoc_pcu_db_clr_offset;
+ u32 aoc_cp_aperture_start_offset;
+ u32 aoc_cp_aperture_end_offset;
+ u32 aoc_clock_divider;
+ u32 aoc_mbox_channels;
+
+ u16 sysmmu_size;
+ struct sysmmu_entry *sysmmu;
+ bool sysmmu_configured;
+ bool sysmmu_config_persistent;
+};
+
+struct aoc_module_parameters {
+ bool aoc_autoload_firmware;
+ bool aoc_disable_restart;
+ bool aoc_panic_on_req_timeout;
+};
+
#define AOC_DEVICE(_d) container_of((_d), struct aoc_service_dev, dev)
phys_addr_t aoc_service_ring_base_phys_addr(struct aoc_service_dev *dev, aoc_direction dir,
@@ -78,13 +227,63 @@ struct aoc_driver {
int aoc_driver_register(struct aoc_driver *driver);
void aoc_driver_unregister(struct aoc_driver *driver);
-typedef int (*aoc_map_handler)(u32 handle, phys_addr_t p, size_t size,
- bool mapped, void *ctx);
void aoc_set_map_handler(struct aoc_service_dev *dev, aoc_map_handler handler,
void *ctx);
void aoc_remove_map_handler(struct aoc_service_dev *dev);
void aoc_trigger_watchdog(const char *reason);
+extern u32 gs_chipid_get_revision(void);
+extern u32 gs_chipid_get_type(void);
+
+bool aoc_release_from_reset(struct aoc_prvdata *prvdata);
+
+void *aoc_sram_translate(u32 offset);
+
+void request_aoc_on(struct aoc_prvdata *p, bool status);
+int wait_for_aoc_status(struct aoc_prvdata *p, bool status);
+
+int aoc_watchdog_restart(struct aoc_prvdata *prvdata,
+ struct aoc_module_parameters *aoc_module_params);
+
+int platform_specific_probe(struct platform_device *pdev, struct aoc_prvdata *prvdata);
+
+int start_firmware_load(struct device *dev);
+
+void reset_sensor_power(struct aoc_prvdata *prvdata, bool is_init);
+
+void aoc_configure_hardware(struct aoc_prvdata *prvdata);
+
+void trigger_aoc_ramdump(struct aoc_prvdata *prvdata);
+
+bool aoc_create_dma_buf_heaps(struct aoc_prvdata *prvdata);
+
+phys_addr_t aoc_dram_translate_to_aoc(struct aoc_prvdata *p, phys_addr_t addr);
+
+long aoc_unlocked_ioctl_handle_ion_fd(unsigned int cmd, unsigned long arg);
+
+int configure_watchdog_interrupt(struct platform_device *pdev, struct aoc_prvdata *prvdata);
+
+int configure_sysmmu_interrupts(struct device *dev, struct device_node *sysmmu_node,
+ struct aoc_prvdata *prvdata);
+
+void aoc_configure_ssmt(struct platform_device *pdev);
+
+int aoc_num_services(void);
+
+aoc_service *service_at_index(struct aoc_prvdata *prvdata,
+ unsigned int index);
+
+struct aoc_service_dev *service_dev_at_index(struct aoc_prvdata *prvdata,
+ unsigned int index);
+
+bool validate_service(struct aoc_prvdata *prv, int i);
+
+bool aoc_is_valid_dram_address(struct aoc_prvdata *prv, void *addr);
+
+bool aoc_fw_ready(void);
+
+u32 dt_property(struct device_node *node, const char *key);
+
#define AOC_SERVICE_NAME_LENGTH 32
/* Rings should have the ring flag set, slots = 1, size = ring size
@@ -98,13 +297,6 @@ void aoc_trigger_watchdog(const char *reason);
#define AOC_DOWNCALL_DOORBELL 12
-#define AOC_GPIO_BASE_WC 0xB70000
-#define AOC_GPIO_BASE_PRO 0xD70000
-
-#define AOC_PCU_BASE_WC 0xB00000
-#define AOC_PCU_BASE_PRO 0xA00000
-#define AOC_PCU_DB_SET_OFFSET 0xD004
-#define AOC_PCU_DB_CLR_OFFSET 0xD008
#define AOC_PCU_REVISION_OFFSET 0xF000
#define AOC_PCU_RESET_CONTROL_OFFSET 0x0
#define AOC_PCU_RESET_CONTROL_RESET_VALUE 0x0
@@ -120,6 +312,7 @@ void aoc_trigger_watchdog(const char *reason);
#define AOC_BINARY_DRAM_OFFSET (AOC_BINARY_LOAD_ADDRESS - AOC_BINARY_DRAM_BASE)
#define AOC_PARAMETER_MAGIC 0x0a0cda7a
+
enum AOC_FIRMWARE_INFORMATION {
kAOCBoardID = 0x1001,
kAOCBoardRevision = 0x1002,
@@ -137,6 +330,10 @@ enum AOC_FIRMWARE_INFORMATION {
kAOCCaptureHeapAddress = 0x100E,
kAOCCaptureHeapSize = 0x100F,
kAOCForceSpeakerUltrasonic = 0x1010,
+ kAOCRandSeed = 0x1011,
+ kAOCChipRevision = 0x1012,
+ kAOCChipType = 0x1013,
+ kAOCGnssType = 0x1014,
};
#define module_aoc_driver(__aoc_driver) \
diff --git a/aoc_channel_dev.c b/aoc_channel_dev.c
index 7da42a1..396b8ab 100644
--- a/aoc_channel_dev.c
+++ b/aoc_channel_dev.c
@@ -56,10 +56,11 @@ static DEFINE_MUTEX(aocc_write_lock);
static DEFINE_MUTEX(s_open_files_lock);
#define AOCC_MAX_MSG_SIZE 1024
-#define AOCC_MAX_PENDING_MSGS 128
-#define AOCC_BLOCK_CHANNEL_THRESHOLD 64
static atomic_t channel_index_counter = ATOMIC_INIT(1);
+static u32 aocc_max_pending_msgs;
+static u32 aocc_block_channel_threshold;
+
/* Driver methods */
static int aocc_probe(struct aoc_service_dev *dev);
static int aocc_remove(struct aoc_service_dev *dev);
@@ -213,10 +214,10 @@ static int aocc_demux_kthread(void *data)
}
if (atomic_read(&entry->pending_msg_count) >
- AOCC_MAX_PENDING_MSGS) {
+ aocc_max_pending_msgs) {
pr_err_ratelimited(
"Too many pending messages on channel %d. More than %d allocated",
- channel, AOCC_MAX_PENDING_MSGS);
+ channel, aocc_max_pending_msgs);
kfree(node);
break;
}
@@ -227,7 +228,7 @@ static int aocc_demux_kthread(void *data)
&entry->pending_aoc_messages);
atomic_inc(&entry->pending_msg_count);
if (atomic_read(&entry->pending_msg_count) >
- AOCC_BLOCK_CHANNEL_THRESHOLD &&
+ aocc_block_channel_threshold &&
!entry->is_channel_blocked) {
rc = aocc_send_cmd_msg(service,
AOCC_CMD_BLOCK_CHANNEL, channel);
@@ -600,7 +601,7 @@ static ssize_t aocc_read(struct file *file, char __user *buf, size_t count,
list_del(&node->msg_list);
atomic_dec(&private->pending_msg_count);
if (atomic_read(&private->pending_msg_count) <
- AOCC_BLOCK_CHANNEL_THRESHOLD && private->is_channel_blocked) {
+ aocc_block_channel_threshold && private->is_channel_blocked) {
rc = aocc_send_cmd_msg(private->aocc_device_entry->service,
AOCC_CMD_UNBLOCK_CHANNEL, private->channel_index);
if (rc >= 0)
@@ -773,6 +774,18 @@ static int aocc_probe(struct aoc_service_dev *dev)
if (!prvdata)
return -ENOMEM;
+ aocc_max_pending_msgs = dt_property(dev->dev.parent->of_node, "channel-max-pending-msgs");
+ if (aocc_max_pending_msgs == DT_PROPERTY_NOT_FOUND) {
+ dev_err(&dev->dev, "AOC DT missing property channel-max-pending-msgs");
+ return -EINVAL;
+ }
+ aocc_block_channel_threshold = dt_property(dev->dev.parent->of_node,
+ "block-channel-threshold");
+ if (aocc_block_channel_threshold == DT_PROPERTY_NOT_FOUND) {
+ dev_err(&dev->dev, "AOC DT missing property block_channel_threshold");
+ return -EINVAL;
+ }
+
if (strcmp(dev_name(&dev->dev), "usf_sh_mem_doorbell") != 0) {
ret = create_character_device(dev);
if (ret)
diff --git a/aoc_control_dev.c b/aoc_control_dev.c
index 80295ab..6148b50 100644
--- a/aoc_control_dev.c
+++ b/aoc_control_dev.c
@@ -46,6 +46,8 @@ const static struct dev_pm_ops aoc_control_pm_ops = {
.complete = aoc_control_complete,
};
+static bool notify_on_state_transition;
+
/* Driver methods */
/*
@@ -440,8 +442,8 @@ static ssize_t read_stat_by_name(struct device *dev, char *buf,
static int aoc_control_prepare(struct device *dev)
{
- #if IS_ENABLED(CONFIG_SOC_GS201)
- struct stats_prvdata *prvdata = dev_get_drvdata(dev);
+ if (notify_on_state_transition) {
+ struct stats_prvdata *prvdata = dev_get_drvdata(dev);
struct CMD_AP_STATE_TRANSITION cmd;
int ret;
@@ -453,14 +455,14 @@ static int aoc_control_prepare(struct device *dev)
dev_err(dev, "notifying AoC of entering deep sleep ret = %d\n", ret);
return ret;
- #else
- return 0;
- #endif
+ }
+
+ return 0;
}
static void aoc_control_complete(struct device *dev)
{
- #if IS_ENABLED(CONFIG_SOC_GS201)
+ if (notify_on_state_transition) {
struct stats_prvdata *prvdata = dev_get_drvdata(dev);
struct CMD_AP_STATE_TRANSITION cmd;
int ret;
@@ -472,7 +474,7 @@ static void aoc_control_complete(struct device *dev)
if (ret < 0)
dev_err(dev, "notifying AoC of exiting deep sleep ret = %d\n", ret);
- #endif
+ }
}
#define DECLARE_STAT(stat_name, sysfs_name) \
@@ -679,6 +681,9 @@ static int aoc_control_probe(struct aoc_service_dev *sd)
mutex_init(&prvdata->lock);
prvdata->memory_vote_core_id = 1;
+ notify_on_state_transition = of_property_read_bool(dev->parent->of_node,
+ "notify-on-state-transition");
+
INIT_WORK(&prvdata->discovery_work, discovery_workitem);
dev_set_drvdata(dev, prvdata);
diff --git a/aoc_firmware.c b/aoc_firmware.c
index 5ae96aa..97df478 100644
--- a/aoc_firmware.c
+++ b/aoc_firmware.c
@@ -219,6 +219,20 @@ bool _aoc_fw_is_valid_sysmmu_size(const struct firmware *fw)
(cfg->sysmmu_size % sizeof(struct sysmmu_entry) == 0);
}
+uint16_t _aoc_fw_bl_size(const struct firmware *fw)
+{
+ struct aoc_image_config *cfg = _aoc_fw_image_config(fw);
+
+ return cfg->bl_size;
+}
+
+u32 *_aoc_fw_bl(const struct firmware *fw)
+{
+ struct aoc_image_config *cfg = _aoc_fw_image_config(fw);
+
+ return (u32 *)((uint8_t *)cfg + cfg->bl_offset);
+}
+
struct sysmmu_entry *_aoc_fw_sysmmu_entry(const struct firmware *fw)
{
struct aoc_image_config *cfg = _aoc_fw_image_config(fw);
diff --git a/aoc_firmware.h b/aoc_firmware.h
index 2b5834c..13c0802 100644
--- a/aoc_firmware.h
+++ b/aoc_firmware.h
@@ -53,3 +53,7 @@ struct sysmmu_entry *_aoc_fw_sysmmu_entry(const struct firmware *fw);
struct aoc_image_config *_aoc_fw_image_config(const struct firmware *fw);
u32 _aoc_fw_get_header_version(const struct firmware *fw);
+
+uint16_t _aoc_fw_bl_size(const struct firmware *fw);
+
+u32 *_aoc_fw_bl(const struct firmware *fw);
diff --git a/aoc_ramdump_regions.h b/aoc_ramdump_regions.h
index b2a8688..f63680c 100644
--- a/aoc_ramdump_regions.h
+++ b/aoc_ramdump_regions.h
@@ -95,11 +95,6 @@ struct aoc_ramdump_header {
#define RAMDUMP_HEADER_ADDR (0x98000000 + RAMDUMP_HEADER_OFFSET) /* Start of DRAM carveout + offset */
#define RAMDUMP_SECTION_SRAM_OFFSET 0x1000
-#if IS_ENABLED(CONFIG_SOC_GS101)
-#define RAMDUMP_SECTION_SRAM_SIZE 0x600000
-#else
-#define RAMDUMP_SECTION_SRAM_SIZE 0x800000
-#endif
#define RAMDUMP_SECTION_CRASH_INFO_SIZE 256
diff --git a/aoc_service_core.c b/aoc_service_core.c
new file mode 100644
index 0000000..846cea5
--- /dev/null
+++ b/aoc_service_core.c
@@ -0,0 +1,563 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Google Whitechapel AoC service library
+ *
+ * Copyright (c) 2019-2023 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "aoc.h"
+
+extern enum AOC_FW_STATE aoc_state;
+extern struct aoc_control_block *aoc_control;
+
+static void signal_aoc(struct mbox_chan *channel);
+
+ssize_t aoc_service_read_timeout(struct aoc_service_dev *dev, uint8_t *buffer,
+ size_t count, long timeout)
+{
+ struct aoc_prvdata *prvdata;
+ aoc_service *service;
+
+ size_t msg_size;
+ int service_number;
+ long ret = 1;
+
+ if (!dev || !buffer || !count)
+ return -EINVAL;
+
+ if (dev->dead)
+ return -ENODEV;
+
+ prvdata = dev_get_drvdata(dev->dev.parent);
+ if (!prvdata)
+ return -ENODEV;
+
+ atomic_inc(&prvdata->aoc_process_active);
+ if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ service_number = dev->service_index;
+ service = service_at_index(prvdata, dev->service_index);
+
+ if (!aoc_is_valid_dram_address(prvdata, service)) {
+ WARN_ONCE(1, "aoc service %d has invalid DRAM region", service_number);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (aoc_service_message_slots(service, AOC_UP) == 0) {
+ ret = -EBADF;
+ goto err;
+ }
+
+ if (!aoc_service_can_read_message(service, AOC_UP)) {
+ set_bit(service_number, prvdata->read_blocked_mask);
+ ret = wait_event_interruptible_timeout(
+ dev->read_queue,
+ aoc_state != AOC_STATE_ONLINE || dev->dead ||
+ aoc_service_can_read_message(service, AOC_UP),
+ timeout);
+ clear_bit(service_number, prvdata->read_blocked_mask);
+ }
+
+ if (dev->dead || (aoc_state != AOC_STATE_ONLINE)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (ret < 0)
+ goto err;
+
+ /* AoC timed out */
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (!aoc_service_is_ring(service) &&
+ count < aoc_service_current_message_size(service, prvdata->ipc_base,
+ AOC_UP)) {
+ ret = -EFBIG;
+ goto err;
+ }
+
+ msg_size = count;
+ aoc_service_read_message(service, prvdata->ipc_base, AOC_UP, buffer,
+ &msg_size);
+
+err:
+ atomic_dec(&prvdata->aoc_process_active);
+
+ if (ret < 0)
+ return ret;
+
+ return msg_size;
+}
+EXPORT_SYMBOL_GPL(aoc_service_read_timeout);
+
+ssize_t aoc_service_write(struct aoc_service_dev *dev, const uint8_t *buffer,
+ size_t count, bool block)
+{
+ const struct device *parent;
+ struct aoc_prvdata *prvdata;
+
+ aoc_service *service;
+ int service_number;
+ int interrupt = dev->mbox_index;
+ int ret = 0;
+
+ if (!dev || !buffer || !count)
+ return -EINVAL;
+
+ if (dev->dead)
+ return -ENODEV;
+
+ if (aoc_state != AOC_STATE_ONLINE)
+ return -ENODEV;
+
+ BUG_ON(!dev->dev.parent);
+
+ parent = dev->dev.parent;
+ prvdata = dev_get_drvdata(parent);
+ if (!prvdata)
+ return -ENODEV;
+
+ if (interrupt >= prvdata->aoc_mbox_channels)
+ return -EINVAL;
+
+ atomic_inc(&prvdata->aoc_process_active);
+ if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ service_number = dev->service_index;
+ service = service_at_index(prvdata, service_number);
+
+ if (!aoc_is_valid_dram_address(prvdata, service)) {
+ WARN_ONCE(1, "aoc service %d has invalid DRAM region", service_number);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (aoc_service_message_slots(service, AOC_DOWN) == 0) {
+ ret = -EBADF;
+ goto err;
+ }
+
+ if (count > aoc_service_message_size(service, AOC_DOWN)) {
+ ret = -EFBIG;
+ goto err;
+ }
+
+ if (!aoc_service_can_write_message(service, AOC_DOWN)) {
+ if (!block) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ set_bit(service_number, prvdata->write_blocked_mask);
+ ret = wait_event_interruptible(dev->write_queue,
+ aoc_state != AOC_STATE_ONLINE || dev->dead ||
+ aoc_service_can_write_message(service, AOC_DOWN));
+ clear_bit(service_number, prvdata->write_blocked_mask);
+ }
+
+ if (dev->dead) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (aoc_state != AOC_STATE_ONLINE) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * The wait can fail if the AoC goes offline in the middle of a
+ * blocking write, so check again after the wait
+ */
+ if (ret != 0) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ ret = aoc_service_write_message(service, prvdata->ipc_base, AOC_DOWN,
+ buffer, count);
+
+ if (!aoc_service_is_ring(service) || aoc_ring_is_push(service))
+ signal_aoc(prvdata->mbox_channels[interrupt].channel);
+err:
+ atomic_dec(&prvdata->aoc_process_active);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(aoc_service_write);
+
+ssize_t aoc_service_write_timeout(struct aoc_service_dev *dev, const uint8_t *buffer,
+ size_t count, long timeout)
+{
+ struct aoc_prvdata *prvdata;
+
+ aoc_service *service;
+ int service_number;
+ int interrupt = dev->mbox_index;
+ long ret = 1;
+
+ if (!dev || !buffer || !count)
+ return -EINVAL;
+
+ if (dev->dead)
+ return -ENODEV;
+
+ prvdata = dev_get_drvdata(dev->dev.parent);
+ if (!prvdata)
+ return -ENODEV;
+
+ atomic_inc(&prvdata->aoc_process_active);
+ if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ service_number = dev->service_index;
+ service = service_at_index(prvdata, service_number);
+
+ if (!aoc_is_valid_dram_address(prvdata, service)) {
+ WARN_ONCE(1, "aoc service %d has invalid DRAM region", service_number);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (aoc_service_message_slots(service, AOC_DOWN) == 0) {
+ ret = -EBADF;
+ goto err;
+ }
+
+ if (count > aoc_service_message_size(service, AOC_DOWN)) {
+ ret = -EFBIG;
+ goto err;
+ }
+
+ if (!aoc_service_can_write_message(service, AOC_DOWN)) {
+ set_bit(service_number, prvdata->write_blocked_mask);
+ ret = wait_event_interruptible_timeout(
+ dev->write_queue,
+ aoc_state != AOC_STATE_ONLINE || dev->dead ||
+ aoc_service_can_write_message(service, AOC_DOWN),
+ timeout);
+ clear_bit(service_number, prvdata->write_blocked_mask);
+ }
+
+ if (dev->dead || aoc_state != AOC_STATE_ONLINE) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (ret < 0)
+ goto err;
+
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ ret = aoc_service_write_message(service, prvdata->ipc_base, AOC_DOWN,
+ buffer, count);
+
+ if (!aoc_service_is_ring(service) || aoc_ring_is_push(service))
+ signal_aoc(prvdata->mbox_channels[interrupt].channel);
+
+err:
+ atomic_dec(&prvdata->aoc_process_active);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(aoc_service_write_timeout);
+
+int aoc_service_can_read(struct aoc_service_dev *dev)
+{
+ const struct device *parent;
+ struct aoc_prvdata *prvdata;
+ aoc_service *service;
+
+ parent = dev->dev.parent;
+ prvdata = dev_get_drvdata(parent);
+ service = service_at_index(prvdata, dev->service_index);
+
+ if (aoc_service_message_slots(service, AOC_UP) == 0)
+ return 0;
+
+ return aoc_service_can_read_message(service, AOC_UP);
+}
+EXPORT_SYMBOL_GPL(aoc_service_can_read);
+
+int aoc_service_can_write(struct aoc_service_dev *dev)
+{
+ const struct device *parent;
+ struct aoc_prvdata *prvdata;
+ aoc_service *service;
+
+ parent = dev->dev.parent;
+ prvdata = dev_get_drvdata(parent);
+ service = service_at_index(prvdata, dev->service_index);
+
+ if (aoc_service_message_slots(service, AOC_DOWN) == 0)
+ return 0;
+
+ return aoc_service_can_write_message(service, AOC_DOWN);
+}
+EXPORT_SYMBOL_GPL(aoc_service_can_write);
+
+void aoc_service_set_read_blocked(struct aoc_service_dev *dev)
+{
+ int service_number;
+ struct device *parent = dev->dev.parent;
+ struct aoc_prvdata *prvdata = dev_get_drvdata(parent);
+
+ service_number = dev->service_index;
+ set_bit(service_number, prvdata->read_blocked_mask);
+}
+EXPORT_SYMBOL_GPL(aoc_service_set_read_blocked);
+
+int aoc_num_services(void)
+{
+ return aoc_fw_ready() ? le32_to_cpu(aoc_control->services) : 0;
+}
+EXPORT_SYMBOL_GPL(aoc_num_services);
+
+aoc_service *service_at_index(struct aoc_prvdata *prvdata,
+ unsigned int index)
+{
+ if (!aoc_fw_ready() || index > aoc_num_services())
+ return NULL;
+
+ return (((uint8_t *)prvdata->ipc_base) + aoc_control->services_offset +
+ (le32_to_cpu(aoc_control->service_size) * index));
+}
+EXPORT_SYMBOL_GPL(service_at_index);
+
+struct aoc_service_dev *service_dev_at_index(struct aoc_prvdata *prvdata,
+ unsigned int index)
+{
+ if (!aoc_fw_ready() || index > aoc_num_services() || aoc_state != AOC_STATE_ONLINE)
+ return NULL;
+
+ return prvdata->services[index];
+}
+EXPORT_SYMBOL_GPL(service_dev_at_index);
+
+bool validate_service(struct aoc_prvdata *prv, int i)
+{
+ struct aoc_ipc_service_header *hdr = service_at_index(prv, i);
+ struct device *dev = prv->dev;
+
+ if (!aoc_is_valid_dram_address(prv, hdr)) {
+ dev_err(dev, "service %d is not in DRAM region\n", i);
+ return false;
+ }
+
+ if (hdr->regions[0].slots == 0 && hdr->regions[1].slots == 0) {
+ dev_err(dev, "service %d is not readable or writable\n", i);
+
+ return false;
+ }
+
+ if (aoc_service_is_ring(hdr) &&
+ (hdr->regions[0].slots > 1 || hdr->regions[1].slots > 1)) {
+ dev_err(dev, "service %d has invalid ring slot configuration\n",
+ i);
+
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(validate_service);
+
+bool aoc_service_flush_read_data(struct aoc_service_dev *dev)
+{
+ const struct device *parent;
+ struct aoc_prvdata *prvdata;
+ aoc_service *service;
+ size_t slots;
+
+ if (!dev)
+ return false;
+
+ parent = dev->dev.parent;
+ prvdata = dev_get_drvdata(parent);
+
+ service = service_at_index(prvdata, dev->service_index);
+
+ slots = aoc_service_slots_available_to_read(service, AOC_UP);
+ if (slots == 0)
+ return false;
+
+ aoc_service_advance_read_index(service, AOC_UP, slots);
+ return true;
+}
+EXPORT_SYMBOL_GPL(aoc_service_flush_read_data);
+
+ssize_t aoc_service_read(struct aoc_service_dev *dev, uint8_t *buffer,
+ size_t count, bool block)
+{
+ const struct device *parent;
+ struct aoc_prvdata *prvdata;
+ aoc_service *service;
+
+ size_t msg_size;
+ int service_number;
+ int ret = 0;
+ bool was_full;
+ int interrupt = dev->mbox_index;
+
+ if (!dev || !buffer || !count)
+ return -EINVAL;
+
+ if (dev->dead)
+ return -ENODEV;
+
+ if (aoc_state != AOC_STATE_ONLINE)
+ return -EBUSY;
+
+ parent = dev->dev.parent;
+ prvdata = dev_get_drvdata(parent);
+ if (!prvdata)
+ return -ENODEV;
+
+ atomic_inc(&prvdata->aoc_process_active);
+ if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ service_number = dev->service_index;
+ service = service_at_index(prvdata, dev->service_index);
+
+ if (!aoc_is_valid_dram_address(prvdata, service)) {
+ WARN_ONCE(1, "aoc service %d has invalid DRAM region", service_number);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (aoc_service_message_slots(service, AOC_UP) == 0) {
+ ret = -EBADF;
+ goto err;
+ }
+
+ if (!aoc_service_can_read_message(service, AOC_UP)) {
+ if (!block) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ set_bit(service_number, prvdata->read_blocked_mask);
+ ret = wait_event_interruptible(dev->read_queue,
+ aoc_state != AOC_STATE_ONLINE || dev->dead ||
+ aoc_service_can_read_message(service, AOC_UP));
+ clear_bit(service_number, prvdata->read_blocked_mask);
+ }
+
+ if (dev->dead) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (aoc_state != AOC_STATE_ONLINE) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * The wait can fail if the AoC goes offline in the middle of a
+ * blocking read, so check again after the wait
+ */
+ if (ret != 0) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ if (!aoc_service_is_ring(service) &&
+ count < aoc_service_current_message_size(service, prvdata->ipc_base,
+ AOC_UP)) {
+ ret = -EFBIG;
+ goto err;
+ }
+
+ msg_size = count;
+ was_full = !aoc_service_can_write_message(service, AOC_UP);
+
+ aoc_service_read_message(service, prvdata->ipc_base, AOC_UP, buffer,
+ &msg_size);
+
+ /*
+ * If the service queue was full right before reading, signal AoC that
+ * there is now space available to write.
+ */
+ if (was_full)
+ signal_aoc(prvdata->mbox_channels[interrupt].channel);
+err:
+ atomic_dec(&prvdata->aoc_process_active);
+ if (ret < 0)
+ return ret;
+
+ return msg_size;
+}
+EXPORT_SYMBOL_GPL(aoc_service_read);
+
+bool aoc_online_state(struct aoc_service_dev *dev)
+{
+ struct aoc_prvdata *prvdata;
+
+ if (!dev)
+ return false;
+
+ prvdata = dev_get_drvdata(dev->dev.parent);
+ if (!prvdata)
+ return false;
+
+ if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work))
+ return false;
+ return true;
+}
+EXPORT_SYMBOL_GPL(aoc_online_state);
+
+void aoc_service_set_write_blocked(struct aoc_service_dev *dev)
+{
+ int service_number;
+ struct device *parent = dev->dev.parent;
+ struct aoc_prvdata *prvdata = dev_get_drvdata(parent);
+
+ service_number = dev->service_index;
+ set_bit(service_number, prvdata->write_blocked_mask);
+}
+EXPORT_SYMBOL_GPL(aoc_service_set_write_blocked);
+
+wait_queue_head_t *aoc_service_get_read_queue(struct aoc_service_dev *dev)
+{
+ return &dev->read_queue;
+}
+EXPORT_SYMBOL_GPL(aoc_service_get_read_queue);
+
+wait_queue_head_t *aoc_service_get_write_queue(struct aoc_service_dev *dev)
+{
+ return &dev->write_queue;
+}
+EXPORT_SYMBOL_GPL(aoc_service_get_write_queue);
+
+static void signal_aoc(struct mbox_chan *channel)
+{
+ mbox_send_message(channel, NULL);
+}
diff --git a/aoc_v1.c b/aoc_v1.c
new file mode 100644
index 0000000..3370e86
--- /dev/null
+++ b/aoc_v1.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Google Whitechapel AoC v1 library
+ *
+ * Copyright (c) 2019-2023 Google LLC
+ */
+
+#include "aoc.h"
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <soc/google/exynos-cpupm.h>
+#include <soc/google/exynos-pmu-if.h>
+#include <soc/google/acpm_ipc_ctrl.h>
+
+#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
+#include <soc/google/exynos-itmon.h>
+#endif
+
+#include "ion_physical_heap.h"
+
+#define SSMT_BYPASS_VALUE 0x80000000U
+#define SSMT_NS_READ_PID(n) (0x4000 + 4 * (n))
+#define SSMT_NS_WRITE_PID(n) (0x4200 + 4 * (n))
+
+extern struct platform_device *aoc_platform_device;
+extern struct resource *aoc_sram_resource;
+extern struct mutex aoc_service_lock;
+
+#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
+static int aoc_itmon_notifier(struct notifier_block *nb, unsigned long action,
+ void *nb_data)
+{
+ struct aoc_prvdata *prvdata;
+ struct itmon_notifier *itmon_info = nb_data;
+
+ prvdata = container_of(nb, struct aoc_prvdata, itmon_nb);
+ if (itmon_info->port && (strncmp(itmon_info->port, "AOC", sizeof("AOC") - 1) == 0))
+ return NOTIFY_STOP;
+
+ if (itmon_info->target_addr == 0) {
+ dev_err(prvdata->dev,
+ "Possible repro of b/174577569, please upload a bugreport and /data/vendor/ssrdump to that bug\n");
+ return NOTIFY_STOP;
+ }
+
+ if ((itmon_info->target_addr >= aoc_sram_resource->start +
+ prvdata->aoc_cp_aperture_start_offset) &&
+ (itmon_info->target_addr <= aoc_sram_resource->start +
+ prvdata->aoc_cp_aperture_end_offset)) {
+ dev_err(prvdata->dev,
+ "Valid memory access triggered ITMON error. Please file a bug with bugreport and contents of /data/vendor/ssrdump\n");
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_OK;
+}
+#endif
+
+static void acpm_aoc_reset_callback(unsigned int *cmd, unsigned int size)
+{
+ struct aoc_prvdata *prvdata;
+
+ if (!aoc_platform_device)
+ return;
+
+ prvdata = platform_get_drvdata(aoc_platform_device);
+ prvdata->aoc_reset_done = true;
+ wake_up(&prvdata->aoc_reset_wait_queue);
+}
+
+bool aoc_release_from_reset(struct aoc_prvdata *prvdata)
+{
+ u32 pcu_value;
+ void __iomem *pcu = aoc_sram_translate(prvdata->aoc_pcu_base);
+
+ if (!pcu)
+ return false;
+
+ pcu_value = ioread32(pcu);
+
+ pcu_value |= 1;
+ iowrite32(pcu_value, pcu);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(aoc_release_from_reset);
+
+void request_aoc_on(struct aoc_prvdata *p, bool status)
+{
+ iowrite32(!!status, p->aoc_req_virt);
+}
+EXPORT_SYMBOL_GPL(request_aoc_on);
+
+int wait_for_aoc_status(struct aoc_prvdata *p, bool status)
+{
+ unsigned long aoc_req_timeout;
+
+ aoc_req_timeout = jiffies + (2 * HZ);
+ while (time_before(jiffies, aoc_req_timeout)) {
+ if (!!readl(p->aoc_req_virt + 0x40) == !!status)
+ return 0;
+ msleep(100);
+ }
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(wait_for_aoc_status);
+
+__attribute__((unused))
+int aoc_watchdog_restart(struct aoc_prvdata *prvdata,
+ struct aoc_module_parameters *aoc_module_params)
+{
+ /* 4100 * 0.244 us * 100 = 100 ms */
+ const int aoc_watchdog_value_ssr = 4100 * 100;
+ const int aoc_reset_timeout_ms = 1000;
+ const int aoc_reset_tries = 3;
+ const u32 aoc_watchdog_control_ssr = 0x3F;
+ const unsigned int custom_in_offset = 0x3AC4;
+ const unsigned int custom_out_offset = 0x3AC0;
+ int aoc_req_rc, rc;
+ void __iomem *pcu;
+ unsigned int custom_in;
+ unsigned int custom_out;
+ int ret;
+ bool aoc_reset_successful;
+ int i;
+
+ pcu = aoc_sram_translate(prvdata->aoc_pcu_base);
+ if (!pcu)
+ return -ENODEV;
+
+ if (aoc_module_params->aoc_disable_restart)
+ return AOC_RESTART_DISABLED_RC;
+
+ aoc_reset_successful = false;
+ disable_irq_nosync(prvdata->sysmmu_nonsecure_irq);
+ disable_irq_nosync(prvdata->sysmmu_secure_irq);
+ for (i = 0; i < aoc_reset_tries; i++) {
+ dev_info(prvdata->dev, "asserting aoc_req\n");
+ request_aoc_on(prvdata, true);
+ aoc_req_rc = wait_for_aoc_status(prvdata, true);
+ if (aoc_req_rc) {
+ dev_err(prvdata->dev, "timed out waiting for aoc_ack\n");
+ continue;
+ }
+ dev_info(prvdata->dev, "resetting aoc\n");
+ writel(AOC_PCU_WATCHDOG_KEY_UNLOCK, pcu + AOC_PCU_WATCHDOG_KEY_OFFSET);
+ if ((readl(pcu + AOC_PCU_WATCHDOG_CONTROL_OFFSET) &
+ AOC_PCU_WATCHDOG_CONTROL_KEY_ENABLED_MASK) == 0) {
+ dev_err(prvdata->dev, "unlock aoc watchdog failed\n");
+ }
+ writel(aoc_watchdog_value_ssr, pcu + AOC_PCU_WATCHDOG_VALUE_OFFSET);
+ writel(aoc_watchdog_control_ssr, pcu + AOC_PCU_WATCHDOG_CONTROL_OFFSET);
+
+ dev_info(prvdata->dev, "waiting for aoc reset to finish\n");
+ if (wait_event_timeout(prvdata->aoc_reset_wait_queue, prvdata->aoc_reset_done,
+ aoc_reset_timeout_ms) == 0) {
+ ret = exynos_pmu_read(custom_out_offset, &custom_out);
+ dev_err(prvdata->dev,
+ "AoC reset timeout custom_out=%d, ret=%d\n", custom_out, ret);
+ ret = exynos_pmu_read(custom_in_offset, &custom_in);
+ dev_err(prvdata->dev,
+ "AoC reset timeout custom_in=%d, ret=%d\n", custom_in, ret);
+ dev_err(prvdata->dev, "PCU_WATCHDOG_CONTROL = 0x%x\n",
+ readl(pcu + AOC_PCU_WATCHDOG_CONTROL_OFFSET));
+ dev_err(prvdata->dev, "PCU_WATCHDOG_VALUE = 0x%x\n",
+ readl(pcu + AOC_PCU_WATCHDOG_VALUE_OFFSET));
+ } else {
+ aoc_reset_successful = true;
+ break;
+ }
+ }
+
+ if (aoc_req_rc && aoc_module_params->aoc_panic_on_req_timeout) {
+ dev_err(prvdata->dev, "timed out too many times waiting for aoc_ack, triggering kernel panic\n");
+ panic("AoC kernel panic: timed out waiting for aoc_ack");
+ }
+
+ enable_irq(prvdata->sysmmu_nonsecure_irq);
+ enable_irq(prvdata->sysmmu_secure_irq);
+ if (!aoc_reset_successful) {
+ /* Trigger acpm ramdump since we timed out the aoc reset request */
+ dbg_snapshot_emergency_reboot("AoC Restart timed out");
+ return -ETIMEDOUT;
+ }
+ reset_sensor_power(prvdata, false);
+ dev_info(prvdata->dev, "aoc reset finished\n");
+ prvdata->aoc_reset_done = false;
+
+ /*
+ * AOC_TZPC has been restored by ACPM, so we can access AOC_S2MPU.
+ * Restore AOC_S2MPU.
+ */
+ writel(prvdata->aoc_s2mpu_saved_value, prvdata->aoc_s2mpu_virt + AOC_S2MPU_CTRL0);
+
+#if IS_ENABLED(CONFIG_SOC_ZUMA)
+ /*
+ * Zuma S2MPU registers changed. S2MPU_CTRL0.ENABLE functionality is
+ * replaced by S2MPU_CTRL_PROTECTION_ENABLE_PER_VID.
+ */
+ writel(AOC_S2MPU_CTRL_PROTECTION_ENABLE_VID_MASK_ALL,
+ prvdata->aoc_s2mpu_virt + AOC_S2MPU_CTRL_PROTECTION_ENABLE_PER_VID_CLR);
+#endif
+
+ /* Restore SysMMU settings by briefly setting AoC to runtime active. Since SysMMU is a
+ * supplier to AoC, it will be set to runtime active as a side effect. */
+ rc = pm_runtime_set_active(prvdata->dev);
+ if (rc < 0) {
+ dev_err(prvdata->dev, "sysmmu restore failed: pm_runtime_resume rc = %d\n", rc);
+ return rc;
+ }
+ rc = pm_runtime_set_suspended(prvdata->dev);
+ if (rc < 0) {
+ dev_err(prvdata->dev, "sysmmu restore failed: pm_runtime_suspend rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = start_firmware_load(prvdata->dev);
+ if (rc) {
+ dev_err(prvdata->dev, "load aoc firmware failed: rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(aoc_watchdog_restart);
+
+int platform_specific_probe(struct platform_device *pdev, struct aoc_prvdata *prvdata)
+{
+ unsigned int acpm_async_size;
+ struct device *dev = &pdev->dev;
+ struct device_node *aoc_node = dev->of_node;
+
+ int rc = 0, ret = acpm_ipc_request_channel(aoc_node, acpm_aoc_reset_callback,
+ &prvdata->acpm_async_id, &acpm_async_size);
+ if (ret < 0) {
+ dev_err(dev, "failed to register acpm aoc reset callback\n");
+ rc = -EIO;
+ }
+
+#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
+ prvdata->itmon_nb.notifier_call = aoc_itmon_notifier;
+ itmon_notifier_chain_register(&prvdata->itmon_nb);
+#endif
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(platform_specific_probe);
+
+static void aoc_clear_gpio_interrupt(struct aoc_prvdata *prvdata)
+{
+#if defined(GPIO_INTERRUPT)
+ int reg = GPIO_INTERRUPT, val;
+ u32 *gpio_register =
+ aoc_sram_translate(prvdata->aoc_gpio_base + ((reg / 32) * 12));
+
+ val = ioread32(gpio_register);
+ val &= ~(1 << (reg % 32));
+ iowrite32(val, gpio_register);
+#endif
+}
+
+void aoc_configure_hardware(struct aoc_prvdata *prvdata)
+{
+ aoc_clear_gpio_interrupt(prvdata);
+}
+EXPORT_SYMBOL_GPL(aoc_configure_hardware);
+
+void trigger_aoc_ramdump(struct aoc_prvdata *prvdata)
+{
+ struct mbox_chan *channel = prvdata->mbox_channels[15].channel;
+ static const uint32_t command[] = { 0, 0, 0, 0, 0x0deada0c, 0, 0, 0 };
+
+ dev_notice(prvdata->dev, "Attempting to force AoC coredump\n");
+
+ mbox_send_message(channel, (void *)&command);
+}
+EXPORT_SYMBOL_GPL(trigger_aoc_ramdump);
+
+static void aoc_pheap_alloc_cb(struct samsung_dma_buffer *buffer, void *ctx)
+{
+ struct device *dev = ctx;
+ struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
+ struct sg_table *sg = &buffer->sg_table;
+ phys_addr_t phys;
+ size_t size;
+
+ if (sg->nents != 1) {
+ dev_warn(dev, "Unable to map sg_table with %d ents\n",
+ sg->nents);
+ return;
+ }
+
+ phys = sg_phys(&sg->sgl[0]);
+ phys = aoc_dram_translate_to_aoc(prvdata, phys);
+ size = sg->sgl[0].length;
+
+ mutex_lock(&aoc_service_lock);
+ if (prvdata->map_handler) {
+ prvdata->map_handler((u64)buffer->priv, phys, size, true,
+ prvdata->map_handler_ctx);
+ }
+ mutex_unlock(&aoc_service_lock);
+}
+
+static void aoc_pheap_free_cb(struct samsung_dma_buffer *buffer, void *ctx)
+{
+ struct device *dev = ctx;
+ struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
+ struct sg_table *sg = &buffer->sg_table;
+ phys_addr_t phys;
+ size_t size;
+
+ if (sg->nents != 1) {
+ dev_warn(dev, "Unable to map sg_table with %d ents\n",
+ sg->nents);
+ return;
+ }
+
+ phys = sg_phys(&sg->sgl[0]);
+ phys = aoc_dram_translate_to_aoc(prvdata, phys);
+ size = sg->sgl[0].length;
+
+ mutex_lock(&aoc_service_lock);
+ if (prvdata->map_handler) {
+ prvdata->map_handler((u64)buffer->priv, phys, size, false,
+ prvdata->map_handler_ctx);
+ }
+ mutex_unlock(&aoc_service_lock);
+}
+
+static struct dma_heap *aoc_create_dma_buf_heap(struct aoc_prvdata *prvdata, const char *name,
+ phys_addr_t base, size_t size)
+{
+ struct device *dev = prvdata->dev;
+ size_t align = SZ_16K;
+ struct dma_heap *heap;
+
+ heap = ion_physical_heap_create(base, size, align, name, aoc_pheap_alloc_cb,
+ aoc_pheap_free_cb, dev);
+ if (IS_ERR(heap))
+ dev_err(dev, "heap \"%s\" creation failure: %ld\n", name, PTR_ERR(heap));
+
+ return heap;
+}
+
+bool aoc_create_dma_buf_heaps(struct aoc_prvdata *prvdata)
+{
+ phys_addr_t base = prvdata->dram_resource.start + resource_size(&prvdata->dram_resource);
+
+ base -= SENSOR_DIRECT_HEAP_SIZE;
+ prvdata->sensor_heap = aoc_create_dma_buf_heap(prvdata, "sensor_direct_heap",
+ base, SENSOR_DIRECT_HEAP_SIZE);
+ prvdata->sensor_heap_base = base;
+ if (IS_ERR(prvdata->sensor_heap))
+ return false;
+
+ base -= PLAYBACK_HEAP_SIZE;
+ prvdata->audio_playback_heap = aoc_create_dma_buf_heap(prvdata, "aaudio_playback_heap",
+ base, PLAYBACK_HEAP_SIZE);
+ prvdata->audio_playback_heap_base = base;
+ if (IS_ERR(prvdata->audio_playback_heap))
+ return false;
+
+ base -= CAPTURE_HEAP_SIZE;
+ prvdata->audio_capture_heap = aoc_create_dma_buf_heap(prvdata, "aaudio_capture_heap",
+ base, CAPTURE_HEAP_SIZE);
+ prvdata->audio_capture_heap_base = base;
+ if (IS_ERR(prvdata->audio_capture_heap))
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(aoc_create_dma_buf_heaps);
+
+long aoc_unlocked_ioctl_handle_ion_fd(unsigned int cmd, unsigned long arg)
+{
+ struct aoc_ion_handle handle;
+ struct dma_buf *dmabuf;
+ struct samsung_dma_buffer *dma_heap_buf;
+ long ret = -EINVAL;
+
+ BUILD_BUG_ON(sizeof(struct aoc_ion_handle) !=
+ _IOC_SIZE(AOC_IOCTL_ION_FD_TO_HANDLE));
+
+ if (copy_from_user(&handle, (struct aoc_ion_handle *)arg, _IOC_SIZE(cmd)))
+ return ret;
+
+ dmabuf = dma_buf_get(handle.fd);
+ if (IS_ERR(dmabuf)) {
+ pr_err("fd is not an ion buffer\n");
+ ret = PTR_ERR(dmabuf);
+ return ret;
+ }
+
+ dma_heap_buf = dmabuf->priv;
+ handle.handle = (u64)dma_heap_buf->priv;
+
+ dma_buf_put(dmabuf);
+
+ if (!copy_to_user((struct aoc_ion_handle *)arg, &handle, _IOC_SIZE(cmd)))
+ ret = 0;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aoc_unlocked_ioctl_handle_ion_fd);
+
+static irqreturn_t watchdog_int_handler(int irq, void *dev)
+{
+ struct aoc_prvdata *prvdata = dev_get_drvdata(dev);
+
+ /* AP shouldn't access AoC registers to clear the IRQ. */
+ /* Mask the IRQ until the IRQ gets cleared by AoC reset during SSR. */
+ disable_irq_nosync(irq);
+ schedule_work(&prvdata->watchdog_work);
+
+ return IRQ_HANDLED;
+}
+
+int configure_watchdog_interrupt(struct platform_device *pdev, struct aoc_prvdata *prvdata)
+{
+ int ret = 0;
+ struct device *dev = &pdev->dev;
+
+ prvdata->watchdog_irq = platform_get_irq_byname(pdev, "watchdog");
+ if (prvdata->watchdog_irq < 0) {
+ dev_err(dev, "failed to find watchdog irq\n");
+ return -EIO;
+ }
+
+ irq_set_status_flags(prvdata->watchdog_irq, IRQ_NOAUTOEN);
+ ret = devm_request_irq(dev, prvdata->watchdog_irq, watchdog_int_handler,
+ IRQF_TRIGGER_HIGH, dev_name(dev), dev);
+ if (ret != 0) {
+ dev_err(dev, "failed to register watchdog irq handler: %d\n",
+ ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(configure_watchdog_interrupt);
+
+int configure_sysmmu_interrupts(struct device *dev, struct device_node *sysmmu_node,
+ struct aoc_prvdata *prvdata)
+{
+ int rc = 0, ret = of_irq_get(sysmmu_node, 0);
+
+ if (ret < 0) {
+ dev_err(dev, "failed to find sysmmu non-secure irq: %d\n", ret);
+ rc = ret;
+ return rc;
+ }
+ prvdata->sysmmu_nonsecure_irq = ret;
+ ret = of_irq_get(sysmmu_node, 1);
+ if (ret < 0) {
+ dev_err(dev, "failed to find sysmmu secure irq: %d\n", ret);
+ rc = ret;
+ return rc;
+ }
+ prvdata->sysmmu_secure_irq = ret;
+ return rc;
+}
+EXPORT_SYMBOL_GPL(configure_sysmmu_interrupts);
+
+#if IS_ENABLED(CONFIG_SOC_GS101)
+void aoc_configure_ssmt(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int stream_id;
+
+ void __iomem *ssmt_base = devm_platform_ioremap_resource_byname(pdev, "ssmt_aoc");
+
+ if (IS_ERR(ssmt_base)) {
+ dev_err(dev, "ssmt_aoc base address failure: %ld\n", PTR_ERR(ssmt_base));
+ return;
+ }
+
+ /* Configure registers NS_READ_PID_<n>, NS_WRITE_PID_<n> for each stream id */
+ for (stream_id = 0; stream_id <= 32; stream_id++) {
+ /* Skip over stream id 31 */
+ if (stream_id == 31)
+ continue;
+ writel_relaxed(SSMT_BYPASS_VALUE, ssmt_base + SSMT_NS_READ_PID(stream_id));
+ writel_relaxed(SSMT_BYPASS_VALUE, ssmt_base + SSMT_NS_WRITE_PID(stream_id));
+ }
+
+ devm_iounmap(dev, ssmt_base);
+}
+#else
+void aoc_configure_ssmt(struct platform_device *pdev
+ __attribute__((unused)))
+{}
+#endif
+EXPORT_SYMBOL_GPL(aoc_configure_ssmt);