diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | alsa/aoc_alsa.h | 6 | ||||
-rw-r--r-- | alsa/aoc_alsa_card.c | 9 | ||||
-rw-r--r-- | alsa/aoc_alsa_ctl.c | 48 | ||||
-rw-r--r-- | alsa/aoc_alsa_drv.h | 3 | ||||
-rw-r--r-- | alsa/aoc_alsa_hw.c | 117 | ||||
-rw-r--r-- | alsa/aoc_alsa_path.c | 1 | ||||
-rw-r--r-- | aoc-interface-gs101.h | 68 | ||||
-rw-r--r-- | aoc.c | 1794 | ||||
-rw-r--r-- | aoc.h | 215 | ||||
-rw-r--r-- | aoc_channel_dev.c | 25 | ||||
-rw-r--r-- | aoc_control_dev.c | 19 | ||||
-rw-r--r-- | aoc_firmware.c | 14 | ||||
-rw-r--r-- | aoc_firmware.h | 4 | ||||
-rw-r--r-- | aoc_ramdump_regions.h | 5 | ||||
-rw-r--r-- | aoc_service_core.c | 563 | ||||
-rw-r--r-- | aoc_v1.c | 498 |
17 files changed, 1910 insertions, 1481 deletions
@@ -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; @@ -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); @@ -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); |