123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
- */
- #include "ipa_i.h"
- #include <linux/delay.h>
- #define IPA_RAM_UC_SMEM_SIZE 128
- #define IPA_HW_INTERFACE_VERSION 0x2000
- #define IPA_PKT_FLUSH_TO_US 100
- #define IPA_UC_POLL_SLEEP_USEC 100
- #define IPA_UC_POLL_MAX_RETRY 10000
- #define IPA_UC_DBG_STATS_GET_PROT_ID(x) (0xff & ((x) >> 24))
- #define IPA_UC_DBG_STATS_GET_OFFSET(x) (0x00ffffff & (x))
- /**
- * Mailbox register to Interrupt HWP for CPU cmd
- * Usage of IPA_UC_MAILBOX_m_n doorbell instead of IPA_IRQ_EE_UC_0
- * due to HW limitation.
- *
- */
- #define IPA_CPU_2_HW_CMD_MBOX_m 0
- #define IPA_CPU_2_HW_CMD_MBOX_n 23
- /**
- * enum ipa3_cpu_2_hw_commands - Values that represent the commands from the CPU
- * IPA_CPU_2_HW_CMD_NO_OP : No operation is required.
- * IPA_CPU_2_HW_CMD_UPDATE_FLAGS : Update SW flags which defines the behavior
- * of HW.
- * IPA_CPU_2_HW_CMD_DEBUG_RUN_TEST : Launch predefined test over HW.
- * IPA_CPU_2_HW_CMD_DEBUG_GET_INFO : Read HW internal debug information.
- * IPA_CPU_2_HW_CMD_ERR_FATAL : CPU instructs HW to perform error fatal
- * handling.
- * IPA_CPU_2_HW_CMD_CLK_GATE : CPU instructs HW to goto Clock Gated state.
- * IPA_CPU_2_HW_CMD_CLK_UNGATE : CPU instructs HW to goto Clock Ungated state.
- * IPA_CPU_2_HW_CMD_MEMCPY : CPU instructs HW to do memcopy using QMB.
- * IPA_CPU_2_HW_CMD_RESET_PIPE : Command to reset a pipe - SW WA for a HW bug.
- * IPA_CPU_2_HW_CMD_GSI_CH_EMPTY : Command to check for GSI channel emptiness.
- * IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO: Command to store remote IPA Info
- */
- enum ipa3_cpu_2_hw_commands {
- IPA_CPU_2_HW_CMD_NO_OP =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
- IPA_CPU_2_HW_CMD_UPDATE_FLAGS =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
- IPA_CPU_2_HW_CMD_DEBUG_RUN_TEST =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
- IPA_CPU_2_HW_CMD_DEBUG_GET_INFO =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 3),
- IPA_CPU_2_HW_CMD_ERR_FATAL =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 4),
- IPA_CPU_2_HW_CMD_CLK_GATE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 5),
- IPA_CPU_2_HW_CMD_CLK_UNGATE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 6),
- IPA_CPU_2_HW_CMD_MEMCPY =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 7),
- IPA_CPU_2_HW_CMD_RESET_PIPE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 8),
- IPA_CPU_2_HW_CMD_REG_WRITE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 9),
- IPA_CPU_2_HW_CMD_GSI_CH_EMPTY =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 10),
- IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 11),
- };
- /**
- * enum ipa3_hw_2_cpu_responses - Values that represent common HW responses
- * to CPU commands.
- * @IPA_HW_2_CPU_RESPONSE_NO_OP : No operation response
- * @IPA_HW_2_CPU_RESPONSE_INIT_COMPLETED : HW shall send this command once
- * boot sequence is completed and HW is ready to serve commands from CPU
- * @IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED: Response to CPU commands
- * @IPA_HW_2_CPU_RESPONSE_DEBUG_GET_INFO : Response to
- * IPA_CPU_2_HW_CMD_DEBUG_GET_INFO command
- */
- enum ipa3_hw_2_cpu_responses {
- IPA_HW_2_CPU_RESPONSE_NO_OP =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
- IPA_HW_2_CPU_RESPONSE_INIT_COMPLETED =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
- IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
- IPA_HW_2_CPU_RESPONSE_DEBUG_GET_INFO =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 3),
- };
- /**
- * struct IpaHwMemCopyData_t - Structure holding the parameters
- * for IPA_CPU_2_HW_CMD_MEMCPY command.
- *
- * The parameters are passed as immediate params in the shared memory
- */
- struct IpaHwMemCopyData_t {
- u32 destination_addr;
- u32 source_addr;
- u32 dest_buffer_size;
- u32 source_buffer_size;
- };
- /**
- * struct IpaHwRegWriteCmdData_t - holds the parameters for
- * IPA_CPU_2_HW_CMD_REG_WRITE command. Parameters are
- * sent as 64b immediate parameters.
- * @RegisterAddress: RG10 register address where the value needs to be written
- * @RegisterValue: 32-Bit value to be written into the register
- */
- struct IpaHwRegWriteCmdData_t {
- u32 RegisterAddress;
- u32 RegisterValue;
- };
- /**
- * union IpaHwCpuCmdCompletedResponseData_t - Structure holding the parameters
- * for IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED response.
- * @originalCmdOp : The original command opcode
- * @status : 0 for success indication, otherwise failure
- * @responseData : 16b responseData
- *
- * Parameters are sent as 32b immediate parameters.
- */
- union IpaHwCpuCmdCompletedResponseData_t {
- struct IpaHwCpuCmdCompletedResponseParams_t {
- u32 originalCmdOp:8;
- u32 status:8;
- u32 responseData:16;
- } __packed params;
- u32 raw32b;
- } __packed;
- /**
- * union IpaHwUpdateFlagsCmdData_t - Structure holding the parameters for
- * IPA_CPU_2_HW_CMD_UPDATE_FLAGS command
- * @newFlags: SW flags defined the behavior of HW.
- * This field is expected to be used as bitmask for enum ipa3_hw_flags
- */
- union IpaHwUpdateFlagsCmdData_t {
- struct IpaHwUpdateFlagsCmdParams_t {
- u32 newFlags;
- } params;
- u32 raw32b;
- };
- /**
- * union IpaHwChkChEmptyCmdData_t - Structure holding the parameters for
- * IPA_CPU_2_HW_CMD_GSI_CH_EMPTY command. Parameters are sent as 32b
- * immediate parameters.
- * @ee_n : EE owner of the channel
- * @vir_ch_id : GSI virtual channel ID of the channel to checked of emptiness
- * @reserved_02_04 : Reserved
- */
- union IpaHwChkChEmptyCmdData_t {
- struct IpaHwChkChEmptyCmdParams_t {
- u8 ee_n;
- u8 vir_ch_id;
- u16 reserved_02_04;
- } __packed params;
- u32 raw32b;
- } __packed;
- /**
- * Structure holding the parameters for IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO
- * command.
- * @remoteIPAAddr: 5G IPA address : uC proxies Q6 doorbell to this address
- * @mboxN: mbox on which Q6 will interrupt uC
- */
- struct IpaHwDbAddrInfo_t {
- u32 remoteIPAAddr;
- uint32_t mboxN;
- } __packed;
- static DEFINE_MUTEX(uc_loaded_nb_lock);
- static BLOCKING_NOTIFIER_HEAD(uc_loaded_notifier);
- struct ipa3_uc_hdlrs ipa3_uc_hdlrs[IPA_HW_NUM_FEATURES] = { { 0 } };
- const char *ipa_hw_error_str(enum ipa3_hw_errors err_type)
- {
- const char *str;
- switch (err_type) {
- case IPA_HW_ERROR_NONE:
- str = "IPA_HW_ERROR_NONE";
- break;
- case IPA_HW_INVALID_DOORBELL_ERROR:
- str = "IPA_HW_INVALID_DOORBELL_ERROR";
- break;
- case IPA_HW_DMA_ERROR:
- str = "IPA_HW_DMA_ERROR";
- break;
- case IPA_HW_FATAL_SYSTEM_ERROR:
- str = "IPA_HW_FATAL_SYSTEM_ERROR";
- break;
- case IPA_HW_INVALID_OPCODE:
- str = "IPA_HW_INVALID_OPCODE";
- break;
- case IPA_HW_INVALID_PARAMS:
- str = "IPA_HW_INVALID_PARAMS";
- break;
- case IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE:
- str = "IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE";
- break;
- case IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE:
- str = "IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE";
- break;
- case IPA_HW_GSI_CH_NOT_EMPTY_FAILURE:
- str = "IPA_HW_GSI_CH_NOT_EMPTY_FAILURE";
- break;
- default:
- str = "INVALID ipa_hw_errors type";
- }
- return str;
- }
- static void ipa3_uc_save_dbg_stats(u32 size)
- {
- u8 protocol_id;
- u32 addr_offset;
- void __iomem *mmio;
- protocol_id = IPA_UC_DBG_STATS_GET_PROT_ID(
- ipa3_ctx->uc_ctx.uc_sram_mmio->responseParams_1);
- addr_offset = IPA_UC_DBG_STATS_GET_OFFSET(
- ipa3_ctx->uc_ctx.uc_sram_mmio->responseParams_1);
- mmio = ioremap(ipa3_ctx->ipa_wrapper_base +
- addr_offset, sizeof(struct IpaHwRingStats_t) *
- MAX_CH_STATS_SUPPORTED);
- if (mmio == NULL) {
- IPAERR("unexpected NULL mmio\n");
- return;
- }
- switch (protocol_id) {
- case IPA_HW_PROTOCOL_AQC:
- break;
- case IPA_HW_PROTOCOL_11ad:
- break;
- case IPA_HW_PROTOCOL_WDI:
- ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_size = size;
- ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_ofst = addr_offset;
- ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_mmio = mmio;
- break;
- case IPA_HW_PROTOCOL_WDI3:
- ipa3_ctx->wdi3_ctx.dbg_stats.uc_dbg_stats_size = size;
- ipa3_ctx->wdi3_ctx.dbg_stats.uc_dbg_stats_ofst = addr_offset;
- ipa3_ctx->wdi3_ctx.dbg_stats.uc_dbg_stats_mmio = mmio;
- break;
- case IPA_HW_PROTOCOL_ETH:
- break;
- case IPA_HW_PROTOCOL_MHIP:
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_size = size;
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_ofst = addr_offset;
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio = mmio;
- break;
- case IPA_HW_PROTOCOL_USB:
- ipa3_ctx->usb_ctx.dbg_stats.uc_dbg_stats_size = size;
- ipa3_ctx->usb_ctx.dbg_stats.uc_dbg_stats_ofst = addr_offset;
- ipa3_ctx->usb_ctx.dbg_stats.uc_dbg_stats_mmio = mmio;
- break;
- default:
- IPAERR("unknown protocols %d\n", protocol_id);
- }
- }
- static void ipa3_log_evt_hdlr(void)
- {
- int i;
- if (!ipa3_ctx->uc_ctx.uc_event_top_ofst) {
- ipa3_ctx->uc_ctx.uc_event_top_ofst =
- ipa3_ctx->uc_ctx.uc_sram_mmio->eventParams;
- if (ipa3_ctx->uc_ctx.uc_event_top_ofst +
- sizeof(struct IpaHwEventLogInfoData_t) >=
- ipa3_ctx->ctrl->ipa_reg_base_ofst +
- ipahal_get_reg_n_ofst(
- IPA_SW_AREA_RAM_DIRECT_ACCESS_n, 0) +
- ipa3_ctx->smem_sz) {
- IPAERR("uc_top 0x%x outside SRAM\n",
- ipa3_ctx->uc_ctx.uc_event_top_ofst);
- goto bad_uc_top_ofst;
- }
- ipa3_ctx->uc_ctx.uc_event_top_mmio = ioremap(
- ipa3_ctx->ipa_wrapper_base +
- ipa3_ctx->uc_ctx.uc_event_top_ofst,
- sizeof(struct IpaHwEventLogInfoData_t));
- if (!ipa3_ctx->uc_ctx.uc_event_top_mmio) {
- IPAERR("fail to ioremap uc top\n");
- goto bad_uc_top_ofst;
- }
- for (i = 0; i < IPA_HW_NUM_FEATURES; i++) {
- if (ipa3_uc_hdlrs[i].ipa_uc_event_log_info_hdlr)
- ipa3_uc_hdlrs[i].ipa_uc_event_log_info_hdlr
- (ipa3_ctx->uc_ctx.uc_event_top_mmio);
- }
- } else {
- if (ipa3_ctx->uc_ctx.uc_sram_mmio->eventParams !=
- ipa3_ctx->uc_ctx.uc_event_top_ofst) {
- IPAERR("uc top ofst changed new=%u cur=%u\n",
- ipa3_ctx->uc_ctx.uc_sram_mmio->eventParams,
- ipa3_ctx->uc_ctx.uc_event_top_ofst);
- }
- }
- return;
- bad_uc_top_ofst:
- ipa3_ctx->uc_ctx.uc_event_top_ofst = 0;
- }
- /**
- * ipa3_uc_state_check() - Check the status of the uC interface
- *
- * Return value: 0 if the uC is loaded, interface is initialized
- * and there was no recent failure in one of the commands.
- * A negative value is returned otherwise.
- */
- int ipa3_uc_state_check(void)
- {
- if (!ipa3_ctx->uc_ctx.uc_inited) {
- IPAERR("uC interface not initialized\n");
- return -EFAULT;
- }
- if (!ipa3_ctx->uc_ctx.uc_loaded) {
- IPAERR("uC is not loaded\n");
- return -EFAULT;
- }
- if (ipa3_ctx->uc_ctx.uc_failed) {
- IPAERR("uC has failed its last command\n");
- return -EFAULT;
- }
- return 0;
- }
- /**
- * ipa3_uc_loaded_check() - Check the uC has been loaded
- *
- * Return value: 1 if the uC is loaded, 0 otherwise
- */
- int ipa3_uc_loaded_check(void)
- {
- return ipa3_ctx->uc_ctx.uc_loaded;
- }
- EXPORT_SYMBOL(ipa3_uc_loaded_check);
- /**
- * ipa3_uc_register_ready_cb() - register a uC ready callback notifier block
- * @nb: notifier
- *
- * Register a callback to be called when uC is ready to receive commands. uC is
- * considered to be ready when it sends %IPA_HW_2_CPU_RESPONSE_INIT_COMPLETED.
- *
- * Return: 0 on successful registration, negative errno otherwise
- *
- * See blocking_notifier_chain_register() for possible errno values
- */
- int ipa3_uc_register_ready_cb(struct notifier_block *nb)
- {
- int rc;
- mutex_lock(&uc_loaded_nb_lock);
- rc = blocking_notifier_chain_register(&uc_loaded_notifier, nb);
- if (!rc && ipa3_ctx->uc_ctx.uc_loaded)
- (void) nb->notifier_call(nb, false, ipa3_ctx);
- mutex_unlock(&uc_loaded_nb_lock);
- return rc;
- }
- EXPORT_SYMBOL(ipa3_uc_register_ready_cb);
- /**
- * ipa3_uc_unregister_ready_cb() - unregister a uC ready callback
- * @nb: notifier
- *
- * Unregister a uC loaded notifier block that was previously registered by
- * ipa3_uc_register_ready_cb().
- *
- * Return: 0 on successful unregistration, negative errno otherwise
- *
- * See blocking_notifier_chain_unregister() for possible errno values
- */
- int ipa3_uc_unregister_ready_cb(struct notifier_block *nb)
- {
- return blocking_notifier_chain_unregister(&uc_loaded_notifier, nb);
- }
- EXPORT_SYMBOL(ipa3_uc_unregister_ready_cb);
- static void ipa3_uc_event_handler(enum ipa_irq_type interrupt,
- void *private_data,
- void *interrupt_data)
- {
- union IpaHwErrorEventData_t evt;
- u8 feature;
- WARN_ON(private_data != ipa3_ctx);
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- IPADBG("uC evt opcode=%u\n",
- ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp);
- feature = EXTRACT_UC_FEATURE(ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp);
- if (feature >= IPA_HW_FEATURE_MAX) {
- IPAERR("Invalid feature %u for event %u\n",
- feature, ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp);
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- return;
- }
- /* Feature specific handling */
- if (ipa3_uc_hdlrs[feature].ipa_uc_event_hdlr)
- ipa3_uc_hdlrs[feature].ipa_uc_event_hdlr
- (ipa3_ctx->uc_ctx.uc_sram_mmio);
- /* General handling */
- if (ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp ==
- IPA_HW_2_CPU_EVENT_ERROR) {
- evt.raw32b = ipa3_ctx->uc_ctx.uc_sram_mmio->eventParams;
- IPAERR("uC Error, evt errorType = %s\n",
- ipa_hw_error_str(evt.params.errorType));
- ipa3_ctx->uc_ctx.uc_failed = true;
- ipa3_ctx->uc_ctx.uc_error_type = evt.params.errorType;
- ipa3_ctx->uc_ctx.uc_error_timestamp =
- ipahal_read_reg(IPA_TAG_TIMER);
- /* Unexpected UC hardware state */
- ipa_assert();
- } else if (ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp ==
- IPA_HW_2_CPU_EVENT_LOG_INFO) {
- IPADBG("uC evt log info ofst=0x%x\n",
- ipa3_ctx->uc_ctx.uc_sram_mmio->eventParams);
- ipa3_log_evt_hdlr();
- } else {
- IPADBG("unsupported uC evt opcode=%u\n",
- ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp);
- }
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- }
- int ipa3_uc_panic_notifier(struct notifier_block *this,
- unsigned long event, void *ptr)
- {
- int result = 0;
- struct ipa_active_client_logging_info log_info;
- IPADBG("this=%pK evt=%lu ptr=%pK\n", this, event, ptr);
- result = ipa3_uc_state_check();
- if (result)
- goto fail;
- IPA_ACTIVE_CLIENTS_PREP_SIMPLE(log_info);
- if (ipa3_inc_client_enable_clks_no_block(&log_info))
- goto fail;
- ipa3_ctx->uc_ctx.uc_sram_mmio->cmdOp =
- IPA_CPU_2_HW_CMD_ERR_FATAL;
- ipa3_ctx->uc_ctx.pending_cmd = ipa3_ctx->uc_ctx.uc_sram_mmio->cmdOp;
- /* ensure write to shared memory is done before triggering uc */
- wmb();
- ipahal_write_reg_n(IPA_IRQ_EE_UC_n, 0, 0x1);
- /* give uc enough time to save state */
- udelay(IPA_PKT_FLUSH_TO_US);
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- IPADBG("err_fatal issued\n");
- fail:
- return NOTIFY_DONE;
- }
- static void ipa3_uc_response_hdlr(enum ipa_irq_type interrupt,
- void *private_data,
- void *interrupt_data)
- {
- union IpaHwCpuCmdCompletedResponseData_t uc_rsp;
- u8 feature;
- int res;
- int i;
- WARN_ON(private_data != ipa3_ctx);
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- IPADBG("uC rsp opcode=%u\n",
- ipa3_ctx->uc_ctx.uc_sram_mmio->responseOp);
- feature = EXTRACT_UC_FEATURE(ipa3_ctx->uc_ctx.uc_sram_mmio->responseOp);
- if (feature >= IPA_HW_FEATURE_MAX) {
- IPAERR("Invalid feature %u for event %u\n",
- feature, ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp);
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- return;
- }
- /* Feature specific handling */
- if (ipa3_uc_hdlrs[feature].ipa3_uc_response_hdlr) {
- res = ipa3_uc_hdlrs[feature].ipa3_uc_response_hdlr(
- ipa3_ctx->uc_ctx.uc_sram_mmio,
- &ipa3_ctx->uc_ctx.uc_status);
- if (res == 0) {
- IPADBG("feature %d specific response handler\n",
- feature);
- complete_all(&ipa3_ctx->uc_ctx.uc_completion);
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- return;
- }
- }
- /* General handling */
- if (ipa3_ctx->uc_ctx.uc_sram_mmio->responseOp ==
- IPA_HW_2_CPU_RESPONSE_INIT_COMPLETED) {
- if (ipa3_ctx->uc_ctx.uc_loaded) {
- IPADBG("uC resp op INIT_COMPLETED is unexpected\n");
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- return;
- }
- mutex_lock(&uc_loaded_nb_lock);
- ipa3_ctx->uc_ctx.uc_loaded = true;
- (void) blocking_notifier_call_chain(&uc_loaded_notifier, true,
- ipa3_ctx);
- mutex_unlock(&uc_loaded_nb_lock);
- IPADBG("IPA uC loaded\n");
- /*
- * The proxy vote is held until uC is loaded to ensure that
- * IPA_HW_2_CPU_RESPONSE_INIT_COMPLETED is received.
- */
- ipa3_proxy_clk_unvote();
- /*
- * To enable ipa power collapse we need to enable rpmh and uc
- * handshake So that uc can do register retention. To enable
- * this handshake we need to send the below message to rpmh.
- */
- ipa_pc_qmp_enable();
- for (i = 0; i < IPA_HW_NUM_FEATURES; i++) {
- if (ipa3_uc_hdlrs[i].ipa_uc_loaded_hdlr)
- ipa3_uc_hdlrs[i].ipa_uc_loaded_hdlr();
- }
- } else if (ipa3_ctx->uc_ctx.uc_sram_mmio->responseOp ==
- IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED) {
- uc_rsp.raw32b = ipa3_ctx->uc_ctx.uc_sram_mmio->responseParams;
- IPADBG("uC cmd response opcode=%u status=%u\n",
- uc_rsp.params.originalCmdOp,
- uc_rsp.params.status);
- if (uc_rsp.params.originalCmdOp ==
- ipa3_ctx->uc_ctx.pending_cmd) {
- ipa3_ctx->uc_ctx.uc_status = uc_rsp.params.status;
- if (uc_rsp.params.originalCmdOp ==
- IPA_CPU_2_HW_CMD_OFFLOAD_STATS_ALLOC)
- ipa3_uc_save_dbg_stats(
- uc_rsp.params.responseData);
- complete_all(&ipa3_ctx->uc_ctx.uc_completion);
- } else {
- IPAERR("Expected cmd=%u rcvd cmd=%u\n",
- ipa3_ctx->uc_ctx.pending_cmd,
- uc_rsp.params.originalCmdOp);
- }
- } else {
- IPAERR("Unsupported uC rsp opcode = %u\n",
- ipa3_ctx->uc_ctx.uc_sram_mmio->responseOp);
- }
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- }
- static void ipa3_uc_wigig_misc_int_handler(enum ipa_irq_type interrupt,
- void *private_data,
- void *interrupt_data)
- {
- IPADBG("\n");
- WARN_ON(private_data != ipa3_ctx);
- if (ipa3_ctx->uc_wigig_ctx.misc_notify_cb)
- ipa3_ctx->uc_wigig_ctx.misc_notify_cb(
- ipa3_ctx->uc_wigig_ctx.priv);
- IPADBG("exit\n");
- }
- static int ipa3_uc_send_cmd_64b_param(u32 cmd_lo, u32 cmd_hi, u32 opcode,
- u32 expected_status, bool polling_mode, unsigned long timeout_jiffies)
- {
- int index;
- union IpaHwCpuCmdCompletedResponseData_t uc_rsp;
- int retries = 0;
- u32 uc_error_type;
- send_cmd_lock:
- mutex_lock(&ipa3_ctx->uc_ctx.uc_lock);
- if (ipa3_uc_state_check()) {
- IPADBG("uC send command aborted\n");
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- return -EBADF;
- }
- send_cmd:
- init_completion(&ipa3_ctx->uc_ctx.uc_completion);
- ipa3_ctx->uc_ctx.uc_sram_mmio->cmdParams = cmd_lo;
- ipa3_ctx->uc_ctx.uc_sram_mmio->cmdParams_hi = cmd_hi;
- ipa3_ctx->uc_ctx.uc_sram_mmio->cmdOp = opcode;
- ipa3_ctx->uc_ctx.pending_cmd = opcode;
- ipa3_ctx->uc_ctx.uc_sram_mmio->responseOp = 0;
- ipa3_ctx->uc_ctx.uc_sram_mmio->responseParams = 0;
- ipa3_ctx->uc_ctx.uc_status = 0;
- /* ensure write to shared memory is done before triggering uc */
- wmb();
- ipahal_write_reg_n(IPA_IRQ_EE_UC_n, 0, 0x1);
- if (polling_mode) {
- struct IpaHwSharedMemCommonMapping_t *uc_sram_ptr =
- ipa3_ctx->uc_ctx.uc_sram_mmio;
- for (index = 0; index < IPA_UC_POLL_MAX_RETRY; index++) {
- if (uc_sram_ptr->responseOp ==
- IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED) {
- uc_rsp.raw32b = uc_sram_ptr->responseParams;
- if (uc_rsp.params.originalCmdOp ==
- ipa3_ctx->uc_ctx.pending_cmd) {
- ipa3_ctx->uc_ctx.uc_status =
- uc_rsp.params.status;
- break;
- }
- }
- usleep_range(IPA_UC_POLL_SLEEP_USEC,
- IPA_UC_POLL_SLEEP_USEC);
- }
- if (index == IPA_UC_POLL_MAX_RETRY) {
- IPAERR("uC max polling retries reached\n");
- if (ipa3_ctx->uc_ctx.uc_failed) {
- uc_error_type = ipa3_ctx->uc_ctx.uc_error_type;
- IPAERR("uC reported on Error, errorType = %s\n",
- ipa_hw_error_str(uc_error_type));
- }
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- /* Unexpected UC hardware state */
- ipa_assert();
- }
- } else {
- if (wait_for_completion_timeout(&ipa3_ctx->uc_ctx.uc_completion,
- timeout_jiffies) == 0) {
- IPAERR("uC timed out\n");
- if (ipa3_ctx->uc_ctx.uc_failed) {
- uc_error_type = ipa3_ctx->uc_ctx.uc_error_type;
- IPAERR("uC reported on Error, errorType = %s\n",
- ipa_hw_error_str(uc_error_type));
- }
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- /* Unexpected UC hardware state */
- ipa_assert();
- }
- }
- if (ipa3_ctx->uc_ctx.uc_status != expected_status) {
- if (ipa3_ctx->uc_ctx.uc_status ==
- IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE ||
- ipa3_ctx->uc_ctx.uc_status ==
- IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE ||
- ipa3_ctx->uc_ctx.uc_status ==
- IPA_HW_CONS_STOP_FAILURE ||
- ipa3_ctx->uc_ctx.uc_status ==
- IPA_HW_PROD_STOP_FAILURE) {
- retries++;
- if (retries == IPA_GSI_CHANNEL_STOP_MAX_RETRY) {
- IPAERR("Failed after %d tries\n", retries);
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- /* Unexpected UC hardware state */
- ipa_assert();
- }
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- if (ipa3_ctx->uc_ctx.uc_status ==
- IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE)
- ipa3_inject_dma_task_for_gsi();
- /* sleep for short period to flush IPA */
- usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC,
- IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC);
- goto send_cmd_lock;
- }
- if (ipa3_ctx->uc_ctx.uc_status ==
- IPA_HW_GSI_CH_NOT_EMPTY_FAILURE) {
- retries++;
- if (retries >= IPA_GSI_CHANNEL_EMPTY_MAX_RETRY) {
- IPAERR("Failed after %d tries\n", retries);
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- return -EFAULT;
- }
- usleep_range(
- IPA_GSI_CHANNEL_EMPTY_SLEEP_MIN_USEC,
- IPA_GSI_CHANNEL_EMPTY_SLEEP_MAX_USEC);
- goto send_cmd;
- }
- IPAERR("Recevied status %u, Expected status %u\n",
- ipa3_ctx->uc_ctx.uc_status, expected_status);
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- return -EFAULT;
- }
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- IPADBG("uC cmd %u send succeeded\n", opcode);
- return 0;
- }
- /**
- * ipa3_uc_interface_init() - Initialize the interface with the uC
- *
- * Return value: 0 on success, negative value otherwise
- */
- int ipa3_uc_interface_init(void)
- {
- int result;
- unsigned long phys_addr;
- if (ipa3_ctx->uc_ctx.uc_inited) {
- IPADBG("uC interface already initialized\n");
- return 0;
- }
- mutex_init(&ipa3_ctx->uc_ctx.uc_lock);
- spin_lock_init(&ipa3_ctx->uc_ctx.uc_spinlock);
- phys_addr = ipa3_ctx->ipa_wrapper_base +
- ipa3_ctx->ctrl->ipa_reg_base_ofst +
- ipahal_get_reg_n_ofst(IPA_SW_AREA_RAM_DIRECT_ACCESS_n, 0);
- ipa3_ctx->uc_ctx.uc_sram_mmio = ioremap(phys_addr,
- IPA_RAM_UC_SMEM_SIZE);
- if (!ipa3_ctx->uc_ctx.uc_sram_mmio) {
- IPAERR("Fail to ioremap IPA uC SRAM\n");
- result = -ENOMEM;
- goto remap_fail;
- }
- result = ipa3_add_interrupt_handler(IPA_UC_IRQ_0,
- ipa3_uc_event_handler, true,
- ipa3_ctx);
- if (result) {
- IPAERR("Fail to register for UC_IRQ0 event interrupt\n");
- result = -EFAULT;
- goto irq_fail0;
- }
- result = ipa3_add_interrupt_handler(IPA_UC_IRQ_1,
- ipa3_uc_response_hdlr, true,
- ipa3_ctx);
- if (result) {
- IPAERR("fail to register for UC_IRQ1 rsp interrupt\n");
- result = -EFAULT;
- goto irq_fail1;
- }
- result = ipa3_add_interrupt_handler(IPA_UC_IRQ_2,
- ipa3_uc_wigig_misc_int_handler, true,
- ipa3_ctx);
- if (result) {
- IPAERR("fail to register for UC_IRQ2 wigig misc interrupt\n");
- result = -EFAULT;
- goto irq_fail2;
- }
- ipa3_ctx->uc_ctx.uc_inited = true;
- IPADBG("IPA uC interface is initialized\n");
- return 0;
- irq_fail2:
- ipa3_remove_interrupt_handler(IPA_UC_IRQ_1);
- irq_fail1:
- ipa3_remove_interrupt_handler(IPA_UC_IRQ_0);
- irq_fail0:
- iounmap(ipa3_ctx->uc_ctx.uc_sram_mmio);
- remap_fail:
- return result;
- }
- /**
- * ipa3_uc_send_cmd() - Send a command to the uC
- *
- * Note1: This function sends command with 32bit parameter and do not
- * use the higher 32bit of the command parameter (set to zero).
- *
- * Note2: In case the operation times out (No response from the uC) or
- * polling maximal amount of retries has reached, the logic
- * considers it as an invalid state of the uC/IPA, and
- * issues a kernel panic.
- *
- * Returns: 0 on success.
- * -EINVAL in case of invalid input.
- * -EBADF in case uC interface is not initialized /
- * or the uC has failed previously.
- * -EFAULT in case the received status doesn't match
- * the expected.
- */
- int ipa3_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status,
- bool polling_mode, unsigned long timeout_jiffies)
- {
- return ipa3_uc_send_cmd_64b_param(cmd, 0, opcode,
- expected_status, polling_mode, timeout_jiffies);
- }
- /**
- * ipa3_uc_register_handlers() - Registers event, response and log event
- * handlers for a specific feature.Please note
- * that currently only one handler can be
- * registered per feature.
- *
- * Return value: None
- */
- void ipa3_uc_register_handlers(enum ipa3_hw_features feature,
- struct ipa3_uc_hdlrs *hdlrs)
- {
- if (0 > feature || IPA_HW_FEATURE_MAX <= feature) {
- IPAERR("Feature %u is invalid, not registering hdlrs\n",
- feature);
- return;
- }
- mutex_lock(&ipa3_ctx->uc_ctx.uc_lock);
- ipa3_uc_hdlrs[feature] = *hdlrs;
- mutex_unlock(&ipa3_ctx->uc_ctx.uc_lock);
- IPADBG("uC handlers registered for feature %u\n", feature);
- }
- int ipa3_uc_is_gsi_channel_empty(enum ipa_client_type ipa_client)
- {
- const struct ipa_gsi_ep_config *gsi_ep_info;
- union IpaHwChkChEmptyCmdData_t cmd;
- int ret;
- gsi_ep_info = ipa3_get_gsi_ep_info(ipa_client);
- if (!gsi_ep_info) {
- IPAERR("Failed getting GSI EP info for client=%d\n",
- ipa_client);
- return 0;
- }
- if (ipa3_uc_state_check()) {
- IPADBG("uC cannot be used to validate ch emptiness clnt=%d\n"
- , ipa_client);
- return 0;
- }
- cmd.params.ee_n = gsi_ep_info->ee;
- cmd.params.vir_ch_id = gsi_ep_info->ipa_gsi_chan_num;
- IPADBG("uC emptiness check for IPA GSI Channel %d\n",
- gsi_ep_info->ipa_gsi_chan_num);
- ret = ipa3_uc_send_cmd(cmd.raw32b, IPA_CPU_2_HW_CMD_GSI_CH_EMPTY, 0,
- false, 10*HZ);
- return ret;
- }
- /**
- * ipa3_uc_notify_clk_state() - notify to uC of clock enable / disable
- * @enabled: true if clock are enabled
- *
- * The function uses the uC interface in order to notify uC before IPA clocks
- * are disabled to make sure uC is not in the middle of operation.
- * Also after clocks are enabled ned to notify uC to start processing.
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa3_uc_notify_clk_state(bool enabled)
- {
- u32 opcode;
- if (ipa3_ctx->ipa_hw_type > IPA_HW_v4_0) {
- IPADBG_LOW("not supported past IPA v4.0\n");
- return 0;
- }
- /*
- * If the uC interface has not been initialized yet,
- * don't notify the uC on the enable/disable
- */
- if (ipa3_uc_state_check()) {
- IPADBG("uC interface will not notify the UC on clock state\n");
- return 0;
- }
- IPADBG("uC clock %s notification\n", (enabled) ? "UNGATE" : "GATE");
- opcode = (enabled) ? IPA_CPU_2_HW_CMD_CLK_UNGATE :
- IPA_CPU_2_HW_CMD_CLK_GATE;
- return ipa3_uc_send_cmd(0, opcode, 0, true, 0);
- }
- /**
- * ipa3_uc_update_hw_flags() - send uC the HW flags to be used
- * @flags: This field is expected to be used as bitmask for enum ipa3_hw_flags
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa3_uc_update_hw_flags(u32 flags)
- {
- union IpaHwUpdateFlagsCmdData_t cmd;
- memset(&cmd, 0, sizeof(cmd));
- cmd.params.newFlags = flags;
- return ipa3_uc_send_cmd(cmd.raw32b, IPA_CPU_2_HW_CMD_UPDATE_FLAGS, 0,
- false, HZ);
- }
- /**
- * ipa3_uc_memcpy() - Perform a memcpy action using IPA uC
- * @dest: physical address to store the copied data.
- * @src: physical address of the source data to copy.
- * @len: number of bytes to copy.
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa3_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len)
- {
- int res;
- struct ipa_mem_buffer mem;
- struct IpaHwMemCopyData_t *cmd;
- IPADBG("dest 0x%pa src 0x%pa len %d\n", &dest, &src, len);
- mem.size = sizeof(cmd);
- mem.base = dma_alloc_coherent(ipa3_ctx->pdev, mem.size, &mem.phys_base,
- GFP_KERNEL);
- if (!mem.base) {
- IPAERR("fail to alloc DMA buff of size %d\n", mem.size);
- return -ENOMEM;
- }
- cmd = (struct IpaHwMemCopyData_t *)mem.base;
- memset(cmd, 0, sizeof(*cmd));
- cmd->destination_addr = dest;
- cmd->dest_buffer_size = len;
- cmd->source_addr = src;
- cmd->source_buffer_size = len;
- res = ipa3_uc_send_cmd((u32)mem.phys_base, IPA_CPU_2_HW_CMD_MEMCPY, 0,
- true, 10 * HZ);
- if (res) {
- IPAERR("ipa3_uc_send_cmd failed %d\n", res);
- goto free_coherent;
- }
- res = 0;
- free_coherent:
- dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base, mem.phys_base);
- return res;
- }
- int ipa3_uc_send_remote_ipa_info(u32 remote_addr, uint32_t mbox_n)
- {
- int res;
- struct ipa_mem_buffer cmd;
- struct IpaHwDbAddrInfo_t *uc_info;
- cmd.size = sizeof(*uc_info);
- cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
- &cmd.phys_base, GFP_KERNEL);
- if (cmd.base == NULL)
- return -ENOMEM;
- uc_info = (struct IpaHwDbAddrInfo_t *) cmd.base;
- uc_info->remoteIPAAddr = remote_addr;
- uc_info->mboxN = mbox_n;
- res = ipa3_uc_send_cmd((u32)(cmd.phys_base),
- IPA_CPU_2_HW_CMD_REMOTE_IPA_INFO, 0,
- false, 10 * HZ);
- if (res) {
- IPAERR("fail to map 0x%x to mbox %d\n",
- uc_info->remoteIPAAddr,
- uc_info->mboxN);
- goto free_coherent;
- }
- res = 0;
- free_coherent:
- dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
- return res;
- }
- int ipa3_uc_debug_stats_alloc(
- struct IpaHwOffloadStatsAllocCmdData_t cmdinfo)
- {
- int result;
- struct ipa_mem_buffer cmd;
- enum ipa_cpu_2_hw_offload_commands command;
- struct IpaHwOffloadStatsAllocCmdData_t *cmd_data;
- cmd.size = sizeof(*cmd_data);
- cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
- &cmd.phys_base, GFP_KERNEL);
- if (cmd.base == NULL) {
- result = -ENOMEM;
- return result;
- }
- cmd_data = (struct IpaHwOffloadStatsAllocCmdData_t *)cmd.base;
- memcpy(cmd_data, &cmdinfo,
- sizeof(struct IpaHwOffloadStatsAllocCmdData_t));
- command = IPA_CPU_2_HW_CMD_OFFLOAD_STATS_ALLOC;
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
- command,
- IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
- false, 10 * HZ);
- if (result) {
- IPAERR("fail to alloc offload stats\n");
- goto cleanup;
- }
- result = 0;
- cleanup:
- dma_free_coherent(ipa3_ctx->uc_pdev,
- cmd.size,
- cmd.base, cmd.phys_base);
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- IPADBG("exit\n");
- return result;
- }
- int ipa3_uc_debug_stats_dealloc(uint32_t protocol)
- {
- int result;
- struct ipa_mem_buffer cmd;
- enum ipa_cpu_2_hw_offload_commands command;
- struct IpaHwOffloadStatsDeAllocCmdData_t *cmd_data;
- cmd.size = sizeof(*cmd_data);
- cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
- &cmd.phys_base, GFP_KERNEL);
- if (cmd.base == NULL) {
- result = -ENOMEM;
- return result;
- }
- cmd_data = (struct IpaHwOffloadStatsDeAllocCmdData_t *)
- cmd.base;
- cmd_data->protocol = protocol;
- command = IPA_CPU_2_HW_CMD_OFFLOAD_STATS_DEALLOC;
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
- command,
- IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
- false, 10 * HZ);
- if (result) {
- IPAERR("fail to dealloc offload stats\n");
- goto cleanup;
- }
- switch (protocol) {
- case IPA_HW_PROTOCOL_AQC:
- break;
- case IPA_HW_PROTOCOL_11ad:
- break;
- case IPA_HW_PROTOCOL_WDI:
- iounmap(ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_mmio);
- ipa3_ctx->wdi2_ctx.dbg_stats.uc_dbg_stats_mmio = NULL;
- break;
- case IPA_HW_PROTOCOL_WDI3:
- iounmap(ipa3_ctx->wdi3_ctx.dbg_stats.uc_dbg_stats_mmio);
- ipa3_ctx->wdi3_ctx.dbg_stats.uc_dbg_stats_mmio = NULL;
- break;
- case IPA_HW_PROTOCOL_ETH:
- break;
- default:
- IPAERR("unknown protocols %d\n", protocol);
- }
- result = 0;
- cleanup:
- dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size,
- cmd.base, cmd.phys_base);
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- IPADBG("exit\n");
- return result;
- }
|