1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/drivers/net/wireless/ath/ath12k/hif.h
Baochen Qiang 809055628b wifi: ath12k: add panic handler
Currently for ath12k PCI devices, firmware could be running in one of
several execution environments, e.g., PBL, SBL and mission mode etc. Among
which PBL is the only stage where PCIe link negotiation could happen. So
normally firmware runs in PBL in order to be enumerated during system reboot.

However it might not work in kernel crash scenario: ath12k target is not
found after warm reboot from kernel crash. This is because when kernel crashes,
ath12k host does nothing to firmware. And during warm reboot, WLAN power
is sustained. So firmware is likely to keep running in mission mode throughout
the bootup process. As a result PCIe link is not established and thus target
not enumerated.

So add a handler in panic notification list for ath12k. When kernel crashes,
this handler gets called and tries to reset target to PBL state. Then PCIe
link negotiation could happen and target gets enumerated.

This change applies to all PCI devices including WCN7850 and QCN9274.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4

Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20240529021533.10861-1-quic_bqiang@quicinc.com
2024-06-03 16:11:38 +03:00

159 lines
4 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);
int (*panic_handler)(struct ath12k_base *ab);
};
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);
}
static inline int ath12k_hif_panic_handler(struct ath12k_base *ab)
{
if (!ab->hif.ops->panic_handler)
return NOTIFY_DONE;
return ab->hif.ops->panic_handler(ab);
}
#endif /* ATH12K_HIF_H */