blob: 7949d91bd3a2bdf993a4cd32bcfe1e20e10dc302 [file] [log] [blame]
/* Copyright (c) 2015, 2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/ipa.h>
#include "ipa_i.h"
/* MHI uC interface definitions */
#define IPA_HW_INTERFACE_MHI_VERSION 0x0004
#define IPA_HW_MAX_NUMBER_OF_CHANNELS 2
#define IPA_HW_MAX_NUMBER_OF_EVENTRINGS 2
#define IPA_HW_MAX_CHANNEL_HANDLE (IPA_HW_MAX_NUMBER_OF_CHANNELS-1)
/**
* Values that represent the MHI commands from CPU to IPA HW.
* @IPA_CPU_2_HW_CMD_MHI_INIT: Initialize HW to be ready for MHI processing.
* Once operation was completed HW shall respond with
* IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED.
* @IPA_CPU_2_HW_CMD_MHI_INIT_CHANNEL: Initialize specific channel to be ready
* to serve MHI transfers. Once initialization was completed HW shall
* respond with IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE.
* IPA_HW_MHI_CHANNEL_STATE_ENABLE
* @IPA_CPU_2_HW_CMD_MHI_UPDATE_MSI: Update MHI MSI interrupts data.
* Once operation was completed HW shall respond with
* IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED.
* @IPA_CPU_2_HW_CMD_MHI_CHANGE_CHANNEL_STATE: Change specific channel
* processing state following host request. Once operation was completed
* HW shall respond with IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE.
* @IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO: Info related to DL UL syncronization.
* @IPA_CPU_2_HW_CMD_MHI_STOP_EVENT_UPDATE: Cmd to stop event ring processing.
*/
enum ipa_cpu_2_hw_mhi_commands {
IPA_CPU_2_HW_CMD_MHI_INIT
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 0),
IPA_CPU_2_HW_CMD_MHI_INIT_CHANNEL
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 1),
IPA_CPU_2_HW_CMD_MHI_UPDATE_MSI
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 2),
IPA_CPU_2_HW_CMD_MHI_CHANGE_CHANNEL_STATE
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 3),
IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 4),
IPA_CPU_2_HW_CMD_MHI_STOP_EVENT_UPDATE
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 5)
};
/**
* Values that represent MHI related HW responses to CPU commands.
* @IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE: Response to
* IPA_CPU_2_HW_CMD_MHI_INIT_CHANNEL or
* IPA_CPU_2_HW_CMD_MHI_CHANGE_CHANNEL_STATE commands.
*/
enum ipa_hw_2_cpu_mhi_responses {
IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 0),
};
/**
* Values that represent MHI related HW event to be sent to CPU.
* @IPA_HW_2_CPU_EVENT_MHI_CHANNEL_ERROR: Event specify the device detected an
* error in an element from the transfer ring associated with the channel
* @IPA_HW_2_CPU_EVENT_MHI_CHANNEL_WAKE_UP_REQUEST: Event specify a bam
* interrupt was asserted when MHI engine is suspended
*/
enum ipa_hw_2_cpu_mhi_events {
IPA_HW_2_CPU_EVENT_MHI_CHANNEL_ERROR
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 0),
IPA_HW_2_CPU_EVENT_MHI_CHANNEL_WAKE_UP_REQUEST
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 1),
};
/**
* Channel error types.
* @IPA_HW_CHANNEL_ERROR_NONE: No error persists.
* @IPA_HW_CHANNEL_INVALID_RE_ERROR: Invalid Ring Element was detected
*/
enum ipa_hw_channel_errors {
IPA_HW_CHANNEL_ERROR_NONE,
IPA_HW_CHANNEL_INVALID_RE_ERROR
};
/**
* MHI error types.
* @IPA_HW_INVALID_MMIO_ERROR: Invalid data read from MMIO space
* @IPA_HW_INVALID_CHANNEL_ERROR: Invalid data read from channel context array
* @IPA_HW_INVALID_EVENT_ERROR: Invalid data read from event ring context array
* @IPA_HW_NO_ED_IN_RING_ERROR: No event descriptors are available to report on
* secondary event ring
* @IPA_HW_LINK_ERROR: Link error
*/
enum ipa_hw_mhi_errors {
IPA_HW_INVALID_MMIO_ERROR
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 0),
IPA_HW_INVALID_CHANNEL_ERROR
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 1),
IPA_HW_INVALID_EVENT_ERROR
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 2),
IPA_HW_NO_ED_IN_RING_ERROR
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 4),
IPA_HW_LINK_ERROR
= FEATURE_ENUM_VAL(IPA_HW_FEATURE_MHI, 5),
};
/**
* Structure referring to the common and MHI section of 128B shared memory
* located in offset zero of SW Partition in IPA SRAM.
* The shared memory is used for communication between IPA HW and CPU.
* @common: common section in IPA SRAM
* @interfaceVersionMhi: The MHI interface version as reported by HW
* @mhiState: Overall MHI state
* @reserved_2B: reserved
* @mhiCnl0State: State of MHI channel 0.
* The state carries information regarding the error type.
* See IPA_HW_MHI_CHANNEL_STATES.
* @mhiCnl0State: State of MHI channel 1.
* @mhiCnl0State: State of MHI channel 2.
* @mhiCnl0State: State of MHI channel 3
* @mhiCnl0State: State of MHI channel 4.
* @mhiCnl0State: State of MHI channel 5.
* @mhiCnl0State: State of MHI channel 6.
* @mhiCnl0State: State of MHI channel 7.
* @reserved_37_34: reserved
* @reserved_3B_38: reserved
* @reserved_3F_3C: reserved
*/
struct IpaHwSharedMemMhiMapping_t {
struct IpaHwSharedMemCommonMapping_t common;
u16 interfaceVersionMhi;
u8 mhiState;
u8 reserved_2B;
u8 mhiCnl0State;
u8 mhiCnl1State;
u8 mhiCnl2State;
u8 mhiCnl3State;
u8 mhiCnl4State;
u8 mhiCnl5State;
u8 mhiCnl6State;
u8 mhiCnl7State;
u32 reserved_37_34;
u32 reserved_3B_38;
u32 reserved_3F_3C;
};
/**
* Structure holding the parameters for IPA_CPU_2_HW_CMD_MHI_INIT command.
* Parameters are sent as pointer thus should be reside in address accessible
* to HW.
* @msiAddress: The MSI base (in device space) used for asserting the interrupt
* (MSI) associated with the event ring
* mmioBaseAddress: The address (in device space) of MMIO structure in
* host space
* deviceMhiCtrlBaseAddress: Base address of the memory region in the device
* address space where the MHI control data structures are allocated by
* the host, including channel context array, event context array,
* and rings. This value is used for host/device address translation.
* deviceMhiDataBaseAddress: Base address of the memory region in the device
* address space where the MHI data buffers are allocated by the host.
* This value is used for host/device address translation.
* firstChannelIndex: First channel ID. Doorbell 0 is mapped to this channel
* firstEventRingIndex: First event ring ID. Doorbell 16 is mapped to this
* event ring.
*/
struct IpaHwMhiInitCmdData_t {
u32 msiAddress;
u32 mmioBaseAddress;
u32 deviceMhiCtrlBaseAddress;
u32 deviceMhiDataBaseAddress;
u32 firstChannelIndex;
u32 firstEventRingIndex;
};
/**
* Structure holding the parameters for IPA_CPU_2_HW_CMD_MHI_INIT_CHANNEL
* command. Parameters are sent as 32b immediate parameters.
* @hannelHandle: The channel identifier as allocated by driver.
* value is within the range 0 to IPA_HW_MAX_CHANNEL_HANDLE
* @contexArrayIndex: Unique index for channels, between 0 and 255. The index is
* used as an index in channel context array structures.
* @bamPipeId: The BAM pipe number for pipe dedicated for this channel
* @channelDirection: The direction of the channel as defined in the channel
* type field (CHTYPE) in the channel context data structure.
* @reserved: reserved.
*/
union IpaHwMhiInitChannelCmdData_t {
struct IpaHwMhiInitChannelCmdParams_t {
u32 channelHandle:8;
u32 contexArrayIndex:8;
u32 bamPipeId:6;
u32 channelDirection:2;
u32 reserved:8;
} params;
u32 raw32b;
};
/**
* Structure holding the parameters for IPA_CPU_2_HW_CMD_MHI_UPDATE_MSI command.
* @msiAddress_low: The MSI lower base addr (in device space) used for asserting
* the interrupt (MSI) associated with the event ring.
* @msiAddress_hi: The MSI higher base addr (in device space) used for asserting
* the interrupt (MSI) associated with the event ring.
* @msiMask: Mask indicating number of messages assigned by the host to device
* @msiData: Data Pattern to use when generating the MSI
*/
struct IpaHwMhiMsiCmdData_t {
u32 msiAddress_low;
u32 msiAddress_hi;
u32 msiMask;
u32 msiData;
};
/**
* Structure holding the parameters for
* IPA_CPU_2_HW_CMD_MHI_CHANGE_CHANNEL_STATE command.
* Parameters are sent as 32b immediate parameters.
* @requestedState: The requested channel state as was indicated from Host.
* Use IPA_HW_MHI_CHANNEL_STATES to specify the requested state
* @channelHandle: The channel identifier as allocated by driver.
* value is within the range 0 to IPA_HW_MAX_CHANNEL_HANDLE
* @LPTransitionRejected: Indication that low power state transition was
* rejected
* @reserved: reserved
*/
union IpaHwMhiChangeChannelStateCmdData_t {
struct IpaHwMhiChangeChannelStateCmdParams_t {
u32 requestedState:8;
u32 channelHandle:8;
u32 LPTransitionRejected:8;
u32 reserved:8;
} params;
u32 raw32b;
};
/**
* Structure holding the parameters for
* IPA_CPU_2_HW_CMD_MHI_STOP_EVENT_UPDATE command.
* Parameters are sent as 32b immediate parameters.
* @channelHandle: The channel identifier as allocated by driver.
* value is within the range 0 to IPA_HW_MAX_CHANNEL_HANDLE
* @reserved: reserved
*/
union IpaHwMhiStopEventUpdateData_t {
struct IpaHwMhiStopEventUpdateDataParams_t {
u32 channelHandle:8;
u32 reserved:24;
} params;
u32 raw32b;
};
/**
* Structure holding the parameters for
* IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE response.
* Parameters are sent as 32b immediate parameters.
* @state: The new channel state. In case state is not as requested this is
* error indication for the last command
* @channelHandle: The channel identifier
* @additonalParams: For stop: the number of pending bam descriptors currently
* queued
*/
union IpaHwMhiChangeChannelStateResponseData_t {
struct IpaHwMhiChangeChannelStateResponseParams_t {
u32 state:8;
u32 channelHandle:8;
u32 additonalParams:16;
} params;
u32 raw32b;
};
/**
* Structure holding the parameters for
* IPA_HW_2_CPU_EVENT_MHI_CHANNEL_ERROR event.
* Parameters are sent as 32b immediate parameters.
* @errorType: Type of error - IPA_HW_CHANNEL_ERRORS
* @channelHandle: The channel identifier as allocated by driver.
* value is within the range 0 to IPA_HW_MAX_CHANNEL_HANDLE
* @reserved: reserved
*/
union IpaHwMhiChannelErrorEventData_t {
struct IpaHwMhiChannelErrorEventParams_t {
u32 errorType:8;
u32 channelHandle:8;
u32 reserved:16;
} params;
u32 raw32b;
};
/**
* Structure holding the parameters for
* IPA_HW_2_CPU_EVENT_MHI_CHANNEL_WAKE_UP_REQUEST event.
* Parameters are sent as 32b immediate parameters.
* @channelHandle: The channel identifier as allocated by driver.
* value is within the range 0 to IPA_HW_MAX_CHANNEL_HANDLE
* @reserved: reserved
*/
union IpaHwMhiChannelWakeupEventData_t {
struct IpaHwMhiChannelWakeupEventParams_t {
u32 channelHandle:8;
u32 reserved:24;
} params;
u32 raw32b;
};
/**
* Structure holding the MHI Common statistics
* @numULDLSync: Number of times UL activity trigged due to DL activity
* @numULTimerExpired: Number of times UL Accm Timer expired
*/
struct IpaHwStatsMhiCmnInfoData_t {
u32 numULDLSync;
u32 numULTimerExpired;
u32 numChEvCtxWpRead;
u32 reserved;
};
/**
* Structure holding the MHI Channel statistics
* @doorbellInt: The number of doorbell int
* @reProccesed: The number of ring elements processed
* @bamFifoFull: Number of times Bam Fifo got full
* @bamFifoEmpty: Number of times Bam Fifo got empty
* @bamFifoUsageHigh: Number of times Bam fifo usage went above 75%
* @bamFifoUsageLow: Number of times Bam fifo usage went below 25%
* @bamInt: Number of BAM Interrupts
* @ringFull: Number of times Transfer Ring got full
* @ringEmpty: umber of times Transfer Ring got empty
* @ringUsageHigh: Number of times Transfer Ring usage went above 75%
* @ringUsageLow: Number of times Transfer Ring usage went below 25%
* @delayedMsi: Number of times device triggered MSI to host after
* Interrupt Moderation Timer expiry
* @immediateMsi: Number of times device triggered MSI to host immediately
* @thresholdMsi: Number of times device triggered MSI due to max pending
* events threshold reached
* @numSuspend: Number of times channel was suspended
* @numResume: Number of times channel was suspended
* @num_OOB: Number of times we indicated that we are OOB
* @num_OOB_timer_expiry: Number of times we indicated that we are OOB
* after timer expiry
* @num_OOB_moderation_timer_start: Number of times we started timer after
* sending OOB and hitting OOB again before we processed threshold
* number of packets
* @num_db_mode_evt: Number of times we indicated that we are in Doorbell mode
*/
struct IpaHwStatsMhiCnlInfoData_t {
u32 doorbellInt;
u32 reProccesed;
u32 bamFifoFull;
u32 bamFifoEmpty;
u32 bamFifoUsageHigh;
u32 bamFifoUsageLow;
u32 bamInt;
u32 ringFull;
u32 ringEmpty;
u32 ringUsageHigh;
u32 ringUsageLow;
u32 delayedMsi;
u32 immediateMsi;
u32 thresholdMsi;
u32 numSuspend;
u32 numResume;
u32 num_OOB;
u32 num_OOB_timer_expiry;
u32 num_OOB_moderation_timer_start;
u32 num_db_mode_evt;
};
/**
* Structure holding the MHI statistics
* @mhiCmnStats: Stats pertaining to MHI
* @mhiCnlStats: Stats pertaining to each channel
*/
struct IpaHwStatsMhiInfoData_t {
struct IpaHwStatsMhiCmnInfoData_t mhiCmnStats;
struct IpaHwStatsMhiCnlInfoData_t mhiCnlStats[
IPA_HW_MAX_NUMBER_OF_CHANNELS];
};
/**
* Structure holding the MHI Common Config info
* @isDlUlSyncEnabled: Flag to indicate if DL-UL synchronization is enabled
* @UlAccmVal: Out Channel(UL) accumulation time in ms when DL UL Sync is
* enabled
* @ulMsiEventThreshold: Threshold at which HW fires MSI to host for UL events
* @dlMsiEventThreshold: Threshold at which HW fires MSI to host for DL events
*/
struct IpaHwConfigMhiCmnInfoData_t {
u8 isDlUlSyncEnabled;
u8 UlAccmVal;
u8 ulMsiEventThreshold;
u8 dlMsiEventThreshold;
};
/**
* Structure holding the parameters for MSI info data
* @msiAddress_low: The MSI lower base addr (in device space) used for asserting
* the interrupt (MSI) associated with the event ring.
* @msiAddress_hi: The MSI higher base addr (in device space) used for asserting
* the interrupt (MSI) associated with the event ring.
* @msiMask: Mask indicating number of messages assigned by the host to device
* @msiData: Data Pattern to use when generating the MSI
*/
struct IpaHwConfigMhiMsiInfoData_t {
u32 msiAddress_low;
u32 msiAddress_hi;
u32 msiMask;
u32 msiData;
};
/**
* Structure holding the MHI Channel Config info
* @transferRingSize: The Transfer Ring size in terms of Ring Elements
* @transferRingIndex: The Transfer Ring channel number as defined by host
* @eventRingIndex: The Event Ring Index associated with this Transfer Ring
* @bamPipeIndex: The BAM Pipe associated with this channel
* @isOutChannel: Indication for the direction of channel
* @reserved_0: Reserved byte for maintaining 4byte alignment
* @reserved_1: Reserved byte for maintaining 4byte alignment
*/
struct IpaHwConfigMhiCnlInfoData_t {
u16 transferRingSize;
u8 transferRingIndex;
u8 eventRingIndex;
u8 bamPipeIndex;
u8 isOutChannel;
u8 reserved_0;
u8 reserved_1;
};
/**
* Structure holding the MHI Event Config info
* @msiVec: msi vector to invoke MSI interrupt
* @intmodtValue: Interrupt moderation timer (in milliseconds)
* @eventRingSize: The Event Ring size in terms of Ring Elements
* @eventRingIndex: The Event Ring number as defined by host
* @reserved_0: Reserved byte for maintaining 4byte alignment
* @reserved_1: Reserved byte for maintaining 4byte alignment
* @reserved_2: Reserved byte for maintaining 4byte alignment
*/
struct IpaHwConfigMhiEventInfoData_t {
u32 msiVec;
u16 intmodtValue;
u16 eventRingSize;
u8 eventRingIndex;
u8 reserved_0;
u8 reserved_1;
u8 reserved_2;
};
/**
* Structure holding the MHI Config info
* @mhiCmnCfg: Common Config pertaining to MHI
* @mhiMsiCfg: Config pertaining to MSI config
* @mhiCnlCfg: Config pertaining to each channel
* @mhiEvtCfg: Config pertaining to each event Ring
*/
struct IpaHwConfigMhiInfoData_t {
struct IpaHwConfigMhiCmnInfoData_t mhiCmnCfg;
struct IpaHwConfigMhiMsiInfoData_t mhiMsiCfg;
struct IpaHwConfigMhiCnlInfoData_t mhiCnlCfg[
IPA_HW_MAX_NUMBER_OF_CHANNELS];
struct IpaHwConfigMhiEventInfoData_t mhiEvtCfg[
IPA_HW_MAX_NUMBER_OF_EVENTRINGS];
};
struct ipa3_uc_mhi_ctx {
u8 expected_responseOp;
u32 expected_responseParams;
void (*ready_cb)(void);
void (*wakeup_request_cb)(void);
u32 mhi_uc_stats_ofst;
struct IpaHwStatsMhiInfoData_t *mhi_uc_stats_mmio;
};
#define PRINT_COMMON_STATS(x) \
(nBytes += scnprintf(&dbg_buff[nBytes], size - nBytes, \
#x "=0x%x\n", ipa3_uc_mhi_ctx->mhi_uc_stats_mmio->mhiCmnStats.x))
#define PRINT_CHANNEL_STATS(ch, x) \
(nBytes += scnprintf(&dbg_buff[nBytes], size - nBytes, \
#x "=0x%x\n", ipa3_uc_mhi_ctx->mhi_uc_stats_mmio->mhiCnlStats[ch].x))
struct ipa3_uc_mhi_ctx *ipa3_uc_mhi_ctx;
static int ipa3_uc_mhi_response_hdlr(struct IpaHwSharedMemCommonMapping_t
*uc_sram_mmio, u32 *uc_status)
{
IPADBG("responseOp=%d\n", uc_sram_mmio->responseOp);
if (uc_sram_mmio->responseOp == ipa3_uc_mhi_ctx->expected_responseOp &&
uc_sram_mmio->responseParams ==
ipa3_uc_mhi_ctx->expected_responseParams) {
*uc_status = 0;
return 0;
}
return -EINVAL;
}
static void ipa3_uc_mhi_event_hdlr(struct IpaHwSharedMemCommonMapping_t
*uc_sram_mmio)
{
if (ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp ==
IPA_HW_2_CPU_EVENT_MHI_CHANNEL_ERROR) {
union IpaHwMhiChannelErrorEventData_t evt;
IPAERR("Channel error\n");
evt.raw32b = uc_sram_mmio->eventParams;
IPAERR("errorType=%d channelHandle=%d reserved=%d\n",
evt.params.errorType, evt.params.channelHandle,
evt.params.reserved);
} else if (ipa3_ctx->uc_ctx.uc_sram_mmio->eventOp ==
IPA_HW_2_CPU_EVENT_MHI_CHANNEL_WAKE_UP_REQUEST) {
union IpaHwMhiChannelWakeupEventData_t evt;
IPADBG("WakeUp channel request\n");
evt.raw32b = uc_sram_mmio->eventParams;
IPADBG("channelHandle=%d reserved=%d\n",
evt.params.channelHandle, evt.params.reserved);
ipa3_uc_mhi_ctx->wakeup_request_cb();
}
}
static void ipa3_uc_mhi_event_log_info_hdlr(
struct IpaHwEventLogInfoData_t *uc_event_top_mmio)
{
if ((uc_event_top_mmio->featureMask & (1 << IPA_HW_FEATURE_MHI)) == 0) {
IPAERR("MHI feature missing 0x%x\n",
uc_event_top_mmio->featureMask);
return;
}
if (uc_event_top_mmio->statsInfo.featureInfo[IPA_HW_FEATURE_MHI].
params.size != sizeof(struct IpaHwStatsMhiInfoData_t)) {
IPAERR("mhi stats sz invalid exp=%zu is=%u\n",
sizeof(struct IpaHwStatsMhiInfoData_t),
uc_event_top_mmio->statsInfo.
featureInfo[IPA_HW_FEATURE_MHI].params.size);
return;
}
ipa3_uc_mhi_ctx->mhi_uc_stats_ofst = uc_event_top_mmio->
statsInfo.baseAddrOffset + uc_event_top_mmio->statsInfo.
featureInfo[IPA_HW_FEATURE_MHI].params.offset;
IPAERR("MHI stats ofst=0x%x\n", ipa3_uc_mhi_ctx->mhi_uc_stats_ofst);
if (ipa3_uc_mhi_ctx->mhi_uc_stats_ofst +
sizeof(struct IpaHwStatsMhiInfoData_t) >=
ipa3_ctx->ctrl->ipa_reg_base_ofst +
ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n, 0) +
ipa3_ctx->smem_sz) {
IPAERR("uc_mhi_stats 0x%x outside SRAM\n",
ipa3_uc_mhi_ctx->mhi_uc_stats_ofst);
return;
}
ipa3_uc_mhi_ctx->mhi_uc_stats_mmio =
ioremap(ipa3_ctx->ipa_wrapper_base +
ipa3_uc_mhi_ctx->mhi_uc_stats_ofst,
sizeof(struct IpaHwStatsMhiInfoData_t));
if (!ipa3_uc_mhi_ctx->mhi_uc_stats_mmio) {
IPAERR("fail to ioremap uc mhi stats\n");
return;
}
}
int ipa3_uc_mhi_init(void (*ready_cb)(void), void (*wakeup_request_cb)(void))
{
struct ipa3_uc_hdlrs hdlrs;
if (ipa3_uc_mhi_ctx) {
IPAERR("Already initialized\n");
return -EFAULT;
}
ipa3_uc_mhi_ctx = kzalloc(sizeof(*ipa3_uc_mhi_ctx), GFP_KERNEL);
if (!ipa3_uc_mhi_ctx) {
IPAERR("no mem\n");
return -ENOMEM;
}
ipa3_uc_mhi_ctx->ready_cb = ready_cb;
ipa3_uc_mhi_ctx->wakeup_request_cb = wakeup_request_cb;
memset(&hdlrs, 0, sizeof(hdlrs));
hdlrs.ipa_uc_loaded_hdlr = ipa3_uc_mhi_ctx->ready_cb;
hdlrs.ipa3_uc_response_hdlr = ipa3_uc_mhi_response_hdlr;
hdlrs.ipa_uc_event_hdlr = ipa3_uc_mhi_event_hdlr;
hdlrs.ipa_uc_event_log_info_hdlr = ipa3_uc_mhi_event_log_info_hdlr;
ipa3_uc_register_handlers(IPA_HW_FEATURE_MHI, &hdlrs);
IPADBG("Done\n");
return 0;
}
void ipa3_uc_mhi_cleanup(void)
{
struct ipa3_uc_hdlrs null_hdlrs = { 0 };
IPADBG("Enter\n");
if (!ipa3_uc_mhi_ctx) {
IPAERR("ipa3_uc_mhi_ctx is not initialized\n");
return;
}
ipa3_uc_register_handlers(IPA_HW_FEATURE_MHI, &null_hdlrs);
kfree(ipa3_uc_mhi_ctx);
ipa3_uc_mhi_ctx = NULL;
IPADBG("Done\n");
}
int ipa3_uc_mhi_init_engine(struct ipa_mhi_msi_info *msi, u32 mmio_addr,
u32 host_ctrl_addr, u32 host_data_addr, u32 first_ch_idx,
u32 first_evt_idx)
{
int res;
struct ipa_mem_buffer mem;
struct IpaHwMhiInitCmdData_t *init_cmd_data;
struct IpaHwMhiMsiCmdData_t *msi_cmd;
if (!ipa3_uc_mhi_ctx) {
IPAERR("Not initialized\n");
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
res = ipa3_uc_update_hw_flags(0);
if (res) {
IPAERR("ipa3_uc_update_hw_flags failed %d\n", res);
goto disable_clks;
}
mem.size = sizeof(*init_cmd_data);
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);
res = -ENOMEM;
goto disable_clks;
}
memset(mem.base, 0, mem.size);
init_cmd_data = (struct IpaHwMhiInitCmdData_t *)mem.base;
init_cmd_data->msiAddress = msi->addr_low;
init_cmd_data->mmioBaseAddress = mmio_addr;
init_cmd_data->deviceMhiCtrlBaseAddress = host_ctrl_addr;
init_cmd_data->deviceMhiDataBaseAddress = host_data_addr;
init_cmd_data->firstChannelIndex = first_ch_idx;
init_cmd_data->firstEventRingIndex = first_evt_idx;
res = ipa3_uc_send_cmd((u32)mem.phys_base, IPA_CPU_2_HW_CMD_MHI_INIT, 0,
false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base,
mem.phys_base);
goto disable_clks;
}
dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base, mem.phys_base);
mem.size = sizeof(*msi_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);
res = -ENOMEM;
goto disable_clks;
}
msi_cmd = (struct IpaHwMhiMsiCmdData_t *)mem.base;
msi_cmd->msiAddress_hi = msi->addr_hi;
msi_cmd->msiAddress_low = msi->addr_low;
msi_cmd->msiData = msi->data;
msi_cmd->msiMask = msi->mask;
res = ipa3_uc_send_cmd((u32)mem.phys_base,
IPA_CPU_2_HW_CMD_MHI_UPDATE_MSI, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base,
mem.phys_base);
goto disable_clks;
}
dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base, mem.phys_base);
res = 0;
disable_clks:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return res;
}
int ipa3_uc_mhi_init_channel(int ipa_ep_idx, int channelHandle,
int contexArrayIndex, int channelDirection)
{
int res;
union IpaHwMhiInitChannelCmdData_t init_cmd;
union IpaHwMhiChangeChannelStateResponseData_t uc_rsp;
if (!ipa3_uc_mhi_ctx) {
IPAERR("Not initialized\n");
return -EFAULT;
}
if (ipa_ep_idx < 0 || ipa_ep_idx >= ipa3_ctx->ipa_num_pipes) {
IPAERR("Invalid ipa_ep_idx.\n");
return -EINVAL;
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
memset(&uc_rsp, 0, sizeof(uc_rsp));
uc_rsp.params.state = IPA_HW_MHI_CHANNEL_STATE_RUN;
uc_rsp.params.channelHandle = channelHandle;
ipa3_uc_mhi_ctx->expected_responseOp =
IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE;
ipa3_uc_mhi_ctx->expected_responseParams = uc_rsp.raw32b;
memset(&init_cmd, 0, sizeof(init_cmd));
init_cmd.params.channelHandle = channelHandle;
init_cmd.params.contexArrayIndex = contexArrayIndex;
init_cmd.params.bamPipeId = ipa_ep_idx;
init_cmd.params.channelDirection = channelDirection;
res = ipa3_uc_send_cmd(init_cmd.raw32b,
IPA_CPU_2_HW_CMD_MHI_INIT_CHANNEL, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
goto disable_clks;
}
res = 0;
disable_clks:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return res;
}
int ipa3_uc_mhi_reset_channel(int channelHandle)
{
union IpaHwMhiChangeChannelStateCmdData_t cmd;
union IpaHwMhiChangeChannelStateResponseData_t uc_rsp;
int res;
if (!ipa3_uc_mhi_ctx) {
IPAERR("Not initialized\n");
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
memset(&uc_rsp, 0, sizeof(uc_rsp));
uc_rsp.params.state = IPA_HW_MHI_CHANNEL_STATE_DISABLE;
uc_rsp.params.channelHandle = channelHandle;
ipa3_uc_mhi_ctx->expected_responseOp =
IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE;
ipa3_uc_mhi_ctx->expected_responseParams = uc_rsp.raw32b;
memset(&cmd, 0, sizeof(cmd));
cmd.params.requestedState = IPA_HW_MHI_CHANNEL_STATE_DISABLE;
cmd.params.channelHandle = channelHandle;
res = ipa3_uc_send_cmd(cmd.raw32b,
IPA_CPU_2_HW_CMD_MHI_CHANGE_CHANNEL_STATE, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
goto disable_clks;
}
res = 0;
disable_clks:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return res;
}
int ipa3_uc_mhi_suspend_channel(int channelHandle)
{
union IpaHwMhiChangeChannelStateCmdData_t cmd;
union IpaHwMhiChangeChannelStateResponseData_t uc_rsp;
int res;
if (!ipa3_uc_mhi_ctx) {
IPAERR("Not initialized\n");
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
memset(&uc_rsp, 0, sizeof(uc_rsp));
uc_rsp.params.state = IPA_HW_MHI_CHANNEL_STATE_SUSPEND;
uc_rsp.params.channelHandle = channelHandle;
ipa3_uc_mhi_ctx->expected_responseOp =
IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE;
ipa3_uc_mhi_ctx->expected_responseParams = uc_rsp.raw32b;
memset(&cmd, 0, sizeof(cmd));
cmd.params.requestedState = IPA_HW_MHI_CHANNEL_STATE_SUSPEND;
cmd.params.channelHandle = channelHandle;
res = ipa3_uc_send_cmd(cmd.raw32b,
IPA_CPU_2_HW_CMD_MHI_CHANGE_CHANNEL_STATE, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
goto disable_clks;
}
res = 0;
disable_clks:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return res;
}
int ipa3_uc_mhi_resume_channel(int channelHandle, bool LPTransitionRejected)
{
union IpaHwMhiChangeChannelStateCmdData_t cmd;
union IpaHwMhiChangeChannelStateResponseData_t uc_rsp;
int res;
if (!ipa3_uc_mhi_ctx) {
IPAERR("Not initialized\n");
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
memset(&uc_rsp, 0, sizeof(uc_rsp));
uc_rsp.params.state = IPA_HW_MHI_CHANNEL_STATE_RUN;
uc_rsp.params.channelHandle = channelHandle;
ipa3_uc_mhi_ctx->expected_responseOp =
IPA_HW_2_CPU_RESPONSE_MHI_CHANGE_CHANNEL_STATE;
ipa3_uc_mhi_ctx->expected_responseParams = uc_rsp.raw32b;
memset(&cmd, 0, sizeof(cmd));
cmd.params.requestedState = IPA_HW_MHI_CHANNEL_STATE_RUN;
cmd.params.channelHandle = channelHandle;
cmd.params.LPTransitionRejected = LPTransitionRejected;
res = ipa3_uc_send_cmd(cmd.raw32b,
IPA_CPU_2_HW_CMD_MHI_CHANGE_CHANNEL_STATE, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
goto disable_clks;
}
res = 0;
disable_clks:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return res;
}
int ipa3_uc_mhi_stop_event_update_channel(int channelHandle)
{
union IpaHwMhiStopEventUpdateData_t cmd;
int res;
if (!ipa3_uc_mhi_ctx) {
IPAERR("Not initialized\n");
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
memset(&cmd, 0, sizeof(cmd));
cmd.params.channelHandle = channelHandle;
ipa3_uc_mhi_ctx->expected_responseOp =
IPA_CPU_2_HW_CMD_MHI_STOP_EVENT_UPDATE;
ipa3_uc_mhi_ctx->expected_responseParams = cmd.raw32b;
res = ipa3_uc_send_cmd(cmd.raw32b,
IPA_CPU_2_HW_CMD_MHI_STOP_EVENT_UPDATE, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
goto disable_clks;
}
res = 0;
disable_clks:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return res;
}
int ipa3_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t *cmd)
{
int res;
if (!ipa3_uc_mhi_ctx) {
IPAERR("Not initialized\n");
return -EFAULT;
}
IPADBG("isDlUlSyncEnabled=0x%x UlAccmVal=0x%x\n",
cmd->params.isDlUlSyncEnabled, cmd->params.UlAccmVal);
IPADBG("ulMsiEventThreshold=0x%x dlMsiEventThreshold=0x%x\n",
cmd->params.ulMsiEventThreshold,
cmd->params.dlMsiEventThreshold);
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
res = ipa3_uc_send_cmd(cmd->raw32b,
IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
goto disable_clks;
}
res = 0;
disable_clks:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return res;
}
int ipa3_uc_mhi_print_stats(char *dbg_buff, int size)
{
int nBytes = 0;
int i;
if (!ipa3_uc_mhi_ctx->mhi_uc_stats_mmio) {
IPAERR("MHI uc stats is not valid\n");
return 0;
}
nBytes += scnprintf(&dbg_buff[nBytes], size - nBytes,
"Common Stats:\n");
PRINT_COMMON_STATS(numULDLSync);
PRINT_COMMON_STATS(numULTimerExpired);
PRINT_COMMON_STATS(numChEvCtxWpRead);
for (i = 0; i < IPA_HW_MAX_NUMBER_OF_CHANNELS; i++) {
nBytes += scnprintf(&dbg_buff[nBytes], size - nBytes,
"Channel %d Stats:\n", i);
PRINT_CHANNEL_STATS(i, doorbellInt);
PRINT_CHANNEL_STATS(i, reProccesed);
PRINT_CHANNEL_STATS(i, bamFifoFull);
PRINT_CHANNEL_STATS(i, bamFifoEmpty);
PRINT_CHANNEL_STATS(i, bamFifoUsageHigh);
PRINT_CHANNEL_STATS(i, bamFifoUsageLow);
PRINT_CHANNEL_STATS(i, bamInt);
PRINT_CHANNEL_STATS(i, ringFull);
PRINT_CHANNEL_STATS(i, ringEmpty);
PRINT_CHANNEL_STATS(i, ringUsageHigh);
PRINT_CHANNEL_STATS(i, ringUsageLow);
PRINT_CHANNEL_STATS(i, delayedMsi);
PRINT_CHANNEL_STATS(i, immediateMsi);
PRINT_CHANNEL_STATS(i, thresholdMsi);
PRINT_CHANNEL_STATS(i, numSuspend);
PRINT_CHANNEL_STATS(i, numResume);
PRINT_CHANNEL_STATS(i, num_OOB);
PRINT_CHANNEL_STATS(i, num_OOB_timer_expiry);
PRINT_CHANNEL_STATS(i, num_OOB_moderation_timer_start);
PRINT_CHANNEL_STATS(i, num_db_mode_evt);
}
return nBytes;
}