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:
Visweswara Tanuku
2018-12-20 18:25:58 +05:30
committed by nshrivas
parent f4ab426805
commit cf1f958b5f
18 changed files with 1943 additions and 920 deletions

View File

@@ -465,8 +465,8 @@ enum hif_disable_type {
* enum hif_device_config_opcode: configure mode * enum hif_device_config_opcode: configure mode
* *
* @HIF_DEVICE_POWER_STATE: device power state * @HIF_DEVICE_POWER_STATE: device power state
* @HIF_DEVICE_GET_MBOX_BLOCK_SIZE: get mbox block size * @HIF_DEVICE_GET_BLOCK_SIZE: get block size
* @HIF_DEVICE_GET_MBOX_ADDR: get mbox block address * @HIF_DEVICE_GET_ADDR: get block address
* @HIF_DEVICE_GET_PENDING_EVENTS_FUNC: get pending events functions * @HIF_DEVICE_GET_PENDING_EVENTS_FUNC: get pending events functions
* @HIF_DEVICE_GET_IRQ_PROC_MODE: get irq proc mode * @HIF_DEVICE_GET_IRQ_PROC_MODE: get irq proc mode
* @HIF_DEVICE_GET_RECV_EVENT_MASK_UNMASK_FUNC: receive event function * @HIF_DEVICE_GET_RECV_EVENT_MASK_UNMASK_FUNC: receive event function

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -25,8 +25,8 @@
#include "if_sdio.h" #include "if_sdio.h"
/** /**
* hif_initialize_sdio_ops() - initialize the pci ops * hif_initialize_sdio_ops() - initialize the sdio ops
* @bus_ops: hif_bus_ops table pointer to initialize * @hif_sc: hif soft context
* *
* Return: QDF_STATUS_SUCCESS * Return: QDF_STATUS_SUCCESS
*/ */

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -26,7 +26,7 @@
#include "hif.h" #include "hif.h"
#include "if_sdio.h" #include "if_sdio.h"
#include "regtable_sdio.h" #include "regtable_sdio.h"
#include "hif_sdio_dev.h"
#include "qdf_module.h" #include "qdf_module.h"
#define CPU_DBG_SEL_ADDRESS 0x00000483 #define CPU_DBG_SEL_ADDRESS 0x00000483

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * 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_AR6320_BASE 0x500
#define MANUFACTURER_ID_QCA9377_BASE 0x700 #define MANUFACTURER_ID_QCA9377_BASE 0x700
#define MANUFACTURER_ID_QCA9379_BASE 0x800 #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_BASE_MASK 0xFF00
#define MANUFACTURER_ID_AR6K_REV_MASK 0x00FF #define MANUFACTURER_ID_AR6K_REV_MASK 0x00FF
#define FUNCTION_CLASS 0x0 #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_ */ #endif /* _HIF_SDIO_COMMON_H_ */

View File

@@ -208,127 +208,6 @@ QDF_STATUS hif_dev_enable_interrupts(struct hif_sdio_device *pdev)
return status; 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. * hif_dev_setup() - set up sdio device.
* @pDev: sdio device context * @pDev: sdio device context
@@ -346,7 +225,6 @@ QDF_STATUS hif_dev_setup(struct hif_sdio_device *pdev)
status = hif_dev_setup_device(pdev); status = hif_dev_setup_device(pdev);
if (status != QDF_STATUS_SUCCESS) { if (status != QDF_STATUS_SUCCESS) {
HIF_ERROR("%s: device specific setup failed", __func__); HIF_ERROR("%s: device specific setup failed", __func__);
return QDF_STATUS_E_INVAL; return QDF_STATUS_E_INVAL;

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -26,6 +26,7 @@
#include <hif.h> #include <hif.h>
#include "athstartpack.h" #include "athstartpack.h"
#include "hif_internal.h" #include "hif_internal.h"
#include "if_sdio.h"
struct hif_sdio_device *hif_dev_from_hif(struct hif_sdio_dev *hif_device); 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); int hif_dev_setup_device(struct hif_sdio_device *pdev);
QDF_STATUS hif_dev_get_fifo_address(struct hif_sdio_dev *pdev, int hif_dev_get_fifo_address(struct hif_sdio_dev *pdev,
struct hif_device_mbox_info *config, void *config,
uint32_t config_len); uint32_t config_len);
void hif_dev_get_block_size(void *config); 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); bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev);
int hif_sdio_set_drvdata(struct sdio_func *func, QDF_STATUS hif_read_write(struct hif_sdio_dev *device, unsigned long address,
struct hif_sdio_dev *hifdevice); 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_ */ #endif /* HIF_SDIO_DEV_H_ */

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -28,7 +28,7 @@
#if defined(CONFIG_SDIO_TRANSFER_MAILBOX) #if defined(CONFIG_SDIO_TRANSFER_MAILBOX)
#include <transfer/mailbox.h> #include <transfer/mailbox.h>
#elif defined(CONFIG_SDIO_TRANSFER_ADMA) #elif defined(CONFIG_SDIO_TRANSFER_ADMA)
#error "Error - Not implemented yet" #include <transfer/adma.h>
#else #else
#error "Error - Invalid transfer method" #error "Error - Invalid transfer method"
#endif #endif
@@ -54,7 +54,6 @@ struct hif_sdio_device {
qdf_spinlock_t TxLock; qdf_spinlock_t TxLock;
qdf_spinlock_t RxLock; qdf_spinlock_t RxLock;
struct hif_msg_callbacks hif_callbacks; struct hif_msg_callbacks hif_callbacks;
struct hif_device_mbox_info MailBoxInfo;
uint32_t BlockSize; uint32_t BlockSize;
uint32_t BlockMask; uint32_t BlockMask;
enum hif_device_irq_mode HifIRQProcessingMode; enum hif_device_irq_mode HifIRQProcessingMode;
@@ -65,8 +64,11 @@ struct hif_sdio_device {
int RecheckIRQStatusCnt; int RecheckIRQStatusCnt;
uint32_t RecvStateFlags; uint32_t RecvStateFlags;
void *pTarget; void *pTarget;
bool swap_mailbox;
struct devRegisters devRegisters; 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) #define LOCK_HIF_DEV(device) qdf_spin_lock(&(device)->Lock)

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * 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); 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);
} }
/** /**

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * 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); const struct sdio_device_id *id);
void hif_sdio_stop(struct hif_softc *hif_ctx); void hif_sdio_stop(struct hif_softc *hif_ctx);
void hif_sdio_shutdown(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_suspend(struct hif_softc *ol_sc, struct device *dev);
int hif_device_resume(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, void hif_register_tbl_attach(struct hif_softc *scn,

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -35,6 +35,7 @@
#include <qdf_status.h> #include <qdf_status.h>
#include <qdf_timer.h> #include <qdf_timer.h>
#include <qdf_atomic.h> #include <qdf_atomic.h>
#include <qdf_list.h>
#include "hif.h" #include "hif.h"
#include "hif_debug.h" #include "hif_debug.h"
#include "hif_sdio_common.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 *next; /* link list of available requests */
struct bus_request *inusenext; /* link list of in use requests */ struct bus_request *inusenext; /* link list of in use requests */
struct semaphore sem_req; struct semaphore sem_req;
uint32_t address; /* request data */ unsigned long address; /* request data */
char *buffer; char *buffer;
uint32_t length; uint32_t length;
uint32_t request; uint32_t request;
@@ -176,6 +177,14 @@ struct bus_request {
struct HIF_SCATTER_REQ_PRIV *scatter_req; 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 hif_sdio_dev {
struct sdio_func *func; struct sdio_func *func;
qdf_spinlock_t asynclock; qdf_spinlock_t asynclock;
@@ -201,6 +210,15 @@ struct hif_sdio_dev {
const struct sdio_device_id *id; const struct sdio_device_id *id;
struct mmc_host *host; struct mmc_host *host;
void *htc_context; 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 { 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, QDF_STATUS hif_attach_htc(struct hif_sdio_dev *device,
struct htc_callbacks *callbacks); 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_ack_interrupt(struct hif_sdio_dev *device);
void hif_mask_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_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 { struct _HIF_SCATTER_ITEM {
u_int8_t *buffer; /* CPU accessible address of buffer */ 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) \ #define SDIO_SET_CMD52_WRITE_ARG(arg, func, address, value) \
SDIO_SET_CMD52_ARG(arg, 1, (func), 0, 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_force_drive_strength(struct hif_softc *ol_sc,
void hif_sdio_quirk_write_cccr(struct sdio_func *func);
int hif_sdio_quirk_mod_strength(struct sdio_func *func);
int hif_sdio_quirk_async_intr(struct sdio_func *func);
int hif_sdio_set_bus_speed(struct sdio_func *func);
int hif_sdio_set_bus_width(struct sdio_func *func);
int hif_sdio_func_enable(struct hif_sdio_dev *device,
struct sdio_func *func); 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, QDF_STATUS hif_sdio_func_disable(struct hif_sdio_dev *device,
struct sdio_func *func, struct sdio_func *func,
bool reset); bool reset);

View File

@@ -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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * 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, MODULE_PARM_DESC(brokenirq,
"Set as 1 to use polling method instead of interrupt mode"); "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 * hif_sdio_force_drive_strength() - Set SDIO drive strength
* @ol_sc: softc instance
* @func: pointer to sdio_func * @func: pointer to sdio_func
* *
* This function forces the driver strength of the SDIO * This function forces the driver strength of the SDIO
@@ -100,24 +99,18 @@ MODULE_PARM_DESC(brokenirq,
* *
* Return: none. * 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; int err = 0;
unsigned char value = 0; unsigned char value = 0;
uint32_t mask = 0, addr = SDIO_CCCR_DRIVE_STRENGTH; 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); err = func0_cmd52_read_byte(func->card, addr, &value);
if (err) { if (err) {
HIF_ERROR("%s: read driver strength 0x%02X fail %d\n", HIF_ERROR("%s: read driver strength 0x%02X fail %d\n",
__func__, addr, err); __func__, addr, err);
break; return;
} }
mask = (SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT); 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: write driver strength failed", __func__);
HIF_ERROR("%s: 0x%02X to 0x%02X failed: %d\n", __func__, HIF_ERROR("%s: 0x%02X to 0x%02X failed: %d\n", __func__,
(uint32_t)value, addr, err); (uint32_t)value, addr, err);
break; return;
} }
value = 0; value = 0;
@@ -136,25 +129,126 @@ void hif_sdio_quirk_force_drive_strength(struct sdio_func *func)
if (err) { if (err) {
HIF_ERROR("%s Read CCCR 0x%02X failed: %d\n", HIF_ERROR("%s Read CCCR 0x%02X failed: %d\n",
__func__, addr, err); __func__, addr, err);
break; return;
} }
mask = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_MASK; mask = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_MASK;
value = (value & ~mask) | value = (value & ~mask) | CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C | CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C |
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D; CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D;
err = func0_cmd52_write_byte(func->card, addr, value); err = func0_cmd52_write_byte(func->card, addr, value);
if (err) if (err)
HIF_ERROR("%s Write CCCR 0x%02X to 0x%02X failed: %d\n", HIF_ERROR("%s Write CCCR 0x%02X to 0x%02X failed: %d\n",
__func__, addr, value, err); __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 * hif_sdio_quirk_write_cccr() - write a desired CCCR register
* @ol_sc: softc instance
* @func: pointer to sdio_func * @func: pointer to sdio_func
* *
* The values are taken from the module parameter writecccr * 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. * 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; 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 * hif_sdio_quirk_mod_strength() - write a desired CCCR register
* @ol_sc: softc instance
* @func: pointer to sdio_func * @func: pointer to sdio_func
* *
* The values are taken from the module parameter writecccr * 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. * 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; int ret = 0;
uint32_t addr, value; 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; uint16_t manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK;
if (!modstrength) /* TODO: Dont set this : scn is not popolated yet */ 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; 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 #if KERNEL_VERSION(3, 4, 0) <= LINUX_VERSION_CODE
#ifdef SDIO_BUS_WIDTH_8BIT #ifdef SDIO_BUS_WIDTH_8BIT
static int hif_cmd52_write_byte_8bit(struct sdio_func *func) 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 * hif_sdio_set_bus_speed() - Set the sdio bus speed
* @ol_sc: softc instance
* @func: pointer to sdio_func * @func: pointer to sdio_func
* *
* Return: 0 on success, error number otherwise. * 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; 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; uint16_t manfid;
manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; 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 * hif_set_bus_width() - Set the sdio bus width
* @ol_sc: softc instance
* @func: pointer to sdio_func * @func: pointer to sdio_func
* *
* Return: 0 on success, error number otherwise. * 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; int ret = 0;
uint16_t manfid; uint16_t manfid;
uint8_t data = 0; 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; 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; 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 * 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) static void hif_irq_handler(struct sdio_func *func)
{ {
struct hif_sdio_dev *device; struct hif_sdio_dev *device = get_hif_device(NULL, func);
HIF_ENTER();
device = get_hif_device(func);
atomic_set(&device->irq_handling, 1); atomic_set(&device->irq_handling, 1);
/* release the host during intr so we can use /* release the host during intr so we can use
* it when we process cmds * 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); device->htc_callbacks.dsr_handler(device->htc_callbacks.context);
sdio_claim_host(device->func); sdio_claim_host(device->func);
atomic_set(&device->irq_handling, 0); atomic_set(&device->irq_handling, 0);
HIF_EXIT();
} }
/** /**

View File

@@ -23,7 +23,6 @@
#include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include <linux/mmc/sd.h> #include <linux/mmc/sd.h>
#include <linux/kthread.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/module.h> #include <linux/module.h>
#include <qdf_atomic.h> #include <qdf_atomic.h>
@@ -38,26 +37,10 @@
#include "hif_internal.h" #include "hif_internal.h"
#include <transfer/transfer.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 HIF_USE_DMA_BOUNCE_BUFFER 1
#define ATH_MODULE_NAME hif #define ATH_MODULE_NAME hif
#include "a_debug.h" #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 #define MAX_HIF_DEVICES 2
#ifdef HIF_MBOX_SLEEP_WAR #ifdef HIF_MBOX_SLEEP_WAR
#define HIF_MIN_SLEEP_INACTIVITY_TIME_MS 50 #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 dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) #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); static void del_hif_device(struct hif_sdio_dev *device);
int reset_sdio_on_unload; int reset_sdio_on_unload;
@@ -171,186 +155,6 @@ ATH_DEBUG_INSTANTIATE_MODULE_VAR(hif,
ATH_DEBUG_MASK_DEFAULTS, 0, NULL); ATH_DEBUG_MASK_DEFAULTS, 0, NULL);
#endif #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 * add_to_async_list() - add bus reqest to async task list
* @device: pointer to hif device * @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); 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 * Setup IRQ mode for deep sleep and WoW
* Switch back to 1 bits mode when we suspend for * 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 * Re-enable async 4-bit irq mode for some host controllers
* after resume. * after resume.
*/ */
#ifdef CONFIG_SDIO_TRANSFER_MAILBOX
static int sdio_enable4bits(struct hif_sdio_dev *device, int enable) static int sdio_enable4bits(struct hif_sdio_dev *device, int enable)
{ {
int ret = 0; int ret = 0;
@@ -675,34 +264,12 @@ static int sdio_enable4bits(struct hif_sdio_dev *device, int enable)
return ret; return ret;
} }
#else
static QDF_STATUS hif_disable_func(struct hif_sdio_dev *device, static int sdio_enable4bits(struct hif_sdio_dev *device, int enable)
struct sdio_func *func,
bool reset)
{ {
QDF_STATUS status = QDF_STATUS_SUCCESS; return 0;
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;
} }
#endif
/** /**
* hif_sdio_probe() - configure sdio device * 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 * 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 sdio_func *func,
struct hif_sdio_dev *device) struct hif_sdio_dev *device)
{ {
@@ -794,7 +361,9 @@ static A_STATUS hif_sdio_probe(struct hif_softc *ol_sc,
goto err_attach1; goto err_attach1;
} }
return 0; ret = hif_dev_register_channels(device, func);
return ret;
err_attach1: err_attach1:
if (scn->ramdump_base) if (scn->ramdump_base)
@@ -803,48 +372,9 @@ err_attach1:
return ret; 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 * power_state_change_notify() - SDIO bus power notification handler
* @ol_sc: HIF device context
* @config: hif device power change type * @config: hif device power change type
* *
* Return: 0 on success, error number otherwise. * 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 * hif_configure_device() - configure sdio device
* @ol_sc: HIF device context
* @device: pointer to hif device structure * @device: pointer to hif device structure
* @opcode: configuration type * @opcode: configuration type
* @config: configuration value to set * @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 * hif_device_inserted() - hif-sdio driver probe handler
* @ol_sc: HIF device context
* @func: pointer to sdio_func * @func: pointer to sdio_func
* @id: pointer to sdio_device_id * @id: pointer to sdio_device_id
* *
@@ -1057,10 +589,10 @@ static int hif_device_inserted(struct hif_softc *ol_sc,
if (hifdevice && if (hifdevice &&
hifdevice->power_config == HIF_DEVICE_POWER_CUT && hifdevice->power_config == HIF_DEVICE_POWER_CUT &&
hifdevice->host == func->card->host) { hifdevice->host == func->card->host) {
device = get_hif_device(func); device = get_hif_device(ol_sc, func);
hifdevice->func = func; hifdevice->func = func;
hifdevice->power_config = HIF_DEVICE_POWER_UP; 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) { if (device->is_suspend) {
HIF_INFO("%s: Resume from suspend", __func__); 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 not found, then it is a new insertion, alloc and add it */
if (!device) { if (!device) {
if (add_hif_device(func) == NULL) if (!add_hif_device(ol_sc, func))
return QDF_STATUS_E_FAILURE; 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) { for (i = 0; i < MAX_HIF_DEVICES; ++i) {
if (!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); 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) if (debugcccr)
hif_dump_cccr(device); hif_dump_cccr(device);
@@ -1174,11 +707,11 @@ void hif_ack_interrupt(struct hif_sdio_dev *device)
* @pdev - HIF layer object * @pdev - HIF layer object
* @func - SDIO bus function 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) int hif_device_suspend(struct hif_softc *ol_sc, struct device *dev)
{ {
struct sdio_func *func = dev_to_sdio_func(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; mmc_pm_flag_t pm_flag = 0;
enum HIF_DEVICE_POWER_CHANGE_TYPE config; enum HIF_DEVICE_POWER_CHANGE_TYPE config;
struct mmc_host *host = func->card->host; 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; enum HIF_DEVICE_POWER_CHANGE_TYPE config;
struct hif_sdio_dev *device; struct hif_sdio_dev *device;
device = get_hif_device(func); device = get_hif_device(ol_sc, func);
if (!device) { if (!device) {
HIF_ERROR("%s: hif object is null", __func__); HIF_ERROR("%s: hif object is null", __func__);
return -EINVAL; return -EINVAL;
@@ -1367,7 +900,8 @@ static A_STATUS hif_sdio_remove(void *context, void *hif_handle)
return 0; 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; QDF_STATUS status = QDF_STATUS_SUCCESS;
struct hif_sdio_dev *device; struct hif_sdio_dev *device;
@@ -1375,7 +909,7 @@ static void hif_device_removed(struct sdio_func *func)
AR_DEBUG_ASSERT(func); AR_DEBUG_ASSERT(func);
HIF_ENTER(); HIF_ENTER();
device = get_hif_device(func); device = get_hif_device(ol_sc, func);
if (device->power_config == HIF_DEVICE_POWER_CUT) { if (device->power_config == HIF_DEVICE_POWER_CUT) {
device->func = NULL; /* func will be free by mmc stack */ 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(); 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; struct hif_sdio_dev *hifdevice = NULL;
int ret = 0; int ret = 0;
@@ -1430,8 +965,8 @@ static struct hif_sdio_dev *add_hif_device(struct sdio_func *func)
hifdevice->func = func; hifdevice->func = func;
hifdevice->power_config = HIF_DEVICE_POWER_UP; hifdevice->power_config = HIF_DEVICE_POWER_UP;
hifdevice->device_state = HIF_DEVICE_STATE_ON; hifdevice->device_state = HIF_DEVICE_STATE_ON;
ret = hif_sdio_set_drvdata(func, hifdevice); ret = hif_sdio_set_drvdata(ol_sc, func, hifdevice);
hif_info("status %d", ret); HIF_INFO("status %d", ret);
return hifdevice; return hifdevice;
} }
@@ -1552,12 +1087,12 @@ int hif_sdio_device_inserted(struct hif_softc *ol_sc,
HIF_ERROR("%s: Enter", __func__); HIF_ERROR("%s: Enter", __func__);
status = hif_device_inserted(ol_sc, func, id); status = hif_device_inserted(ol_sc, func, id);
HIF_ERROR("%s: Exit", __func__); HIF_ERROR("%s: Exit: status:%d", __func__, status);
return 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);
} }

View 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);
}

View 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

View File

@@ -1,7 +1,6 @@
/* /*
* Copyright (c) 2013-2019 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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all * above copyright notice and this permission notice appear in all
@@ -19,6 +18,7 @@
#ifdef CONFIG_SDIO_TRANSFER_MAILBOX #ifdef CONFIG_SDIO_TRANSFER_MAILBOX
#define ATH_MODULE_NAME hif #define ATH_MODULE_NAME hif
#include <linux/kthread.h>
#include <qdf_types.h> #include <qdf_types.h>
#include <qdf_status.h> #include <qdf_status.h>
#include <qdf_timer.h> #include <qdf_timer.h>
@@ -44,6 +44,24 @@
#include "regtable.h" #include "regtable.h"
#include "transfer.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 #ifdef SDIO_3_0
/** /**
* set_extended_mbox_size() - set extended MBOX size * 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 * hif_dev_get_fifo_address() - get the fifo addresses for dma
* @pdev: SDIO HIF object * @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 * Return : 0 for success, non-zero for error
*/ */
QDF_STATUS hif_dev_get_fifo_address(struct hif_sdio_dev *pdev, int hif_dev_get_fifo_address(struct hif_sdio_dev *pdev,
struct hif_device_mbox_info *config, void *config,
uint32_t config_len) uint32_t config_len)
{ {
uint32_t count; uint32_t count;
struct hif_device_mbox_info *cfg =
(struct hif_device_mbox_info *)config;
for (count = 0; count < 4; count++) 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)) { if (config_len >= sizeof(struct hif_device_mbox_info)) {
set_extended_mbox_window_info((uint16_t)pdev->func->device, set_extended_mbox_window_info((uint16_t)pdev->func->device,
config); cfg);
return QDF_STATUS_SUCCESS; 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 * Return 0 for success and non-zero for failure to map
*/ */
int hif_get_send_address(struct hif_sdio_device *pdev, 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; 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 */ /* 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, __func__, packet->PktInfo.AsRx.ExpectedHdr, recv_length,
padded_length, mbox_index); 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) { if (status == QDF_STATUS_SUCCESS) {
HTC_FRAME_HDR *hdr = (HTC_FRAME_HDR *) packet->pBuffer; 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__, __func__,
hdr->EndpointID, hdr->PayloadLen, hdr->EndpointID, hdr->PayloadLen,
hdr->Flags, hdr->ControlBytes0, 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; HTC_PACKET_QUEUE recv_q, sync_comp_q;
QDF_STATUS (*rxCompletion)(void *, qdf_nbuf_t, uint8_t); 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) if (num_pkts_fetched)
*num_pkts_fetched = 0; *num_pkts_fetched = 0;
@@ -1321,33 +1375,610 @@ QDF_STATUS hif_dev_process_pending_irqs(struct hif_sdio_device *pdev,
return status; return status;
} }
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) && \ #define DEV_CHECK_RECV_YIELD(pdev) \
!defined(WITH_BACKPORTS) ((pdev)->CurrentDSRRecvCount >= \
(pdev)->HifIRQYieldParams.recv_packet_yield_count)
/** /**
* hif_sdio_set_drvdata() - set wlan driver data into upper layer private * hif_dev_dsr_handler() - Synchronous interrupt handler
* @func: pointer to sdio function
* @hifdevice: pointer to hif device
* *
* 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, QDF_STATUS hif_dev_dsr_handler(void *context)
struct hif_sdio_dev *hifdevice)
{ {
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; 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 */ #endif /* CONFIG_SDIO_TRANSFER_MAILBOX */

View File

@@ -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 #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 { PREPACK struct MBOX_IRQ_PROC_REGISTERS {
uint8_t host_int_status; uint8_t host_int_status;
uint8_t cpu_int_status; uint8_t cpu_int_status;
@@ -190,4 +185,9 @@ struct devRegisters {
#define DEV_REGISTERS_SIZE (sizeof(struct MBOX_IRQ_PROC_REGISTERS) + \ #define DEV_REGISTERS_SIZE (sizeof(struct MBOX_IRQ_PROC_REGISTERS) + \
sizeof(struct MBOX_IRQ_ENABLE_REGISTERS) + \ sizeof(struct MBOX_IRQ_ENABLE_REGISTERS) + \
sizeof(struct MBOX_COUNTER_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_ */ #endif /* _MAILBOX_H_ */

View File

@@ -91,7 +91,8 @@ QDF_STATUS hif_dev_send_buffer(struct hif_sdio_device *pdev, uint32_t xfer_id,
unsigned char *pData; unsigned char *pData;
struct hif_sendContext *sctx; struct hif_sendContext *sctx;
uint32_t request = hif_get_send_buffer_flags(pdev); 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; int frag_count = 0, i, count, head_len;
if (hif_get_send_address(pdev, pipe, &addr)) { 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_RECORD_HDR *record;
HTC_LOOKAHEAD_REPORT *look_ahead; HTC_LOOKAHEAD_REPORT *look_ahead;
HIF_INFO("%s: length:%d", __func__, length); HIF_INFO_HI("%s: length:%d", __func__, length);
orig_buffer = buffer; orig_buffer = buffer;
orig_length = length; 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, debug_dump_bytes(orig_buffer, orig_length,
"BAD Recv Trailer"); "BAD Recv Trailer");
HIF_INFO("%s: status = %d", __func__, status); HIF_INFO_HI("%s: status = %d", __func__, status);
return status; return status;
} }

View File

@@ -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; 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, 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, QDF_STATUS hif_dev_alloc_and_prepare_rx_packets(struct hif_sdio_device *pdev,
uint32_t look_aheads[], uint32_t look_aheads[],
@@ -104,7 +99,11 @@ static inline uint32_t hif_get_send_buffer_flags(struct hif_sdio_device *pdev)
return 0; return 0;
} }
#elif defined(CONFIG_SDIO_TRANSFER_ADMA) #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
#endif /* __TRANSFER_H__ */ #endif /* __TRANSFER_H__ */