Merge "ath10k: Enable Subsystem Restart for ath10k WCN3990 driver"
This commit is contained in:
commit
3e5a44882b
9 changed files with 338 additions and 6 deletions
|
@ -26,6 +26,7 @@ ath10k_pci-y += pci.o \
|
|||
ce.o
|
||||
obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o
|
||||
ath10k_snoc-y += snoc.o \
|
||||
qmi.o \
|
||||
ce.o
|
||||
|
||||
ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
|
||||
|
|
|
@ -455,6 +455,9 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
|
|||
u32 desc_flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
if (nbytes > ce_state->src_sz_max)
|
||||
ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n",
|
||||
__func__, nbytes, ce_state->src_sz_max);
|
||||
|
@ -942,6 +945,9 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
|
|||
struct ath10k_ce_pipe *ce_state;
|
||||
struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
|
||||
|
||||
if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
|
||||
return;
|
||||
|
||||
if (ar->target_version == ATH10K_HW_WCN3990)
|
||||
intr_summary = 0xFFF;
|
||||
else
|
||||
|
|
|
@ -1536,7 +1536,6 @@ static void ath10k_core_restart(struct work_struct *work)
|
|||
struct ath10k *ar = container_of(work, struct ath10k, restart_work);
|
||||
|
||||
set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
|
||||
ath10k_gen_set_base_mac_addr(ar, ar->base_mac_addr);
|
||||
|
||||
/* Place a barrier to make sure the compiler doesn't reorder
|
||||
* CRASH_FLUSH and calling other functions.
|
||||
|
|
|
@ -4450,7 +4450,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
|
|||
ar->state = ATH10K_STATE_ON;
|
||||
break;
|
||||
case ATH10K_STATE_RESTARTING:
|
||||
ath10k_halt(ar);
|
||||
if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
|
||||
ath10k_halt(ar);
|
||||
ar->state = ATH10K_STATE_RESTARTED;
|
||||
break;
|
||||
case ATH10K_STATE_ON:
|
||||
|
|
230
drivers/net/wireless/ath/ath10k/qmi.c
Normal file
230
drivers/net/wireless/ath/ath10k/qmi.c
Normal file
|
@ -0,0 +1,230 @@
|
|||
/* Copyright (c) 2017, 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 <soc/qcom/subsystem_notif.h>
|
||||
#include <soc/qcom/subsystem_restart.h>
|
||||
#include <soc/qcom/service-notifier.h>
|
||||
#include "core.h"
|
||||
#include "qmi.h"
|
||||
#include "snoc.h"
|
||||
#include <soc/qcom/icnss.h>
|
||||
|
||||
static int
|
||||
ath10k_snoc_service_notifier_notify(struct notifier_block *nb,
|
||||
unsigned long notification, void *data)
|
||||
{
|
||||
struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
|
||||
service_notifier_nb);
|
||||
enum pd_subsys_state *state = data;
|
||||
struct ath10k *ar = ar_snoc->ar;
|
||||
|
||||
switch (notification) {
|
||||
case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service down, data: 0x%pK\n",
|
||||
data);
|
||||
|
||||
if (!state || *state != ROOT_PD_SHUTDOWN)
|
||||
atomic_set(&ar_snoc->fw_crashed, 1);
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD went down %d\n",
|
||||
ar_snoc->fw_crashed);
|
||||
break;
|
||||
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service up\n");
|
||||
queue_work(ar->workqueue, &ar->restart_work);
|
||||
break;
|
||||
default:
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC,
|
||||
"Service state Unknown, notification: 0x%lx\n",
|
||||
notification);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int ath10k_snoc_get_service_location_notify(struct notifier_block *nb,
|
||||
unsigned long opcode,
|
||||
void *data)
|
||||
{
|
||||
struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
|
||||
get_service_nb);
|
||||
struct ath10k *ar = ar_snoc->ar;
|
||||
struct pd_qmi_client_data *pd = data;
|
||||
int curr_state;
|
||||
int ret;
|
||||
int i;
|
||||
struct ath10k_service_notifier_context *notifier;
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service notify opcode: %lu\n",
|
||||
opcode);
|
||||
|
||||
if (opcode != LOCATOR_UP)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (!pd->total_domains) {
|
||||
ath10k_err(ar, "Did not find any domains\n");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
notifier = kcalloc(pd->total_domains,
|
||||
sizeof(struct ath10k_service_notifier_context),
|
||||
GFP_KERNEL);
|
||||
if (!notifier) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ar_snoc->service_notifier_nb.notifier_call =
|
||||
ath10k_snoc_service_notifier_notify;
|
||||
|
||||
for (i = 0; i < pd->total_domains; i++) {
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC,
|
||||
"%d: domain_name: %s, instance_id: %d\n", i,
|
||||
pd->domain_list[i].name,
|
||||
pd->domain_list[i].instance_id);
|
||||
|
||||
notifier[i].handle =
|
||||
service_notif_register_notifier(
|
||||
pd->domain_list[i].name,
|
||||
pd->domain_list[i].instance_id,
|
||||
&ar_snoc->service_notifier_nb,
|
||||
&curr_state);
|
||||
notifier[i].instance_id = pd->domain_list[i].instance_id;
|
||||
strlcpy(notifier[i].name, pd->domain_list[i].name,
|
||||
QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
|
||||
|
||||
if (IS_ERR(notifier[i].handle)) {
|
||||
ath10k_err(ar, "%d: Unable to register notifier for %s(0x%x)\n",
|
||||
i, pd->domain_list->name,
|
||||
pd->domain_list->instance_id);
|
||||
ret = PTR_ERR(notifier[i].handle);
|
||||
goto free_handle;
|
||||
}
|
||||
}
|
||||
|
||||
ar_snoc->service_notifier = notifier;
|
||||
ar_snoc->total_domains = pd->total_domains;
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD restart enabled\n");
|
||||
|
||||
return NOTIFY_OK;
|
||||
|
||||
free_handle:
|
||||
for (i = 0; i < pd->total_domains; i++) {
|
||||
if (notifier[i].handle) {
|
||||
service_notif_unregister_notifier(
|
||||
notifier[i].handle,
|
||||
&ar_snoc->service_notifier_nb);
|
||||
}
|
||||
}
|
||||
kfree(notifier);
|
||||
|
||||
out:
|
||||
ath10k_err(ar, "PD restart not enabled: %d\n", ret);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
int ath10k_snoc_pd_restart_enable(struct ath10k *ar)
|
||||
{
|
||||
int ret;
|
||||
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service location\n");
|
||||
|
||||
ar_snoc->get_service_nb.notifier_call =
|
||||
ath10k_snoc_get_service_location_notify;
|
||||
ret = get_service_location(ATH10K_SERVICE_LOCATION_CLIENT_NAME,
|
||||
ATH10K_WLAN_SERVICE_NAME,
|
||||
&ar_snoc->get_service_nb);
|
||||
if (ret) {
|
||||
ath10k_err(ar, "Get service location failed: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
ath10k_err(ar, "PD restart not enabled: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar)
|
||||
{
|
||||
int i;
|
||||
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
|
||||
|
||||
for (i = 0; i < ar_snoc->total_domains; i++) {
|
||||
if (ar_snoc->service_notifier[i].handle)
|
||||
service_notif_unregister_notifier(
|
||||
ar_snoc->service_notifier[i].handle,
|
||||
&ar_snoc->service_notifier_nb);
|
||||
}
|
||||
|
||||
kfree(ar_snoc->service_notifier);
|
||||
|
||||
ar_snoc->service_notifier = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath10k_snoc_modem_notifier_nb(struct notifier_block *nb,
|
||||
unsigned long code,
|
||||
void *data)
|
||||
{
|
||||
struct notif_data *notif = data;
|
||||
struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
|
||||
modem_ssr_nb);
|
||||
struct ath10k *ar = ar_snoc->ar;
|
||||
|
||||
if (code != SUBSYS_BEFORE_SHUTDOWN)
|
||||
return NOTIFY_OK;
|
||||
|
||||
if (notif->crashed)
|
||||
atomic_set(&ar_snoc->fw_crashed, 1);
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "Modem went down %d\n",
|
||||
ar_snoc->fw_crashed);
|
||||
if (notif->crashed)
|
||||
queue_work(ar->workqueue, &ar->restart_work);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
|
||||
|
||||
ar_snoc->modem_ssr_nb.notifier_call = ath10k_snoc_modem_notifier_nb;
|
||||
|
||||
ar_snoc->modem_notify_handler =
|
||||
subsys_notif_register_notifier("modem", &ar_snoc->modem_ssr_nb);
|
||||
|
||||
if (IS_ERR(ar_snoc->modem_notify_handler)) {
|
||||
ret = PTR_ERR(ar_snoc->modem_notify_handler);
|
||||
ath10k_err(ar, "Modem register notifier failed: %d\n", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
|
||||
|
||||
subsys_notif_unregister_notifier(ar_snoc->modem_notify_handler,
|
||||
&ar_snoc->modem_ssr_nb);
|
||||
ar_snoc->modem_notify_handler = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
19
drivers/net/wireless/ath/ath10k/qmi.h
Normal file
19
drivers/net/wireless/ath/ath10k/qmi.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* Copyright (c) 2017, 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.
|
||||
*/
|
||||
#ifndef _QMI_H_
|
||||
#define _QMI_H_
|
||||
int ath10k_snoc_pd_restart_enable(struct ath10k *ar);
|
||||
int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar);
|
||||
int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar);
|
||||
int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar);
|
||||
|
||||
#endif
|
|
@ -24,6 +24,7 @@
|
|||
#include "htc.h"
|
||||
#include "ce.h"
|
||||
#include "snoc.h"
|
||||
#include "qmi.h"
|
||||
#include <soc/qcom/icnss.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -413,6 +414,20 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = {
|
|||
{ 11, WCN3990_DST_WR_INDEX_OFFSET},
|
||||
};
|
||||
|
||||
static bool ath10k_snoc_has_fw_crashed(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
|
||||
|
||||
return atomic_read(&ar_snoc->fw_crashed);
|
||||
}
|
||||
|
||||
static void ath10k_snoc_fw_crashed_clear(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
|
||||
|
||||
atomic_set(&ar_snoc->fw_crashed, 0);
|
||||
}
|
||||
|
||||
void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
|
||||
{
|
||||
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
|
||||
|
@ -655,6 +670,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
|
|||
"snoc tx item %d paddr %pad len %d n_items %d\n",
|
||||
i, &items[i].paddr, items[i].len, n_items);
|
||||
|
||||
if (ath10k_snoc_has_fw_crashed(ar))
|
||||
return -EINVAL;
|
||||
|
||||
err = ath10k_ce_send_nolock(ce_pipe,
|
||||
items[i].transfer_context,
|
||||
items[i].paddr,
|
||||
|
@ -867,11 +885,17 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar)
|
|||
{
|
||||
if (!ar)
|
||||
return;
|
||||
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
|
||||
ath10k_snoc_irq_disable(ar);
|
||||
if (ath10k_snoc_has_fw_crashed(ar) ||
|
||||
test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
|
||||
ath10k_snoc_free_irq(ar);
|
||||
} else {
|
||||
ath10k_snoc_irq_disable(ar);
|
||||
}
|
||||
|
||||
ath10k_snoc_flush(ar);
|
||||
napi_synchronize(&ar->napi);
|
||||
napi_disable(&ar->napi);
|
||||
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
|
||||
}
|
||||
|
||||
static int ath10k_snoc_alloc_pipes(struct ath10k *ar)
|
||||
|
@ -1087,9 +1111,14 @@ static int ath10k_snoc_bus_configure(struct ath10k *ar)
|
|||
|
||||
static int ath10k_snoc_hif_start(struct ath10k *ar)
|
||||
{
|
||||
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
|
||||
if (ath10k_snoc_has_fw_crashed(ar)) {
|
||||
ath10k_snoc_request_irq(ar);
|
||||
ath10k_snoc_fw_crashed_clear(ar);
|
||||
}
|
||||
ath10k_snoc_irq_enable(ar);
|
||||
ath10k_snoc_rx_post(ar);
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1110,7 +1139,8 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar)
|
|||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n",
|
||||
__func__, ar->state);
|
||||
|
||||
if (ar->state == ATH10K_STATE_ON) {
|
||||
if (ar->state == ATH10K_STATE_ON ||
|
||||
test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
|
||||
ret = ath10k_snoc_bus_configure(ar);
|
||||
if (ret)
|
||||
ath10k_err(ar, "failed to configure bus: %d\n", ret);
|
||||
|
@ -1133,6 +1163,10 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
|
|||
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
|
||||
int done = 0;
|
||||
|
||||
if (ath10k_snoc_has_fw_crashed(ar)) {
|
||||
napi_complete(ctx);
|
||||
return done;
|
||||
}
|
||||
ath10k_ce_per_engine_service_any(ar);
|
||||
|
||||
done = ath10k_htt_txrx_compl_task(ar, budget);
|
||||
|
@ -1254,6 +1288,10 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
|
|||
ath10k_err(ar, "failed to register driver core: %d\n", ret);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
ath10k_snoc_modem_ssr_register_notifier(ar);
|
||||
ath10k_snoc_pd_restart_enable(ar);
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 probed\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
@ -1282,6 +1320,8 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
|
||||
ath10k_core_unregister(ar);
|
||||
ath10k_snoc_pdr_unregister_notifier(ar);
|
||||
ath10k_snoc_modem_ssr_unregister_notifier(ar);
|
||||
ath10k_snoc_free_irq(ar);
|
||||
ath10k_snoc_release_resource(ar);
|
||||
ath10k_snoc_free_pipes(ar);
|
||||
|
|
|
@ -16,8 +16,11 @@
|
|||
#include "hw.h"
|
||||
#include "ce.h"
|
||||
#include "pci.h"
|
||||
#include <soc/qcom/service-locator.h>
|
||||
#define ATH10K_SNOC_RX_POST_RETRY_MS 50
|
||||
#define CE_POLL_PIPE 4
|
||||
#define ATH10K_SERVICE_LOCATION_CLIENT_NAME "ATH10K-WLAN"
|
||||
#define ATH10K_WLAN_SERVICE_NAME "wlan/fw"
|
||||
|
||||
/* struct snoc_state: SNOC target state
|
||||
* @pipe_cfg_addr: pipe configuration address
|
||||
|
@ -88,6 +91,17 @@ struct ath10k_target_info {
|
|||
u32 soc_version;
|
||||
};
|
||||
|
||||
/* struct ath10k_service_notifier_context: service notification context
|
||||
* @handle: notifier handle
|
||||
* @instance_id: domain instance id
|
||||
* @name: domain name
|
||||
*/
|
||||
struct ath10k_service_notifier_context {
|
||||
void *handle;
|
||||
u32 instance_id;
|
||||
char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
|
||||
};
|
||||
|
||||
/* struct ath10k_snoc: SNOC info struct
|
||||
* @dev: device structure
|
||||
* @ar:ath10k base structure
|
||||
|
@ -101,6 +115,13 @@ struct ath10k_target_info {
|
|||
* @rx_post_retry: rx buffer post processing timer
|
||||
* @vaddr_rri_on_ddr: virtual address for RRI
|
||||
* @is_driver_probed: flag to indicate driver state
|
||||
* @modem_ssr_nb: notifier callback for modem notification
|
||||
* @modem_notify_handler: modem notification handler
|
||||
* @service_notifier: notifier context for service notification
|
||||
* @service_notifier_nb: notifier callback for service notification
|
||||
* @total_domains: no of service domains
|
||||
* @get_service_nb: notifier callback for service discovery
|
||||
* @fw_crashed: fw state flag
|
||||
*/
|
||||
struct ath10k_snoc {
|
||||
struct bus_opaque opaque_ctx;
|
||||
|
@ -115,6 +136,13 @@ struct ath10k_snoc {
|
|||
u32 ce_irqs[CE_COUNT_MAX];
|
||||
u32 *vaddr_rri_on_ddr;
|
||||
bool is_driver_probed;
|
||||
struct notifier_block modem_ssr_nb;
|
||||
void *modem_notify_handler;
|
||||
struct ath10k_service_notifier_context *service_notifier;
|
||||
struct notifier_block service_notifier_nb;
|
||||
int total_domains;
|
||||
struct notifier_block get_service_nb;
|
||||
atomic_t fw_crashed;
|
||||
};
|
||||
|
||||
/* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration
|
||||
|
@ -171,6 +199,11 @@ struct ath10k_wlan_enable_cfg {
|
|||
struct ath10k_shadow_reg_cfg *shadow_reg_cfg;
|
||||
};
|
||||
|
||||
struct ath10k_event_pd_down_data {
|
||||
bool crashed;
|
||||
bool fw_rejuvenate;
|
||||
};
|
||||
|
||||
/* enum ath10k_driver_mode: ath10k driver mode
|
||||
* @ATH10K_MISSION: mission mode
|
||||
* @ATH10K_FTM: ftm mode
|
||||
|
|
|
@ -1799,6 +1799,9 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
|
|||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (cmd_id == WMI_CMD_UNSUPPORTED) {
|
||||
|
|
Loading…
Add table
Reference in a new issue