qcacmn: SDIO HIF Layer refactor
1. Add ADMA channel registration APIs 2. Add ADMA support for TX and RX path 3. Remove async_task thread for ADMA, since transfer thread is present in the SDIO AL layer 4. Move hif functions which are legacy only to mailbox.c 5. get_hif_device and hif_sdio_set_drvdata changes for mailbox and adma 6. Add rx buffer allocation offload work Change-Id: Ie98b302176381035b1bd590ef35a977aeef4f09c CRs-Fixed: 2274807
This commit is contained in:

committed by
nshrivas

parent
f4ab426805
commit
cf1f958b5f
@@ -465,8 +465,8 @@ enum hif_disable_type {
|
||||
* 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
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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
|
||||
|
@@ -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_ */
|
||||
|
@@ -208,127 +208,6 @@ QDF_STATUS hif_dev_enable_interrupts(struct hif_sdio_device *pdev)
|
||||
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 @@ QDF_STATUS hif_dev_setup(struct hif_sdio_device *pdev)
|
||||
|
||||
status = hif_dev_setup_device(pdev);
|
||||
|
||||
|
||||
if (status != QDF_STATUS_SUCCESS) {
|
||||
HIF_ERROR("%s: device specific setup failed", __func__);
|
||||
return QDF_STATUS_E_INVAL;
|
||||
|
@@ -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);
|
||||
|
||||
@@ -60,8 +61,8 @@ void hif_dev_unmask_interrupts(struct hif_sdio_device *pdev);
|
||||
|
||||
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,
|
||||
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 @@ void hif_dev_set_mailbox_swap(struct hif_sdio_dev *pdev);
|
||||
|
||||
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_ */
|
||||
|
@@ -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 @@ struct hif_sdio_device {
|
||||
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 @@ struct hif_sdio_device {
|
||||
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)
|
||||
|
@@ -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 @@ void hif_sdio_disable_bus(struct hif_softc *hif_sc)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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 @@ int hif_sdio_device_inserted(struct hif_softc *ol_sc,
|
||||
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,
|
||||
|
@@ -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 {
|
||||
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 bus_request {
|
||||
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 @@ struct hif_sdio_dev {
|
||||
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_configure_device(struct hif_softc *ol_sc,
|
||||
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 @@ static inline QDF_STATUS do_hif_read_write_scatter(struct hif_sdio_dev *device,
|
||||
#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,
|
||||
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);
|
||||
|
@@ -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_param(brokenirq, uint, 0644);
|
||||
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,24 +99,18 @@ MODULE_PARM_DESC(brokenirq,
|
||||
*
|
||||
* 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;
|
||||
return;
|
||||
}
|
||||
|
||||
mask = (SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT);
|
||||
@@ -127,7 +120,7 @@ void hif_sdio_quirk_force_drive_strength(struct sdio_func *func)
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
value = 0;
|
||||
@@ -136,25 +129,126 @@ void hif_sdio_quirk_force_drive_strength(struct sdio_func *func)
|
||||
if (err) {
|
||||
HIF_ERROR("%s Read CCCR 0x%02X failed: %d\n",
|
||||
__func__, addr, err);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
mask = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_MASK;
|
||||
value = (value & ~mask) |
|
||||
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ void hif_sdio_quirk_force_drive_strength(struct sdio_func *func)
|
||||
*
|
||||
* 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 @@ void hif_sdio_quirk_write_cccr(struct sdio_func *func)
|
||||
|
||||
/**
|
||||
* 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 @@ void hif_sdio_quirk_write_cccr(struct sdio_func *func)
|
||||
*
|
||||
* 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 @@ int hif_sdio_quirk_mod_strength(struct sdio_func *func)
|
||||
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 @@ static int hif_cmd52_write_byte_8bit(struct sdio_func *func)
|
||||
|
||||
/**
|
||||
* 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 @@ int hif_sdio_set_bus_speed(struct sdio_func *func)
|
||||
|
||||
/**
|
||||
* 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 @@ int hif_sdio_set_bus_width(struct sdio_func *func)
|
||||
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 @@ void hif_mask_interrupt(struct hif_sdio_dev *device)
|
||||
*/
|
||||
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 @@ static void hif_irq_handler(struct sdio_func *func)
|
||||
device->htc_callbacks.dsr_handler(device->htc_callbacks.context);
|
||||
sdio_claim_host(device->func);
|
||||
atomic_set(&device->irq_handling, 0);
|
||||
|
||||
HIF_EXIT();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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 @@ MODULE_PARM_DESC(debugcccr, "Output this cccr values");
|
||||
|
||||
#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;
|
||||
@@ -171,186 +155,6 @@ ATH_DEBUG_INSTANTIATE_MODULE_VAR(hif,
|
||||
ATH_DEBUG_MASK_DEFAULTS, 0, NULL);
|
||||
#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
|
||||
@@ -380,222 +184,6 @@ void add_to_async_list(struct hif_sdio_dev *device,
|
||||
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 @@ static int async_task(void *param)
|
||||
* 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 @@ static int sdio_enable4bits(struct hif_sdio_dev *device, int enable)
|
||||
|
||||
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,7 +279,7 @@ static QDF_STATUS hif_disable_func(struct hif_sdio_dev *device,
|
||||
*
|
||||
* Return: 0 for success and non-zero for failure
|
||||
*/
|
||||
static A_STATUS hif_sdio_probe(struct hif_softc *ol_sc,
|
||||
A_STATUS hif_sdio_probe(struct hif_softc *ol_sc,
|
||||
struct sdio_func *func,
|
||||
struct hif_sdio_dev *device)
|
||||
{
|
||||
@@ -794,7 +361,9 @@ static A_STATUS hif_sdio_probe(struct hif_softc *ol_sc,
|
||||
goto err_attach1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
ret = hif_dev_register_channels(device, func);
|
||||
|
||||
return ret;
|
||||
|
||||
err_attach1:
|
||||
if (scn->ramdump_base)
|
||||
@@ -803,48 +372,9 @@ err_attach1:
|
||||
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 @@ power_state_change_notify(struct hif_softc *ol_sc,
|
||||
|
||||
/**
|
||||
* 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 @@ void hif_sdio_shutdown(struct hif_softc *hif_ctx)
|
||||
|
||||
/**
|
||||
* 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 @@ static int hif_device_inserted(struct hif_softc *ol_sc,
|
||||
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 @@ static int hif_device_inserted(struct hif_softc *ol_sc,
|
||||
|
||||
/* 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 @@ static int hif_device_inserted(struct hif_softc *ol_sc,
|
||||
*/
|
||||
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 @@ void hif_ack_interrupt(struct hif_sdio_dev *device)
|
||||
* @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 @@ void hif_free_bus_request(struct hif_sdio_dev *device,
|
||||
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 @@ int hif_device_resume(struct hif_softc *ol_sc, struct device *dev)
|
||||
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 @@ static A_STATUS hif_sdio_remove(void *context, void *hif_handle)
|
||||
|
||||
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 @@ static void hif_device_removed(struct sdio_func *func)
|
||||
|
||||
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 @@ static void hif_device_removed(struct sdio_func *func)
|
||||
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 @@ static struct hif_sdio_dev *add_hif_device(struct sdio_func *func)
|
||||
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 @@ int hif_sdio_device_inserted(struct hif_softc *ol_sc,
|
||||
|
||||
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);
|
||||
}
|
||||
|
858
hif/src/sdio/transfer/adma.c
Normal file
858
hif/src/sdio/transfer/adma.c
Normal file
@@ -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);
|
||||
}
|
63
hif/src/sdio/transfer/adma.h
Normal file
63
hif/src/sdio/transfer/adma.h
Normal file
@@ -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
|
@@ -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 @@ static void set_extended_mbox_window_info(uint16_t manf_id,
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 @@ static void set_extended_mbox_window_info(uint16_t manf_id,
|
||||
*
|
||||
* 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,
|
||||
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 @@ static uint8_t hif_dev_map_mail_box_to_pipe(struct hif_sdio_device *pdev,
|
||||
* 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,7 +640,7 @@ static QDF_STATUS hif_dev_recv_packet(struct hif_sdio_device *pdev,
|
||||
}
|
||||
|
||||
/* mailbox index is saved in Endpoint member */
|
||||
HIF_INFO("%s : hdr:0x%x, len:%d, padded length: %d Mbox:0x%x",
|
||||
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);
|
||||
|
||||
@@ -604,7 +658,7 @@ static QDF_STATUS hif_dev_recv_packet(struct hif_sdio_device *pdev,
|
||||
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",
|
||||
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,
|
||||
@@ -745,7 +799,7 @@ QDF_STATUS hif_dev_recv_message_pending_handler(struct hif_sdio_device *pdev,
|
||||
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 @@ QDF_STATUS hif_dev_process_pending_irqs(struct hif_sdio_device *pdev,
|
||||
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 */
|
||||
|
@@ -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 @@ struct devRegisters {
|
||||
#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_ */
|
||||
|
@@ -91,7 +91,8 @@ QDF_STATUS hif_dev_send_buffer(struct hif_sdio_device *pdev, uint32_t xfer_id,
|
||||
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 @@ QDF_STATUS hif_dev_process_trailer(struct hif_sdio_device *pdev,
|
||||
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 @@ QDF_STATUS hif_dev_process_trailer(struct hif_sdio_device *pdev,
|
||||
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;
|
||||
}
|
||||
|
@@ -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 @@ struct hif_sendContext {
|
||||
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 @@ static inline uint32_t hif_get_send_buffer_flags(struct hif_sdio_device *pdev)
|
||||
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__ */
|
||||
|
Reference in New Issue
Block a user