Now that all infrastructure is in place and ath12k is fixed to handle all the corner cases, power down the ath12k firmware during suspend and power it back up during resume. For suspend, two conditions needs to be satisfied: 1. since MHI channel unprepare would be done in late suspend stage, ath12k needs to get all QMI-dependent things done before that stage. 2. and because unprepare MHI channels requires a working MHI stack, ath12k is not allowed to call mhi_power_down() until that finishes. So the original suspend callback is separated into two parts: the first part handles all QMI-dependent things in suspend callback; while the second part powers down MHI in suspend_late callback. This is valid because kernel calls ath12k's suspend callback before calling all suspend_late callbacks, making the first condition satisfied. And because MHI devices are children of ath12k device (ab->dev), kernel guarantees that ath12k's suspend_late callback is called after QRTR's suspend_late callback, this satisfies the second condition. Above analysis also applies to resume process. so the original resume callback is separated into two parts: the first part powers up MHI stack in resume_early callback, this guarantees MHI stack is working when QRTR tries to prepare MHI channels (kernel calls QRTR's resume_early callback after ath12k's resume_early callback, due to the child-father relationship); the second part waits for the completion of restart, which would succeed since MHI channels are ready for use by QMI. Another notable change is in power down path, we tell mhi_power_down() to not to destroy MHI devices, making it possible for QRTR to help unprepare/prepare MHI channels, and finally get us rid of the potential probe-defer issue when resume. Also change related code due to interface changes. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com> Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> Link: https://msgid.link/20240412060620.27519-10-quic_bqiang@quicinc.com
150 lines
3.8 KiB
C
150 lines
3.8 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
|
/*
|
|
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#ifndef ATH12K_HIF_H
|
|
#define ATH12K_HIF_H
|
|
|
|
#include "core.h"
|
|
|
|
struct ath12k_hif_ops {
|
|
u32 (*read32)(struct ath12k_base *ab, u32 address);
|
|
void (*write32)(struct ath12k_base *ab, u32 address, u32 data);
|
|
void (*irq_enable)(struct ath12k_base *ab);
|
|
void (*irq_disable)(struct ath12k_base *ab);
|
|
int (*start)(struct ath12k_base *ab);
|
|
void (*stop)(struct ath12k_base *ab);
|
|
int (*power_up)(struct ath12k_base *ab);
|
|
void (*power_down)(struct ath12k_base *ab, bool is_suspend);
|
|
int (*suspend)(struct ath12k_base *ab);
|
|
int (*resume)(struct ath12k_base *ab);
|
|
int (*map_service_to_pipe)(struct ath12k_base *ab, u16 service_id,
|
|
u8 *ul_pipe, u8 *dl_pipe);
|
|
int (*get_user_msi_vector)(struct ath12k_base *ab, char *user_name,
|
|
int *num_vectors, u32 *user_base_data,
|
|
u32 *base_vector);
|
|
void (*get_msi_address)(struct ath12k_base *ab, u32 *msi_addr_lo,
|
|
u32 *msi_addr_hi);
|
|
void (*ce_irq_enable)(struct ath12k_base *ab);
|
|
void (*ce_irq_disable)(struct ath12k_base *ab);
|
|
void (*get_ce_msi_idx)(struct ath12k_base *ab, u32 ce_id, u32 *msi_idx);
|
|
};
|
|
|
|
static inline int ath12k_hif_map_service_to_pipe(struct ath12k_base *ab, u16 service_id,
|
|
u8 *ul_pipe, u8 *dl_pipe)
|
|
{
|
|
return ab->hif.ops->map_service_to_pipe(ab, service_id,
|
|
ul_pipe, dl_pipe);
|
|
}
|
|
|
|
static inline int ath12k_hif_get_user_msi_vector(struct ath12k_base *ab,
|
|
char *user_name,
|
|
int *num_vectors,
|
|
u32 *user_base_data,
|
|
u32 *base_vector)
|
|
{
|
|
if (!ab->hif.ops->get_user_msi_vector)
|
|
return -EOPNOTSUPP;
|
|
|
|
return ab->hif.ops->get_user_msi_vector(ab, user_name, num_vectors,
|
|
user_base_data,
|
|
base_vector);
|
|
}
|
|
|
|
static inline void ath12k_hif_get_msi_address(struct ath12k_base *ab,
|
|
u32 *msi_addr_lo,
|
|
u32 *msi_addr_hi)
|
|
{
|
|
if (!ab->hif.ops->get_msi_address)
|
|
return;
|
|
|
|
ab->hif.ops->get_msi_address(ab, msi_addr_lo, msi_addr_hi);
|
|
}
|
|
|
|
static inline void ath12k_hif_get_ce_msi_idx(struct ath12k_base *ab, u32 ce_id,
|
|
u32 *msi_data_idx)
|
|
{
|
|
if (ab->hif.ops->get_ce_msi_idx)
|
|
ab->hif.ops->get_ce_msi_idx(ab, ce_id, msi_data_idx);
|
|
else
|
|
*msi_data_idx = ce_id;
|
|
}
|
|
|
|
static inline void ath12k_hif_ce_irq_enable(struct ath12k_base *ab)
|
|
{
|
|
if (ab->hif.ops->ce_irq_enable)
|
|
ab->hif.ops->ce_irq_enable(ab);
|
|
}
|
|
|
|
static inline void ath12k_hif_ce_irq_disable(struct ath12k_base *ab)
|
|
{
|
|
if (ab->hif.ops->ce_irq_disable)
|
|
ab->hif.ops->ce_irq_disable(ab);
|
|
}
|
|
|
|
static inline void ath12k_hif_irq_enable(struct ath12k_base *ab)
|
|
{
|
|
ab->hif.ops->irq_enable(ab);
|
|
}
|
|
|
|
static inline void ath12k_hif_irq_disable(struct ath12k_base *ab)
|
|
{
|
|
ab->hif.ops->irq_disable(ab);
|
|
}
|
|
|
|
static inline int ath12k_hif_suspend(struct ath12k_base *ab)
|
|
{
|
|
if (ab->hif.ops->suspend)
|
|
return ab->hif.ops->suspend(ab);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int ath12k_hif_resume(struct ath12k_base *ab)
|
|
{
|
|
if (ab->hif.ops->resume)
|
|
return ab->hif.ops->resume(ab);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int ath12k_hif_start(struct ath12k_base *ab)
|
|
{
|
|
return ab->hif.ops->start(ab);
|
|
}
|
|
|
|
static inline void ath12k_hif_stop(struct ath12k_base *ab)
|
|
{
|
|
ab->hif.ops->stop(ab);
|
|
}
|
|
|
|
static inline u32 ath12k_hif_read32(struct ath12k_base *ab, u32 address)
|
|
{
|
|
return ab->hif.ops->read32(ab, address);
|
|
}
|
|
|
|
static inline void ath12k_hif_write32(struct ath12k_base *ab, u32 address,
|
|
u32 data)
|
|
{
|
|
ab->hif.ops->write32(ab, address, data);
|
|
}
|
|
|
|
static inline int ath12k_hif_power_up(struct ath12k_base *ab)
|
|
{
|
|
if (!ab->hif.ops->power_up)
|
|
return -EOPNOTSUPP;
|
|
|
|
return ab->hif.ops->power_up(ab);
|
|
}
|
|
|
|
static inline void ath12k_hif_power_down(struct ath12k_base *ab, bool is_suspend)
|
|
{
|
|
if (!ab->hif.ops->power_down)
|
|
return;
|
|
|
|
ab->hif.ops->power_down(ab, is_suspend);
|
|
}
|
|
|
|
#endif /* ATH12K_HIF_H */
|