Merge changes into wlan-cmn.driver.lnx.2.0
diff --git a/hif/inc/hif.h b/hif/inc/hif.h
index de46bd4..7d190a1 100644
--- a/hif/inc/hif.h
+++ b/hif/inc/hif.h
@@ -465,8 +465,8 @@
* enum hif_device_config_opcode: configure mode
*
* @HIF_DEVICE_POWER_STATE: device power state
- * @HIF_DEVICE_GET_MBOX_BLOCK_SIZE: get mbox block size
- * @HIF_DEVICE_GET_MBOX_ADDR: get mbox block address
+ * @HIF_DEVICE_GET_BLOCK_SIZE: get block size
+ * @HIF_DEVICE_GET_ADDR: get block address
* @HIF_DEVICE_GET_PENDING_EVENTS_FUNC: get pending events functions
* @HIF_DEVICE_GET_IRQ_PROC_MODE: get irq proc mode
* @HIF_DEVICE_GET_RECV_EVENT_MASK_UNMASK_FUNC: receive event function
diff --git a/hif/src/dispatcher/multibus_sdio.c b/hif/src/dispatcher/multibus_sdio.c
index 3c220be..c5962a9 100644
--- a/hif/src/dispatcher/multibus_sdio.c
+++ b/hif/src/dispatcher/multibus_sdio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -25,8 +25,8 @@
#include "if_sdio.h"
/**
- * hif_initialize_sdio_ops() - initialize the pci ops
- * @bus_ops: hif_bus_ops table pointer to initialize
+ * hif_initialize_sdio_ops() - initialize the sdio ops
+ * @hif_sc: hif soft context
*
* Return: QDF_STATUS_SUCCESS
*/
diff --git a/hif/src/sdio/hif_diag_reg_access.c b/hif/src/sdio/hif_diag_reg_access.c
index 0f4d50c..30e3635 100644
--- a/hif/src/sdio/hif_diag_reg_access.c
+++ b/hif/src/sdio/hif_diag_reg_access.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -26,7 +26,7 @@
#include "hif.h"
#include "if_sdio.h"
#include "regtable_sdio.h"
-
+#include "hif_sdio_dev.h"
#include "qdf_module.h"
#define CPU_DBG_SEL_ADDRESS 0x00000483
diff --git a/hif/src/sdio/hif_sdio_common.h b/hif/src/sdio/hif_sdio_common.h
index dc0900d..21977f2 100644
--- a/hif/src/sdio/hif_sdio_common.h
+++ b/hif/src/sdio/hif_sdio_common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -26,11 +26,13 @@
#define MANUFACTURER_ID_AR6320_BASE 0x500
#define MANUFACTURER_ID_QCA9377_BASE 0x700
#define MANUFACTURER_ID_QCA9379_BASE 0x800
-#define MANUFACTURER_ID_QCN7605_BASE 0x0000 /*TODO - GenoaSDIO */
+#define MANUFACTURER_ID_QCN7605 0x400B
+#define MANUFACTURER_ID_QCN7605_BASE 0x4000
#define MANUFACTURER_ID_AR6K_BASE_MASK 0xFF00
#define MANUFACTURER_ID_AR6K_REV_MASK 0x00FF
#define FUNCTION_CLASS 0x0
-#define MANUFACTURER_CODE 0x271
+#define MANUFACTURER_CODE 0x271 /* Atheros Manufacturer ID */
+#define MANUFACTURER_QC_CODE 0x70 /* QC Manufacturer ID */
#endif /* _HIF_SDIO_COMMON_H_ */
diff --git a/hif/src/sdio/hif_sdio_dev.c b/hif/src/sdio/hif_sdio_dev.c
index 253cf27..ba2277a 100644
--- a/hif/src/sdio/hif_sdio_dev.c
+++ b/hif/src/sdio/hif_sdio_dev.c
@@ -208,127 +208,6 @@
return status;
}
-#define DEV_CHECK_RECV_YIELD(pdev) \
- ((pdev)->CurrentDSRRecvCount >= \
- (pdev)->HifIRQYieldParams.recv_packet_yield_count)
-
-/**
- * hif_dev_dsr_handler() - Synchronous interrupt handler
- *
- * @context: hif send context
- *
- * Return: 0 for success and non-zero for failure
- */
-QDF_STATUS hif_dev_dsr_handler(void *context)
-{
- struct hif_sdio_device *pdev = (struct hif_sdio_device *)context;
- QDF_STATUS status = QDF_STATUS_SUCCESS;
- bool done = false;
- bool async_proc = false;
-
- HIF_ENTER();
-
- /* reset the recv counter that tracks when we need
- * to yield from the DSR
- */
- pdev->CurrentDSRRecvCount = 0;
- /* reset counter used to flag a re-scan of IRQ
- * status registers on the target
- */
- pdev->RecheckIRQStatusCnt = 0;
-
- while (!done) {
- status = hif_dev_process_pending_irqs(pdev, &done, &async_proc);
- if (QDF_IS_STATUS_ERROR(status))
- break;
-
- if (pdev->HifIRQProcessingMode == HIF_DEVICE_IRQ_SYNC_ONLY) {
- /* the HIF layer does not allow async IRQ processing,
- * override the asyncProc flag
- */
- async_proc = false;
- /* this will cause us to re-enter ProcessPendingIRQ()
- * and re-read interrupt status registers.
- * This has a nice side effect of blocking us until all
- * async read requests are completed. This behavior is
- * required as we do not allow ASYNC processing
- * in interrupt handlers (like Windows CE)
- */
-
- if (pdev->DSRCanYield && DEV_CHECK_RECV_YIELD(pdev))
- /* ProcessPendingIRQs() pulled enough recv
- * messages to satisfy the yield count, stop
- * checking for more messages and return
- */
- break;
- }
-
- if (async_proc) {
- /* the function does some async I/O for performance,
- * we need to exit the ISR immediately, the check below
- * will prevent the interrupt from being
- * Ack'd while we handle it asynchronously
- */
- break;
- }
- }
-
- if (QDF_IS_STATUS_SUCCESS(status) && !async_proc) {
- /* Ack the interrupt only if :
- * 1. we did not get any errors in processing interrupts
- * 2. there are no outstanding async processing requests
- */
- if (pdev->DSRCanYield) {
- /* if the DSR can yield do not ACK the interrupt, there
- * could be more pending messages. The HIF layer
- * must ACK the interrupt on behalf of HTC
- */
- HIF_INFO("%s: Yield (RX count: %d)",
- __func__, pdev->CurrentDSRRecvCount);
- } else {
- HIF_INFO("%s: Ack interrupt", __func__);
- hif_ack_interrupt(pdev->HIFDevice);
- }
- }
-
- HIF_EXIT();
- return status;
-}
-
-/** hif_dev_set_mailbox_swap() - Set the mailbox swap from firmware
- * @pdev : The HIF layer object
- *
- * Return: none
- */
-void hif_dev_set_mailbox_swap(struct hif_sdio_dev *pdev)
-{
- struct hif_sdio_device *hif_device = hif_dev_from_hif(pdev);
-
- HIF_ENTER();
-
- hif_device->swap_mailbox = true;
-
- HIF_EXIT();
-}
-
-/** hif_dev_get_mailbox_swap() - Get the mailbox swap setting
- * @pdev : The HIF layer object
- *
- * Return: none
- */
-bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev)
-{
- struct hif_sdio_device *hif_device;
-
- HIF_ENTER();
-
- hif_device = hif_dev_from_hif(pdev);
-
- HIF_EXIT();
-
- return hif_device->swap_mailbox;
-}
-
/**
* hif_dev_setup() - set up sdio device.
* @pDev: sdio device context
@@ -346,7 +225,6 @@
status = hif_dev_setup_device(pdev);
-
if (status != QDF_STATUS_SUCCESS) {
HIF_ERROR("%s: device specific setup failed", __func__);
return QDF_STATUS_E_INVAL;
diff --git a/hif/src/sdio/hif_sdio_dev.h b/hif/src/sdio/hif_sdio_dev.h
index 5dbe682..7ee6582 100644
--- a/hif/src/sdio/hif_sdio_dev.h
+++ b/hif/src/sdio/hif_sdio_dev.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016, 2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, 2018-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -26,6 +26,7 @@
#include <hif.h>
#include "athstartpack.h"
#include "hif_internal.h"
+#include "if_sdio.h"
struct hif_sdio_device *hif_dev_from_hif(struct hif_sdio_dev *hif_device);
@@ -47,7 +48,7 @@
QDF_STATUS hif_dev_process_pending_irqs(struct hif_sdio_device *pdev,
bool *done,
- bool *async_processing);
+ bool *async_processing);
void hif_dev_mask_interrupts(struct hif_sdio_device *pdev);
@@ -60,9 +61,9 @@
int hif_dev_setup_device(struct hif_sdio_device *pdev);
-QDF_STATUS hif_dev_get_fifo_address(struct hif_sdio_dev *pdev,
- struct hif_device_mbox_info *config,
- uint32_t config_len);
+int hif_dev_get_fifo_address(struct hif_sdio_dev *pdev,
+ void *config,
+ uint32_t config_len);
void hif_dev_get_block_size(void *config);
@@ -70,8 +71,93 @@
bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev);
-int hif_sdio_set_drvdata(struct sdio_func *func,
- struct hif_sdio_dev *hifdevice);
+QDF_STATUS hif_read_write(struct hif_sdio_dev *device, unsigned long address,
+ char *buffer, uint32_t length, uint32_t request,
+ void *context);
-struct hif_sdio_dev *get_hif_device(struct sdio_func *func);
+#ifdef CONFIG_SDIO_TRANSFER_MAILBOX
+static inline struct hif_sdio_dev *get_hif_device(struct hif_softc *hif_ctx,
+ struct sdio_func *func)
+{
+ qdf_assert(func);
+ return (struct hif_sdio_dev *)sdio_get_drvdata(func);
+}
+
+/**
+ * hif_sdio_set_drvdata() - set wlan driver data into upper layer private
+ * @hif_ctx: HIF object
+ * @func: pointer to sdio function
+ * @hifdevice: pointer to hif device
+ *
+ * Return: zero for success.
+ */
+static inline int hif_sdio_set_drvdata(struct hif_softc *hif_ctx,
+ struct sdio_func *func,
+ struct hif_sdio_dev *hifdevice)
+{
+ sdio_set_drvdata(func, hifdevice);
+ return 0;
+}
+
+static inline int hif_dev_configure_pipes(struct hif_sdio_dev *pdev,
+ struct sdio_func *func)
+{
+ return 0;
+}
+
+static inline int hif_dev_register_channels(struct hif_sdio_dev *dev,
+ struct sdio_func *func)
+{
+ return 0;
+}
+
+static inline void hif_dev_unregister_channels(struct hif_sdio_dev *dev,
+ struct sdio_func *func)
+{
+}
+#else
+static inline struct hif_sdio_dev *get_hif_device(struct hif_softc *hif_ctx,
+ struct sdio_func *func)
+{
+ struct hif_sdio_softc *scn = (struct hif_sdio_softc *)hif_ctx;
+
+ return (struct hif_sdio_dev *)scn->hif_handle;
+}
+
+/**
+ * hif_sdio_set_drvdata() - set wlan driver data into upper layer private
+ * @hif_ctx: HIF object
+ * @func: pointer to sdio function
+ * @hifdevice: pointer to hif device
+ *
+ * Return: zero for success.
+ */
+static inline int hif_sdio_set_drvdata(struct hif_softc *hif_ctx,
+ struct sdio_func *func,
+ struct hif_sdio_dev *hifdevice)
+{
+ struct hif_sdio_softc *sc = (struct hif_sdio_softc *)hif_ctx;
+
+ sc->hif_handle = hifdevice;
+
+ return 0;
+}
+
+int hif_dev_configure_pipes(struct hif_sdio_dev *pdev,
+ struct sdio_func *func);
+
+int hif_dev_register_channels(struct hif_sdio_dev *dev,
+ struct sdio_func *func);
+
+void hif_dev_unregister_channels(struct hif_sdio_dev *dev,
+ struct sdio_func *func);
+#endif /* SDIO_TRANSFER */
+QDF_STATUS hif_enable_func(struct hif_softc *ol_sc, struct hif_sdio_dev *device,
+ struct sdio_func *func, bool resume);
+QDF_STATUS hif_disable_func(struct hif_sdio_dev *device,
+ struct sdio_func *func,
+ bool reset);
+A_STATUS hif_sdio_probe(struct hif_softc *ol_sc,
+ struct sdio_func *func,
+ struct hif_sdio_dev *device);
#endif /* HIF_SDIO_DEV_H_ */
diff --git a/hif/src/sdio/hif_sdio_internal.h b/hif/src/sdio/hif_sdio_internal.h
index 67c8ebf..b378575 100644
--- a/hif/src/sdio/hif_sdio_internal.h
+++ b/hif/src/sdio/hif_sdio_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -28,7 +28,7 @@
#if defined(CONFIG_SDIO_TRANSFER_MAILBOX)
#include <transfer/mailbox.h>
#elif defined(CONFIG_SDIO_TRANSFER_ADMA)
-#error "Error - Not implemented yet"
+#include <transfer/adma.h>
#else
#error "Error - Invalid transfer method"
#endif
@@ -54,7 +54,6 @@
qdf_spinlock_t TxLock;
qdf_spinlock_t RxLock;
struct hif_msg_callbacks hif_callbacks;
- struct hif_device_mbox_info MailBoxInfo;
uint32_t BlockSize;
uint32_t BlockMask;
enum hif_device_irq_mode HifIRQProcessingMode;
@@ -65,8 +64,11 @@
int RecheckIRQStatusCnt;
uint32_t RecvStateFlags;
void *pTarget;
- bool swap_mailbox;
struct devRegisters devRegisters;
+#ifdef CONFIG_SDIO_TRANSFER_MAILBOX
+ bool swap_mailbox;
+ struct hif_device_mbox_info MailBoxInfo;
+#endif
};
#define LOCK_HIF_DEV(device) qdf_spin_lock(&(device)->Lock)
diff --git a/hif/src/sdio/if_sdio.c b/hif/src/sdio/if_sdio.c
index 0b98d0b..102b84d 100644
--- a/hif/src/sdio/if_sdio.c
+++ b/hif/src/sdio/if_sdio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -181,7 +181,7 @@
{
struct sdio_func *func = dev_to_sdio_func(hif_sc->qdf_dev->dev);
- hif_sdio_device_removed(func);
+ hif_sdio_device_removed(hif_sc, func);
}
/**
diff --git a/hif/src/sdio/if_sdio.h b/hif/src/sdio/if_sdio.h
index 1891c8b..191ea61 100644
--- a/hif/src/sdio/if_sdio.h
+++ b/hif/src/sdio/if_sdio.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -83,7 +83,7 @@
const struct sdio_device_id *id);
void hif_sdio_stop(struct hif_softc *hif_ctx);
void hif_sdio_shutdown(struct hif_softc *hif_ctx);
-void hif_sdio_device_removed(struct sdio_func *func);
+void hif_sdio_device_removed(struct hif_softc *hif_ctx, struct sdio_func *func);
int hif_device_suspend(struct hif_softc *ol_sc, struct device *dev);
int hif_device_resume(struct hif_softc *ol_sc, struct device *dev);
void hif_register_tbl_attach(struct hif_softc *scn,
diff --git a/hif/src/sdio/native_sdio/include/hif_internal.h b/hif/src/sdio/native_sdio/include/hif_internal.h
index 8f2719a..41001c8 100644
--- a/hif/src/sdio/native_sdio/include/hif_internal.h
+++ b/hif/src/sdio/native_sdio/include/hif_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -35,6 +35,7 @@
#include <qdf_status.h>
#include <qdf_timer.h>
#include <qdf_atomic.h>
+#include <qdf_list.h>
#include "hif.h"
#include "hif_debug.h"
#include "hif_sdio_common.h"
@@ -167,7 +168,7 @@
struct bus_request *next; /* link list of available requests */
struct bus_request *inusenext; /* link list of in use requests */
struct semaphore sem_req;
- uint32_t address; /* request data */
+ unsigned long address; /* request data */
char *buffer;
uint32_t length;
uint32_t request;
@@ -176,6 +177,14 @@
struct HIF_SCATTER_REQ_PRIV *scatter_req;
};
+#define HIF_ADMA_MAX_CHANS 2
+#ifdef CONFIG_SDIO_TRANSFER_ADMA
+struct rx_q_entry {
+ qdf_list_node_t entry;
+ qdf_nbuf_t nbuf;
+};
+#endif
+
struct hif_sdio_dev {
struct sdio_func *func;
qdf_spinlock_t asynclock;
@@ -201,6 +210,15 @@
const struct sdio_device_id *id;
struct mmc_host *host;
void *htc_context;
+#ifdef CONFIG_SDIO_TRANSFER_ADMA
+ struct sdio_al_client_handle *al_client;
+ struct sdio_al_channel_handle *al_chan[HIF_ADMA_MAX_CHANS];
+ uint8_t adma_chans_used;
+ qdf_list_t rx_q;
+ qdf_spinlock_t rx_q_lock;
+ qdf_work_t rx_q_alloc_work;
+ bool rx_q_alloc_work_scheduled;
+#endif
};
struct HIF_DEVICE_OS_DEVICE_INFO {
@@ -270,18 +288,13 @@
QDF_STATUS hif_attach_htc(struct hif_sdio_dev *device,
struct htc_callbacks *callbacks);
-QDF_STATUS hif_read_write(struct hif_sdio_dev *device,
- uint32_t address,
- char *buffer,
- uint32_t length, uint32_t request, void *context);
-
void hif_ack_interrupt(struct hif_sdio_dev *device);
void hif_mask_interrupt(struct hif_sdio_dev *device);
void hif_un_mask_interrupt(struct hif_sdio_dev *device);
-void hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func);
+int hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func);
struct _HIF_SCATTER_ITEM {
u_int8_t *buffer; /* CPU accessible address of buffer */
@@ -400,14 +413,14 @@
#define SDIO_SET_CMD52_WRITE_ARG(arg, func, address, value) \
SDIO_SET_CMD52_ARG(arg, 1, (func), 0, address, value)
-void hif_sdio_quirk_force_drive_strength(struct sdio_func *func);
-void hif_sdio_quirk_write_cccr(struct sdio_func *func);
-int hif_sdio_quirk_mod_strength(struct sdio_func *func);
-int hif_sdio_quirk_async_intr(struct sdio_func *func);
-int hif_sdio_set_bus_speed(struct sdio_func *func);
-int hif_sdio_set_bus_width(struct sdio_func *func);
-int hif_sdio_func_enable(struct hif_sdio_dev *device,
- struct sdio_func *func);
+void hif_sdio_quirk_force_drive_strength(struct hif_softc *ol_sc,
+ struct sdio_func *func);
+void hif_sdio_quirk_write_cccr(struct hif_softc *ol_sc, struct sdio_func *func);
+int hif_sdio_quirk_mod_strength(struct hif_softc *ol_sc,
+ struct sdio_func *func);
+int hif_sdio_quirk_async_intr(struct hif_softc *ol_sc, struct sdio_func *func);
+int hif_sdio_set_bus_speed(struct hif_softc *ol_sc, struct sdio_func *func);
+int hif_sdio_set_bus_width(struct hif_softc *ol_sc, struct sdio_func *func);
QDF_STATUS hif_sdio_func_disable(struct hif_sdio_dev *device,
struct sdio_func *func,
bool reset);
diff --git a/hif/src/sdio/native_sdio/src/dev_quirks.c b/hif/src/sdio/native_sdio/src/dev_quirks.c
index b84f1ab..7c7e63b 100644
--- a/hif/src/sdio/native_sdio/src/dev_quirks.c
+++ b/hif/src/sdio/native_sdio/src/dev_quirks.c
@@ -1,8 +1,5 @@
/*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
- *
- *
- *
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -91,8 +88,10 @@
MODULE_PARM_DESC(brokenirq,
"Set as 1 to use polling method instead of interrupt mode");
+#ifdef CONFIG_SDIO_TRANSFER_MAILBOX
/**
* hif_sdio_force_drive_strength() - Set SDIO drive strength
+ * @ol_sc: softc instance
* @func: pointer to sdio_func
*
* This function forces the driver strength of the SDIO
@@ -100,61 +99,156 @@
*
* Return: none.
*/
-void hif_sdio_quirk_force_drive_strength(struct sdio_func *func)
+void hif_sdio_quirk_force_drive_strength(struct hif_softc *ol_sc,
+ struct sdio_func *func)
{
int err = 0;
unsigned char value = 0;
uint32_t mask = 0, addr = SDIO_CCCR_DRIVE_STRENGTH;
- struct hif_sdio_dev *device = sdio_get_drvdata(func);
- uint16_t manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
-
- switch (manfid) {
- case MANUFACTURER_ID_QCN7605_BASE:
- break;
- default:
- err = func0_cmd52_read_byte(func->card, addr, &value);
- if (err) {
- HIF_ERROR("%s: read driver strength 0x%02X fail %d\n",
- __func__, addr, err);
- break;
- }
-
- mask = (SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT);
- value = (value & ~mask) | SDIO_DTSx_SET_TYPE_D;
- err = func0_cmd52_write_byte(func->card, addr, value);
- if (err) {
- HIF_ERROR("%s: write driver strength failed", __func__);
- HIF_ERROR("%s: 0x%02X to 0x%02X failed: %d\n", __func__,
- (uint32_t)value, addr, err);
- break;
- }
-
- value = 0;
- addr = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR;
- err = func0_cmd52_read_byte(func->card, addr, &value);
- if (err) {
- HIF_ERROR("%s Read CCCR 0x%02X failed: %d\n",
- __func__, addr, err);
- break;
- }
-
- mask = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_MASK;
- value = (value & ~mask) |
- CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
- CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C |
- CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D;
- err = func0_cmd52_write_byte(func->card, addr, value);
- if (err)
- HIF_ERROR("%s Write CCCR 0x%02X to 0x%02X failed: %d\n",
- __func__, addr, value, err);
-
- break;
+ err = func0_cmd52_read_byte(func->card, addr, &value);
+ if (err) {
+ HIF_ERROR("%s: read driver strength 0x%02X fail %d\n",
+ __func__, addr, err);
+ return;
}
+
+ mask = (SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT);
+ value = (value & ~mask) | SDIO_DTSx_SET_TYPE_D;
+ err = func0_cmd52_write_byte(func->card, addr, value);
+ if (err) {
+ HIF_ERROR("%s: write driver strength failed", __func__);
+ HIF_ERROR("%s: 0x%02X to 0x%02X failed: %d\n", __func__,
+ (uint32_t)value, addr, err);
+ return;
+ }
+
+ value = 0;
+ addr = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR;
+ err = func0_cmd52_read_byte(func->card, addr, &value);
+ if (err) {
+ HIF_ERROR("%s Read CCCR 0x%02X failed: %d\n",
+ __func__, addr, err);
+ return;
+ }
+
+ mask = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_MASK;
+ value = (value & ~mask) | CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
+ CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C |
+ CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D;
+ err = func0_cmd52_write_byte(func->card, addr, value);
+ if (err)
+ HIF_ERROR("%s Write CCCR 0x%02X to 0x%02X failed: %d\n",
+ __func__, addr, value, err);
}
/**
+ * hif_sdio_quirk_async_intr() - Set asynchronous interrupt settings
+ * @ol_sc: softc instance
+ * @func: pointer to sdio_func
+ *
+ * The values are taken from the module parameter asyncintdelay
+ * Call this with the sdhci host claimed
+ *
+ * Return: none.
+ */
+int hif_sdio_quirk_async_intr(struct hif_softc *ol_sc, struct sdio_func *func)
+{
+ uint8_t data;
+ uint16_t manfid;
+ int set_async_irq = 0, ret = 0;
+ struct hif_sdio_dev *device = get_hif_device(ol_sc, func);
+
+ manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
+
+ switch (manfid) {
+ case MANUFACTURER_ID_AR6003_BASE:
+ set_async_irq = 1;
+ ret =
+ func0_cmd52_write_byte(func->card,
+ CCCR_SDIO_IRQ_MODE_REG_AR6003,
+ SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6003);
+ if (ret)
+ return ret;
+ break;
+ case MANUFACTURER_ID_AR6320_BASE:
+ case MANUFACTURER_ID_QCA9377_BASE:
+ case MANUFACTURER_ID_QCA9379_BASE:
+ set_async_irq = 1;
+ ret = func0_cmd52_read_byte(func->card,
+ CCCR_SDIO_IRQ_MODE_REG_AR6320,
+ &data);
+ if (ret)
+ return ret;
+
+ data |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6320;
+ ret = func0_cmd52_write_byte(func->card,
+ CCCR_SDIO_IRQ_MODE_REG_AR6320,
+ data);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ if (asyncintdelay) {
+ /* Set CCCR 0xF0[7:6] to increase async interrupt delay clock
+ * to fix interrupt missing issue on dell 8460p
+ */
+
+ ret = func0_cmd52_read_byte(func->card,
+ CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
+ &data);
+ if (ret)
+ return ret;
+
+ data = (data & ~CCCR_SDIO_ASYNC_INT_DELAY_MASK) |
+ ((asyncintdelay << CCCR_SDIO_ASYNC_INT_DELAY_LSB) &
+ CCCR_SDIO_ASYNC_INT_DELAY_MASK);
+
+ ret = func0_cmd52_write_byte(func->card,
+ CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
+ data);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+#else
+/**
+ * hif_sdio_force_drive_strength() - Set SDIO drive strength
+ * @ol_sc: softc instance
+ * @func: pointer to sdio_func
+ *
+ * This function forces the driver strength of the SDIO
+ * Call this with the sdhci host claimed
+ *
+ * Return: none.
+ */
+void hif_sdio_quirk_force_drive_strength(struct hif_softc *ol_sc,
+ struct sdio_func *func)
+{
+}
+
+/**
+ * hif_sdio_quirk_async_intr() - Set asynchronous interrupt settings
+ * @ol_sc: softc instance
+ * @func: pointer to sdio_func
+ *
+ * The values are taken from the module parameter asyncintdelay
+ * Call this with the sdhci host claimed
+ *
+ * Return: none.
+ */
+int hif_sdio_quirk_async_intr(struct hif_softc *ol_sc, struct sdio_func *func)
+{
+ return 0;
+}
+#endif
+
+/**
* hif_sdio_quirk_write_cccr() - write a desired CCCR register
+ * @ol_sc: softc instance
* @func: pointer to sdio_func
*
* The values are taken from the module parameter writecccr
@@ -162,7 +256,7 @@
*
* Return: none.
*/
-void hif_sdio_quirk_write_cccr(struct sdio_func *func)
+void hif_sdio_quirk_write_cccr(struct hif_softc *ol_sc, struct sdio_func *func)
{
int32_t err;
@@ -231,6 +325,7 @@
/**
* hif_sdio_quirk_mod_strength() - write a desired CCCR register
+ * @ol_sc: softc instance
* @func: pointer to sdio_func
*
* The values are taken from the module parameter writecccr
@@ -238,11 +333,11 @@
*
* Return: none.
*/
-int hif_sdio_quirk_mod_strength(struct sdio_func *func)
+int hif_sdio_quirk_mod_strength(struct hif_softc *ol_sc, struct sdio_func *func)
{
int ret = 0;
uint32_t addr, value;
- struct hif_sdio_dev *device = sdio_get_drvdata(func);
+ struct hif_sdio_dev *device = get_hif_device(ol_sc, func);
uint16_t manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
if (!modstrength) /* TODO: Dont set this : scn is not popolated yet */
@@ -287,82 +382,6 @@
return ret;
}
-/**
- * hif_sdio_quirk_async_intr() - Set asynchronous interrupt settings
- * @func: pointer to sdio_func
- *
- * The values are taken from the module parameter asyncintdelay
- * Call this with the sdhci host claimed
- *
- * Return: none.
- */
-int hif_sdio_quirk_async_intr(struct sdio_func *func)
-{
- uint8_t data;
- uint16_t manfid;
- int set_async_irq = 0, ret = 0;
- struct hif_sdio_dev *device = sdio_get_drvdata(func);
-
- manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
-
- switch (manfid) {
- case MANUFACTURER_ID_AR6003_BASE:
- set_async_irq = 1;
- ret =
- func0_cmd52_write_byte(func->card,
- CCCR_SDIO_IRQ_MODE_REG_AR6003,
- SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6003);
- if (ret)
- return ret;
- break;
- case MANUFACTURER_ID_AR6320_BASE:
- case MANUFACTURER_ID_QCA9377_BASE:
- case MANUFACTURER_ID_QCA9379_BASE:
- set_async_irq = 1;
- ret = func0_cmd52_read_byte(func->card,
- CCCR_SDIO_IRQ_MODE_REG_AR6320,
- &data);
- if (ret)
- return ret;
-
- data |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6320;
- ret = func0_cmd52_write_byte(func->card,
- CCCR_SDIO_IRQ_MODE_REG_AR6320,
- data);
- if (ret)
- return ret;
- break;
- case MANUFACTURER_ID_QCN7605_BASE:
- /* No async intr delay settings */
- asyncintdelay = 0;
- return ret;
- }
-
- if (asyncintdelay) {
- /* Set CCCR 0xF0[7:6] to increase async interrupt delay clock
- * to fix interrupt missing issue on dell 8460p
- */
-
- ret = func0_cmd52_read_byte(func->card,
- CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
- &data);
- if (ret)
- return ret;
-
- data = (data & ~CCCR_SDIO_ASYNC_INT_DELAY_MASK) |
- ((asyncintdelay << CCCR_SDIO_ASYNC_INT_DELAY_LSB) &
- CCCR_SDIO_ASYNC_INT_DELAY_MASK);
-
- ret = func0_cmd52_write_byte(func->card,
- CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
- data);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
#if KERNEL_VERSION(3, 4, 0) <= LINUX_VERSION_CODE
#ifdef SDIO_BUS_WIDTH_8BIT
static int hif_cmd52_write_byte_8bit(struct sdio_func *func)
@@ -381,14 +400,15 @@
/**
* hif_sdio_set_bus_speed() - Set the sdio bus speed
+ * @ol_sc: softc instance
* @func: pointer to sdio_func
*
* Return: 0 on success, error number otherwise.
*/
-int hif_sdio_set_bus_speed(struct sdio_func *func)
+int hif_sdio_set_bus_speed(struct hif_softc *ol_sc, struct sdio_func *func)
{
uint32_t clock, clock_set = 12500000;
- struct hif_sdio_dev *device = get_hif_device(func);
+ struct hif_sdio_dev *device = get_hif_device(ol_sc, func);
uint16_t manfid;
manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
@@ -427,16 +447,17 @@
/**
* hif_set_bus_width() - Set the sdio bus width
+ * @ol_sc: softc instance
* @func: pointer to sdio_func
*
* Return: 0 on success, error number otherwise.
*/
-int hif_sdio_set_bus_width(struct sdio_func *func)
+int hif_sdio_set_bus_width(struct hif_softc *ol_sc, struct sdio_func *func)
{
int ret = 0;
uint16_t manfid;
uint8_t data = 0;
- struct hif_sdio_dev *device = get_hif_device(func);
+ struct hif_sdio_dev *device = get_hif_device(ol_sc, func);
manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
@@ -491,66 +512,6 @@
return ret;
}
-/**
- * hif_sdio_func_enable() - Handle device enabling as per device
- * @device: HIF device object
- * @func: function pointer
- *
- * Return success or failure
- */
-int hif_sdio_func_enable(struct hif_sdio_dev *device,
- struct sdio_func *func)
-{
- uint16_t manfid;
-
- manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
-
- if (manfid == MANUFACTURER_ID_QCN7605_BASE)
- return 0;
-
- if (device->is_disabled) {
- int ret = 0;
-
- sdio_claim_host(func);
-
- ret = hif_sdio_quirk_async_intr(func);
- if (ret) {
- HIF_ERROR("%s: Error setting async intr:%d",
- __func__, ret);
- sdio_release_host(func);
- return QDF_STATUS_E_FAILURE;
- }
-
- func->enable_timeout = 100;
- ret = sdio_enable_func(func);
- if (ret) {
- HIF_ERROR("%s: Unable to enable function: %d",
- __func__, ret);
- sdio_release_host(func);
- return QDF_STATUS_E_FAILURE;
- }
-
- ret = sdio_set_block_size(func, HIF_BLOCK_SIZE);
- if (ret) {
- HIF_ERROR("%s: Unable to set block size 0x%X : %d\n",
- __func__, HIF_BLOCK_SIZE, ret);
- sdio_release_host(func);
- return QDF_STATUS_E_FAILURE;
- }
-
- ret = hif_sdio_quirk_mod_strength(func);
- if (ret) {
- HIF_ERROR("%s: Error setting mod strength : %d\n",
- __func__, ret);
- sdio_release_host(func);
- return QDF_STATUS_E_FAILURE;
- }
-
- sdio_release_host(func);
- }
-
- return 0;
-}
/**
* hif_mask_interrupt() - Disable hif device irq
@@ -594,11 +555,7 @@
*/
static void hif_irq_handler(struct sdio_func *func)
{
- struct hif_sdio_dev *device;
-
- HIF_ENTER();
-
- device = get_hif_device(func);
+ struct hif_sdio_dev *device = get_hif_device(NULL, func);
atomic_set(&device->irq_handling, 1);
/* release the host during intr so we can use
* it when we process cmds
@@ -607,8 +564,6 @@
device->htc_callbacks.dsr_handler(device->htc_callbacks.context);
sdio_claim_host(device->func);
atomic_set(&device->irq_handling, 0);
-
- HIF_EXIT();
}
/**
diff --git a/hif/src/sdio/native_sdio/src/hif.c b/hif/src/sdio/native_sdio/src/hif.c
index 926067c..7100baf 100644
--- a/hif/src/sdio/native_sdio/src/hif.c
+++ b/hif/src/sdio/native_sdio/src/hif.c
@@ -23,7 +23,6 @@
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sd.h>
-#include <linux/kthread.h>
#include <linux/version.h>
#include <linux/module.h>
#include <qdf_atomic.h>
@@ -38,26 +37,10 @@
#include "hif_internal.h"
#include <transfer/transfer.h>
-/* by default setup a bounce buffer for the data packets,
- * if the underlying host controller driver
- * does not use DMA you may be able to skip this step
- * and save the memory allocation and transfer time
- */
#define HIF_USE_DMA_BOUNCE_BUFFER 1
#define ATH_MODULE_NAME hif
#include "a_debug.h"
-#if HIF_USE_DMA_BOUNCE_BUFFER
-/* macro to check if DMA buffer is WORD-aligned and DMA-able.
- * Most host controllers assume the
- * buffer is DMA'able and will bug-check otherwise (i.e. buffers on the stack).
- * virt_addr_valid check fails on stack memory.
- */
-#define BUFFER_NEEDS_BOUNCE(buffer) (((unsigned long)(buffer) & 0x3) || \
- !virt_addr_valid((buffer)))
-#else
-#define BUFFER_NEEDS_BOUNCE(buffer) (false)
-#endif
#define MAX_HIF_DEVICES 2
#ifdef HIF_MBOX_SLEEP_WAR
#define HIF_MIN_SLEEP_INACTIVITY_TIME_MS 50
@@ -83,7 +66,8 @@
#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
-static struct hif_sdio_dev *add_hif_device(struct sdio_func *func);
+static struct hif_sdio_dev *add_hif_device(struct hif_softc *hif_ctx,
+ struct sdio_func *func);
static void del_hif_device(struct hif_sdio_dev *device);
int reset_sdio_on_unload;
@@ -172,186 +156,6 @@
#endif
/**
- * __hif_read_write() - sdio read/write wrapper
- * @device: pointer to hif device structure
- * @address: address to read
- * @buffer: buffer to hold read/write data
- * @length: length to read/write
- * @request: read/write/sync/async request
- * @context: pointer to hold calling context
- *
- * Return: 0 on success, error number otherwise.
- */
-static QDF_STATUS
-__hif_read_write(struct hif_sdio_dev *device,
- uint32_t address, char *buffer,
- uint32_t length, uint32_t request, void *context)
-{
- uint8_t opcode;
- QDF_STATUS status = QDF_STATUS_SUCCESS;
- int ret = A_OK;
- uint8_t *tbuffer;
- bool bounced = false;
-
- if (!device) {
- HIF_ERROR("%s: device null!", __func__);
- return QDF_STATUS_E_INVAL;
- }
-
- if (!device->func) {
- HIF_ERROR("%s: func null!", __func__);
- return QDF_STATUS_E_INVAL;
- }
-
- HIF_INFO_HI("%s: addr:0X%06X, len:%08d, %s, %s", __func__,
- address, length,
- request & HIF_SDIO_READ ? "Read " : "Write",
- request & HIF_ASYNCHRONOUS ? "Async" : "Sync ");
-
- do {
- if (request & HIF_EXTENDED_IO) {
- HIF_INFO_HI("%s: Command type: CMD53\n", __func__);
- } else {
- HIF_ERROR("%s: Invalid command type: 0x%08x\n",
- __func__, request);
- status = QDF_STATUS_E_INVAL;
- break;
- }
-
- if (request & HIF_BLOCK_BASIS) {
- /* round to whole block length size */
- length =
- (length / HIF_BLOCK_SIZE) *
- HIF_BLOCK_SIZE;
- HIF_INFO_HI("%s: Block mode (BlockLen: %d)\n",
- __func__, length);
- } else if (request & HIF_BYTE_BASIS) {
- HIF_INFO_HI("%s: Byte mode (BlockLen: %d)\n",
- __func__, length);
- } else {
- HIF_ERROR("%s: Invalid data mode: 0x%08x\n",
- __func__, request);
- status = QDF_STATUS_E_INVAL;
- break;
- }
- if (request & HIF_SDIO_WRITE) {
- hif_fixup_write_param(device, request,
- &length, &address);
-
- HIF_INFO_HI("addr:%08X, len:0x%08X, dummy:0x%04X\n",
- address, length,
- (request & HIF_DUMMY_SPACE_MASK) >> 16);
- }
-
- if (request & HIF_FIXED_ADDRESS) {
- opcode = CMD53_FIXED_ADDRESS;
- HIF_INFO_HI("%s: Addr mode: fixed 0x%X\n",
- __func__, address);
- } else if (request & HIF_INCREMENTAL_ADDRESS) {
- opcode = CMD53_INCR_ADDRESS;
- HIF_INFO_HI("%s: Address mode: Incremental 0x%X\n",
- __func__, address);
- } else {
- HIF_ERROR("%s: Invalid address mode: 0x%08x\n",
- __func__, request);
- status = QDF_STATUS_E_INVAL;
- break;
- }
-
- if (request & HIF_SDIO_WRITE) {
-#if HIF_USE_DMA_BOUNCE_BUFFER
- if (BUFFER_NEEDS_BOUNCE(buffer)) {
- AR_DEBUG_ASSERT(device->dma_buffer);
- tbuffer = device->dma_buffer;
- /* copy the write data to the dma buffer */
- AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE);
- if (length > HIF_DMA_BUFFER_SIZE) {
- HIF_ERROR("%s: Invalid write len: %d\n",
- __func__, length);
- status = QDF_STATUS_E_INVAL;
- break;
- }
- memcpy(tbuffer, buffer, length);
- bounced = true;
- } else {
- tbuffer = buffer;
- }
-#else
- tbuffer = buffer;
-#endif
- if (opcode == CMD53_FIXED_ADDRESS && tbuffer) {
- ret = sdio_writesb(device->func, address,
- tbuffer, length);
- HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
- __func__, ret, address, length,
- *(int *)tbuffer);
- } else if (tbuffer) {
- ret = sdio_memcpy_toio(device->func, address,
- tbuffer, length);
- HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
- __func__, ret, address, length,
- *(int *)tbuffer);
- }
- } else if (request & HIF_SDIO_READ) {
-#if HIF_USE_DMA_BOUNCE_BUFFER
- if (BUFFER_NEEDS_BOUNCE(buffer)) {
- AR_DEBUG_ASSERT(device->dma_buffer);
- AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE);
- if (length > HIF_DMA_BUFFER_SIZE) {
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
- ("%s: Invalid read length: %d\n",
- __func__, length));
- status = QDF_STATUS_E_INVAL;
- break;
- }
- tbuffer = device->dma_buffer;
- bounced = true;
- } else {
- tbuffer = buffer;
- }
-#else
- tbuffer = buffer;
-#endif
- if (opcode == CMD53_FIXED_ADDRESS && tbuffer) {
- ret = sdio_readsb(device->func, tbuffer,
- address, length);
- HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
- __func__, ret, address, length,
- *(int *)tbuffer);
- } else if (tbuffer) {
- ret = sdio_memcpy_fromio(device->func,
- tbuffer, address,
- length);
- HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
- __func__, ret, address, length,
- *(int *)tbuffer);
- }
-#if HIF_USE_DMA_BOUNCE_BUFFER
- if (bounced && tbuffer)
- memcpy(buffer, tbuffer, length);
-#endif
- } else {
- HIF_ERROR("%s: Invalid dir: 0x%08x", __func__, request);
- status = QDF_STATUS_E_INVAL;
- return status;
- }
-
- if (ret) {
- HIF_ERROR("%s: SDIO bus operation failed!", __func__);
- HIF_ERROR("%s: MMC stack returned : %d", __func__, ret);
- HIF_ERROR("%s: addr:0X%06X, len:%08d, %s, %s",
- __func__, address, length,
- request & HIF_SDIO_READ ? "Read " : "Write",
- request & HIF_ASYNCHRONOUS ?
- "Async" : "Sync");
- status = QDF_STATUS_E_FAILURE;
- }
- } while (false);
-
- return status;
-}
-
-/**
* add_to_async_list() - add bus reqest to async task list
* @device: pointer to hif device
* @busrequest: pointer to type of bus request
@@ -380,222 +184,6 @@
qdf_spin_unlock_irqrestore(&device->asynclock);
}
-/**
- * hif_read_write() - queue a read/write request
- * @device: pointer to hif device structure
- * @address: address to read
- * @buffer: buffer to hold read/write data
- * @length: length to read/write
- * @request: read/write/sync/async request
- * @context: pointer to hold calling context
- *
- * Return: 0 on success, error number otherwise.
- */
-QDF_STATUS
-hif_read_write(struct hif_sdio_dev *device,
- uint32_t address,
- char *buffer, uint32_t length,
- uint32_t request, void *context)
-{
- QDF_STATUS status = QDF_STATUS_SUCCESS;
- struct bus_request *busrequest;
-
- AR_DEBUG_ASSERT(device);
- AR_DEBUG_ASSERT(device->func);
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: device 0x%pK addr 0x%X buffer 0x%pK len %d req 0x%X context 0x%pK",
- __func__, device, address, buffer,
- length, request, context));
-
- /*sdio r/w action is not needed when suspend, so just return */
- if ((device->is_suspend == true)
- && (device->power_config == HIF_DEVICE_POWER_CUT)) {
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("skip io when suspending\n"));
- return QDF_STATUS_SUCCESS;
- }
- do {
- if ((request & HIF_ASYNCHRONOUS) ||
- (request & HIF_SYNCHRONOUS)) {
- /* serialize all requests through the async thread */
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: Execution mode: %s\n", __func__,
- (request & HIF_ASYNCHRONOUS) ? "Async"
- : "Synch"));
- busrequest = hif_allocate_bus_request(device);
- if (!busrequest) {
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
- ("no async bus requests available (%s, addr:0x%X, len:%d)\n",
- request & HIF_SDIO_READ ? "READ" :
- "WRITE", address, length));
- return QDF_STATUS_E_FAILURE;
- }
- busrequest->address = address;
- busrequest->buffer = buffer;
- busrequest->length = length;
- busrequest->request = request;
- busrequest->context = context;
-
- add_to_async_list(device, busrequest);
-
- if (request & HIF_SYNCHRONOUS) {
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: queued sync req: 0x%lX\n",
- __func__, (unsigned long)busrequest));
-
- /* wait for completion */
- up(&device->sem_async);
- if (down_interruptible(&busrequest->sem_req) !=
- 0) {
- /* interrupted, exit */
- return QDF_STATUS_E_FAILURE;
- } else {
- QDF_STATUS status = busrequest->status;
-
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: sync return freeing 0x%lX: 0x%X\n",
- __func__,
- (unsigned long)
- busrequest,
- busrequest->status));
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: freeing req: 0x%X\n",
- __func__,
- (unsigned int)
- request));
- hif_free_bus_request(device,
- busrequest);
- return status;
- }
- } else {
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: queued async req: 0x%lX\n",
- __func__,
- (unsigned long)busrequest));
- up(&device->sem_async);
- return QDF_STATUS_E_PENDING;
- }
- } else {
- AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
- ("%s: Invalid execution mode: 0x%08x\n",
- __func__,
- (unsigned int)request));
- status = QDF_STATUS_E_INVAL;
- break;
- }
- } while (0);
-
- return status;
-}
-
-/**
- * async_task() - thread function to serialize all bus requests
- * @param: pointer to hif device
- *
- * thread function to serialize all requests, both sync and async
- * Return: 0 on success, error number otherwise.
- */
-static int async_task(void *param)
-{
- struct hif_sdio_dev *device;
- struct bus_request *request;
- QDF_STATUS status;
- bool claimed = false;
-
- device = (struct hif_sdio_dev *) param;
- set_current_state(TASK_INTERRUPTIBLE);
- while (!device->async_shutdown) {
- /* wait for work */
- if (down_interruptible(&device->sem_async) != 0) {
- /* interrupted, exit */
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: async task interrupted\n",
- __func__));
- break;
- }
- if (device->async_shutdown) {
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: async task stopping\n",
- __func__));
- break;
- }
- /* we want to hold the host over multiple cmds
- * if possible, but holding the host blocks
- * card interrupts
- */
- qdf_spin_lock_irqsave(&device->asynclock);
- /* pull the request to work on */
- while (device->asyncreq) {
- request = device->asyncreq;
- if (request->inusenext)
- device->asyncreq = request->inusenext;
- else
- device->asyncreq = NULL;
- qdf_spin_unlock_irqrestore(&device->asynclock);
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: async_task processing req: 0x%lX\n",
- __func__, (unsigned long)request));
-
- if (!claimed) {
- sdio_claim_host(device->func);
- claimed = true;
- }
- if (request->scatter_req) {
- A_ASSERT(device->scatter_enabled);
- /* pass the request to scatter routine which
- * executes it synchronously, note, no need
- * to free the request since scatter requests
- * are maintained on a separate list
- */
- status = do_hif_read_write_scatter(device,
- request);
- } else {
- /* call hif_read_write in sync mode */
- status =
- __hif_read_write(device,
- request->address,
- request->buffer,
- request->length,
- request->
- request &
- ~HIF_SYNCHRONOUS,
- NULL);
- if (request->request & HIF_ASYNCHRONOUS) {
- void *context = request->context;
-
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: freeing req: 0x%lX\n",
- __func__, (unsigned long)
- request));
- hif_free_bus_request(device, request);
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: async_task completion req 0x%lX\n",
- __func__, (unsigned long)
- request));
- device->htc_callbacks.
- rw_compl_handler(context, status);
- } else {
- AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
- ("%s: async_task upping req: 0x%lX\n",
- __func__, (unsigned long)
- request));
- request->status = status;
- up(&request->sem_req);
- }
- }
- qdf_spin_lock_irqsave(&device->asynclock);
- }
- qdf_spin_unlock_irqrestore(&device->asynclock);
- if (claimed) {
- sdio_release_host(device->func);
- claimed = false;
- }
- }
-
- complete_and_exit(&device->async_completion, 0);
-
- return 0;
-}
-
/*
* Setup IRQ mode for deep sleep and WoW
* Switch back to 1 bits mode when we suspend for
@@ -603,6 +191,7 @@
* Re-enable async 4-bit irq mode for some host controllers
* after resume.
*/
+#ifdef CONFIG_SDIO_TRANSFER_MAILBOX
static int sdio_enable4bits(struct hif_sdio_dev *device, int enable)
{
int ret = 0;
@@ -675,34 +264,12 @@
return ret;
}
-
-static QDF_STATUS hif_disable_func(struct hif_sdio_dev *device,
- struct sdio_func *func,
- bool reset)
+#else
+static int sdio_enable4bits(struct hif_sdio_dev *device, int enable)
{
- QDF_STATUS status = QDF_STATUS_SUCCESS;
-
- HIF_ENTER();
- device = get_hif_device(func);
- if (!IS_ERR(device->async_task)) {
- init_completion(&device->async_completion);
- device->async_shutdown = 1;
- up(&device->sem_async);
- wait_for_completion(&device->async_completion);
- device->async_task = NULL;
- sema_init(&device->sem_async, 0);
- }
-
- status = hif_sdio_func_disable(device, func, reset);
- if (status == QDF_STATUS_SUCCESS)
- device->is_disabled = true;
-
- cleanup_hif_scatter_resources(device);
-
- HIF_EXIT();
-
- return status;
+ return 0;
}
+#endif
/**
* hif_sdio_probe() - configure sdio device
@@ -712,9 +279,9 @@
*
* Return: 0 for success and non-zero for failure
*/
-static A_STATUS hif_sdio_probe(struct hif_softc *ol_sc,
- struct sdio_func *func,
- struct hif_sdio_dev *device)
+A_STATUS hif_sdio_probe(struct hif_softc *ol_sc,
+ struct sdio_func *func,
+ struct hif_sdio_dev *device)
{
int ret = 0;
const struct sdio_device_id *id;
@@ -794,7 +361,9 @@
goto err_attach1;
}
- return 0;
+ ret = hif_dev_register_channels(device, func);
+
+ return ret;
err_attach1:
if (scn->ramdump_base)
@@ -803,48 +372,9 @@
return ret;
}
-static QDF_STATUS hif_enable_func(struct hif_softc *ol_sc,
- struct hif_sdio_dev *device,
- struct sdio_func *func,
- bool resume)
-{
- int ret = QDF_STATUS_SUCCESS;
-
- HIF_ENTER();
-
- if (!device) {
- HIF_ERROR("%s: HIF device is NULL", __func__);
- return QDF_STATUS_E_INVAL;
- }
-
- if (hif_sdio_func_enable(device, func))
- return QDF_STATUS_E_FAILURE;
-
- /* create async I/O thread */
- if (!device->async_task && device->is_disabled) {
- device->async_shutdown = 0;
- device->async_task = kthread_create(async_task,
- (void *)device,
- "AR6K Async");
- if (IS_ERR(device->async_task)) {
- HIF_ERROR("%s: Error creating async task",
- __func__);
- return QDF_STATUS_E_FAILURE;
- }
- device->is_disabled = false;
- wake_up_process(device->async_task);
- }
-
- if (resume == false)
- ret = hif_sdio_probe(ol_sc, func, device);
-
- HIF_EXIT();
-
- return ret;
-}
-
/**
* power_state_change_notify() - SDIO bus power notification handler
+ * @ol_sc: HIF device context
* @config: hif device power change type
*
* Return: 0 on success, error number otherwise.
@@ -910,6 +440,7 @@
/**
* hif_configure_device() - configure sdio device
+ * @ol_sc: HIF device context
* @device: pointer to hif device structure
* @opcode: configuration type
* @config: configuration value to set
@@ -1032,6 +563,7 @@
/**
* hif_device_inserted() - hif-sdio driver probe handler
+ * @ol_sc: HIF device context
* @func: pointer to sdio_func
* @id: pointer to sdio_device_id
*
@@ -1057,10 +589,10 @@
if (hifdevice &&
hifdevice->power_config == HIF_DEVICE_POWER_CUT &&
hifdevice->host == func->card->host) {
- device = get_hif_device(func);
+ device = get_hif_device(ol_sc, func);
hifdevice->func = func;
hifdevice->power_config = HIF_DEVICE_POWER_UP;
- hif_sdio_set_drvdata(func, hifdevice);
+ hif_sdio_set_drvdata(ol_sc, func, hifdevice);
if (device->is_suspend) {
HIF_INFO("%s: Resume from suspend", __func__);
@@ -1072,9 +604,10 @@
/* If device not found, then it is a new insertion, alloc and add it */
if (!device) {
- if (add_hif_device(func) == NULL)
+ if (!add_hif_device(ol_sc, func))
return QDF_STATUS_E_FAILURE;
- device = get_hif_device(func);
+
+ device = get_hif_device(ol_sc, func);
for (i = 0; i < MAX_HIF_DEVICES; ++i) {
if (!hif_devices[i]) {
@@ -1095,13 +628,13 @@
*/
sdio_claim_host(func);
- hif_sdio_quirk_force_drive_strength(func);
+ hif_sdio_quirk_force_drive_strength(ol_sc, func);
- hif_sdio_quirk_write_cccr(func);
+ hif_sdio_quirk_write_cccr(ol_sc, func);
- ret = hif_sdio_set_bus_speed(func);
+ ret = hif_sdio_set_bus_speed(ol_sc, func);
- ret = hif_sdio_set_bus_width(func);
+ ret = hif_sdio_set_bus_width(ol_sc, func);
if (debugcccr)
hif_dump_cccr(device);
@@ -1174,11 +707,11 @@
* @pdev - HIF layer object
* @func - SDIO bus function object
*
- * Return - NONE
+ * Return - error in case of failure to configure, else success
*/
-void hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func)
+int hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func)
{
- /* ADMA-TODO */
+ return hif_dev_configure_pipes(dev, func);
}
/**
@@ -1233,7 +766,7 @@
int hif_device_suspend(struct hif_softc *ol_sc, struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
- struct hif_sdio_dev *device = get_hif_device(func);
+ struct hif_sdio_dev *device = get_hif_device(ol_sc, func);
mmc_pm_flag_t pm_flag = 0;
enum HIF_DEVICE_POWER_CHANGE_TYPE config;
struct mmc_host *host = func->card->host;
@@ -1314,7 +847,7 @@
enum HIF_DEVICE_POWER_CHANGE_TYPE config;
struct hif_sdio_dev *device;
- device = get_hif_device(func);
+ device = get_hif_device(ol_sc, func);
if (!device) {
HIF_ERROR("%s: hif object is null", __func__);
return -EINVAL;
@@ -1367,7 +900,8 @@
return 0;
}
-static void hif_device_removed(struct sdio_func *func)
+
+static void hif_device_removed(struct hif_softc *ol_sc, struct sdio_func *func)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct hif_sdio_dev *device;
@@ -1375,7 +909,7 @@
AR_DEBUG_ASSERT(func);
HIF_ENTER();
- device = get_hif_device(func);
+ device = get_hif_device(ol_sc, func);
if (device->power_config == HIF_DEVICE_POWER_CUT) {
device->func = NULL; /* func will be free by mmc stack */
@@ -1406,7 +940,8 @@
HIF_EXIT();
}
-static struct hif_sdio_dev *add_hif_device(struct sdio_func *func)
+static struct hif_sdio_dev *add_hif_device(struct hif_softc *ol_sc,
+ struct sdio_func *func)
{
struct hif_sdio_dev *hifdevice = NULL;
int ret = 0;
@@ -1430,8 +965,8 @@
hifdevice->func = func;
hifdevice->power_config = HIF_DEVICE_POWER_UP;
hifdevice->device_state = HIF_DEVICE_STATE_ON;
- ret = hif_sdio_set_drvdata(func, hifdevice);
- hif_info("status %d", ret);
+ ret = hif_sdio_set_drvdata(ol_sc, func, hifdevice);
+ HIF_INFO("status %d", ret);
return hifdevice;
}
@@ -1552,12 +1087,12 @@
HIF_ERROR("%s: Enter", __func__);
status = hif_device_inserted(ol_sc, func, id);
- HIF_ERROR("%s: Exit", __func__);
+ HIF_ERROR("%s: Exit: status:%d", __func__, status);
return status;
}
-void hif_sdio_device_removed(struct sdio_func *func)
+void hif_sdio_device_removed(struct hif_softc *ol_sc, struct sdio_func *func)
{
- hif_device_removed(func);
+ hif_device_removed(ol_sc, func);
}
diff --git a/hif/src/sdio/transfer/adma.c b/hif/src/sdio/transfer/adma.c
new file mode 100644
index 0000000..3526ee9
--- /dev/null
+++ b/hif/src/sdio/transfer/adma.c
@@ -0,0 +1,858 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <qdf_lock.h>
+#include "adma.h"
+#include "hif_sdio_internal.h"
+#include "pld_sdio.h"
+#include "if_sdio.h"
+
+/**
+ * hif_dev_get_fifo_address() - get the fifo addresses for dma
+ * @pdev: SDIO HIF object
+ * @c : FIFO address config pointer
+ *
+ * Return : 0 for success, non-zero for error
+ */
+int hif_dev_get_fifo_address(struct hif_sdio_dev *pdev,
+ void *c,
+ uint32_t config_len)
+{
+ /* SDIO AL handles DMA Addresses */
+ return 0;
+}
+
+/**
+ * hif_dev_get_block_size() - get the adma block size for dma
+ * @config : block size config pointer
+ *
+ * Return : NONE
+ */
+void hif_dev_get_block_size(void *config)
+{
+ /* TODO Get block size used by AL Layer in Mission ROM Mode */
+ *((uint32_t *)config) = HIF_BLOCK_SIZE; /* QCN_SDIO_MROM_BLK_SZ TODO */
+}
+
+/**
+ * hif_dev_configure_pipes() - configure pipes
+ * @pdev: SDIO HIF object
+ * @func: sdio function object
+ *
+ * Return : 0 for success, non-zero for error
+ */
+int hif_dev_configure_pipes(struct hif_sdio_dev *pdev, struct sdio_func *func)
+{
+ /* SDIO AL Configures SDIO Channels */
+ return 0;
+}
+
+/** hif_dev_set_mailbox_swap() - Set the mailbox swap
+ * @pdev : The HIF layer object
+ *
+ * Return: none
+ */
+void hif_dev_set_mailbox_swap(struct hif_sdio_dev *pdev)
+{
+ /* SDIO AL doesn't use mailbox architecture */
+}
+
+/** hif_dev_get_mailbox_swap() - Get the mailbox swap setting
+ * @pdev : The HIF layer object
+ *
+ * Return: true or false
+ */
+bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev)
+{
+ /* SDIO AL doesn't use mailbox architecture */
+ return false;
+}
+
+/**
+ * hif_dev_dsr_handler() - Synchronous interrupt handler
+ *
+ * @context: hif send context
+ *
+ * Return: 0 for success and non-zero for failure
+ */
+QDF_STATUS hif_dev_dsr_handler(void *context)
+{
+ /* SDIO AL handles interrupts */
+ return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * hif_dev_map_service_to_pipe() - maps ul/dl pipe to service id.
+ * @pDev: SDIO HIF object
+ * @ServiceId: sevice index
+ * @ULPipe: uplink pipe id
+ * @DLPipe: down-linklink pipe id
+ *
+ * Return: 0 on success, error value on invalid map
+ */
+QDF_STATUS hif_dev_map_service_to_pipe(struct hif_sdio_dev *pdev, uint16_t svc,
+ uint8_t *ul_pipe, uint8_t *dl_pipe)
+{
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+ switch (svc) {
+ case HTT_DATA_MSG_SVC:
+ *dl_pipe = 2;
+ *ul_pipe = 3;
+ break;
+
+ case HTC_CTRL_RSVD_SVC:
+ case HTC_RAW_STREAMS_SVC:
+ *dl_pipe = 0;
+ *ul_pipe = 1;
+ break;
+
+ case WMI_DATA_BE_SVC:
+ case WMI_DATA_BK_SVC:
+ case WMI_DATA_VI_SVC:
+ case WMI_DATA_VO_SVC:
+ *dl_pipe = 2;
+ *ul_pipe = 3;
+ break;
+
+ case WMI_CONTROL_SVC:
+ *dl_pipe = 0;
+ *ul_pipe = 1;
+ break;
+
+ default:
+ HIF_ERROR("%s: Err : Invalid service (%d)",
+ __func__, svc);
+ status = QDF_STATUS_E_INVAL;
+ break;
+ }
+ return status;
+}
+
+/** hif_dev_setup_device() - Setup device specific stuff here required for hif
+ * @pdev : HIF layer object
+ *
+ * return 0 on success, error otherwise
+ */
+int hif_dev_setup_device(struct hif_sdio_device *pdev)
+{
+ hif_dev_get_block_size(&pdev->BlockSize);
+
+ return 0;
+}
+
+/** hif_dev_mask_interrupts() - Disable the interrupts in the device
+ * @pdev SDIO HIF Object
+ *
+ * Return: NONE
+ */
+void hif_dev_mask_interrupts(struct hif_sdio_device *pdev)
+{
+ /* SDIO AL Handles Interrupts */
+}
+
+/** hif_dev_unmask_interrupts() - Enable the interrupts in the device
+ * @pdev SDIO HIF Object
+ *
+ * Return: NONE
+ */
+void hif_dev_unmask_interrupts(struct hif_sdio_device *pdev)
+{
+ /* SDIO AL Handles Interrupts */
+}
+
+/**
+ * hif_dev_map_pipe_to_adma_chan() - maps pipe id to adma chan
+ * @pdev: The pointer to the hif device object
+ * @pipeid: pipe index
+ *
+ * Return: adma channel handle
+ */
+struct sdio_al_channel_handle *hif_dev_map_pipe_to_adma_chan
+(
+struct hif_sdio_device *dev,
+uint8_t pipeid
+)
+{
+ struct hif_sdio_dev *pdev = dev->HIFDevice;
+
+ HIF_ENTER();
+
+ if ((pipeid == 0) || (pipeid == 1))
+ return pdev->al_chan[0];
+ else if ((pipeid == 2) || (pipeid == 3))
+ return pdev->al_chan[1];
+ else
+ return NULL;
+}
+
+/**
+ * hif_dev_map_adma_chan_to_pipe() - map adma chan to htc pipe
+ * @pdev: The pointer to the hif device object
+ * @chan: channel number
+ * @upload: boolean to decide upload or download
+ *
+ * Return: Invalid pipe index
+ */
+uint8_t hif_dev_map_adma_chan_to_pipe(struct hif_sdio_device *pdev,
+ uint8_t chan, bool upload)
+{
+ HIF_INFO("%s: chan: %u, %s", __func__, chan,
+ upload ? "Upload" : "Download");
+
+ if (chan == 0) /* chan 0 is mapped to HTT */
+ return upload ? 1 : 0;
+ else if (chan == 1) /* chan 1 is mapped to WMI */
+ return upload ? 3 : 2;
+
+ return (uint8_t)-1; /* invalid channel id */
+}
+
+/**
+ * hif_get_send_address() - Get the transfer pipe address
+ * @pdev: The pointer to the hif device object
+ * @pipe: The pipe identifier
+ *
+ * Return 0 for success and non-zero for failure to map
+ */
+int hif_get_send_address(struct hif_sdio_device *pdev,
+ uint8_t pipe, unsigned long *addr)
+{
+ struct sdio_al_channel_handle *chan = NULL;
+
+ HIF_INFO("pipe: %u", pipe);
+
+ if (!addr)
+ return -EINVAL;
+
+ *addr = 0;
+ chan = hif_dev_map_pipe_to_adma_chan(pdev, pipe);
+
+ if (!chan)
+ return -EINVAL;
+
+ *addr = (unsigned long)chan;
+
+ return 0;
+}
+
+/**
+ * hif_fixup_write_param() - Tweak the address and length parameters
+ * @pdev: The pointer to the hif device object
+ * @length: The length pointer
+ * @addr: The addr pointer
+ *
+ * Return: None
+ */
+void hif_fixup_write_param(struct hif_sdio_dev *pdev, uint32_t req,
+ uint32_t *length, uint32_t *addr)
+{
+ HIF_ENTER();
+ /* ADMA-TODO */
+ HIF_EXIT();
+}
+
+#define HIF_MAX_RX_Q_ALLOC 0 /* TODO */
+#define HIF_RX_Q_ALLOC_THRESHOLD 100
+QDF_STATUS hif_disable_func(struct hif_sdio_dev *device,
+ struct sdio_func *func,
+ bool reset)
+{
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+#if HIF_MAX_RX_Q_ALLOC
+ qdf_list_node_t *node;
+ struct rx_q_entry *rx_q_elem;
+#endif
+ HIF_ENTER();
+
+#if HIF_MAX_RX_Q_ALLOC
+ qdf_spin_lock_irqsave(&device->rx_q_lock);
+
+ for (; device->rx_q.count; ) {
+ qdf_list_remove_back(&device->rx_q, &node);
+ rx_q_elem = container_of(node, struct rx_q_entry, entry);
+ if (rx_q_elem) {
+ if (rx_q_elem->nbuf)
+ qdf_nbuf_free(rx_q_elem->nbuf);
+ qdf_mem_free(rx_q_elem);
+ }
+ }
+ qdf_destroy_work(0, &device->rx_q_alloc_work);
+
+ qdf_spin_unlock_irqrestore(&device->rx_q_lock);
+
+ qdf_spinlock_destroy(&device->rx_q_lock);
+#endif
+
+ status = hif_sdio_func_disable(device, func, reset);
+ if (status == QDF_STATUS_SUCCESS)
+ device->is_disabled = true;
+
+ cleanup_hif_scatter_resources(device);
+
+ HIF_EXIT();
+
+ return status;
+}
+
+/**
+ * hif_enable_func() - Enable SDIO function
+ *
+ * @ol_sc: HIF object pointer
+ * @device: HIF device pointer
+ * @sdio_func: SDIO function pointer
+ * @resume: If this is called from resume or probe
+ *
+ * Return: 0 in case of success, else error value
+ */
+QDF_STATUS hif_enable_func(struct hif_softc *ol_sc, struct hif_sdio_dev *device,
+ struct sdio_func *func, bool resume)
+{
+ int ret = QDF_STATUS_SUCCESS;
+
+ if (!device) {
+ HIF_ERROR("%s: HIF device is NULL", __func__);
+ return QDF_STATUS_E_INVAL;
+ }
+
+ if (!resume)
+ ret = hif_sdio_probe(ol_sc, func, device);
+
+#if HIF_MAX_RX_Q_ALLOC
+ if (!ret) {
+ qdf_list_create(&device->rx_q, HIF_MAX_RX_Q_ALLOC);
+ qdf_spinlock_create(&device->rx_q_lock);
+ qdf_create_work(0, &device->rx_q_alloc_work,
+ hif_sdio_rx_q_alloc, (void *)device);
+ device->rx_q_alloc_work_scheduled = true;
+ qdf_sched_work(0, &device->rx_q_alloc_work);
+ }
+#endif
+ return ret;
+}
+
+/**
+ * hif_sdio_get_net_buf() - Get a network buffer from the rx q
+ * @dev - HIF device object
+ *
+ * Return - NULL if out of buffers, else qdf_nbuf_t
+ */
+#define HEAD_ROOM 256
+#define len_head_room(len) ((len) + HEAD_ROOM)
+#define is_pad_block(buf) (*((uint32_t *)buf) == 0xbabababa)
+#if HIF_MAX_RX_Q_ALLOC
+qdf_nbuf_t hif_sdio_get_nbuf(struct hif_sdio_dev *dev)
+{
+ qdf_list_node_t *node;
+ qdf_nbuf_t nbuf = NULL;
+ qdf_list_t *q = &dev->rx_q;
+ struct rx_q_entry *elem = NULL;
+
+ qdf_spin_lock_irqsave(&dev->rx_q_lock);
+
+ if (q->count) {
+ qdf_list_remove_front(q, &node);
+ elem = qdf_container_of(node, struct rx_q_entry, entry);
+ nbuf = elem->nbuf;
+ } else {
+ HIF_ERROR("%s: no rx q elements", __func__);
+ }
+
+ if (q->count <= HIF_RX_Q_ALLOC_THRESHOLD &&
+ !dev->rx_q_alloc_work_scheduled) {
+ dev->rx_q_alloc_work_scheduled = true;
+ qdf_sched_work(0, &dev->rx_q_alloc_work);
+ }
+
+ qdf_spin_unlock_irqrestore(&dev->rx_q_lock);
+
+ qdf_mem_free(elem);
+
+ return nbuf;
+}
+#else
+qdf_nbuf_t hif_sdio_get_nbuf(struct hif_sdio_dev *dev)
+{
+ qdf_nbuf_t nbuf;
+
+ nbuf = qdf_nbuf_alloc(NULL, HIF_SDIO_RX_BUFFER_SIZE + HEAD_ROOM,
+ HEAD_ROOM, 4, 0);
+ return nbuf;
+}
+#endif
+/**
+ * hif_sdio_rx_q_alloc() - Deferred work for pre-alloc rx q
+ * @ctx - Pointer to context object
+ *
+ * Return NONE
+ */
+#if HIF_MAX_RX_Q_ALLOC
+void hif_sdio_rx_q_alloc(void *ctx)
+{
+ struct rx_q_entry *rx_q_elem;
+ struct hif_sdio_dev *dev = (struct hif_sdio_dev *)ctx;
+ unsigned int rx_q_count = dev->rx_q.count;
+
+ HIF_ENTER();
+ qdf_spin_lock_irqsave(&dev->rx_q_lock);
+
+ for (; rx_q_count < dev->rx_q.max_size; rx_q_count++) {
+ rx_q_elem = qdf_mem_malloc(sizeof(struct rx_q_entry));
+ if (!rx_q_elem) {
+ HIF_ERROR("%s: failed to alloc rx q elem", __func__);
+ break;
+ }
+
+ rx_q_elem->nbuf = qdf_nbuf_alloc(NULL, HIF_SDIO_RX_BUFFER_SIZE +
+ HEAD_ROOM, HEAD_ROOM, 4, 0);
+ if (!rx_q_elem->nbuf) {
+ HIF_ERROR("%s: failed to alloc nbuf for rx", __func__);
+ qdf_mem_free(rx_q_elem);
+ break;
+ }
+
+ qdf_list_insert_back(&dev->rx_q, &rx_q_elem->entry);
+ }
+ dev->rx_q_alloc_work_scheduled = false;
+
+ qdf_spin_unlock_irqrestore(&dev->rx_q_lock);
+ HIF_EXIT();
+}
+#else
+void hif_sdio_rx_q_alloc(void *ctx)
+{
+}
+#endif
+
+#include <linux/qcn_sdio_al.h>
+
+struct sdio_al_channel_data qcn7605_chan[HIF_SDIO_MAX_AL_CHANNELS] = {
+ {
+ .name = "SDIO_AL_WLAN_CH0", /* HTT */
+ .client_data = NULL, /* populate from client handle */
+ .ul_xfer_cb = ul_xfer_cb,
+ .dl_xfer_cb = dl_xfer_cb,
+ .dl_data_avail_cb = dl_data_avail_cb,
+ .dl_meta_data_cb = NULL
+ },
+ {
+ .name = "SDIO_AL_WLAN_CH1", /* WMI */
+ .client_data = NULL, /* populate from client handle */
+ .ul_xfer_cb = ul_xfer_cb,
+ .dl_xfer_cb = dl_xfer_cb,
+ .dl_data_avail_cb = dl_data_avail_cb,
+ .dl_meta_data_cb = NULL
+ }
+};
+
+/**
+ * hif_dev_register_channels()- Register transport layer channels
+ * @dev : HIF device object
+ * @func : SDIO function pointer
+ *
+ * Return : success on configuration, else failure
+ */
+int hif_dev_register_channels(struct hif_sdio_dev *dev, struct sdio_func *func)
+{
+ int ret = 0;
+ unsigned int chan;
+ struct sdio_al_channel_data *chan_data[HIF_ADMA_MAX_CHANS];
+
+ HIF_ENTER();
+
+ dev->al_client = pld_sdio_get_sdio_al_client_handle(func);
+ if (ret || !dev->al_client) {
+ HIF_ERROR("%s: Failed to get get sdio al handle", __func__);
+ return ret;
+ }
+
+ if ((func->device & MANUFACTURER_ID_AR6K_BASE_MASK) ==
+ MANUFACTURER_ID_QCN7605_BASE) {
+ dev->adma_chans_used = 2;
+ qcn7605_chan[0].client_data = dev->al_client->client_data;
+ qcn7605_chan[1].client_data = dev->al_client->client_data;
+ chan_data[0] = &qcn7605_chan[0];
+ chan_data[1] = &qcn7605_chan[1];
+ } else {
+ dev->adma_chans_used = 0;
+ }
+
+ for (chan = 0; chan < dev->adma_chans_used; chan++) {
+ dev->al_chan[chan] =
+ pld_sdio_register_sdio_al_channel(dev->al_client,
+ chan_data[chan]);
+ if (!dev->al_chan[chan] || IS_ERR(dev->al_chan[chan])) {
+ ret = -EINVAL;
+ HIF_ERROR("%s: Channel registration failed", __func__);
+ } else {
+ dev->al_chan[chan]->priv = (void *)dev;
+ HIF_INFO("%s: chan %s : id : %u", __func__,
+ chan_data[chan]->name,
+ dev->al_chan[chan]->channel_id);
+ }
+ }
+
+ HIF_EXIT();
+
+ return ret;
+}
+
+/**
+ * hif_dev_unregister_channels()- Register transport layer channels
+ * @dev : HIF device object
+ * @func : SDIO Function pointer
+ *
+ * Return : None
+ */
+void hif_dev_unregister_channels(struct hif_sdio_dev *dev,
+ struct sdio_func *func)
+{
+ unsigned int chan;
+
+ if (!dev) {
+ HIF_ERROR("%s: hif_sdio_dev is null", __func__);
+ return;
+ }
+
+ for (chan = 0; chan < dev->adma_chans_used; chan++) {
+ dev->al_chan[chan]->priv = NULL;
+ pld_sdio_unregister_sdio_al_channel(dev->al_chan[chan]);
+ }
+}
+
+/**
+ * hif_read_write() - queue a read/write request
+ * @dev: pointer to hif device structure
+ * @address: address to read, actually channel pointer
+ * @buffer: buffer to hold read/write data
+ * @length: length to read/write
+ * @request: read/write/sync/async request
+ * @context: pointer to hold calling context
+ *
+ * Return: 0, pending on success, error number otherwise.
+ */
+QDF_STATUS
+hif_read_write(struct hif_sdio_dev *dev,
+ unsigned long sdio_al_ch_handle,
+ char *cbuffer, uint32_t length,
+ uint32_t request, void *context)
+{
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+ struct sdio_al_channel_handle *ch;
+ struct bus_request *bus_req;
+ enum sdio_al_dma_direction dir;
+ struct hif_sdio_device *device;
+ QDF_STATUS (*rx_comp)(void *, qdf_nbuf_t, uint8_t);
+ qdf_nbuf_t nbuf;
+ int ret = 0, payload_len = 0;
+ unsigned char *buffer = (unsigned char *)cbuffer;
+
+ if (!dev || !sdio_al_ch_handle) {
+ HIF_ERROR("%s: device = %pK, addr = %lu", __func__,
+ dev, sdio_al_ch_handle);
+ return QDF_STATUS_E_INVAL;
+ }
+
+ if (!(request & HIF_ASYNCHRONOUS) &&
+ !(request & HIF_SYNCHRONOUS)) {
+ HIF_ERROR("%s: Invalid request mode", __func__);
+ return QDF_STATUS_E_INVAL;
+ }
+
+ /*sdio r/w action is not needed when suspend, so just return */
+ if ((dev->is_suspend) &&
+ (dev->power_config == HIF_DEVICE_POWER_CUT)) {
+ HIF_INFO("%s: skip in suspend", __func__);
+ return QDF_STATUS_SUCCESS;
+ }
+
+ ch = (struct sdio_al_channel_handle *)sdio_al_ch_handle;
+
+ bus_req = hif_allocate_bus_request(dev);
+ if (!bus_req) {
+ HIF_ERROR("%s: Bus alloc failed", __func__);
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ bus_req->address = sdio_al_ch_handle;
+ bus_req->length = length;
+ bus_req->request = request;
+ bus_req->context = context;
+ bus_req->buffer = buffer;
+
+ /* Request SDIO AL to do transfer */
+ dir = (request & HIF_SDIO_WRITE) ? SDIO_AL_TX : SDIO_AL_RX;
+
+ if (request & HIF_SDIO_WRITE)
+ HIF_TRACE("%s: Tx len %d", __func__, length);
+
+ if (request & HIF_SDIO_READ)
+ HIF_TRACE("%s: Rx len %d", __func__, length);
+
+ if (request & HIF_SYNCHRONOUS) {
+ ret = sdio_al_queue_transfer(ch,
+ dir,
+ bus_req->buffer,
+ bus_req->length,
+ 1); /* higher priority */
+ if (ret) {
+ status = QDF_STATUS_E_FAILURE;
+ HIF_ERROR("%s: SYNC REQ failed ret=%d", __func__, ret);
+ } else {
+ status = QDF_STATUS_SUCCESS;
+ }
+
+ hif_free_bus_request(dev, bus_req);
+
+ if ((status == QDF_STATUS_SUCCESS) && (dir == SDIO_AL_RX)) {
+ nbuf = (qdf_nbuf_t)context;
+ payload_len = HTC_GET_FIELD(bus_req->buffer,
+ HTC_FRAME_HDR,
+ PAYLOADLEN);
+ qdf_nbuf_set_pktlen(nbuf, payload_len + HTC_HDR_LENGTH);
+ device = (struct hif_sdio_device *)dev->htc_context;
+ rx_comp = device->hif_callbacks.rxCompletionHandler;
+ rx_comp(device->hif_callbacks.Context, nbuf, 0);
+ }
+ } else {
+ ret = sdio_al_queue_transfer_async(ch,
+ dir,
+ bus_req->buffer,
+ bus_req->length,
+ 1, /* higher priority */
+ (void *)bus_req);
+ if (ret) {
+ status = QDF_STATUS_E_FAILURE;
+ HIF_ERROR("%s: ASYNC REQ fail ret=%d for len=%d ch=%d",
+ __func__, ret, length, ch->channel_id);
+ hif_free_bus_request(dev, bus_req);
+ } else {
+ status = QDF_STATUS_E_PENDING;
+ }
+ }
+ return status;
+}
+
+/**
+ * ul_xfer_cb() - Completion call back for asyncronous transfer
+ * @ch_handle: The sdio al channel handle
+ * @result: The result of the operation
+ * @context: pointer to request context
+ *
+ * Return: None
+ */
+void ul_xfer_cb(struct sdio_al_channel_handle *ch_handle,
+ struct sdio_al_xfer_result *result,
+ void *ctx)
+{
+ struct bus_request *req = (struct bus_request *)ctx;
+ struct hif_sdio_dev *dev;
+
+ if (!ch_handle || !result) {
+ HIF_ERROR("%s: Invalid args", __func__);
+ qdf_assert_always(0);
+ return;
+ }
+
+ dev = (struct hif_sdio_dev *)ch_handle->priv;
+
+ if (result->xfer_status) {
+ req->status = QDF_STATUS_E_FAILURE;
+ HIF_ERROR("%s: ASYNC Tx failed status=%d", __func__,
+ result->xfer_status);
+ } else {
+ req->status = QDF_STATUS_SUCCESS;
+ }
+
+ dev->htc_callbacks.rw_compl_handler(req->context, req->status);
+
+ hif_free_bus_request(dev, req);
+}
+
+/**
+ * dl_data_avail_cb() - Called when data is available on a channel
+ * @ch_handle: The sdio al channel handle
+ * @len: The len of data available to download
+ *
+ * Return: None
+ */
+/* Use the asynchronous method of transfer. This will help in
+ * completing READ in the transfer done callback later which
+ * runs in sdio al thread context. If we do the syncronous
+ * transfer here, the thread context won't be available and
+ * perhaps a new thread may be reaquired here.
+ */
+void dl_data_avail_cb(struct sdio_al_channel_handle *ch_handle,
+ unsigned int len)
+{
+ struct hif_sdio_dev *dev;
+ unsigned int chan;
+ qdf_nbuf_t nbuf;
+
+ if (!ch_handle || !len) {
+ HIF_ERROR("%s: Invalid args %u", __func__, len);
+ qdf_assert_always(0);
+ return;
+ }
+
+ dev = (struct hif_sdio_dev *)ch_handle->priv;
+ chan = ch_handle->channel_id;
+
+ if (chan > HIF_SDIO_MAX_AL_CHANNELS) {
+ HIF_ERROR("%s: Invalid Ch ID %d", __func__, chan);
+ return;
+ }
+
+ /* allocate a buffer for reading the data from the chip.
+ * Note that this is raw, unparsed buffer and will be
+ * processed in the transfer done callback.
+ */
+ /* TODO, use global buffer instead of runtime allocations */
+ nbuf = qdf_nbuf_alloc(NULL, len_head_room(len), HEAD_ROOM, 4, 0);
+
+ if (!nbuf) {
+ HIF_ERROR("%s: Unable to alloc netbuf %u bytes", __func__, len);
+ return;
+ }
+
+ hif_read_write(dev, (unsigned long)ch_handle, nbuf->data, len,
+ HIF_RD_ASYNC_BLOCK_FIX, nbuf);
+}
+
+/**
+ * dl_xfer_cb() - Call from lower layer after transfer is completed
+ * @ch_handle: The sdio al channel handle
+ * @result: The xfer result
+ * @ctx: Context passed in the transfer queuing
+ *
+ * Return: None
+ */
+void dl_xfer_cb(struct sdio_al_channel_handle *ch_handle,
+ struct sdio_al_xfer_result *result,
+ void *ctx)
+{
+ unsigned char *buf;
+ qdf_nbuf_t nbuf;
+ uint32_t len, nbuflen, payload_len = 0;
+ uint8_t *nbufdata;
+ struct hif_sdio_dev *dev;
+ struct hif_sdio_device *device;
+ struct bus_request *bus_req = (struct bus_request *)ctx;
+ bool last_htc_packet = false;
+ QDF_STATUS (*rx_completion)(void *, qdf_nbuf_t, uint8_t);
+
+ if (!ctx)
+ HIF_ERROR("%s: Net buf context NULL!!!", __func__);
+
+ if (!ctx || !ch_handle || !result) {
+ HIF_ERROR("%s: Invalid args", __func__);
+ qdf_assert_always(0);
+ return;
+ }
+
+ dev = (struct hif_sdio_dev *)ch_handle->priv;
+ if (result->xfer_status) {
+ HIF_ERROR("%s: ASYNC Rx failed %d", __func__,
+ result->xfer_status);
+ /* TODO - Free nbuf if hif_sdio_get_nbuf is used, when bundling
+ * is enabled
+ */
+ hif_free_bus_request(dev, (struct bus_request *)ctx);
+ return;
+ }
+
+ device = (struct hif_sdio_device *)dev->htc_context;
+ rx_completion = device->hif_callbacks.rxCompletionHandler;
+
+ buf = (unsigned char *)result->buf_addr;
+ len = (unsigned int)result->xfer_len;
+
+ /* ADMA-TODO - Discard the padding data
+ * Its still not decided that the padding is informed
+ * via the hdr->flags or a padding magic inline in the
+ * buffer. So, lets see.
+ */
+ while (len > 0 && len >= sizeof(HTC_FRAME_HDR)) {
+ if (last_htc_packet) {
+ HIF_ERROR("ERRR last htc_packet processed already\n");
+ break;
+ }
+ if (HTC_GET_FIELD(buf, HTC_FRAME_HDR, ENDPOINTID) >=
+ ENDPOINT_MAX) {
+ HIF_ERROR("%s: invalid endpoint id: %u", __func__,
+ HTC_GET_FIELD(buf, HTC_FRAME_HDR,
+ ENDPOINTID));
+ hif_free_bus_request(dev, (struct bus_request *)ctx);
+ return;
+ }
+ /* last_htc_packet is currently used to test non bundling case
+ * on Rumi Emulation platform.
+ * TODO - Remove last_htc_packet logic when bundling is
+ * enabled and use is_pad_block for bundling
+ */
+ last_htc_packet = 1;
+ /* TODO - get net buf using hif_sdio_get_nbuf, when bundling is
+ * enabled
+ */
+ nbuf = (qdf_nbuf_t)bus_req->context;
+ if (!nbuf) {
+ HIF_ERROR("%s: failed to alloc rx buffer", __func__);
+ break;
+ }
+ nbufdata = qdf_nbuf_data(nbuf);
+ nbuflen = qdf_nbuf_len(nbuf);
+
+ /* Copy the HTC frame to the alloc'd packet buffer */
+ payload_len = HTC_GET_FIELD(buf, HTC_FRAME_HDR, PAYLOADLEN);
+ if (!payload_len) {
+ HIF_ERROR("%s:Invalid Payload len %d bytes", __func__,
+ payload_len);
+ break;
+ }
+
+ /* TODO - Check if payload fits in netbuf data */
+ if (0) { //nbuflen < payload_len) {
+ HIF_ERROR("%s: nbuf %d < payload %d bytes", __func__,
+ nbuflen, payload_len);
+ break;
+ }
+
+ qdf_mem_copy(nbufdata, buf,
+ payload_len + HTC_HEADER_LEN);
+
+ qdf_nbuf_set_pktlen(nbuf, payload_len + HTC_HDR_LENGTH);
+ rx_completion(device->hif_callbacks.Context,
+ nbuf,
+ 0); /* don't care, not used */
+
+ if (len && last_htc_packet) {
+ unsigned int pad;
+
+ pad = DEV_CALC_RECV_PADDED_LEN(device, payload_len +
+ HTC_HEADER_LEN);
+ /* Decrement the length to be processed yet */
+ len -= pad;
+ /* Move the data pointer */
+ buf += pad;
+ } else {
+ len -= payload_len + HTC_HDR_LENGTH;
+ buf += payload_len + HTC_HDR_LENGTH;
+ }
+ }
+ hif_free_bus_request(dev, (struct bus_request *)ctx);
+}
diff --git a/hif/src/sdio/transfer/adma.h b/hif/src/sdio/transfer/adma.h
new file mode 100644
index 0000000..f69faf5
--- /dev/null
+++ b/hif/src/sdio/transfer/adma.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ADMA_H_
+#define _ADMA_H_
+
+#include "hif_sdio_dev.h"
+#include "htc_packet.h"
+#include "htc_api.h"
+#include "hif_internal.h"
+
+/* This should align with the underlying transport layer */
+#define HIF_DEFAULT_IO_BLOCK_SIZE 512
+#define HIF_BLOCK_SIZE HIF_DEFAULT_IO_BLOCK_SIZE
+#define HIF_DUMMY_SPACE_MASK 0x0FFFFFFF
+
+#define HIF_SDIO_MAX_AL_CHANNELS 2
+
+struct devRegisters {
+ uint32_t dummy;
+};
+
+#include "transfer.h"
+#define DEV_REGISTERS_SIZE sizeof(struct devRegisters)
+
+uint8_t hif_dev_map_adma_chan_to_pipe(struct hif_sdio_device *pdev,
+ uint8_t chan, bool upload);
+
+struct sdio_al_channel_handle *hif_dev_map_pipe_to_adma_chan
+(
+struct hif_sdio_device *pdev,
+uint8_t pipeid
+);
+
+void dl_xfer_cb(struct sdio_al_channel_handle *ch_handle,
+ struct sdio_al_xfer_result *result,
+ void *ctx);
+void ul_xfer_cb(struct sdio_al_channel_handle *ch_handle,
+ struct sdio_al_xfer_result *result,
+ void *ctx);
+
+void dl_data_avail_cb(struct sdio_al_channel_handle *ch_handle,
+ unsigned int len);
+
+void hif_sdio_rx_q_alloc(void *ctx);
+qdf_nbuf_t hif_sdio_get_nbuf(struct hif_sdio_dev *dev);
+#endif
diff --git a/hif/src/sdio/transfer/mailbox.c b/hif/src/sdio/transfer/mailbox.c
index 84a0a69..30c8b27 100644
--- a/hif/src/sdio/transfer/mailbox.c
+++ b/hif/src/sdio/transfer/mailbox.c
@@ -1,7 +1,6 @@
/*
* Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
- *
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
@@ -19,6 +18,7 @@
#ifdef CONFIG_SDIO_TRANSFER_MAILBOX
#define ATH_MODULE_NAME hif
+#include <linux/kthread.h>
#include <qdf_types.h>
#include <qdf_status.h>
#include <qdf_timer.h>
@@ -44,6 +44,24 @@
#include "regtable.h"
#include "transfer.h"
+/* by default setup a bounce buffer for the data packets,
+ * if the underlying host controller driver
+ * does not use DMA you may be able to skip this step
+ * and save the memory allocation and transfer time
+ */
+#define HIF_USE_DMA_BOUNCE_BUFFER 1
+#if HIF_USE_DMA_BOUNCE_BUFFER
+/* macro to check if DMA buffer is WORD-aligned and DMA-able.
+ * Most host controllers assume the
+ * buffer is DMA'able and will bug-check otherwise (i.e. buffers on the stack).
+ * virt_addr_valid check fails on stack memory.
+ */
+#define BUFFER_NEEDS_BOUNCE(buffer) (((unsigned long)(buffer) & 0x3) || \
+ !virt_addr_valid((buffer)))
+#else
+#define BUFFER_NEEDS_BOUNCE(buffer) (false)
+#endif
+
#ifdef SDIO_3_0
/**
* set_extended_mbox_size() - set extended MBOX size
@@ -170,6 +188,40 @@
}
}
+/** hif_dev_set_mailbox_swap() - Set the mailbox swap from firmware
+ * @pdev : The HIF layer object
+ *
+ * Return: none
+ */
+void hif_dev_set_mailbox_swap(struct hif_sdio_dev *pdev)
+{
+ struct hif_sdio_device *hif_device = hif_dev_from_hif(pdev);
+
+ HIF_ENTER();
+
+ hif_device->swap_mailbox = true;
+
+ HIF_EXIT();
+}
+
+/** hif_dev_get_mailbox_swap() - Get the mailbox swap setting
+ * @pdev : The HIF layer object
+ *
+ * Return: true or false
+ */
+bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev)
+{
+ struct hif_sdio_device *hif_device;
+
+ HIF_ENTER();
+
+ hif_device = hif_dev_from_hif(pdev);
+
+ HIF_EXIT();
+
+ return hif_device->swap_mailbox;
+}
+
/**
* hif_dev_get_fifo_address() - get the fifo addresses for dma
* @pdev: SDIO HIF object
@@ -177,22 +229,24 @@
*
* Return : 0 for success, non-zero for error
*/
-QDF_STATUS hif_dev_get_fifo_address(struct hif_sdio_dev *pdev,
- struct hif_device_mbox_info *config,
- uint32_t config_len)
+int hif_dev_get_fifo_address(struct hif_sdio_dev *pdev,
+ void *config,
+ uint32_t config_len)
{
uint32_t count;
+ struct hif_device_mbox_info *cfg =
+ (struct hif_device_mbox_info *)config;
for (count = 0; count < 4; count++)
- config->mbox_addresses[count] = HIF_MBOX_START_ADDR(count);
+ cfg->mbox_addresses[count] = HIF_MBOX_START_ADDR(count);
if (config_len >= sizeof(struct hif_device_mbox_info)) {
set_extended_mbox_window_info((uint16_t)pdev->func->device,
- config);
- return QDF_STATUS_SUCCESS;
+ cfg);
+ return 0;
}
- return QDF_STATUS_E_INVAL;
+ return -EINVAL;
}
/**
@@ -488,7 +542,7 @@
* Return 0 for success and non-zero for failure to map
*/
int hif_get_send_address(struct hif_sdio_device *pdev,
- uint8_t pipe, uint32_t *addr)
+ uint8_t pipe, unsigned long *addr)
{
uint8_t mbox_index = INVALID_MAILBOX_NUMBER;
@@ -586,9 +640,9 @@
}
/* mailbox index is saved in Endpoint member */
- HIF_INFO("%s : hdr:0x%x, len:%d, padded length: %d Mbox:0x%x",
- __func__, packet->PktInfo.AsRx.ExpectedHdr, recv_length,
- padded_length, mbox_index);
+ HIF_INFO_HI("%s : hdr:0x%x, len:%d, padded length: %d Mbox:0x%x",
+ __func__, packet->PktInfo.AsRx.ExpectedHdr, recv_length,
+ padded_length, mbox_index);
status = hif_read_write(pdev->HIFDevice,
pdev->MailBoxInfo.mbox_addresses[mbox_index],
@@ -604,11 +658,11 @@
if (status == QDF_STATUS_SUCCESS) {
HTC_FRAME_HDR *hdr = (HTC_FRAME_HDR *) packet->pBuffer;
- HIF_INFO("%s: EP:%d,Len:%d,Flag:%d,CB:0x%02X,0x%02X\n",
- __func__,
- hdr->EndpointID, hdr->PayloadLen,
- hdr->Flags, hdr->ControlBytes0,
- hdr->ControlBytes1);
+ HIF_INFO_HI("%s:EP:%d,Len:%d,Flg:%d,CB:0x%02X,0x%02X\n",
+ __func__,
+ hdr->EndpointID, hdr->PayloadLen,
+ hdr->Flags, hdr->ControlBytes0,
+ hdr->ControlBytes1);
}
}
@@ -745,7 +799,7 @@
HTC_PACKET_QUEUE recv_q, sync_comp_q;
QDF_STATUS (*rxCompletion)(void *, qdf_nbuf_t, uint8_t);
- HIF_INFO("%s: NumLookAheads: %d\n", __func__, num_look_aheads);
+ HIF_INFO_HI("%s: NumLookAheads: %d\n", __func__, num_look_aheads);
if (num_pkts_fetched)
*num_pkts_fetched = 0;
@@ -1321,33 +1375,610 @@
return status;
}
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) && \
- !defined(WITH_BACKPORTS)
+#define DEV_CHECK_RECV_YIELD(pdev) \
+ ((pdev)->CurrentDSRRecvCount >= \
+ (pdev)->HifIRQYieldParams.recv_packet_yield_count)
/**
- * hif_sdio_set_drvdata() - set wlan driver data into upper layer private
- * @func: pointer to sdio function
- * @hifdevice: pointer to hif device
+ * hif_dev_dsr_handler() - Synchronous interrupt handler
*
- * Return: non zero for success.
+ * @context: hif send context
+ *
+ * Return: 0 for success and non-zero for failure
*/
-int hif_sdio_set_drvdata(struct sdio_func *func,
- struct hif_sdio_dev *hifdevice)
+QDF_STATUS hif_dev_dsr_handler(void *context)
{
- return sdio_set_drvdata(func, hifdevice);
+ struct hif_sdio_device *pdev = (struct hif_sdio_device *)context;
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+ bool done = false;
+ bool async_proc = false;
+
+ /* reset the recv counter that tracks when we need
+ * to yield from the DSR
+ */
+ pdev->CurrentDSRRecvCount = 0;
+ /* reset counter used to flag a re-scan of IRQ
+ * status registers on the target
+ */
+ pdev->RecheckIRQStatusCnt = 0;
+
+ while (!done) {
+ status = hif_dev_process_pending_irqs(pdev, &done, &async_proc);
+ if (QDF_IS_STATUS_ERROR(status))
+ break;
+
+ if (pdev->HifIRQProcessingMode == HIF_DEVICE_IRQ_SYNC_ONLY) {
+ /* the HIF layer does not allow async IRQ processing,
+ * override the asyncProc flag
+ */
+ async_proc = false;
+ /* this will cause us to re-enter ProcessPendingIRQ()
+ * and re-read interrupt status registers.
+ * This has a nice side effect of blocking us until all
+ * async read requests are completed. This behavior is
+ * required as we do not allow ASYNC processing
+ * in interrupt handlers (like Windows CE)
+ */
+
+ if (pdev->DSRCanYield && DEV_CHECK_RECV_YIELD(pdev))
+ /* ProcessPendingIRQs() pulled enough recv
+ * messages to satisfy the yield count, stop
+ * checking for more messages and return
+ */
+ break;
+ }
+
+ if (async_proc) {
+ /* the function does some async I/O for performance,
+ * we need to exit the ISR immediately, the check below
+ * will prevent the interrupt from being
+ * Ack'd while we handle it asynchronously
+ */
+ break;
+ }
+ }
+
+ if (QDF_IS_STATUS_SUCCESS(status) && !async_proc) {
+ /* Ack the interrupt only if :
+ * 1. we did not get any errors in processing interrupts
+ * 2. there are no outstanding async processing requests
+ */
+ if (pdev->DSRCanYield) {
+ /* if the DSR can yield do not ACK the interrupt, there
+ * could be more pending messages. The HIF layer
+ * must ACK the interrupt on behalf of HTC
+ */
+ HIF_INFO("%s: Yield (RX count: %d)",
+ __func__, pdev->CurrentDSRRecvCount);
+ } else {
+ hif_ack_interrupt(pdev->HIFDevice);
+ }
+ }
+
+ return status;
}
-#else
-int hif_sdio_set_drvdata(struct sdio_func *func,
- struct hif_sdio_dev *hifdevice)
+
+/**
+ * hif_read_write() - queue a read/write request
+ * @device: pointer to hif device structure
+ * @address: address to read
+ * @buffer: buffer to hold read/write data
+ * @length: length to read/write
+ * @request: read/write/sync/async request
+ * @context: pointer to hold calling context
+ *
+ * Return: 0 on success, error number otherwise.
+ */
+QDF_STATUS
+hif_read_write(struct hif_sdio_dev *device,
+ unsigned long address,
+ char *buffer, uint32_t length,
+ uint32_t request, void *context)
{
- sdio_set_drvdata(func, hifdevice);
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+ struct bus_request *busrequest;
+
+ AR_DEBUG_ASSERT(device);
+ AR_DEBUG_ASSERT(device->func);
+ HIF_TRACE("%s: device 0x%pK addr 0x%lX buffer 0x%pK",
+ __func__, device, address, buffer);
+ HIF_TRACE("%s: len %d req 0x%X context 0x%pK",
+ __func__, length, request, context);
+
+ /*sdio r/w action is not needed when suspend, so just return */
+ if ((device->is_suspend) &&
+ (device->power_config == HIF_DEVICE_POWER_CUT)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("skip io when suspending\n"));
+ return QDF_STATUS_SUCCESS;
+ }
+ do {
+ if ((request & HIF_ASYNCHRONOUS) ||
+ (request & HIF_SYNCHRONOUS)) {
+ /* serialize all requests through the async thread */
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("%s: Execution mode: %s\n", __func__,
+ (request & HIF_ASYNCHRONOUS) ? "Async"
+ : "Synch"));
+ busrequest = hif_allocate_bus_request(device);
+ if (!busrequest) {
+ HIF_ERROR("%s:bus requests unavail", __func__);
+ HIF_ERROR("%s, addr:0x%lX, len:%d",
+ request & HIF_SDIO_READ ? "READ" :
+ "WRITE", address, length);
+ return QDF_STATUS_E_FAILURE;
+ }
+ busrequest->address = address;
+ busrequest->buffer = buffer;
+ busrequest->length = length;
+ busrequest->request = request;
+ busrequest->context = context;
+
+ add_to_async_list(device, busrequest);
+
+ if (request & HIF_SYNCHRONOUS) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("%s: queued sync req: 0x%lX\n",
+ __func__,
+ (unsigned long)busrequest));
+
+ /* wait for completion */
+ up(&device->sem_async);
+ if (down_interruptible(&busrequest->sem_req) ==
+ 0) {
+ QDF_STATUS status = busrequest->status;
+
+ HIF_TRACE("%s: sync freeing 0x%lX:0x%X",
+ __func__,
+ (unsigned long)busrequest,
+ busrequest->status);
+ HIF_TRACE("%s: freeing req: 0x%X",
+ __func__,
+ (unsigned int)request);
+ hif_free_bus_request(device,
+ busrequest);
+ return status;
+ } else {
+ /* interrupted, exit */
+ return QDF_STATUS_E_FAILURE;
+ }
+ } else {
+ HIF_TRACE("%s: queued async req: 0x%lX",
+ __func__, (unsigned long)busrequest);
+ up(&device->sem_async);
+ return QDF_STATUS_E_PENDING;
+ }
+ } else {
+ HIF_ERROR("%s: Invalid execution mode: 0x%08x",
+ __func__, (unsigned int)request);
+ status = QDF_STATUS_E_INVAL;
+ break;
+ }
+ } while (0);
+
+ return status;
+}
+
+/**
+ * hif_sdio_func_enable() - Handle device enabling as per device
+ * @device: HIF device object
+ * @func: function pointer
+ *
+ * Return success or failure
+ */
+static int hif_sdio_func_enable(struct hif_softc *ol_sc,
+ struct sdio_func *func)
+{
+ struct hif_sdio_dev *device = get_hif_device(ol_sc, func);
+
+ if (device->is_disabled) {
+ int ret = 0;
+
+ sdio_claim_host(func);
+
+ ret = hif_sdio_quirk_async_intr(ol_sc, func);
+ if (ret) {
+ HIF_ERROR("%s: Error setting async intr:%d",
+ __func__, ret);
+ sdio_release_host(func);
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ func->enable_timeout = 100;
+ ret = sdio_enable_func(func);
+ if (ret) {
+ HIF_ERROR("%s: Unable to enable function: %d",
+ __func__, ret);
+ sdio_release_host(func);
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ ret = sdio_set_block_size(func, HIF_BLOCK_SIZE);
+ if (ret) {
+ HIF_ERROR("%s: Unable to set block size 0x%X : %d\n",
+ __func__, HIF_BLOCK_SIZE, ret);
+ sdio_release_host(func);
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ ret = hif_sdio_quirk_mod_strength(ol_sc, func);
+ if (ret) {
+ HIF_ERROR("%s: Error setting mod strength : %d\n",
+ __func__, ret);
+ sdio_release_host(func);
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ sdio_release_host(func);
+ }
+
return 0;
}
-#endif /* LINUX VERSION */
-struct hif_sdio_dev *get_hif_device(struct sdio_func *func)
+/**
+ * __hif_read_write() - sdio read/write wrapper
+ * @device: pointer to hif device structure
+ * @address: address to read
+ * @buffer: buffer to hold read/write data
+ * @length: length to read/write
+ * @request: read/write/sync/async request
+ * @context: pointer to hold calling context
+ *
+ * Return: 0 on success, error number otherwise.
+ */
+static QDF_STATUS
+__hif_read_write(struct hif_sdio_dev *device,
+ uint32_t address, char *buffer,
+ uint32_t length, uint32_t request, void *context)
{
- qdf_assert(func);
+ uint8_t opcode;
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+ int ret = A_OK;
+ uint8_t *tbuffer;
+ bool bounced = false;
- return (struct hif_sdio_dev *)sdio_get_drvdata(func);
+ if (!device) {
+ HIF_ERROR("%s: device null!", __func__);
+ return QDF_STATUS_E_INVAL;
+ }
+
+ if (!device->func) {
+ HIF_ERROR("%s: func null!", __func__);
+ return QDF_STATUS_E_INVAL;
+ }
+
+ HIF_INFO_HI("%s: addr:0X%06X, len:%08d, %s, %s", __func__,
+ address, length,
+ request & HIF_SDIO_READ ? "Read " : "Write",
+ request & HIF_ASYNCHRONOUS ? "Async" : "Sync ");
+
+ do {
+ if (request & HIF_EXTENDED_IO) {
+ HIF_INFO_HI("%s: Command type: CMD53\n", __func__);
+ } else {
+ HIF_ERROR("%s: Invalid command type: 0x%08x\n",
+ __func__, request);
+ status = QDF_STATUS_E_INVAL;
+ break;
+ }
+
+ if (request & HIF_BLOCK_BASIS) {
+ /* round to whole block length size */
+ length =
+ (length / HIF_BLOCK_SIZE) *
+ HIF_BLOCK_SIZE;
+ HIF_INFO_HI("%s: Block mode (BlockLen: %d)\n",
+ __func__, length);
+ } else if (request & HIF_BYTE_BASIS) {
+ HIF_INFO_HI("%s: Byte mode (BlockLen: %d)\n",
+ __func__, length);
+ } else {
+ HIF_ERROR("%s: Invalid data mode: 0x%08x\n",
+ __func__, request);
+ status = QDF_STATUS_E_INVAL;
+ break;
+ }
+ if (request & HIF_SDIO_WRITE) {
+ hif_fixup_write_param(device, request,
+ &length, &address);
+
+ HIF_INFO_HI("addr:%08X, len:0x%08X, dummy:0x%04X\n",
+ address, length,
+ (request & HIF_DUMMY_SPACE_MASK) >> 16);
+ }
+
+ if (request & HIF_FIXED_ADDRESS) {
+ opcode = CMD53_FIXED_ADDRESS;
+ HIF_INFO_HI("%s: Addr mode: fixed 0x%X\n",
+ __func__, address);
+ } else if (request & HIF_INCREMENTAL_ADDRESS) {
+ opcode = CMD53_INCR_ADDRESS;
+ HIF_INFO_HI("%s: Address mode: Incremental 0x%X\n",
+ __func__, address);
+ } else {
+ HIF_ERROR("%s: Invalid address mode: 0x%08x\n",
+ __func__, request);
+ status = QDF_STATUS_E_INVAL;
+ break;
+ }
+
+ if (request & HIF_SDIO_WRITE) {
+#if HIF_USE_DMA_BOUNCE_BUFFER
+ if (BUFFER_NEEDS_BOUNCE(buffer)) {
+ AR_DEBUG_ASSERT(device->dma_buffer);
+ tbuffer = device->dma_buffer;
+ /* copy the write data to the dma buffer */
+ AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE);
+ if (length > HIF_DMA_BUFFER_SIZE) {
+ HIF_ERROR("%s: Invalid write len: %d\n",
+ __func__, length);
+ status = QDF_STATUS_E_INVAL;
+ break;
+ }
+ memcpy(tbuffer, buffer, length);
+ bounced = true;
+ } else {
+ tbuffer = buffer;
+ }
+#else
+ tbuffer = buffer;
+#endif
+ if (opcode == CMD53_FIXED_ADDRESS && tbuffer) {
+ ret = sdio_writesb(device->func, address,
+ tbuffer, length);
+ HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
+ __func__, ret, address, length,
+ *(int *)tbuffer);
+ } else if (tbuffer) {
+ ret = sdio_memcpy_toio(device->func, address,
+ tbuffer, length);
+ HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
+ __func__, ret, address, length,
+ *(int *)tbuffer);
+ }
+ } else if (request & HIF_SDIO_READ) {
+#if HIF_USE_DMA_BOUNCE_BUFFER
+ if (BUFFER_NEEDS_BOUNCE(buffer)) {
+ AR_DEBUG_ASSERT(device->dma_buffer);
+ AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE);
+ if (length > HIF_DMA_BUFFER_SIZE) {
+ HIF_ERROR("%s: Invalid read len: %d\n",
+ __func__, length);
+ status = QDF_STATUS_E_INVAL;
+ break;
+ }
+ tbuffer = device->dma_buffer;
+ bounced = true;
+ } else {
+ tbuffer = buffer;
+ }
+#else
+ tbuffer = buffer;
+#endif
+ if (opcode == CMD53_FIXED_ADDRESS && tbuffer) {
+ ret = sdio_readsb(device->func, tbuffer,
+ address, length);
+ HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
+ __func__, ret, address, length,
+ *(int *)tbuffer);
+ } else if (tbuffer) {
+ ret = sdio_memcpy_fromio(device->func,
+ tbuffer, address,
+ length);
+ HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n",
+ __func__, ret, address, length,
+ *(int *)tbuffer);
+ }
+#if HIF_USE_DMA_BOUNCE_BUFFER
+ if (bounced && tbuffer)
+ memcpy(buffer, tbuffer, length);
+#endif
+ } else {
+ HIF_ERROR("%s: Invalid dir: 0x%08x", __func__, request);
+ status = QDF_STATUS_E_INVAL;
+ return status;
+ }
+
+ if (ret) {
+ HIF_ERROR("%s: SDIO bus operation failed!", __func__);
+ HIF_ERROR("%s: MMC stack returned : %d", __func__, ret);
+ HIF_ERROR("%s: addr:0X%06X, len:%08d, %s, %s",
+ __func__, address, length,
+ request & HIF_SDIO_READ ? "Read " : "Write",
+ request & HIF_ASYNCHRONOUS ?
+ "Async" : "Sync");
+ status = QDF_STATUS_E_FAILURE;
+ }
+ } while (false);
+
+ return status;
+}
+
+/**
+ * async_task() - thread function to serialize all bus requests
+ * @param: pointer to hif device
+ *
+ * thread function to serialize all requests, both sync and async
+ * Return: 0 on success, error number otherwise.
+ */
+static int async_task(void *param)
+{
+ struct hif_sdio_dev *device;
+ struct bus_request *request;
+ QDF_STATUS status;
+ bool claimed = false;
+
+ device = (struct hif_sdio_dev *)param;
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!device->async_shutdown) {
+ /* wait for work */
+ if (down_interruptible(&device->sem_async) != 0) {
+ /* interrupted, exit */
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("%s: async task interrupted\n",
+ __func__));
+ break;
+ }
+ if (device->async_shutdown) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("%s: async task stopping\n",
+ __func__));
+ break;
+ }
+ /* we want to hold the host over multiple cmds
+ * if possible, but holding the host blocks
+ * card interrupts
+ */
+ qdf_spin_lock_irqsave(&device->asynclock);
+ /* pull the request to work on */
+ while (device->asyncreq) {
+ request = device->asyncreq;
+ if (request->inusenext)
+ device->asyncreq = request->inusenext;
+ else
+ device->asyncreq = NULL;
+ qdf_spin_unlock_irqrestore(&device->asynclock);
+ HIF_TRACE("%s: processing req: 0x%lX",
+ __func__, (unsigned long)request);
+
+ if (!claimed) {
+ sdio_claim_host(device->func);
+ claimed = true;
+ }
+ if (request->scatter_req) {
+ A_ASSERT(device->scatter_enabled);
+ /* pass the request to scatter routine which
+ * executes it synchronously, note, no need
+ * to free the request since scatter requests
+ * are maintained on a separate list
+ */
+ status = do_hif_read_write_scatter(device,
+ request);
+ } else {
+ /* call hif_read_write in sync mode */
+ status =
+ __hif_read_write(device,
+ request->address,
+ request->buffer,
+ request->length,
+ request->
+ request &
+ ~HIF_SYNCHRONOUS,
+ NULL);
+ if (request->request & HIF_ASYNCHRONOUS) {
+ void *context = request->context;
+
+ HIF_TRACE("%s: freeing req: 0x%lX",
+ __func__,
+ (unsigned long)request);
+ hif_free_bus_request(device, request);
+
+ HIF_TRACE("%s: completion req 0x%lX",
+ __func__,
+ (unsigned long)request);
+ device->htc_callbacks.
+ rw_compl_handler(context, status);
+ } else {
+ HIF_TRACE("%s: upping req: 0x%lX",
+ __func__,
+ (unsigned long)request);
+ request->status = status;
+ up(&request->sem_req);
+ }
+ }
+ qdf_spin_lock_irqsave(&device->asynclock);
+ }
+ qdf_spin_unlock_irqrestore(&device->asynclock);
+ if (claimed) {
+ sdio_release_host(device->func);
+ claimed = false;
+ }
+ }
+
+ complete_and_exit(&device->async_completion, 0);
+
+ return 0;
+}
+
+/**
+ * hif_disable_func() - Disable SDIO function
+ *
+ * @device: HIF device pointer
+ * @func: SDIO function pointer
+ * @reset: If this is called from resume or probe
+ *
+ * Return: 0 in case of success, else error value
+ */
+QDF_STATUS hif_disable_func(struct hif_sdio_dev *device,
+ struct sdio_func *func,
+ bool reset)
+{
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+ HIF_ENTER();
+ if (!IS_ERR(device->async_task)) {
+ init_completion(&device->async_completion);
+ device->async_shutdown = 1;
+ up(&device->sem_async);
+ wait_for_completion(&device->async_completion);
+ device->async_task = NULL;
+ sema_init(&device->sem_async, 0);
+ }
+
+ status = hif_sdio_func_disable(device, func, reset);
+ if (status == QDF_STATUS_SUCCESS)
+ device->is_disabled = true;
+
+ cleanup_hif_scatter_resources(device);
+
+ HIF_EXIT();
+
+ return status;
+}
+
+/**
+ * hif_enable_func() - Enable SDIO function
+ *
+ * @ol_sc: HIF object pointer
+ * @device: HIF device pointer
+ * @sdio_func: SDIO function pointer
+ * @resume: If this is called from resume or probe
+ *
+ * Return: 0 in case of success, else error value
+ */
+QDF_STATUS hif_enable_func(struct hif_softc *ol_sc, struct hif_sdio_dev *device,
+ struct sdio_func *func, bool resume)
+{
+ int ret = QDF_STATUS_SUCCESS;
+
+ HIF_ENTER();
+
+ if (!device) {
+ HIF_ERROR("%s: HIF device is NULL", __func__);
+ return QDF_STATUS_E_INVAL;
+ }
+
+ if (hif_sdio_func_enable(ol_sc, func))
+ return QDF_STATUS_E_FAILURE;
+
+ /* create async I/O thread */
+ if (!device->async_task && device->is_disabled) {
+ device->async_shutdown = 0;
+ device->async_task = kthread_create(async_task,
+ (void *)device,
+ "AR6K Async");
+ if (IS_ERR(device->async_task)) {
+ HIF_ERROR("%s: Error creating async task",
+ __func__);
+ return QDF_STATUS_E_FAILURE;
+ }
+ device->is_disabled = false;
+ wake_up_process(device->async_task);
+ }
+
+ if (!resume)
+ ret = hif_sdio_probe(ol_sc, func, device);
+
+ HIF_EXIT();
+
+ return ret;
}
#endif /* CONFIG_SDIO_TRANSFER_MAILBOX */
diff --git a/hif/src/sdio/transfer/mailbox.h b/hif/src/sdio/transfer/mailbox.h
index 9123d87..3e59132 100644
--- a/hif/src/sdio/transfer/mailbox.h
+++ b/hif/src/sdio/transfer/mailbox.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-2019 The Linux Foundation. All rights reserved.
*
*
*
@@ -146,11 +146,6 @@
*/
#define HIF_DUMMY_SPACE_MASK 0xFFFF0000
-/*
- * data written into the dummy space will not put into the final mbox FIFO
- */
-#define HIF_DUMMY_SPACE_MASK 0xFFFF0000
-
PREPACK struct MBOX_IRQ_PROC_REGISTERS {
uint8_t host_int_status;
uint8_t cpu_int_status;
@@ -190,4 +185,9 @@
#define DEV_REGISTERS_SIZE (sizeof(struct MBOX_IRQ_PROC_REGISTERS) + \
sizeof(struct MBOX_IRQ_ENABLE_REGISTERS) + \
sizeof(struct MBOX_COUNTER_REGISTERS))
+
+void hif_dev_dump_registers(struct hif_sdio_device *pdev,
+ struct MBOX_IRQ_PROC_REGISTERS *irq_proc,
+ struct MBOX_IRQ_ENABLE_REGISTERS *irq_en,
+ struct MBOX_COUNTER_REGISTERS *mbox_regs);
#endif /* _MAILBOX_H_ */
diff --git a/hif/src/sdio/transfer/transfer.c b/hif/src/sdio/transfer/transfer.c
index 07ebf52..a7837c8 100644
--- a/hif/src/sdio/transfer/transfer.c
+++ b/hif/src/sdio/transfer/transfer.c
@@ -91,7 +91,8 @@
unsigned char *pData;
struct hif_sendContext *sctx;
uint32_t request = hif_get_send_buffer_flags(pdev);
- uint32_t padded_length, addr = 0;
+ uint32_t padded_length;
+ unsigned long addr = 0;
int frag_count = 0, i, count, head_len;
if (hif_get_send_address(pdev, pipe, &addr)) {
@@ -381,7 +382,7 @@
HTC_RECORD_HDR *record;
HTC_LOOKAHEAD_REPORT *look_ahead;
- HIF_INFO("%s: length:%d", __func__, length);
+ HIF_INFO_HI("%s: length:%d", __func__, length);
orig_buffer = buffer;
orig_length = length;
@@ -515,7 +516,7 @@
debug_dump_bytes(orig_buffer, orig_length,
"BAD Recv Trailer");
- HIF_INFO("%s: status = %d", __func__, status);
+ HIF_INFO_HI("%s: status = %d", __func__, status);
return status;
}
diff --git a/hif/src/sdio/transfer/transfer.h b/hif/src/sdio/transfer/transfer.h
index 4a72838..73d6b53 100644
--- a/hif/src/sdio/transfer/transfer.h
+++ b/hif/src/sdio/transfer/transfer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
*
*
*
@@ -65,13 +65,8 @@
unsigned int head_data_len;
};
-void hif_dev_dump_registers(struct hif_sdio_device *pdev,
- struct MBOX_IRQ_PROC_REGISTERS *irq_proc,
- struct MBOX_IRQ_ENABLE_REGISTERS *irq_en,
- struct MBOX_COUNTER_REGISTERS *mbox_regs);
-
int hif_get_send_address(struct hif_sdio_device *pdev,
- uint8_t pipe, uint32_t *addr);
+ uint8_t pipe, unsigned long *addr);
QDF_STATUS hif_dev_alloc_and_prepare_rx_packets(struct hif_sdio_device *pdev,
uint32_t look_aheads[],
@@ -104,7 +99,11 @@
return 0;
}
#elif defined(CONFIG_SDIO_TRANSFER_ADMA)
-#error "Error - Not implemented yet"
+static inline uint32_t hif_get_send_buffer_flags(struct hif_sdio_device *pdev)
+{
+ /* ADAM-TODO */
+ return (uint32_t)HIF_WR_ASYNC_BLOCK_FIX;
+}
#endif
#endif /* __TRANSFER_H__ */
diff --git a/umac/cp_stats/core/src/wlan_cp_stats_defs.h b/umac/cp_stats/core/src/wlan_cp_stats_defs.h
index a71c392..762cdfe 100644
--- a/umac/cp_stats/core/src/wlan_cp_stats_defs.h
+++ b/umac/cp_stats/core/src/wlan_cp_stats_defs.h
@@ -47,6 +47,8 @@
* @psoc_cp_stats_lock: lock to protect object
* @cmn_stats: stats common for AP and STA devices
* @obj_stats: stats specific to AP or STA devices
+ * @legacy_stats_cb: callback to update the stats received from FW through
+ * asynchronous events.
*/
struct psoc_cp_stats {
struct wlan_objmgr_psoc *psoc_obj;
@@ -54,6 +56,7 @@
qdf_spinlock_t psoc_cp_stats_lock;
struct psoc_cmn_cp_stats *cmn_stats;
psoc_ext_cp_stats_t *obj_stats;
+ void (*legacy_stats_cb)(void *stats);
};
/**
diff --git a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h
index 5b14851..200eef9 100644
--- a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h
+++ b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h
@@ -205,6 +205,17 @@
QDF_STATUS ucfg_mc_cp_stats_set_rate_flags(struct wlan_objmgr_vdev *vdev,
enum tx_rate_info flags);
+/**
+ * ucfg_mc_cp_stats_register_lost_link_info_cb() - API to register lost link
+ * info callback
+ * @psoc: pointer to psoc object
+ * @lost_link_cp_stats_info_cb: Lost link info callback to be registered
+ *
+ */
+void ucfg_mc_cp_stats_register_lost_link_info_cb(
+ struct wlan_objmgr_psoc *psoc,
+ void (*lost_link_cp_stats_info_cb)(void *stats_ev));
+
#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD
/**
* ucfg_mc_cp_stats_register_pmo_handler() - API to register pmo handler
diff --git a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c
index a77494c..e517fb8 100644
--- a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c
+++ b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c
@@ -848,6 +848,16 @@
}
}
+static void tgt_mc_cp_send_lost_link_stats(struct wlan_objmgr_psoc *psoc,
+ struct stats_event *ev)
+{
+ struct psoc_cp_stats *psoc_cp_stats_priv;
+
+ psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc);
+ if (psoc_cp_stats_priv && psoc_cp_stats_priv->legacy_stats_cb)
+ psoc_cp_stats_priv->legacy_stats_cb(ev);
+}
+
QDF_STATUS tgt_mc_cp_stats_process_stats_event(struct wlan_objmgr_psoc *psoc,
struct stats_event *ev)
{
@@ -862,6 +872,7 @@
tgt_mc_cp_stats_extract_cca_stats(psoc, ev);
+ tgt_mc_cp_send_lost_link_stats(psoc, ev);
return QDF_STATUS_SUCCESS;
}
diff --git a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c
index 97779ef..ca85ca1 100644
--- a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c
+++ b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c
@@ -640,6 +640,21 @@
return QDF_STATUS_SUCCESS;
}
+void ucfg_mc_cp_stats_register_lost_link_info_cb(
+ struct wlan_objmgr_psoc *psoc,
+ void (*lost_link_cp_stats_info_cb)(void *stats_ev))
+{
+ struct psoc_cp_stats *psoc_cp_stats_priv;
+
+ psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc);
+ if (!psoc_cp_stats_priv) {
+ cp_stats_err("psoc cp stats object is null");
+ return;
+ }
+
+ psoc_cp_stats_priv->legacy_stats_cb = lost_link_cp_stats_info_cb;
+}
+
#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD
static QDF_STATUS
ucfg_mc_cp_stats_suspend_req_handler(struct wlan_objmgr_psoc *psoc)
diff --git a/umac/dfs/core/src/dfs.h b/umac/dfs/core/src/dfs.h
index 99ac566..4dc9c14 100644
--- a/umac/dfs/core/src/dfs.h
+++ b/umac/dfs/core/src/dfs.h
@@ -1031,8 +1031,13 @@
* @dfs_nol_ie_bitmap: The bitmap of radar affected subchannels
* in the current channel list
* to be sent in NOL IE with RCSA.
- * @dfs_is_rcsa_ie_sent To send or to not send RCSA IE.
- * @dfs_is_nol_ie_sent To send or to not send NOL IE.
+ * @dfs_is_rcsa_ie_sent: To send or to not send RCSA IE.
+ * @dfs_is_nol_ie_sent: To send or to not send NOL IE.
+ * @dfs_allow_hw_pulses: Allow/Block HW pulses. When synthetic
+ * pulses are injected, the HW pulses should
+ * be blocked and this variable should be
+ * false so that HW pulses and synthetic
+ * pulses do not get mixed up.
*/
struct wlan_dfs {
uint32_t dfs_debug_mask;
@@ -1174,6 +1179,9 @@
bool dfs_is_rcsa_ie_sent;
bool dfs_is_nol_ie_sent;
bool dfs_agile_precac_enable;
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
+ bool dfs_allow_hw_pulses;
+#endif
};
#if defined(QCA_SUPPORT_AGILE_DFS) || defined(ATH_SUPPORT_ZERO_CAC_DFS)
diff --git a/umac/dfs/core/src/dfs_partial_offload_radar.h b/umac/dfs/core/src/dfs_partial_offload_radar.h
index 40b94f6..4fb8f7e 100644
--- a/umac/dfs/core/src/dfs_partial_offload_radar.h
+++ b/umac/dfs/core/src/dfs_partial_offload_radar.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
*
*
* Permission to use, copy, modify, and/or distribute this software for
@@ -197,4 +197,56 @@
{
}
#endif
+
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
+/**
+ * dfs_allow_hw_pulses() - Set or unset dfs_allow_hw_pulses
+ * which allow or disallow HW pulses.
+ * @dfs: Pointer to DFS pdev object.
+ * @allow_hw_pulses: allow/disallow synthetic pulse detection true/false.
+ *
+ * Return: void
+ */
+void dfs_allow_hw_pulses(struct wlan_dfs *dfs, bool allow_hw_pulses);
+#else
+static inline void dfs_allow_hw_pulses(struct wlan_dfs *dfs,
+ bool allow_hw_pulses)
+{
+}
+#endif
+
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
+/**
+ * dfs_is_hw_pulses_allowed() - Check if HW pulses are allowed or not.
+ * @pdev: Pointer to DFS pdev object.
+ *
+ * Return: bool
+ */
+bool dfs_is_hw_pulses_allowed(struct wlan_dfs *dfs);
+#else
+static inline bool dfs_is_hw_pulses_allowed(struct wlan_dfs *dfs)
+{
+ return true;
+}
+#endif
+
+/**
+ * dfs_inject_synthetic_pulse_sequence() - Inject the synthetic pulse to the
+ * phyerror processing algorithm.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Pointer to buffer of pulses.
+ *
+ * Return: QDF_STATUS
+ */
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
+QDF_STATUS dfs_inject_synthetic_pulse_sequence(struct wlan_dfs *dfs,
+ unsigned char *buf);
+#else
+static inline
+QDF_STATUS dfs_inject_synthetic_pulse_sequence(struct wlan_dfs *dfs,
+ unsigned char *buf)
+{
+ return QDF_STATUS_SUCCESS;
+}
+#endif /* WLAN_DFS_PARTIAL_OFFLOAD && WLAN_DFS_SYNTHETIC_RADAR */
#endif /* _DFS_PARTIAL_OFFLOAD_RADAR_H_ */
diff --git a/umac/dfs/core/src/misc/dfs.c b/umac/dfs/core/src/misc/dfs.c
index 9420f39..0c2a3a0 100644
--- a/umac/dfs/core/src/misc/dfs.c
+++ b/umac/dfs/core/src/misc/dfs.c
@@ -705,6 +705,20 @@
dfs_reset_precac_lists(dfs);
dfs_reset_etsi_precac_lists(dfs);
break;
+ case DFS_INJECT_SEQUENCE:
+ error = dfs_inject_synthetic_pulse_sequence(dfs, indata);
+ if (error)
+ dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
+ "Not injected Synthetic pulse");
+ break;
+
+ case DFS_ALLOW_HW_PULSES:
+ if (insize < sizeof(u_int8_t) || !indata) {
+ error = -EINVAL;
+ break;
+ }
+ dfs_allow_hw_pulses(dfs, !!(*(u_int8_t *)indata));
+ break;
default:
error = -EINVAL;
}
diff --git a/umac/dfs/core/src/misc/dfs_filter_init.c b/umac/dfs/core/src/misc/dfs_filter_init.c
index 7a7ba25..01f3b82 100644
--- a/umac/dfs/core/src/misc/dfs_filter_init.c
+++ b/umac/dfs/core/src/misc/dfs_filter_init.c
@@ -218,6 +218,7 @@
/*Verify : Passing NULL to qdf_timer_init().*/
dfs_main_task_timer_init(dfs);
+ dfs_allow_hw_pulses(dfs, true);
dfs_host_wait_timer_init(dfs);
WLAN_DFSQ_LOCK_CREATE(dfs);
diff --git a/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h b/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h
index a1f11c2..9cc3344 100644
--- a/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h
+++ b/umac/dfs/dispatcher/inc/wlan_dfs_ioctl.h
@@ -53,12 +53,15 @@
#define DFS_SET_DISABLE_RADAR_MARKING 25
#define DFS_GET_DISABLE_RADAR_MARKING 26
+#define DFS_INJECT_SEQUENCE 27
+#define DFS_ALLOW_HW_PULSES 28
+
/*
* Spectral IOCTLs use DFS_LAST_IOCTL as the base.
* This must always be the last IOCTL in DFS and have
* the highest value.
*/
-#define DFS_LAST_IOCTL 27
+#define DFS_LAST_IOCTL 29
#ifndef DFS_CHAN_MAX
#define DFS_CHAN_MAX 1023
@@ -271,4 +274,69 @@
WLAN_EV_NOL_FINISHED,
};
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
+/**
+ * Structure of Pulse to be injected into the DFS Module
+ * ******************************************************
+ * Header
+ * ======
+ * ----------|--------------|
+ * num_pulses| total_len_seq|
+ * ----------|--------------|
+ * Buffer Contents per pulse:
+ * ==========================
+ * ------|----------|-----------|----------|-----------|---------------|--------
+ * r_rssi|r_ext_rssi|r_rs_tstamp|r_fulltsf |fft_datalen|total_len_pulse|FFT
+ * | | | | | |Buffer..
+ * ------|----------|-----------|----------|-----------|---------------|--------
+ */
+
+/**
+ * struct synthetic_pulse - Radar Pulse Structure to be filled on reading the
+ * user file.
+ * @r_rssi: RSSI of the pulse.
+ * @r_ext_rssi: Extension Channel RSSI.
+ * @r_rs_tstamp: Timestamp.
+ * @r_fulltsf: TSF64.
+ * @fft_datalen: Total len of FFT.
+ * @total_len_pulse: Total len of the pulse.
+ * @fft_buf: Pointer to fft data.
+ */
+
+struct synthetic_pulse {
+ uint8_t r_rssi;
+ uint8_t r_ext_rssi;
+ uint32_t r_rs_tstamp;
+ uint64_t r_fulltsf;
+ uint16_t fft_datalen;
+ uint16_t total_len_pulse;
+ unsigned char *fft_buf;
+} qdf_packed;
+
+/**
+ * struct synthetic_seq - Structure to hold an array of pointers to the
+ * pulse structure.
+ * @num_pulses: Total num of pulses in the sequence.
+ * @total_len_seq: Total len of the sequence.
+ * @pulse: Array of pointers to synthetic_pulse structure.
+ */
+
+struct synthetic_seq {
+ uint8_t num_pulses;
+ uint32_t total_len_seq;
+ struct synthetic_pulse *pulse[0];
+};
+
+/**
+ * struct seq_store - Structure to hold an array of pointers to the synthetic
+ * sequence structure.
+ * @num_sequence: Total number of "sequence of pulses" in the file.
+ * @seq_arr: Array of pointers to synthetic_seq structure.
+ */
+
+struct seq_store {
+ uint8_t num_sequence;
+ struct synthetic_seq *seq_arr[0];
+};
+#endif /* WLAN_DFS_PARTIAL_OFFLOAD && WLAN_DFS_SYNTHETIC_RADAR */
#endif /* _DFS_IOCTL_H_ */
diff --git a/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h b/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h
index 9c409cc..35f6f7f 100644
--- a/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h
+++ b/umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h
@@ -345,6 +345,46 @@
}
#endif
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
+/**
+ * ucfg_dfs_allow_hw_pulses() - Set or unset dfs-allow_hw_pulses
+ * which isolates synthetic radar pulse detection from actual radar detection.
+ * @pdev: Pointer to DFS pdev object.
+ * @allow_hw_pulses: Allow synthetic pulse detection true/false.
+ *
+ * Wrapper function for dfs_set_allow_hw_pulses().
+ * This function called from outside of dfs component.
+ *
+ * Return: void
+ */
+void ucfg_dfs_allow_hw_pulses(struct wlan_objmgr_pdev *pdev,
+ bool allow_hw_pulses);
+
+/**
+ * ucfg_dfs_is_hw_pulses_allowed() - Check if actual radar detection is allowed
+ * or synthetic pulse detection is enabled.
+ * @pdev: Pointer to DFS pdev object.
+ *
+ * Wrapper function for dfs_is_hw_pulses_allowed().
+ * This function called from outside of dfs component.
+ *
+ * Return: bool
+ */
+bool ucfg_dfs_is_hw_pulses_allowed(struct wlan_objmgr_pdev *pdev);
+#else
+static inline
+void ucfg_dfs_allow_hw_pulses(struct wlan_objmgr_pdev *pdev,
+ bool allow_hw_pulses)
+{
+}
+
+static inline
+bool ucfg_dfs_is_hw_pulses_allowed(struct wlan_objmgr_pdev *pdev)
+{
+ return true;
+}
+#endif
+
/**
* ucfg_dfs_get_override_status_timeout() - Get the value of host dfs status
* wait timeout.
diff --git a/umac/dfs/dispatcher/src/wlan_dfs_ucfg_api.c b/umac/dfs/dispatcher/src/wlan_dfs_ucfg_api.c
index 1709fe1..2f284b4 100644
--- a/umac/dfs/dispatcher/src/wlan_dfs_ucfg_api.c
+++ b/umac/dfs/dispatcher/src/wlan_dfs_ucfg_api.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
*
*
* Permission to use, copy, modify, and/or distribute this software for
@@ -310,3 +310,42 @@
qdf_export_symbol(ucfg_dfs_get_override_status_timeout);
#endif
+
+#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(WLAN_DFS_SYNTHETIC_RADAR)
+void ucfg_dfs_allow_hw_pulses(struct wlan_objmgr_pdev *pdev,
+ bool allow_hw_pulses)
+{
+ struct wlan_dfs *dfs;
+
+ if (!tgt_dfs_is_pdev_5ghz(pdev))
+ return;
+
+ dfs = wlan_pdev_get_dfs_obj(pdev);
+ if (!dfs) {
+ dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+ return;
+ }
+
+ dfs_allow_hw_pulses(dfs, allow_hw_pulses);
+}
+
+qdf_export_symbol(ucfg_dfs_allow_hw_pulses);
+
+bool ucfg_dfs_is_hw_pulses_allowed(struct wlan_objmgr_pdev *pdev)
+{
+ struct wlan_dfs *dfs;
+
+ if (!tgt_dfs_is_pdev_5ghz(pdev))
+ return false;
+
+ dfs = wlan_pdev_get_dfs_obj(pdev);
+ if (!dfs) {
+ dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
+ return false;
+ }
+
+ return dfs_is_hw_pulses_allowed(dfs);
+}
+
+qdf_export_symbol(ucfg_dfs_is_hw_pulses_allowed);
+#endif
diff --git a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h
index 764349b..4062353 100644
--- a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h
+++ b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h
@@ -1267,6 +1267,9 @@
* timeout.
* @dfs_reset_spoof_test: Checks if radar detection is enabled.
* @dfs_is_disable_radar_marking_set: Check if dis_radar_marking param is set.
+ * @dfs_allow_hw_pulses: Set or unset dfs_allow_hw_pulses which
+ * allow or disallow HW pulses.
+ * @dfs_is_hw_pulses_allowed: Check if HW pulses are allowed or not.
*/
struct wlan_lmac_if_dfs_rx_ops {
QDF_STATUS (*dfs_get_radars)(struct wlan_objmgr_pdev *pdev);
@@ -1375,6 +1378,9 @@
bool value);
QDF_STATUS (*dfs_is_bw_reduction_needed)(struct wlan_objmgr_pdev *pdev,
bool *bw_reduce);
+ void (*dfs_allow_hw_pulses)(struct wlan_objmgr_pdev *pdev,
+ bool allow_hw_pulses);
+ bool (*dfs_is_hw_pulses_allowed)(struct wlan_objmgr_pdev *pdev);
};
/**
diff --git a/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c b/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c
index d5ada6a..c4d8433 100644
--- a/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c
+++ b/umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c
@@ -428,6 +428,10 @@
utils_dfs_bw_reduce;
dfs_rx_ops->dfs_is_bw_reduction_needed =
utils_dfs_is_bw_reduce;
+ dfs_rx_ops->dfs_allow_hw_pulses =
+ ucfg_dfs_allow_hw_pulses;
+ dfs_rx_ops->dfs_is_hw_pulses_allowed =
+ ucfg_dfs_is_hw_pulses_allowed;
register_precac_auto_chan_rx_ops(dfs_rx_ops);