diff --git a/hif/inc/hif.h b/hif/inc/hif.h index de46bd4600..7d190a1dcd 100644 --- a/hif/inc/hif.h +++ b/hif/inc/hif.h @@ -465,8 +465,8 @@ enum hif_disable_type { * enum hif_device_config_opcode: configure mode * * @HIF_DEVICE_POWER_STATE: device power state - * @HIF_DEVICE_GET_MBOX_BLOCK_SIZE: get mbox block size - * @HIF_DEVICE_GET_MBOX_ADDR: get mbox block address + * @HIF_DEVICE_GET_BLOCK_SIZE: get block size + * @HIF_DEVICE_GET_ADDR: get block address * @HIF_DEVICE_GET_PENDING_EVENTS_FUNC: get pending events functions * @HIF_DEVICE_GET_IRQ_PROC_MODE: get irq proc mode * @HIF_DEVICE_GET_RECV_EVENT_MASK_UNMASK_FUNC: receive event function diff --git a/hif/src/dispatcher/multibus_sdio.c b/hif/src/dispatcher/multibus_sdio.c index 3c220be0b4..c5962a974f 100644 --- a/hif/src/dispatcher/multibus_sdio.c +++ b/hif/src/dispatcher/multibus_sdio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -25,8 +25,8 @@ #include "if_sdio.h" /** - * hif_initialize_sdio_ops() - initialize the pci ops - * @bus_ops: hif_bus_ops table pointer to initialize + * hif_initialize_sdio_ops() - initialize the sdio ops + * @hif_sc: hif soft context * * Return: QDF_STATUS_SUCCESS */ diff --git a/hif/src/sdio/hif_diag_reg_access.c b/hif/src/sdio/hif_diag_reg_access.c index 0f4d50c83a..30e363552f 100644 --- a/hif/src/sdio/hif_diag_reg_access.c +++ b/hif/src/sdio/hif_diag_reg_access.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -26,7 +26,7 @@ #include "hif.h" #include "if_sdio.h" #include "regtable_sdio.h" - +#include "hif_sdio_dev.h" #include "qdf_module.h" #define CPU_DBG_SEL_ADDRESS 0x00000483 diff --git a/hif/src/sdio/hif_sdio_common.h b/hif/src/sdio/hif_sdio_common.h index dc0900da89..21977f2a70 100644 --- a/hif/src/sdio/hif_sdio_common.h +++ b/hif/src/sdio/hif_sdio_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -26,11 +26,13 @@ #define MANUFACTURER_ID_AR6320_BASE 0x500 #define MANUFACTURER_ID_QCA9377_BASE 0x700 #define MANUFACTURER_ID_QCA9379_BASE 0x800 -#define MANUFACTURER_ID_QCN7605_BASE 0x0000 /*TODO - GenoaSDIO */ +#define MANUFACTURER_ID_QCN7605 0x400B +#define MANUFACTURER_ID_QCN7605_BASE 0x4000 #define MANUFACTURER_ID_AR6K_BASE_MASK 0xFF00 #define MANUFACTURER_ID_AR6K_REV_MASK 0x00FF #define FUNCTION_CLASS 0x0 -#define MANUFACTURER_CODE 0x271 +#define MANUFACTURER_CODE 0x271 /* Atheros Manufacturer ID */ +#define MANUFACTURER_QC_CODE 0x70 /* QC Manufacturer ID */ #endif /* _HIF_SDIO_COMMON_H_ */ diff --git a/hif/src/sdio/hif_sdio_dev.c b/hif/src/sdio/hif_sdio_dev.c index 253cf27680..ba2277a3bb 100644 --- a/hif/src/sdio/hif_sdio_dev.c +++ b/hif/src/sdio/hif_sdio_dev.c @@ -208,127 +208,6 @@ QDF_STATUS hif_dev_enable_interrupts(struct hif_sdio_device *pdev) return status; } -#define DEV_CHECK_RECV_YIELD(pdev) \ - ((pdev)->CurrentDSRRecvCount >= \ - (pdev)->HifIRQYieldParams.recv_packet_yield_count) - -/** - * hif_dev_dsr_handler() - Synchronous interrupt handler - * - * @context: hif send context - * - * Return: 0 for success and non-zero for failure - */ -QDF_STATUS hif_dev_dsr_handler(void *context) -{ - struct hif_sdio_device *pdev = (struct hif_sdio_device *)context; - QDF_STATUS status = QDF_STATUS_SUCCESS; - bool done = false; - bool async_proc = false; - - HIF_ENTER(); - - /* reset the recv counter that tracks when we need - * to yield from the DSR - */ - pdev->CurrentDSRRecvCount = 0; - /* reset counter used to flag a re-scan of IRQ - * status registers on the target - */ - pdev->RecheckIRQStatusCnt = 0; - - while (!done) { - status = hif_dev_process_pending_irqs(pdev, &done, &async_proc); - if (QDF_IS_STATUS_ERROR(status)) - break; - - if (pdev->HifIRQProcessingMode == HIF_DEVICE_IRQ_SYNC_ONLY) { - /* the HIF layer does not allow async IRQ processing, - * override the asyncProc flag - */ - async_proc = false; - /* this will cause us to re-enter ProcessPendingIRQ() - * and re-read interrupt status registers. - * This has a nice side effect of blocking us until all - * async read requests are completed. This behavior is - * required as we do not allow ASYNC processing - * in interrupt handlers (like Windows CE) - */ - - if (pdev->DSRCanYield && DEV_CHECK_RECV_YIELD(pdev)) - /* ProcessPendingIRQs() pulled enough recv - * messages to satisfy the yield count, stop - * checking for more messages and return - */ - break; - } - - if (async_proc) { - /* the function does some async I/O for performance, - * we need to exit the ISR immediately, the check below - * will prevent the interrupt from being - * Ack'd while we handle it asynchronously - */ - break; - } - } - - if (QDF_IS_STATUS_SUCCESS(status) && !async_proc) { - /* Ack the interrupt only if : - * 1. we did not get any errors in processing interrupts - * 2. there are no outstanding async processing requests - */ - if (pdev->DSRCanYield) { - /* if the DSR can yield do not ACK the interrupt, there - * could be more pending messages. The HIF layer - * must ACK the interrupt on behalf of HTC - */ - HIF_INFO("%s: Yield (RX count: %d)", - __func__, pdev->CurrentDSRRecvCount); - } else { - HIF_INFO("%s: Ack interrupt", __func__); - hif_ack_interrupt(pdev->HIFDevice); - } - } - - HIF_EXIT(); - return status; -} - -/** hif_dev_set_mailbox_swap() - Set the mailbox swap from firmware - * @pdev : The HIF layer object - * - * Return: none - */ -void hif_dev_set_mailbox_swap(struct hif_sdio_dev *pdev) -{ - struct hif_sdio_device *hif_device = hif_dev_from_hif(pdev); - - HIF_ENTER(); - - hif_device->swap_mailbox = true; - - HIF_EXIT(); -} - -/** hif_dev_get_mailbox_swap() - Get the mailbox swap setting - * @pdev : The HIF layer object - * - * Return: none - */ -bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev) -{ - struct hif_sdio_device *hif_device; - - HIF_ENTER(); - - hif_device = hif_dev_from_hif(pdev); - - HIF_EXIT(); - - return hif_device->swap_mailbox; -} - /** * hif_dev_setup() - set up sdio device. * @pDev: sdio device context @@ -346,7 +225,6 @@ QDF_STATUS hif_dev_setup(struct hif_sdio_device *pdev) status = hif_dev_setup_device(pdev); - if (status != QDF_STATUS_SUCCESS) { HIF_ERROR("%s: device specific setup failed", __func__); return QDF_STATUS_E_INVAL; diff --git a/hif/src/sdio/hif_sdio_dev.h b/hif/src/sdio/hif_sdio_dev.h index 5dbe6820c9..7ee6582e6a 100644 --- a/hif/src/sdio/hif_sdio_dev.h +++ b/hif/src/sdio/hif_sdio_dev.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016, 2018-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -26,6 +26,7 @@ #include #include "athstartpack.h" #include "hif_internal.h" +#include "if_sdio.h" struct hif_sdio_device *hif_dev_from_hif(struct hif_sdio_dev *hif_device); @@ -47,7 +48,7 @@ QDF_STATUS hif_dev_send_buffer(struct hif_sdio_device *htc_sdio_device, QDF_STATUS hif_dev_process_pending_irqs(struct hif_sdio_device *pdev, bool *done, - bool *async_processing); + bool *async_processing); void hif_dev_mask_interrupts(struct hif_sdio_device *pdev); @@ -60,9 +61,9 @@ void hif_dev_unmask_interrupts(struct hif_sdio_device *pdev); int hif_dev_setup_device(struct hif_sdio_device *pdev); -QDF_STATUS hif_dev_get_fifo_address(struct hif_sdio_dev *pdev, - struct hif_device_mbox_info *config, - uint32_t config_len); +int hif_dev_get_fifo_address(struct hif_sdio_dev *pdev, + void *config, + uint32_t config_len); void hif_dev_get_block_size(void *config); @@ -70,8 +71,93 @@ void hif_dev_set_mailbox_swap(struct hif_sdio_dev *pdev); bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev); -int hif_sdio_set_drvdata(struct sdio_func *func, - struct hif_sdio_dev *hifdevice); +QDF_STATUS hif_read_write(struct hif_sdio_dev *device, unsigned long address, + char *buffer, uint32_t length, uint32_t request, + void *context); -struct hif_sdio_dev *get_hif_device(struct sdio_func *func); +#ifdef CONFIG_SDIO_TRANSFER_MAILBOX +static inline struct hif_sdio_dev *get_hif_device(struct hif_softc *hif_ctx, + struct sdio_func *func) +{ + qdf_assert(func); + return (struct hif_sdio_dev *)sdio_get_drvdata(func); +} + +/** + * hif_sdio_set_drvdata() - set wlan driver data into upper layer private + * @hif_ctx: HIF object + * @func: pointer to sdio function + * @hifdevice: pointer to hif device + * + * Return: zero for success. + */ +static inline int hif_sdio_set_drvdata(struct hif_softc *hif_ctx, + struct sdio_func *func, + struct hif_sdio_dev *hifdevice) +{ + sdio_set_drvdata(func, hifdevice); + return 0; +} + +static inline int hif_dev_configure_pipes(struct hif_sdio_dev *pdev, + struct sdio_func *func) +{ + return 0; +} + +static inline int hif_dev_register_channels(struct hif_sdio_dev *dev, + struct sdio_func *func) +{ + return 0; +} + +static inline void hif_dev_unregister_channels(struct hif_sdio_dev *dev, + struct sdio_func *func) +{ +} +#else +static inline struct hif_sdio_dev *get_hif_device(struct hif_softc *hif_ctx, + struct sdio_func *func) +{ + struct hif_sdio_softc *scn = (struct hif_sdio_softc *)hif_ctx; + + return (struct hif_sdio_dev *)scn->hif_handle; +} + +/** + * hif_sdio_set_drvdata() - set wlan driver data into upper layer private + * @hif_ctx: HIF object + * @func: pointer to sdio function + * @hifdevice: pointer to hif device + * + * Return: zero for success. + */ +static inline int hif_sdio_set_drvdata(struct hif_softc *hif_ctx, + struct sdio_func *func, + struct hif_sdio_dev *hifdevice) +{ + struct hif_sdio_softc *sc = (struct hif_sdio_softc *)hif_ctx; + + sc->hif_handle = hifdevice; + + return 0; +} + +int hif_dev_configure_pipes(struct hif_sdio_dev *pdev, + struct sdio_func *func); + +int hif_dev_register_channels(struct hif_sdio_dev *dev, + struct sdio_func *func); + +void hif_dev_unregister_channels(struct hif_sdio_dev *dev, + struct sdio_func *func); +#endif /* SDIO_TRANSFER */ +QDF_STATUS hif_enable_func(struct hif_softc *ol_sc, struct hif_sdio_dev *device, + struct sdio_func *func, bool resume); +QDF_STATUS hif_disable_func(struct hif_sdio_dev *device, + struct sdio_func *func, + bool reset); +A_STATUS hif_sdio_probe(struct hif_softc *ol_sc, + struct sdio_func *func, + struct hif_sdio_dev *device); #endif /* HIF_SDIO_DEV_H_ */ diff --git a/hif/src/sdio/hif_sdio_internal.h b/hif/src/sdio/hif_sdio_internal.h index 67c8ebfab7..b378575d08 100644 --- a/hif/src/sdio/hif_sdio_internal.h +++ b/hif/src/sdio/hif_sdio_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2016-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -28,7 +28,7 @@ #if defined(CONFIG_SDIO_TRANSFER_MAILBOX) #include #elif defined(CONFIG_SDIO_TRANSFER_ADMA) -#error "Error - Not implemented yet" +#include #else #error "Error - Invalid transfer method" #endif @@ -54,7 +54,6 @@ struct hif_sdio_device { qdf_spinlock_t TxLock; qdf_spinlock_t RxLock; struct hif_msg_callbacks hif_callbacks; - struct hif_device_mbox_info MailBoxInfo; uint32_t BlockSize; uint32_t BlockMask; enum hif_device_irq_mode HifIRQProcessingMode; @@ -65,8 +64,11 @@ struct hif_sdio_device { int RecheckIRQStatusCnt; uint32_t RecvStateFlags; void *pTarget; - bool swap_mailbox; struct devRegisters devRegisters; +#ifdef CONFIG_SDIO_TRANSFER_MAILBOX + bool swap_mailbox; + struct hif_device_mbox_info MailBoxInfo; +#endif }; #define LOCK_HIF_DEV(device) qdf_spin_lock(&(device)->Lock) diff --git a/hif/src/sdio/if_sdio.c b/hif/src/sdio/if_sdio.c index 0b98d0baf5..102b84d09f 100644 --- a/hif/src/sdio/if_sdio.c +++ b/hif/src/sdio/if_sdio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -181,7 +181,7 @@ void hif_sdio_disable_bus(struct hif_softc *hif_sc) { struct sdio_func *func = dev_to_sdio_func(hif_sc->qdf_dev->dev); - hif_sdio_device_removed(func); + hif_sdio_device_removed(hif_sc, func); } /** diff --git a/hif/src/sdio/if_sdio.h b/hif/src/sdio/if_sdio.h index 1891c8b3e9..191ea615ec 100644 --- a/hif/src/sdio/if_sdio.h +++ b/hif/src/sdio/if_sdio.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -83,7 +83,7 @@ int hif_sdio_device_inserted(struct hif_softc *ol_sc, const struct sdio_device_id *id); void hif_sdio_stop(struct hif_softc *hif_ctx); void hif_sdio_shutdown(struct hif_softc *hif_ctx); -void hif_sdio_device_removed(struct sdio_func *func); +void hif_sdio_device_removed(struct hif_softc *hif_ctx, struct sdio_func *func); int hif_device_suspend(struct hif_softc *ol_sc, struct device *dev); int hif_device_resume(struct hif_softc *ol_sc, struct device *dev); void hif_register_tbl_attach(struct hif_softc *scn, diff --git a/hif/src/sdio/native_sdio/include/hif_internal.h b/hif/src/sdio/native_sdio/include/hif_internal.h index 8f2719a228..41001c848a 100644 --- a/hif/src/sdio/native_sdio/include/hif_internal.h +++ b/hif/src/sdio/native_sdio/include/hif_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -35,6 +35,7 @@ #include #include #include +#include #include "hif.h" #include "hif_debug.h" #include "hif_sdio_common.h" @@ -167,7 +168,7 @@ struct bus_request { struct bus_request *next; /* link list of available requests */ struct bus_request *inusenext; /* link list of in use requests */ struct semaphore sem_req; - uint32_t address; /* request data */ + unsigned long address; /* request data */ char *buffer; uint32_t length; uint32_t request; @@ -176,6 +177,14 @@ struct bus_request { struct HIF_SCATTER_REQ_PRIV *scatter_req; }; +#define HIF_ADMA_MAX_CHANS 2 +#ifdef CONFIG_SDIO_TRANSFER_ADMA +struct rx_q_entry { + qdf_list_node_t entry; + qdf_nbuf_t nbuf; +}; +#endif + struct hif_sdio_dev { struct sdio_func *func; qdf_spinlock_t asynclock; @@ -201,6 +210,15 @@ struct hif_sdio_dev { const struct sdio_device_id *id; struct mmc_host *host; void *htc_context; +#ifdef CONFIG_SDIO_TRANSFER_ADMA + struct sdio_al_client_handle *al_client; + struct sdio_al_channel_handle *al_chan[HIF_ADMA_MAX_CHANS]; + uint8_t adma_chans_used; + qdf_list_t rx_q; + qdf_spinlock_t rx_q_lock; + qdf_work_t rx_q_alloc_work; + bool rx_q_alloc_work_scheduled; +#endif }; struct HIF_DEVICE_OS_DEVICE_INFO { @@ -270,18 +288,13 @@ QDF_STATUS hif_configure_device(struct hif_softc *ol_sc, QDF_STATUS hif_attach_htc(struct hif_sdio_dev *device, struct htc_callbacks *callbacks); -QDF_STATUS hif_read_write(struct hif_sdio_dev *device, - uint32_t address, - char *buffer, - uint32_t length, uint32_t request, void *context); - void hif_ack_interrupt(struct hif_sdio_dev *device); void hif_mask_interrupt(struct hif_sdio_dev *device); void hif_un_mask_interrupt(struct hif_sdio_dev *device); -void hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func); +int hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func); struct _HIF_SCATTER_ITEM { u_int8_t *buffer; /* CPU accessible address of buffer */ @@ -400,14 +413,14 @@ static inline QDF_STATUS do_hif_read_write_scatter(struct hif_sdio_dev *device, #define SDIO_SET_CMD52_WRITE_ARG(arg, func, address, value) \ SDIO_SET_CMD52_ARG(arg, 1, (func), 0, address, value) -void hif_sdio_quirk_force_drive_strength(struct sdio_func *func); -void hif_sdio_quirk_write_cccr(struct sdio_func *func); -int hif_sdio_quirk_mod_strength(struct sdio_func *func); -int hif_sdio_quirk_async_intr(struct sdio_func *func); -int hif_sdio_set_bus_speed(struct sdio_func *func); -int hif_sdio_set_bus_width(struct sdio_func *func); -int hif_sdio_func_enable(struct hif_sdio_dev *device, - struct sdio_func *func); +void hif_sdio_quirk_force_drive_strength(struct hif_softc *ol_sc, + struct sdio_func *func); +void hif_sdio_quirk_write_cccr(struct hif_softc *ol_sc, struct sdio_func *func); +int hif_sdio_quirk_mod_strength(struct hif_softc *ol_sc, + struct sdio_func *func); +int hif_sdio_quirk_async_intr(struct hif_softc *ol_sc, struct sdio_func *func); +int hif_sdio_set_bus_speed(struct hif_softc *ol_sc, struct sdio_func *func); +int hif_sdio_set_bus_width(struct hif_softc *ol_sc, struct sdio_func *func); QDF_STATUS hif_sdio_func_disable(struct hif_sdio_dev *device, struct sdio_func *func, bool reset); diff --git a/hif/src/sdio/native_sdio/src/dev_quirks.c b/hif/src/sdio/native_sdio/src/dev_quirks.c index b84f1ab8b7..7c7e63ba01 100644 --- a/hif/src/sdio/native_sdio/src/dev_quirks.c +++ b/hif/src/sdio/native_sdio/src/dev_quirks.c @@ -1,8 +1,5 @@ /* - * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. - * - * - * + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -91,8 +88,10 @@ module_param(brokenirq, uint, 0644); MODULE_PARM_DESC(brokenirq, "Set as 1 to use polling method instead of interrupt mode"); +#ifdef CONFIG_SDIO_TRANSFER_MAILBOX /** * hif_sdio_force_drive_strength() - Set SDIO drive strength + * @ol_sc: softc instance * @func: pointer to sdio_func * * This function forces the driver strength of the SDIO @@ -100,61 +99,156 @@ MODULE_PARM_DESC(brokenirq, * * Return: none. */ -void hif_sdio_quirk_force_drive_strength(struct sdio_func *func) +void hif_sdio_quirk_force_drive_strength(struct hif_softc *ol_sc, + struct sdio_func *func) { int err = 0; unsigned char value = 0; uint32_t mask = 0, addr = SDIO_CCCR_DRIVE_STRENGTH; - struct hif_sdio_dev *device = sdio_get_drvdata(func); - uint16_t manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; - - switch (manfid) { - case MANUFACTURER_ID_QCN7605_BASE: - break; - default: - err = func0_cmd52_read_byte(func->card, addr, &value); - if (err) { - HIF_ERROR("%s: read driver strength 0x%02X fail %d\n", - __func__, addr, err); - break; - } - - mask = (SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT); - value = (value & ~mask) | SDIO_DTSx_SET_TYPE_D; - err = func0_cmd52_write_byte(func->card, addr, value); - if (err) { - HIF_ERROR("%s: write driver strength failed", __func__); - HIF_ERROR("%s: 0x%02X to 0x%02X failed: %d\n", __func__, - (uint32_t)value, addr, err); - break; - } - - value = 0; - addr = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR; - err = func0_cmd52_read_byte(func->card, addr, &value); - if (err) { - HIF_ERROR("%s Read CCCR 0x%02X failed: %d\n", - __func__, addr, err); - break; - } - - mask = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_MASK; - value = (value & ~mask) | - CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A | - CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C | - CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D; - err = func0_cmd52_write_byte(func->card, addr, value); - if (err) - HIF_ERROR("%s Write CCCR 0x%02X to 0x%02X failed: %d\n", - __func__, addr, value, err); - - break; + err = func0_cmd52_read_byte(func->card, addr, &value); + if (err) { + HIF_ERROR("%s: read driver strength 0x%02X fail %d\n", + __func__, addr, err); + return; } + + mask = (SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT); + value = (value & ~mask) | SDIO_DTSx_SET_TYPE_D; + err = func0_cmd52_write_byte(func->card, addr, value); + if (err) { + HIF_ERROR("%s: write driver strength failed", __func__); + HIF_ERROR("%s: 0x%02X to 0x%02X failed: %d\n", __func__, + (uint32_t)value, addr, err); + return; + } + + value = 0; + addr = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR; + err = func0_cmd52_read_byte(func->card, addr, &value); + if (err) { + HIF_ERROR("%s Read CCCR 0x%02X failed: %d\n", + __func__, addr, err); + return; + } + + mask = CCCR_SDIO_DRIVER_STRENGTH_ENABLE_MASK; + value = (value & ~mask) | CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A | + CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C | + CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D; + err = func0_cmd52_write_byte(func->card, addr, value); + if (err) + HIF_ERROR("%s Write CCCR 0x%02X to 0x%02X failed: %d\n", + __func__, addr, value, err); } +/** + * hif_sdio_quirk_async_intr() - Set asynchronous interrupt settings + * @ol_sc: softc instance + * @func: pointer to sdio_func + * + * The values are taken from the module parameter asyncintdelay + * Call this with the sdhci host claimed + * + * Return: none. + */ +int hif_sdio_quirk_async_intr(struct hif_softc *ol_sc, struct sdio_func *func) +{ + uint8_t data; + uint16_t manfid; + int set_async_irq = 0, ret = 0; + struct hif_sdio_dev *device = get_hif_device(ol_sc, func); + + manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; + + switch (manfid) { + case MANUFACTURER_ID_AR6003_BASE: + set_async_irq = 1; + ret = + func0_cmd52_write_byte(func->card, + CCCR_SDIO_IRQ_MODE_REG_AR6003, + SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6003); + if (ret) + return ret; + break; + case MANUFACTURER_ID_AR6320_BASE: + case MANUFACTURER_ID_QCA9377_BASE: + case MANUFACTURER_ID_QCA9379_BASE: + set_async_irq = 1; + ret = func0_cmd52_read_byte(func->card, + CCCR_SDIO_IRQ_MODE_REG_AR6320, + &data); + if (ret) + return ret; + + data |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6320; + ret = func0_cmd52_write_byte(func->card, + CCCR_SDIO_IRQ_MODE_REG_AR6320, + data); + if (ret) + return ret; + break; + } + + if (asyncintdelay) { + /* Set CCCR 0xF0[7:6] to increase async interrupt delay clock + * to fix interrupt missing issue on dell 8460p + */ + + ret = func0_cmd52_read_byte(func->card, + CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS, + &data); + if (ret) + return ret; + + data = (data & ~CCCR_SDIO_ASYNC_INT_DELAY_MASK) | + ((asyncintdelay << CCCR_SDIO_ASYNC_INT_DELAY_LSB) & + CCCR_SDIO_ASYNC_INT_DELAY_MASK); + + ret = func0_cmd52_write_byte(func->card, + CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS, + data); + if (ret) + return ret; + } + + return ret; +} +#else +/** + * hif_sdio_force_drive_strength() - Set SDIO drive strength + * @ol_sc: softc instance + * @func: pointer to sdio_func + * + * This function forces the driver strength of the SDIO + * Call this with the sdhci host claimed + * + * Return: none. + */ +void hif_sdio_quirk_force_drive_strength(struct hif_softc *ol_sc, + struct sdio_func *func) +{ +} + +/** + * hif_sdio_quirk_async_intr() - Set asynchronous interrupt settings + * @ol_sc: softc instance + * @func: pointer to sdio_func + * + * The values are taken from the module parameter asyncintdelay + * Call this with the sdhci host claimed + * + * Return: none. + */ +int hif_sdio_quirk_async_intr(struct hif_softc *ol_sc, struct sdio_func *func) +{ + return 0; +} +#endif + /** * hif_sdio_quirk_write_cccr() - write a desired CCCR register + * @ol_sc: softc instance * @func: pointer to sdio_func * * The values are taken from the module parameter writecccr @@ -162,7 +256,7 @@ void hif_sdio_quirk_force_drive_strength(struct sdio_func *func) * * Return: none. */ -void hif_sdio_quirk_write_cccr(struct sdio_func *func) +void hif_sdio_quirk_write_cccr(struct hif_softc *ol_sc, struct sdio_func *func) { int32_t err; @@ -231,6 +325,7 @@ void hif_sdio_quirk_write_cccr(struct sdio_func *func) /** * hif_sdio_quirk_mod_strength() - write a desired CCCR register + * @ol_sc: softc instance * @func: pointer to sdio_func * * The values are taken from the module parameter writecccr @@ -238,11 +333,11 @@ void hif_sdio_quirk_write_cccr(struct sdio_func *func) * * Return: none. */ -int hif_sdio_quirk_mod_strength(struct sdio_func *func) +int hif_sdio_quirk_mod_strength(struct hif_softc *ol_sc, struct sdio_func *func) { int ret = 0; uint32_t addr, value; - struct hif_sdio_dev *device = sdio_get_drvdata(func); + struct hif_sdio_dev *device = get_hif_device(ol_sc, func); uint16_t manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; if (!modstrength) /* TODO: Dont set this : scn is not popolated yet */ @@ -287,82 +382,6 @@ int hif_sdio_quirk_mod_strength(struct sdio_func *func) return ret; } -/** - * hif_sdio_quirk_async_intr() - Set asynchronous interrupt settings - * @func: pointer to sdio_func - * - * The values are taken from the module parameter asyncintdelay - * Call this with the sdhci host claimed - * - * Return: none. - */ -int hif_sdio_quirk_async_intr(struct sdio_func *func) -{ - uint8_t data; - uint16_t manfid; - int set_async_irq = 0, ret = 0; - struct hif_sdio_dev *device = sdio_get_drvdata(func); - - manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; - - switch (manfid) { - case MANUFACTURER_ID_AR6003_BASE: - set_async_irq = 1; - ret = - func0_cmd52_write_byte(func->card, - CCCR_SDIO_IRQ_MODE_REG_AR6003, - SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6003); - if (ret) - return ret; - break; - case MANUFACTURER_ID_AR6320_BASE: - case MANUFACTURER_ID_QCA9377_BASE: - case MANUFACTURER_ID_QCA9379_BASE: - set_async_irq = 1; - ret = func0_cmd52_read_byte(func->card, - CCCR_SDIO_IRQ_MODE_REG_AR6320, - &data); - if (ret) - return ret; - - data |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_AR6320; - ret = func0_cmd52_write_byte(func->card, - CCCR_SDIO_IRQ_MODE_REG_AR6320, - data); - if (ret) - return ret; - break; - case MANUFACTURER_ID_QCN7605_BASE: - /* No async intr delay settings */ - asyncintdelay = 0; - return ret; - } - - if (asyncintdelay) { - /* Set CCCR 0xF0[7:6] to increase async interrupt delay clock - * to fix interrupt missing issue on dell 8460p - */ - - ret = func0_cmd52_read_byte(func->card, - CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS, - &data); - if (ret) - return ret; - - data = (data & ~CCCR_SDIO_ASYNC_INT_DELAY_MASK) | - ((asyncintdelay << CCCR_SDIO_ASYNC_INT_DELAY_LSB) & - CCCR_SDIO_ASYNC_INT_DELAY_MASK); - - ret = func0_cmd52_write_byte(func->card, - CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS, - data); - if (ret) - return ret; - } - - return ret; -} - #if KERNEL_VERSION(3, 4, 0) <= LINUX_VERSION_CODE #ifdef SDIO_BUS_WIDTH_8BIT static int hif_cmd52_write_byte_8bit(struct sdio_func *func) @@ -381,14 +400,15 @@ static int hif_cmd52_write_byte_8bit(struct sdio_func *func) /** * hif_sdio_set_bus_speed() - Set the sdio bus speed + * @ol_sc: softc instance * @func: pointer to sdio_func * * Return: 0 on success, error number otherwise. */ -int hif_sdio_set_bus_speed(struct sdio_func *func) +int hif_sdio_set_bus_speed(struct hif_softc *ol_sc, struct sdio_func *func) { uint32_t clock, clock_set = 12500000; - struct hif_sdio_dev *device = get_hif_device(func); + struct hif_sdio_dev *device = get_hif_device(ol_sc, func); uint16_t manfid; manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; @@ -427,16 +447,17 @@ int hif_sdio_set_bus_speed(struct sdio_func *func) /** * hif_set_bus_width() - Set the sdio bus width + * @ol_sc: softc instance * @func: pointer to sdio_func * * Return: 0 on success, error number otherwise. */ -int hif_sdio_set_bus_width(struct sdio_func *func) +int hif_sdio_set_bus_width(struct hif_softc *ol_sc, struct sdio_func *func) { int ret = 0; uint16_t manfid; uint8_t data = 0; - struct hif_sdio_dev *device = get_hif_device(func); + struct hif_sdio_dev *device = get_hif_device(ol_sc, func); manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; @@ -491,66 +512,6 @@ int hif_sdio_set_bus_width(struct sdio_func *func) return ret; } -/** - * hif_sdio_func_enable() - Handle device enabling as per device - * @device: HIF device object - * @func: function pointer - * - * Return success or failure - */ -int hif_sdio_func_enable(struct hif_sdio_dev *device, - struct sdio_func *func) -{ - uint16_t manfid; - - manfid = device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK; - - if (manfid == MANUFACTURER_ID_QCN7605_BASE) - return 0; - - if (device->is_disabled) { - int ret = 0; - - sdio_claim_host(func); - - ret = hif_sdio_quirk_async_intr(func); - if (ret) { - HIF_ERROR("%s: Error setting async intr:%d", - __func__, ret); - sdio_release_host(func); - return QDF_STATUS_E_FAILURE; - } - - func->enable_timeout = 100; - ret = sdio_enable_func(func); - if (ret) { - HIF_ERROR("%s: Unable to enable function: %d", - __func__, ret); - sdio_release_host(func); - return QDF_STATUS_E_FAILURE; - } - - ret = sdio_set_block_size(func, HIF_BLOCK_SIZE); - if (ret) { - HIF_ERROR("%s: Unable to set block size 0x%X : %d\n", - __func__, HIF_BLOCK_SIZE, ret); - sdio_release_host(func); - return QDF_STATUS_E_FAILURE; - } - - ret = hif_sdio_quirk_mod_strength(func); - if (ret) { - HIF_ERROR("%s: Error setting mod strength : %d\n", - __func__, ret); - sdio_release_host(func); - return QDF_STATUS_E_FAILURE; - } - - sdio_release_host(func); - } - - return 0; -} /** * hif_mask_interrupt() - Disable hif device irq @@ -594,11 +555,7 @@ void hif_mask_interrupt(struct hif_sdio_dev *device) */ static void hif_irq_handler(struct sdio_func *func) { - struct hif_sdio_dev *device; - - HIF_ENTER(); - - device = get_hif_device(func); + struct hif_sdio_dev *device = get_hif_device(NULL, func); atomic_set(&device->irq_handling, 1); /* release the host during intr so we can use * it when we process cmds @@ -607,8 +564,6 @@ static void hif_irq_handler(struct sdio_func *func) device->htc_callbacks.dsr_handler(device->htc_callbacks.context); sdio_claim_host(device->func); atomic_set(&device->irq_handling, 0); - - HIF_EXIT(); } /** diff --git a/hif/src/sdio/native_sdio/src/hif.c b/hif/src/sdio/native_sdio/src/hif.c index 926067c3ca..7100baf1d6 100644 --- a/hif/src/sdio/native_sdio/src/hif.c +++ b/hif/src/sdio/native_sdio/src/hif.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -38,26 +37,10 @@ #include "hif_internal.h" #include -/* by default setup a bounce buffer for the data packets, - * if the underlying host controller driver - * does not use DMA you may be able to skip this step - * and save the memory allocation and transfer time - */ #define HIF_USE_DMA_BOUNCE_BUFFER 1 #define ATH_MODULE_NAME hif #include "a_debug.h" -#if HIF_USE_DMA_BOUNCE_BUFFER -/* macro to check if DMA buffer is WORD-aligned and DMA-able. - * Most host controllers assume the - * buffer is DMA'able and will bug-check otherwise (i.e. buffers on the stack). - * virt_addr_valid check fails on stack memory. - */ -#define BUFFER_NEEDS_BOUNCE(buffer) (((unsigned long)(buffer) & 0x3) || \ - !virt_addr_valid((buffer))) -#else -#define BUFFER_NEEDS_BOUNCE(buffer) (false) -#endif #define MAX_HIF_DEVICES 2 #ifdef HIF_MBOX_SLEEP_WAR #define HIF_MIN_SLEEP_INACTIVITY_TIME_MS 50 @@ -83,7 +66,8 @@ MODULE_PARM_DESC(debugcccr, "Output this cccr values"); #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) #define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) -static struct hif_sdio_dev *add_hif_device(struct sdio_func *func); +static struct hif_sdio_dev *add_hif_device(struct hif_softc *hif_ctx, + struct sdio_func *func); static void del_hif_device(struct hif_sdio_dev *device); int reset_sdio_on_unload; @@ -171,186 +155,6 @@ ATH_DEBUG_INSTANTIATE_MODULE_VAR(hif, ATH_DEBUG_MASK_DEFAULTS, 0, NULL); #endif -/** - * __hif_read_write() - sdio read/write wrapper - * @device: pointer to hif device structure - * @address: address to read - * @buffer: buffer to hold read/write data - * @length: length to read/write - * @request: read/write/sync/async request - * @context: pointer to hold calling context - * - * Return: 0 on success, error number otherwise. - */ -static QDF_STATUS -__hif_read_write(struct hif_sdio_dev *device, - uint32_t address, char *buffer, - uint32_t length, uint32_t request, void *context) -{ - uint8_t opcode; - QDF_STATUS status = QDF_STATUS_SUCCESS; - int ret = A_OK; - uint8_t *tbuffer; - bool bounced = false; - - if (!device) { - HIF_ERROR("%s: device null!", __func__); - return QDF_STATUS_E_INVAL; - } - - if (!device->func) { - HIF_ERROR("%s: func null!", __func__); - return QDF_STATUS_E_INVAL; - } - - HIF_INFO_HI("%s: addr:0X%06X, len:%08d, %s, %s", __func__, - address, length, - request & HIF_SDIO_READ ? "Read " : "Write", - request & HIF_ASYNCHRONOUS ? "Async" : "Sync "); - - do { - if (request & HIF_EXTENDED_IO) { - HIF_INFO_HI("%s: Command type: CMD53\n", __func__); - } else { - HIF_ERROR("%s: Invalid command type: 0x%08x\n", - __func__, request); - status = QDF_STATUS_E_INVAL; - break; - } - - if (request & HIF_BLOCK_BASIS) { - /* round to whole block length size */ - length = - (length / HIF_BLOCK_SIZE) * - HIF_BLOCK_SIZE; - HIF_INFO_HI("%s: Block mode (BlockLen: %d)\n", - __func__, length); - } else if (request & HIF_BYTE_BASIS) { - HIF_INFO_HI("%s: Byte mode (BlockLen: %d)\n", - __func__, length); - } else { - HIF_ERROR("%s: Invalid data mode: 0x%08x\n", - __func__, request); - status = QDF_STATUS_E_INVAL; - break; - } - if (request & HIF_SDIO_WRITE) { - hif_fixup_write_param(device, request, - &length, &address); - - HIF_INFO_HI("addr:%08X, len:0x%08X, dummy:0x%04X\n", - address, length, - (request & HIF_DUMMY_SPACE_MASK) >> 16); - } - - if (request & HIF_FIXED_ADDRESS) { - opcode = CMD53_FIXED_ADDRESS; - HIF_INFO_HI("%s: Addr mode: fixed 0x%X\n", - __func__, address); - } else if (request & HIF_INCREMENTAL_ADDRESS) { - opcode = CMD53_INCR_ADDRESS; - HIF_INFO_HI("%s: Address mode: Incremental 0x%X\n", - __func__, address); - } else { - HIF_ERROR("%s: Invalid address mode: 0x%08x\n", - __func__, request); - status = QDF_STATUS_E_INVAL; - break; - } - - if (request & HIF_SDIO_WRITE) { -#if HIF_USE_DMA_BOUNCE_BUFFER - if (BUFFER_NEEDS_BOUNCE(buffer)) { - AR_DEBUG_ASSERT(device->dma_buffer); - tbuffer = device->dma_buffer; - /* copy the write data to the dma buffer */ - AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); - if (length > HIF_DMA_BUFFER_SIZE) { - HIF_ERROR("%s: Invalid write len: %d\n", - __func__, length); - status = QDF_STATUS_E_INVAL; - break; - } - memcpy(tbuffer, buffer, length); - bounced = true; - } else { - tbuffer = buffer; - } -#else - tbuffer = buffer; -#endif - if (opcode == CMD53_FIXED_ADDRESS && tbuffer) { - ret = sdio_writesb(device->func, address, - tbuffer, length); - HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", - __func__, ret, address, length, - *(int *)tbuffer); - } else if (tbuffer) { - ret = sdio_memcpy_toio(device->func, address, - tbuffer, length); - HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", - __func__, ret, address, length, - *(int *)tbuffer); - } - } else if (request & HIF_SDIO_READ) { -#if HIF_USE_DMA_BOUNCE_BUFFER - if (BUFFER_NEEDS_BOUNCE(buffer)) { - AR_DEBUG_ASSERT(device->dma_buffer); - AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); - if (length > HIF_DMA_BUFFER_SIZE) { - AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, - ("%s: Invalid read length: %d\n", - __func__, length)); - status = QDF_STATUS_E_INVAL; - break; - } - tbuffer = device->dma_buffer; - bounced = true; - } else { - tbuffer = buffer; - } -#else - tbuffer = buffer; -#endif - if (opcode == CMD53_FIXED_ADDRESS && tbuffer) { - ret = sdio_readsb(device->func, tbuffer, - address, length); - HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", - __func__, ret, address, length, - *(int *)tbuffer); - } else if (tbuffer) { - ret = sdio_memcpy_fromio(device->func, - tbuffer, address, - length); - HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", - __func__, ret, address, length, - *(int *)tbuffer); - } -#if HIF_USE_DMA_BOUNCE_BUFFER - if (bounced && tbuffer) - memcpy(buffer, tbuffer, length); -#endif - } else { - HIF_ERROR("%s: Invalid dir: 0x%08x", __func__, request); - status = QDF_STATUS_E_INVAL; - return status; - } - - if (ret) { - HIF_ERROR("%s: SDIO bus operation failed!", __func__); - HIF_ERROR("%s: MMC stack returned : %d", __func__, ret); - HIF_ERROR("%s: addr:0X%06X, len:%08d, %s, %s", - __func__, address, length, - request & HIF_SDIO_READ ? "Read " : "Write", - request & HIF_ASYNCHRONOUS ? - "Async" : "Sync"); - status = QDF_STATUS_E_FAILURE; - } - } while (false); - - return status; -} - /** * add_to_async_list() - add bus reqest to async task list * @device: pointer to hif device @@ -380,222 +184,6 @@ void add_to_async_list(struct hif_sdio_dev *device, qdf_spin_unlock_irqrestore(&device->asynclock); } -/** - * hif_read_write() - queue a read/write request - * @device: pointer to hif device structure - * @address: address to read - * @buffer: buffer to hold read/write data - * @length: length to read/write - * @request: read/write/sync/async request - * @context: pointer to hold calling context - * - * Return: 0 on success, error number otherwise. - */ -QDF_STATUS -hif_read_write(struct hif_sdio_dev *device, - uint32_t address, - char *buffer, uint32_t length, - uint32_t request, void *context) -{ - QDF_STATUS status = QDF_STATUS_SUCCESS; - struct bus_request *busrequest; - - AR_DEBUG_ASSERT(device); - AR_DEBUG_ASSERT(device->func); - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: device 0x%pK addr 0x%X buffer 0x%pK len %d req 0x%X context 0x%pK", - __func__, device, address, buffer, - length, request, context)); - - /*sdio r/w action is not needed when suspend, so just return */ - if ((device->is_suspend == true) - && (device->power_config == HIF_DEVICE_POWER_CUT)) { - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("skip io when suspending\n")); - return QDF_STATUS_SUCCESS; - } - do { - if ((request & HIF_ASYNCHRONOUS) || - (request & HIF_SYNCHRONOUS)) { - /* serialize all requests through the async thread */ - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: Execution mode: %s\n", __func__, - (request & HIF_ASYNCHRONOUS) ? "Async" - : "Synch")); - busrequest = hif_allocate_bus_request(device); - if (!busrequest) { - AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, - ("no async bus requests available (%s, addr:0x%X, len:%d)\n", - request & HIF_SDIO_READ ? "READ" : - "WRITE", address, length)); - return QDF_STATUS_E_FAILURE; - } - busrequest->address = address; - busrequest->buffer = buffer; - busrequest->length = length; - busrequest->request = request; - busrequest->context = context; - - add_to_async_list(device, busrequest); - - if (request & HIF_SYNCHRONOUS) { - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: queued sync req: 0x%lX\n", - __func__, (unsigned long)busrequest)); - - /* wait for completion */ - up(&device->sem_async); - if (down_interruptible(&busrequest->sem_req) != - 0) { - /* interrupted, exit */ - return QDF_STATUS_E_FAILURE; - } else { - QDF_STATUS status = busrequest->status; - - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: sync return freeing 0x%lX: 0x%X\n", - __func__, - (unsigned long) - busrequest, - busrequest->status)); - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: freeing req: 0x%X\n", - __func__, - (unsigned int) - request)); - hif_free_bus_request(device, - busrequest); - return status; - } - } else { - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: queued async req: 0x%lX\n", - __func__, - (unsigned long)busrequest)); - up(&device->sem_async); - return QDF_STATUS_E_PENDING; - } - } else { - AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, - ("%s: Invalid execution mode: 0x%08x\n", - __func__, - (unsigned int)request)); - status = QDF_STATUS_E_INVAL; - break; - } - } while (0); - - return status; -} - -/** - * async_task() - thread function to serialize all bus requests - * @param: pointer to hif device - * - * thread function to serialize all requests, both sync and async - * Return: 0 on success, error number otherwise. - */ -static int async_task(void *param) -{ - struct hif_sdio_dev *device; - struct bus_request *request; - QDF_STATUS status; - bool claimed = false; - - device = (struct hif_sdio_dev *) param; - set_current_state(TASK_INTERRUPTIBLE); - while (!device->async_shutdown) { - /* wait for work */ - if (down_interruptible(&device->sem_async) != 0) { - /* interrupted, exit */ - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: async task interrupted\n", - __func__)); - break; - } - if (device->async_shutdown) { - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: async task stopping\n", - __func__)); - break; - } - /* we want to hold the host over multiple cmds - * if possible, but holding the host blocks - * card interrupts - */ - qdf_spin_lock_irqsave(&device->asynclock); - /* pull the request to work on */ - while (device->asyncreq) { - request = device->asyncreq; - if (request->inusenext) - device->asyncreq = request->inusenext; - else - device->asyncreq = NULL; - qdf_spin_unlock_irqrestore(&device->asynclock); - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: async_task processing req: 0x%lX\n", - __func__, (unsigned long)request)); - - if (!claimed) { - sdio_claim_host(device->func); - claimed = true; - } - if (request->scatter_req) { - A_ASSERT(device->scatter_enabled); - /* pass the request to scatter routine which - * executes it synchronously, note, no need - * to free the request since scatter requests - * are maintained on a separate list - */ - status = do_hif_read_write_scatter(device, - request); - } else { - /* call hif_read_write in sync mode */ - status = - __hif_read_write(device, - request->address, - request->buffer, - request->length, - request-> - request & - ~HIF_SYNCHRONOUS, - NULL); - if (request->request & HIF_ASYNCHRONOUS) { - void *context = request->context; - - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: freeing req: 0x%lX\n", - __func__, (unsigned long) - request)); - hif_free_bus_request(device, request); - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: async_task completion req 0x%lX\n", - __func__, (unsigned long) - request)); - device->htc_callbacks. - rw_compl_handler(context, status); - } else { - AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, - ("%s: async_task upping req: 0x%lX\n", - __func__, (unsigned long) - request)); - request->status = status; - up(&request->sem_req); - } - } - qdf_spin_lock_irqsave(&device->asynclock); - } - qdf_spin_unlock_irqrestore(&device->asynclock); - if (claimed) { - sdio_release_host(device->func); - claimed = false; - } - } - - complete_and_exit(&device->async_completion, 0); - - return 0; -} - /* * Setup IRQ mode for deep sleep and WoW * Switch back to 1 bits mode when we suspend for @@ -603,6 +191,7 @@ static int async_task(void *param) * Re-enable async 4-bit irq mode for some host controllers * after resume. */ +#ifdef CONFIG_SDIO_TRANSFER_MAILBOX static int sdio_enable4bits(struct hif_sdio_dev *device, int enable) { int ret = 0; @@ -675,34 +264,12 @@ static int sdio_enable4bits(struct hif_sdio_dev *device, int enable) return ret; } - -static QDF_STATUS hif_disable_func(struct hif_sdio_dev *device, - struct sdio_func *func, - bool reset) +#else +static int sdio_enable4bits(struct hif_sdio_dev *device, int enable) { - QDF_STATUS status = QDF_STATUS_SUCCESS; - - HIF_ENTER(); - device = get_hif_device(func); - if (!IS_ERR(device->async_task)) { - init_completion(&device->async_completion); - device->async_shutdown = 1; - up(&device->sem_async); - wait_for_completion(&device->async_completion); - device->async_task = NULL; - sema_init(&device->sem_async, 0); - } - - status = hif_sdio_func_disable(device, func, reset); - if (status == QDF_STATUS_SUCCESS) - device->is_disabled = true; - - cleanup_hif_scatter_resources(device); - - HIF_EXIT(); - - return status; + return 0; } +#endif /** * hif_sdio_probe() - configure sdio device @@ -712,9 +279,9 @@ static QDF_STATUS hif_disable_func(struct hif_sdio_dev *device, * * Return: 0 for success and non-zero for failure */ -static A_STATUS hif_sdio_probe(struct hif_softc *ol_sc, - struct sdio_func *func, - struct hif_sdio_dev *device) +A_STATUS hif_sdio_probe(struct hif_softc *ol_sc, + struct sdio_func *func, + struct hif_sdio_dev *device) { int ret = 0; const struct sdio_device_id *id; @@ -794,7 +361,9 @@ static A_STATUS hif_sdio_probe(struct hif_softc *ol_sc, goto err_attach1; } - return 0; + ret = hif_dev_register_channels(device, func); + + return ret; err_attach1: if (scn->ramdump_base) @@ -803,48 +372,9 @@ err_attach1: return ret; } -static QDF_STATUS hif_enable_func(struct hif_softc *ol_sc, - struct hif_sdio_dev *device, - struct sdio_func *func, - bool resume) -{ - int ret = QDF_STATUS_SUCCESS; - - HIF_ENTER(); - - if (!device) { - HIF_ERROR("%s: HIF device is NULL", __func__); - return QDF_STATUS_E_INVAL; - } - - if (hif_sdio_func_enable(device, func)) - return QDF_STATUS_E_FAILURE; - - /* create async I/O thread */ - if (!device->async_task && device->is_disabled) { - device->async_shutdown = 0; - device->async_task = kthread_create(async_task, - (void *)device, - "AR6K Async"); - if (IS_ERR(device->async_task)) { - HIF_ERROR("%s: Error creating async task", - __func__); - return QDF_STATUS_E_FAILURE; - } - device->is_disabled = false; - wake_up_process(device->async_task); - } - - if (resume == false) - ret = hif_sdio_probe(ol_sc, func, device); - - HIF_EXIT(); - - return ret; -} - /** * power_state_change_notify() - SDIO bus power notification handler + * @ol_sc: HIF device context * @config: hif device power change type * * Return: 0 on success, error number otherwise. @@ -910,6 +440,7 @@ power_state_change_notify(struct hif_softc *ol_sc, /** * hif_configure_device() - configure sdio device + * @ol_sc: HIF device context * @device: pointer to hif device structure * @opcode: configuration type * @config: configuration value to set @@ -1032,6 +563,7 @@ void hif_sdio_shutdown(struct hif_softc *hif_ctx) /** * hif_device_inserted() - hif-sdio driver probe handler + * @ol_sc: HIF device context * @func: pointer to sdio_func * @id: pointer to sdio_device_id * @@ -1057,10 +589,10 @@ static int hif_device_inserted(struct hif_softc *ol_sc, if (hifdevice && hifdevice->power_config == HIF_DEVICE_POWER_CUT && hifdevice->host == func->card->host) { - device = get_hif_device(func); + device = get_hif_device(ol_sc, func); hifdevice->func = func; hifdevice->power_config = HIF_DEVICE_POWER_UP; - hif_sdio_set_drvdata(func, hifdevice); + hif_sdio_set_drvdata(ol_sc, func, hifdevice); if (device->is_suspend) { HIF_INFO("%s: Resume from suspend", __func__); @@ -1072,9 +604,10 @@ static int hif_device_inserted(struct hif_softc *ol_sc, /* If device not found, then it is a new insertion, alloc and add it */ if (!device) { - if (add_hif_device(func) == NULL) + if (!add_hif_device(ol_sc, func)) return QDF_STATUS_E_FAILURE; - device = get_hif_device(func); + + device = get_hif_device(ol_sc, func); for (i = 0; i < MAX_HIF_DEVICES; ++i) { if (!hif_devices[i]) { @@ -1095,13 +628,13 @@ static int hif_device_inserted(struct hif_softc *ol_sc, */ sdio_claim_host(func); - hif_sdio_quirk_force_drive_strength(func); + hif_sdio_quirk_force_drive_strength(ol_sc, func); - hif_sdio_quirk_write_cccr(func); + hif_sdio_quirk_write_cccr(ol_sc, func); - ret = hif_sdio_set_bus_speed(func); + ret = hif_sdio_set_bus_speed(ol_sc, func); - ret = hif_sdio_set_bus_width(func); + ret = hif_sdio_set_bus_width(ol_sc, func); if (debugcccr) hif_dump_cccr(device); @@ -1174,11 +707,11 @@ void hif_ack_interrupt(struct hif_sdio_dev *device) * @pdev - HIF layer object * @func - SDIO bus function object * - * Return - NONE + * Return - error in case of failure to configure, else success */ -void hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func) +int hif_sdio_configure_pipes(struct hif_sdio_dev *dev, struct sdio_func *func) { - /* ADMA-TODO */ + return hif_dev_configure_pipes(dev, func); } /** @@ -1233,7 +766,7 @@ void hif_free_bus_request(struct hif_sdio_dev *device, int hif_device_suspend(struct hif_softc *ol_sc, struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); - struct hif_sdio_dev *device = get_hif_device(func); + struct hif_sdio_dev *device = get_hif_device(ol_sc, func); mmc_pm_flag_t pm_flag = 0; enum HIF_DEVICE_POWER_CHANGE_TYPE config; struct mmc_host *host = func->card->host; @@ -1314,7 +847,7 @@ int hif_device_resume(struct hif_softc *ol_sc, struct device *dev) enum HIF_DEVICE_POWER_CHANGE_TYPE config; struct hif_sdio_dev *device; - device = get_hif_device(func); + device = get_hif_device(ol_sc, func); if (!device) { HIF_ERROR("%s: hif object is null", __func__); return -EINVAL; @@ -1367,7 +900,8 @@ static A_STATUS hif_sdio_remove(void *context, void *hif_handle) return 0; } -static void hif_device_removed(struct sdio_func *func) + +static void hif_device_removed(struct hif_softc *ol_sc, struct sdio_func *func) { QDF_STATUS status = QDF_STATUS_SUCCESS; struct hif_sdio_dev *device; @@ -1375,7 +909,7 @@ static void hif_device_removed(struct sdio_func *func) AR_DEBUG_ASSERT(func); HIF_ENTER(); - device = get_hif_device(func); + device = get_hif_device(ol_sc, func); if (device->power_config == HIF_DEVICE_POWER_CUT) { device->func = NULL; /* func will be free by mmc stack */ @@ -1406,7 +940,8 @@ static void hif_device_removed(struct sdio_func *func) HIF_EXIT(); } -static struct hif_sdio_dev *add_hif_device(struct sdio_func *func) +static struct hif_sdio_dev *add_hif_device(struct hif_softc *ol_sc, + struct sdio_func *func) { struct hif_sdio_dev *hifdevice = NULL; int ret = 0; @@ -1430,8 +965,8 @@ static struct hif_sdio_dev *add_hif_device(struct sdio_func *func) hifdevice->func = func; hifdevice->power_config = HIF_DEVICE_POWER_UP; hifdevice->device_state = HIF_DEVICE_STATE_ON; - ret = hif_sdio_set_drvdata(func, hifdevice); - hif_info("status %d", ret); + ret = hif_sdio_set_drvdata(ol_sc, func, hifdevice); + HIF_INFO("status %d", ret); return hifdevice; } @@ -1552,12 +1087,12 @@ int hif_sdio_device_inserted(struct hif_softc *ol_sc, HIF_ERROR("%s: Enter", __func__); status = hif_device_inserted(ol_sc, func, id); - HIF_ERROR("%s: Exit", __func__); + HIF_ERROR("%s: Exit: status:%d", __func__, status); return status; } -void hif_sdio_device_removed(struct sdio_func *func) +void hif_sdio_device_removed(struct hif_softc *ol_sc, struct sdio_func *func) { - hif_device_removed(func); + hif_device_removed(ol_sc, func); } diff --git a/hif/src/sdio/transfer/adma.c b/hif/src/sdio/transfer/adma.c new file mode 100644 index 0000000000..3526ee95c2 --- /dev/null +++ b/hif/src/sdio/transfer/adma.c @@ -0,0 +1,858 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#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 + +struct sdio_al_channel_data qcn7605_chan[HIF_SDIO_MAX_AL_CHANNELS] = { + { + .name = "SDIO_AL_WLAN_CH0", /* HTT */ + .client_data = NULL, /* populate from client handle */ + .ul_xfer_cb = ul_xfer_cb, + .dl_xfer_cb = dl_xfer_cb, + .dl_data_avail_cb = dl_data_avail_cb, + .dl_meta_data_cb = NULL + }, + { + .name = "SDIO_AL_WLAN_CH1", /* WMI */ + .client_data = NULL, /* populate from client handle */ + .ul_xfer_cb = ul_xfer_cb, + .dl_xfer_cb = dl_xfer_cb, + .dl_data_avail_cb = dl_data_avail_cb, + .dl_meta_data_cb = NULL + } +}; + +/** + * hif_dev_register_channels()- Register transport layer channels + * @dev : HIF device object + * @func : SDIO function pointer + * + * Return : success on configuration, else failure + */ +int hif_dev_register_channels(struct hif_sdio_dev *dev, struct sdio_func *func) +{ + int ret = 0; + unsigned int chan; + struct sdio_al_channel_data *chan_data[HIF_ADMA_MAX_CHANS]; + + HIF_ENTER(); + + dev->al_client = pld_sdio_get_sdio_al_client_handle(func); + if (ret || !dev->al_client) { + HIF_ERROR("%s: Failed to get get sdio al handle", __func__); + return ret; + } + + if ((func->device & MANUFACTURER_ID_AR6K_BASE_MASK) == + MANUFACTURER_ID_QCN7605_BASE) { + dev->adma_chans_used = 2; + qcn7605_chan[0].client_data = dev->al_client->client_data; + qcn7605_chan[1].client_data = dev->al_client->client_data; + chan_data[0] = &qcn7605_chan[0]; + chan_data[1] = &qcn7605_chan[1]; + } else { + dev->adma_chans_used = 0; + } + + for (chan = 0; chan < dev->adma_chans_used; chan++) { + dev->al_chan[chan] = + pld_sdio_register_sdio_al_channel(dev->al_client, + chan_data[chan]); + if (!dev->al_chan[chan] || IS_ERR(dev->al_chan[chan])) { + ret = -EINVAL; + HIF_ERROR("%s: Channel registration failed", __func__); + } else { + dev->al_chan[chan]->priv = (void *)dev; + HIF_INFO("%s: chan %s : id : %u", __func__, + chan_data[chan]->name, + dev->al_chan[chan]->channel_id); + } + } + + HIF_EXIT(); + + return ret; +} + +/** + * hif_dev_unregister_channels()- Register transport layer channels + * @dev : HIF device object + * @func : SDIO Function pointer + * + * Return : None + */ +void hif_dev_unregister_channels(struct hif_sdio_dev *dev, + struct sdio_func *func) +{ + unsigned int chan; + + if (!dev) { + HIF_ERROR("%s: hif_sdio_dev is null", __func__); + return; + } + + for (chan = 0; chan < dev->adma_chans_used; chan++) { + dev->al_chan[chan]->priv = NULL; + pld_sdio_unregister_sdio_al_channel(dev->al_chan[chan]); + } +} + +/** + * hif_read_write() - queue a read/write request + * @dev: pointer to hif device structure + * @address: address to read, actually channel pointer + * @buffer: buffer to hold read/write data + * @length: length to read/write + * @request: read/write/sync/async request + * @context: pointer to hold calling context + * + * Return: 0, pending on success, error number otherwise. + */ +QDF_STATUS +hif_read_write(struct hif_sdio_dev *dev, + unsigned long sdio_al_ch_handle, + char *cbuffer, uint32_t length, + uint32_t request, void *context) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct sdio_al_channel_handle *ch; + struct bus_request *bus_req; + enum sdio_al_dma_direction dir; + struct hif_sdio_device *device; + QDF_STATUS (*rx_comp)(void *, qdf_nbuf_t, uint8_t); + qdf_nbuf_t nbuf; + int ret = 0, payload_len = 0; + unsigned char *buffer = (unsigned char *)cbuffer; + + if (!dev || !sdio_al_ch_handle) { + HIF_ERROR("%s: device = %pK, addr = %lu", __func__, + dev, sdio_al_ch_handle); + return QDF_STATUS_E_INVAL; + } + + if (!(request & HIF_ASYNCHRONOUS) && + !(request & HIF_SYNCHRONOUS)) { + HIF_ERROR("%s: Invalid request mode", __func__); + return QDF_STATUS_E_INVAL; + } + + /*sdio r/w action is not needed when suspend, so just return */ + if ((dev->is_suspend) && + (dev->power_config == HIF_DEVICE_POWER_CUT)) { + HIF_INFO("%s: skip in suspend", __func__); + return QDF_STATUS_SUCCESS; + } + + ch = (struct sdio_al_channel_handle *)sdio_al_ch_handle; + + bus_req = hif_allocate_bus_request(dev); + if (!bus_req) { + HIF_ERROR("%s: Bus alloc failed", __func__); + return QDF_STATUS_E_FAILURE; + } + + bus_req->address = sdio_al_ch_handle; + bus_req->length = length; + bus_req->request = request; + bus_req->context = context; + bus_req->buffer = buffer; + + /* Request SDIO AL to do transfer */ + dir = (request & HIF_SDIO_WRITE) ? SDIO_AL_TX : SDIO_AL_RX; + + if (request & HIF_SDIO_WRITE) + HIF_TRACE("%s: Tx len %d", __func__, length); + + if (request & HIF_SDIO_READ) + HIF_TRACE("%s: Rx len %d", __func__, length); + + if (request & HIF_SYNCHRONOUS) { + ret = sdio_al_queue_transfer(ch, + dir, + bus_req->buffer, + bus_req->length, + 1); /* higher priority */ + if (ret) { + status = QDF_STATUS_E_FAILURE; + HIF_ERROR("%s: SYNC REQ failed ret=%d", __func__, ret); + } else { + status = QDF_STATUS_SUCCESS; + } + + hif_free_bus_request(dev, bus_req); + + if ((status == QDF_STATUS_SUCCESS) && (dir == SDIO_AL_RX)) { + nbuf = (qdf_nbuf_t)context; + payload_len = HTC_GET_FIELD(bus_req->buffer, + HTC_FRAME_HDR, + PAYLOADLEN); + qdf_nbuf_set_pktlen(nbuf, payload_len + HTC_HDR_LENGTH); + device = (struct hif_sdio_device *)dev->htc_context; + rx_comp = device->hif_callbacks.rxCompletionHandler; + rx_comp(device->hif_callbacks.Context, nbuf, 0); + } + } else { + ret = sdio_al_queue_transfer_async(ch, + dir, + bus_req->buffer, + bus_req->length, + 1, /* higher priority */ + (void *)bus_req); + if (ret) { + status = QDF_STATUS_E_FAILURE; + HIF_ERROR("%s: ASYNC REQ fail ret=%d for len=%d ch=%d", + __func__, ret, length, ch->channel_id); + hif_free_bus_request(dev, bus_req); + } else { + status = QDF_STATUS_E_PENDING; + } + } + return status; +} + +/** + * ul_xfer_cb() - Completion call back for asyncronous transfer + * @ch_handle: The sdio al channel handle + * @result: The result of the operation + * @context: pointer to request context + * + * Return: None + */ +void ul_xfer_cb(struct sdio_al_channel_handle *ch_handle, + struct sdio_al_xfer_result *result, + void *ctx) +{ + struct bus_request *req = (struct bus_request *)ctx; + struct hif_sdio_dev *dev; + + if (!ch_handle || !result) { + HIF_ERROR("%s: Invalid args", __func__); + qdf_assert_always(0); + return; + } + + dev = (struct hif_sdio_dev *)ch_handle->priv; + + if (result->xfer_status) { + req->status = QDF_STATUS_E_FAILURE; + HIF_ERROR("%s: ASYNC Tx failed status=%d", __func__, + result->xfer_status); + } else { + req->status = QDF_STATUS_SUCCESS; + } + + dev->htc_callbacks.rw_compl_handler(req->context, req->status); + + hif_free_bus_request(dev, req); +} + +/** + * dl_data_avail_cb() - Called when data is available on a channel + * @ch_handle: The sdio al channel handle + * @len: The len of data available to download + * + * Return: None + */ +/* Use the asynchronous method of transfer. This will help in + * completing READ in the transfer done callback later which + * runs in sdio al thread context. If we do the syncronous + * transfer here, the thread context won't be available and + * perhaps a new thread may be reaquired here. + */ +void dl_data_avail_cb(struct sdio_al_channel_handle *ch_handle, + unsigned int len) +{ + struct hif_sdio_dev *dev; + unsigned int chan; + qdf_nbuf_t nbuf; + + if (!ch_handle || !len) { + HIF_ERROR("%s: Invalid args %u", __func__, len); + qdf_assert_always(0); + return; + } + + dev = (struct hif_sdio_dev *)ch_handle->priv; + chan = ch_handle->channel_id; + + if (chan > HIF_SDIO_MAX_AL_CHANNELS) { + HIF_ERROR("%s: Invalid Ch ID %d", __func__, chan); + return; + } + + /* allocate a buffer for reading the data from the chip. + * Note that this is raw, unparsed buffer and will be + * processed in the transfer done callback. + */ + /* TODO, use global buffer instead of runtime allocations */ + nbuf = qdf_nbuf_alloc(NULL, len_head_room(len), HEAD_ROOM, 4, 0); + + if (!nbuf) { + HIF_ERROR("%s: Unable to alloc netbuf %u bytes", __func__, len); + return; + } + + hif_read_write(dev, (unsigned long)ch_handle, nbuf->data, len, + HIF_RD_ASYNC_BLOCK_FIX, nbuf); +} + +/** + * dl_xfer_cb() - Call from lower layer after transfer is completed + * @ch_handle: The sdio al channel handle + * @result: The xfer result + * @ctx: Context passed in the transfer queuing + * + * Return: None + */ +void dl_xfer_cb(struct sdio_al_channel_handle *ch_handle, + struct sdio_al_xfer_result *result, + void *ctx) +{ + unsigned char *buf; + qdf_nbuf_t nbuf; + uint32_t len, nbuflen, payload_len = 0; + uint8_t *nbufdata; + struct hif_sdio_dev *dev; + struct hif_sdio_device *device; + struct bus_request *bus_req = (struct bus_request *)ctx; + bool last_htc_packet = false; + QDF_STATUS (*rx_completion)(void *, qdf_nbuf_t, uint8_t); + + if (!ctx) + HIF_ERROR("%s: Net buf context NULL!!!", __func__); + + if (!ctx || !ch_handle || !result) { + HIF_ERROR("%s: Invalid args", __func__); + qdf_assert_always(0); + return; + } + + dev = (struct hif_sdio_dev *)ch_handle->priv; + if (result->xfer_status) { + HIF_ERROR("%s: ASYNC Rx failed %d", __func__, + result->xfer_status); + /* TODO - Free nbuf if hif_sdio_get_nbuf is used, when bundling + * is enabled + */ + hif_free_bus_request(dev, (struct bus_request *)ctx); + return; + } + + device = (struct hif_sdio_device *)dev->htc_context; + rx_completion = device->hif_callbacks.rxCompletionHandler; + + buf = (unsigned char *)result->buf_addr; + len = (unsigned int)result->xfer_len; + + /* ADMA-TODO - Discard the padding data + * Its still not decided that the padding is informed + * via the hdr->flags or a padding magic inline in the + * buffer. So, lets see. + */ + while (len > 0 && len >= sizeof(HTC_FRAME_HDR)) { + if (last_htc_packet) { + HIF_ERROR("ERRR last htc_packet processed already\n"); + break; + } + if (HTC_GET_FIELD(buf, HTC_FRAME_HDR, ENDPOINTID) >= + ENDPOINT_MAX) { + HIF_ERROR("%s: invalid endpoint id: %u", __func__, + HTC_GET_FIELD(buf, HTC_FRAME_HDR, + ENDPOINTID)); + hif_free_bus_request(dev, (struct bus_request *)ctx); + return; + } + /* last_htc_packet is currently used to test non bundling case + * on Rumi Emulation platform. + * TODO - Remove last_htc_packet logic when bundling is + * enabled and use is_pad_block for bundling + */ + last_htc_packet = 1; + /* TODO - get net buf using hif_sdio_get_nbuf, when bundling is + * enabled + */ + nbuf = (qdf_nbuf_t)bus_req->context; + if (!nbuf) { + HIF_ERROR("%s: failed to alloc rx buffer", __func__); + break; + } + nbufdata = qdf_nbuf_data(nbuf); + nbuflen = qdf_nbuf_len(nbuf); + + /* Copy the HTC frame to the alloc'd packet buffer */ + payload_len = HTC_GET_FIELD(buf, HTC_FRAME_HDR, PAYLOADLEN); + if (!payload_len) { + HIF_ERROR("%s:Invalid Payload len %d bytes", __func__, + payload_len); + break; + } + + /* TODO - Check if payload fits in netbuf data */ + if (0) { //nbuflen < payload_len) { + HIF_ERROR("%s: nbuf %d < payload %d bytes", __func__, + nbuflen, payload_len); + break; + } + + qdf_mem_copy(nbufdata, buf, + payload_len + HTC_HEADER_LEN); + + qdf_nbuf_set_pktlen(nbuf, payload_len + HTC_HDR_LENGTH); + rx_completion(device->hif_callbacks.Context, + nbuf, + 0); /* don't care, not used */ + + if (len && last_htc_packet) { + unsigned int pad; + + pad = DEV_CALC_RECV_PADDED_LEN(device, payload_len + + HTC_HEADER_LEN); + /* Decrement the length to be processed yet */ + len -= pad; + /* Move the data pointer */ + buf += pad; + } else { + len -= payload_len + HTC_HDR_LENGTH; + buf += payload_len + HTC_HDR_LENGTH; + } + } + hif_free_bus_request(dev, (struct bus_request *)ctx); +} diff --git a/hif/src/sdio/transfer/adma.h b/hif/src/sdio/transfer/adma.h new file mode 100644 index 0000000000..f69faf532b --- /dev/null +++ b/hif/src/sdio/transfer/adma.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ADMA_H_ +#define _ADMA_H_ + +#include "hif_sdio_dev.h" +#include "htc_packet.h" +#include "htc_api.h" +#include "hif_internal.h" + +/* This should align with the underlying transport layer */ +#define HIF_DEFAULT_IO_BLOCK_SIZE 512 +#define HIF_BLOCK_SIZE HIF_DEFAULT_IO_BLOCK_SIZE +#define HIF_DUMMY_SPACE_MASK 0x0FFFFFFF + +#define HIF_SDIO_MAX_AL_CHANNELS 2 + +struct devRegisters { + uint32_t dummy; +}; + +#include "transfer.h" +#define DEV_REGISTERS_SIZE sizeof(struct devRegisters) + +uint8_t hif_dev_map_adma_chan_to_pipe(struct hif_sdio_device *pdev, + uint8_t chan, bool upload); + +struct sdio_al_channel_handle *hif_dev_map_pipe_to_adma_chan +( +struct hif_sdio_device *pdev, +uint8_t pipeid +); + +void dl_xfer_cb(struct sdio_al_channel_handle *ch_handle, + struct sdio_al_xfer_result *result, + void *ctx); +void ul_xfer_cb(struct sdio_al_channel_handle *ch_handle, + struct sdio_al_xfer_result *result, + void *ctx); + +void dl_data_avail_cb(struct sdio_al_channel_handle *ch_handle, + unsigned int len); + +void hif_sdio_rx_q_alloc(void *ctx); +qdf_nbuf_t hif_sdio_get_nbuf(struct hif_sdio_dev *dev); +#endif diff --git a/hif/src/sdio/transfer/mailbox.c b/hif/src/sdio/transfer/mailbox.c index 84a0a690ee..30c8b27edd 100644 --- a/hif/src/sdio/transfer/mailbox.c +++ b/hif/src/sdio/transfer/mailbox.c @@ -1,7 +1,6 @@ /* * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * - * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all @@ -19,6 +18,7 @@ #ifdef CONFIG_SDIO_TRANSFER_MAILBOX #define ATH_MODULE_NAME hif +#include #include #include #include @@ -44,6 +44,24 @@ #include "regtable.h" #include "transfer.h" +/* by default setup a bounce buffer for the data packets, + * if the underlying host controller driver + * does not use DMA you may be able to skip this step + * and save the memory allocation and transfer time + */ +#define HIF_USE_DMA_BOUNCE_BUFFER 1 +#if HIF_USE_DMA_BOUNCE_BUFFER +/* macro to check if DMA buffer is WORD-aligned and DMA-able. + * Most host controllers assume the + * buffer is DMA'able and will bug-check otherwise (i.e. buffers on the stack). + * virt_addr_valid check fails on stack memory. + */ +#define BUFFER_NEEDS_BOUNCE(buffer) (((unsigned long)(buffer) & 0x3) || \ + !virt_addr_valid((buffer))) +#else +#define BUFFER_NEEDS_BOUNCE(buffer) (false) +#endif + #ifdef SDIO_3_0 /** * set_extended_mbox_size() - set extended MBOX size @@ -170,6 +188,40 @@ static void set_extended_mbox_window_info(uint16_t manf_id, } } +/** hif_dev_set_mailbox_swap() - Set the mailbox swap from firmware + * @pdev : The HIF layer object + * + * Return: none + */ +void hif_dev_set_mailbox_swap(struct hif_sdio_dev *pdev) +{ + struct hif_sdio_device *hif_device = hif_dev_from_hif(pdev); + + HIF_ENTER(); + + hif_device->swap_mailbox = true; + + HIF_EXIT(); +} + +/** hif_dev_get_mailbox_swap() - Get the mailbox swap setting + * @pdev : The HIF layer object + * + * Return: true or false + */ +bool hif_dev_get_mailbox_swap(struct hif_sdio_dev *pdev) +{ + struct hif_sdio_device *hif_device; + + HIF_ENTER(); + + hif_device = hif_dev_from_hif(pdev); + + HIF_EXIT(); + + return hif_device->swap_mailbox; +} + /** * hif_dev_get_fifo_address() - get the fifo addresses for dma * @pdev: SDIO HIF object @@ -177,22 +229,24 @@ static void set_extended_mbox_window_info(uint16_t manf_id, * * Return : 0 for success, non-zero for error */ -QDF_STATUS hif_dev_get_fifo_address(struct hif_sdio_dev *pdev, - struct hif_device_mbox_info *config, - uint32_t config_len) +int hif_dev_get_fifo_address(struct hif_sdio_dev *pdev, + void *config, + uint32_t config_len) { uint32_t count; + struct hif_device_mbox_info *cfg = + (struct hif_device_mbox_info *)config; for (count = 0; count < 4; count++) - config->mbox_addresses[count] = HIF_MBOX_START_ADDR(count); + cfg->mbox_addresses[count] = HIF_MBOX_START_ADDR(count); if (config_len >= sizeof(struct hif_device_mbox_info)) { set_extended_mbox_window_info((uint16_t)pdev->func->device, - config); - return QDF_STATUS_SUCCESS; + cfg); + return 0; } - return QDF_STATUS_E_INVAL; + return -EINVAL; } /** @@ -488,7 +542,7 @@ static uint8_t hif_dev_map_mail_box_to_pipe(struct hif_sdio_device *pdev, * Return 0 for success and non-zero for failure to map */ int hif_get_send_address(struct hif_sdio_device *pdev, - uint8_t pipe, uint32_t *addr) + uint8_t pipe, unsigned long *addr) { uint8_t mbox_index = INVALID_MAILBOX_NUMBER; @@ -586,9 +640,9 @@ static QDF_STATUS hif_dev_recv_packet(struct hif_sdio_device *pdev, } /* mailbox index is saved in Endpoint member */ - HIF_INFO("%s : hdr:0x%x, len:%d, padded length: %d Mbox:0x%x", - __func__, packet->PktInfo.AsRx.ExpectedHdr, recv_length, - padded_length, mbox_index); + HIF_INFO_HI("%s : hdr:0x%x, len:%d, padded length: %d Mbox:0x%x", + __func__, packet->PktInfo.AsRx.ExpectedHdr, recv_length, + padded_length, mbox_index); status = hif_read_write(pdev->HIFDevice, pdev->MailBoxInfo.mbox_addresses[mbox_index], @@ -604,11 +658,11 @@ static QDF_STATUS hif_dev_recv_packet(struct hif_sdio_device *pdev, if (status == QDF_STATUS_SUCCESS) { HTC_FRAME_HDR *hdr = (HTC_FRAME_HDR *) packet->pBuffer; - HIF_INFO("%s: EP:%d,Len:%d,Flag:%d,CB:0x%02X,0x%02X\n", - __func__, - hdr->EndpointID, hdr->PayloadLen, - hdr->Flags, hdr->ControlBytes0, - hdr->ControlBytes1); + HIF_INFO_HI("%s:EP:%d,Len:%d,Flg:%d,CB:0x%02X,0x%02X\n", + __func__, + hdr->EndpointID, hdr->PayloadLen, + hdr->Flags, hdr->ControlBytes0, + hdr->ControlBytes1); } } @@ -745,7 +799,7 @@ QDF_STATUS hif_dev_recv_message_pending_handler(struct hif_sdio_device *pdev, HTC_PACKET_QUEUE recv_q, sync_comp_q; QDF_STATUS (*rxCompletion)(void *, qdf_nbuf_t, uint8_t); - HIF_INFO("%s: NumLookAheads: %d\n", __func__, num_look_aheads); + HIF_INFO_HI("%s: NumLookAheads: %d\n", __func__, num_look_aheads); if (num_pkts_fetched) *num_pkts_fetched = 0; @@ -1321,33 +1375,610 @@ QDF_STATUS hif_dev_process_pending_irqs(struct hif_sdio_device *pdev, return status; } -#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) && \ - !defined(WITH_BACKPORTS) +#define DEV_CHECK_RECV_YIELD(pdev) \ + ((pdev)->CurrentDSRRecvCount >= \ + (pdev)->HifIRQYieldParams.recv_packet_yield_count) /** - * hif_sdio_set_drvdata() - set wlan driver data into upper layer private - * @func: pointer to sdio function - * @hifdevice: pointer to hif device + * hif_dev_dsr_handler() - Synchronous interrupt handler * - * Return: non zero for success. + * @context: hif send context + * + * Return: 0 for success and non-zero for failure */ -int hif_sdio_set_drvdata(struct sdio_func *func, - struct hif_sdio_dev *hifdevice) +QDF_STATUS hif_dev_dsr_handler(void *context) { - return sdio_set_drvdata(func, hifdevice); + struct hif_sdio_device *pdev = (struct hif_sdio_device *)context; + QDF_STATUS status = QDF_STATUS_SUCCESS; + bool done = false; + bool async_proc = false; + + /* reset the recv counter that tracks when we need + * to yield from the DSR + */ + pdev->CurrentDSRRecvCount = 0; + /* reset counter used to flag a re-scan of IRQ + * status registers on the target + */ + pdev->RecheckIRQStatusCnt = 0; + + while (!done) { + status = hif_dev_process_pending_irqs(pdev, &done, &async_proc); + if (QDF_IS_STATUS_ERROR(status)) + break; + + if (pdev->HifIRQProcessingMode == HIF_DEVICE_IRQ_SYNC_ONLY) { + /* the HIF layer does not allow async IRQ processing, + * override the asyncProc flag + */ + async_proc = false; + /* this will cause us to re-enter ProcessPendingIRQ() + * and re-read interrupt status registers. + * This has a nice side effect of blocking us until all + * async read requests are completed. This behavior is + * required as we do not allow ASYNC processing + * in interrupt handlers (like Windows CE) + */ + + if (pdev->DSRCanYield && DEV_CHECK_RECV_YIELD(pdev)) + /* ProcessPendingIRQs() pulled enough recv + * messages to satisfy the yield count, stop + * checking for more messages and return + */ + break; + } + + if (async_proc) { + /* the function does some async I/O for performance, + * we need to exit the ISR immediately, the check below + * will prevent the interrupt from being + * Ack'd while we handle it asynchronously + */ + break; + } + } + + if (QDF_IS_STATUS_SUCCESS(status) && !async_proc) { + /* Ack the interrupt only if : + * 1. we did not get any errors in processing interrupts + * 2. there are no outstanding async processing requests + */ + if (pdev->DSRCanYield) { + /* if the DSR can yield do not ACK the interrupt, there + * could be more pending messages. The HIF layer + * must ACK the interrupt on behalf of HTC + */ + HIF_INFO("%s: Yield (RX count: %d)", + __func__, pdev->CurrentDSRRecvCount); + } else { + hif_ack_interrupt(pdev->HIFDevice); + } + } + + return status; } -#else -int hif_sdio_set_drvdata(struct sdio_func *func, - struct hif_sdio_dev *hifdevice) + +/** + * hif_read_write() - queue a read/write request + * @device: pointer to hif device structure + * @address: address to read + * @buffer: buffer to hold read/write data + * @length: length to read/write + * @request: read/write/sync/async request + * @context: pointer to hold calling context + * + * Return: 0 on success, error number otherwise. + */ +QDF_STATUS +hif_read_write(struct hif_sdio_dev *device, + unsigned long address, + char *buffer, uint32_t length, + uint32_t request, void *context) { - sdio_set_drvdata(func, hifdevice); + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct bus_request *busrequest; + + AR_DEBUG_ASSERT(device); + AR_DEBUG_ASSERT(device->func); + HIF_TRACE("%s: device 0x%pK addr 0x%lX buffer 0x%pK", + __func__, device, address, buffer); + HIF_TRACE("%s: len %d req 0x%X context 0x%pK", + __func__, length, request, context); + + /*sdio r/w action is not needed when suspend, so just return */ + if ((device->is_suspend) && + (device->power_config == HIF_DEVICE_POWER_CUT)) { + AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("skip io when suspending\n")); + return QDF_STATUS_SUCCESS; + } + do { + if ((request & HIF_ASYNCHRONOUS) || + (request & HIF_SYNCHRONOUS)) { + /* serialize all requests through the async thread */ + AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, + ("%s: Execution mode: %s\n", __func__, + (request & HIF_ASYNCHRONOUS) ? "Async" + : "Synch")); + busrequest = hif_allocate_bus_request(device); + if (!busrequest) { + HIF_ERROR("%s:bus requests unavail", __func__); + HIF_ERROR("%s, addr:0x%lX, len:%d", + request & HIF_SDIO_READ ? "READ" : + "WRITE", address, length); + return QDF_STATUS_E_FAILURE; + } + busrequest->address = address; + busrequest->buffer = buffer; + busrequest->length = length; + busrequest->request = request; + busrequest->context = context; + + add_to_async_list(device, busrequest); + + if (request & HIF_SYNCHRONOUS) { + AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, + ("%s: queued sync req: 0x%lX\n", + __func__, + (unsigned long)busrequest)); + + /* wait for completion */ + up(&device->sem_async); + if (down_interruptible(&busrequest->sem_req) == + 0) { + QDF_STATUS status = busrequest->status; + + HIF_TRACE("%s: sync freeing 0x%lX:0x%X", + __func__, + (unsigned long)busrequest, + busrequest->status); + HIF_TRACE("%s: freeing req: 0x%X", + __func__, + (unsigned int)request); + hif_free_bus_request(device, + busrequest); + return status; + } else { + /* interrupted, exit */ + return QDF_STATUS_E_FAILURE; + } + } else { + HIF_TRACE("%s: queued async req: 0x%lX", + __func__, (unsigned long)busrequest); + up(&device->sem_async); + return QDF_STATUS_E_PENDING; + } + } else { + HIF_ERROR("%s: Invalid execution mode: 0x%08x", + __func__, (unsigned int)request); + status = QDF_STATUS_E_INVAL; + break; + } + } while (0); + + return status; +} + +/** + * hif_sdio_func_enable() - Handle device enabling as per device + * @device: HIF device object + * @func: function pointer + * + * Return success or failure + */ +static int hif_sdio_func_enable(struct hif_softc *ol_sc, + struct sdio_func *func) +{ + struct hif_sdio_dev *device = get_hif_device(ol_sc, func); + + if (device->is_disabled) { + int ret = 0; + + sdio_claim_host(func); + + ret = hif_sdio_quirk_async_intr(ol_sc, func); + if (ret) { + HIF_ERROR("%s: Error setting async intr:%d", + __func__, ret); + sdio_release_host(func); + return QDF_STATUS_E_FAILURE; + } + + func->enable_timeout = 100; + ret = sdio_enable_func(func); + if (ret) { + HIF_ERROR("%s: Unable to enable function: %d", + __func__, ret); + sdio_release_host(func); + return QDF_STATUS_E_FAILURE; + } + + ret = sdio_set_block_size(func, HIF_BLOCK_SIZE); + if (ret) { + HIF_ERROR("%s: Unable to set block size 0x%X : %d\n", + __func__, HIF_BLOCK_SIZE, ret); + sdio_release_host(func); + return QDF_STATUS_E_FAILURE; + } + + ret = hif_sdio_quirk_mod_strength(ol_sc, func); + if (ret) { + HIF_ERROR("%s: Error setting mod strength : %d\n", + __func__, ret); + sdio_release_host(func); + return QDF_STATUS_E_FAILURE; + } + + sdio_release_host(func); + } + return 0; } -#endif /* LINUX VERSION */ -struct hif_sdio_dev *get_hif_device(struct sdio_func *func) +/** + * __hif_read_write() - sdio read/write wrapper + * @device: pointer to hif device structure + * @address: address to read + * @buffer: buffer to hold read/write data + * @length: length to read/write + * @request: read/write/sync/async request + * @context: pointer to hold calling context + * + * Return: 0 on success, error number otherwise. + */ +static QDF_STATUS +__hif_read_write(struct hif_sdio_dev *device, + uint32_t address, char *buffer, + uint32_t length, uint32_t request, void *context) { - qdf_assert(func); + uint8_t opcode; + QDF_STATUS status = QDF_STATUS_SUCCESS; + int ret = A_OK; + uint8_t *tbuffer; + bool bounced = false; - return (struct hif_sdio_dev *)sdio_get_drvdata(func); + if (!device) { + HIF_ERROR("%s: device null!", __func__); + return QDF_STATUS_E_INVAL; + } + + if (!device->func) { + HIF_ERROR("%s: func null!", __func__); + return QDF_STATUS_E_INVAL; + } + + HIF_INFO_HI("%s: addr:0X%06X, len:%08d, %s, %s", __func__, + address, length, + request & HIF_SDIO_READ ? "Read " : "Write", + request & HIF_ASYNCHRONOUS ? "Async" : "Sync "); + + do { + if (request & HIF_EXTENDED_IO) { + HIF_INFO_HI("%s: Command type: CMD53\n", __func__); + } else { + HIF_ERROR("%s: Invalid command type: 0x%08x\n", + __func__, request); + status = QDF_STATUS_E_INVAL; + break; + } + + if (request & HIF_BLOCK_BASIS) { + /* round to whole block length size */ + length = + (length / HIF_BLOCK_SIZE) * + HIF_BLOCK_SIZE; + HIF_INFO_HI("%s: Block mode (BlockLen: %d)\n", + __func__, length); + } else if (request & HIF_BYTE_BASIS) { + HIF_INFO_HI("%s: Byte mode (BlockLen: %d)\n", + __func__, length); + } else { + HIF_ERROR("%s: Invalid data mode: 0x%08x\n", + __func__, request); + status = QDF_STATUS_E_INVAL; + break; + } + if (request & HIF_SDIO_WRITE) { + hif_fixup_write_param(device, request, + &length, &address); + + HIF_INFO_HI("addr:%08X, len:0x%08X, dummy:0x%04X\n", + address, length, + (request & HIF_DUMMY_SPACE_MASK) >> 16); + } + + if (request & HIF_FIXED_ADDRESS) { + opcode = CMD53_FIXED_ADDRESS; + HIF_INFO_HI("%s: Addr mode: fixed 0x%X\n", + __func__, address); + } else if (request & HIF_INCREMENTAL_ADDRESS) { + opcode = CMD53_INCR_ADDRESS; + HIF_INFO_HI("%s: Address mode: Incremental 0x%X\n", + __func__, address); + } else { + HIF_ERROR("%s: Invalid address mode: 0x%08x\n", + __func__, request); + status = QDF_STATUS_E_INVAL; + break; + } + + if (request & HIF_SDIO_WRITE) { +#if HIF_USE_DMA_BOUNCE_BUFFER + if (BUFFER_NEEDS_BOUNCE(buffer)) { + AR_DEBUG_ASSERT(device->dma_buffer); + tbuffer = device->dma_buffer; + /* copy the write data to the dma buffer */ + AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); + if (length > HIF_DMA_BUFFER_SIZE) { + HIF_ERROR("%s: Invalid write len: %d\n", + __func__, length); + status = QDF_STATUS_E_INVAL; + break; + } + memcpy(tbuffer, buffer, length); + bounced = true; + } else { + tbuffer = buffer; + } +#else + tbuffer = buffer; +#endif + if (opcode == CMD53_FIXED_ADDRESS && tbuffer) { + ret = sdio_writesb(device->func, address, + tbuffer, length); + HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", + __func__, ret, address, length, + *(int *)tbuffer); + } else if (tbuffer) { + ret = sdio_memcpy_toio(device->func, address, + tbuffer, length); + HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", + __func__, ret, address, length, + *(int *)tbuffer); + } + } else if (request & HIF_SDIO_READ) { +#if HIF_USE_DMA_BOUNCE_BUFFER + if (BUFFER_NEEDS_BOUNCE(buffer)) { + AR_DEBUG_ASSERT(device->dma_buffer); + AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); + if (length > HIF_DMA_BUFFER_SIZE) { + HIF_ERROR("%s: Invalid read len: %d\n", + __func__, length); + status = QDF_STATUS_E_INVAL; + break; + } + tbuffer = device->dma_buffer; + bounced = true; + } else { + tbuffer = buffer; + } +#else + tbuffer = buffer; +#endif + if (opcode == CMD53_FIXED_ADDRESS && tbuffer) { + ret = sdio_readsb(device->func, tbuffer, + address, length); + HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", + __func__, ret, address, length, + *(int *)tbuffer); + } else if (tbuffer) { + ret = sdio_memcpy_fromio(device->func, + tbuffer, address, + length); + HIF_INFO_HI("%s:r=%d addr:0x%X, len:%d, 0x%X\n", + __func__, ret, address, length, + *(int *)tbuffer); + } +#if HIF_USE_DMA_BOUNCE_BUFFER + if (bounced && tbuffer) + memcpy(buffer, tbuffer, length); +#endif + } else { + HIF_ERROR("%s: Invalid dir: 0x%08x", __func__, request); + status = QDF_STATUS_E_INVAL; + return status; + } + + if (ret) { + HIF_ERROR("%s: SDIO bus operation failed!", __func__); + HIF_ERROR("%s: MMC stack returned : %d", __func__, ret); + HIF_ERROR("%s: addr:0X%06X, len:%08d, %s, %s", + __func__, address, length, + request & HIF_SDIO_READ ? "Read " : "Write", + request & HIF_ASYNCHRONOUS ? + "Async" : "Sync"); + status = QDF_STATUS_E_FAILURE; + } + } while (false); + + return status; +} + +/** + * async_task() - thread function to serialize all bus requests + * @param: pointer to hif device + * + * thread function to serialize all requests, both sync and async + * Return: 0 on success, error number otherwise. + */ +static int async_task(void *param) +{ + struct hif_sdio_dev *device; + struct bus_request *request; + QDF_STATUS status; + bool claimed = false; + + device = (struct hif_sdio_dev *)param; + set_current_state(TASK_INTERRUPTIBLE); + while (!device->async_shutdown) { + /* wait for work */ + if (down_interruptible(&device->sem_async) != 0) { + /* interrupted, exit */ + AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, + ("%s: async task interrupted\n", + __func__)); + break; + } + if (device->async_shutdown) { + AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, + ("%s: async task stopping\n", + __func__)); + break; + } + /* we want to hold the host over multiple cmds + * if possible, but holding the host blocks + * card interrupts + */ + qdf_spin_lock_irqsave(&device->asynclock); + /* pull the request to work on */ + while (device->asyncreq) { + request = device->asyncreq; + if (request->inusenext) + device->asyncreq = request->inusenext; + else + device->asyncreq = NULL; + qdf_spin_unlock_irqrestore(&device->asynclock); + HIF_TRACE("%s: processing req: 0x%lX", + __func__, (unsigned long)request); + + if (!claimed) { + sdio_claim_host(device->func); + claimed = true; + } + if (request->scatter_req) { + A_ASSERT(device->scatter_enabled); + /* pass the request to scatter routine which + * executes it synchronously, note, no need + * to free the request since scatter requests + * are maintained on a separate list + */ + status = do_hif_read_write_scatter(device, + request); + } else { + /* call hif_read_write in sync mode */ + status = + __hif_read_write(device, + request->address, + request->buffer, + request->length, + request-> + request & + ~HIF_SYNCHRONOUS, + NULL); + if (request->request & HIF_ASYNCHRONOUS) { + void *context = request->context; + + HIF_TRACE("%s: freeing req: 0x%lX", + __func__, + (unsigned long)request); + hif_free_bus_request(device, request); + + HIF_TRACE("%s: completion req 0x%lX", + __func__, + (unsigned long)request); + device->htc_callbacks. + rw_compl_handler(context, status); + } else { + HIF_TRACE("%s: upping req: 0x%lX", + __func__, + (unsigned long)request); + request->status = status; + up(&request->sem_req); + } + } + qdf_spin_lock_irqsave(&device->asynclock); + } + qdf_spin_unlock_irqrestore(&device->asynclock); + if (claimed) { + sdio_release_host(device->func); + claimed = false; + } + } + + complete_and_exit(&device->async_completion, 0); + + return 0; +} + +/** + * hif_disable_func() - Disable SDIO function + * + * @device: HIF device pointer + * @func: SDIO function pointer + * @reset: If this is called from resume or probe + * + * Return: 0 in case of success, else error value + */ +QDF_STATUS hif_disable_func(struct hif_sdio_dev *device, + struct sdio_func *func, + bool reset) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + HIF_ENTER(); + if (!IS_ERR(device->async_task)) { + init_completion(&device->async_completion); + device->async_shutdown = 1; + up(&device->sem_async); + wait_for_completion(&device->async_completion); + device->async_task = NULL; + sema_init(&device->sem_async, 0); + } + + status = hif_sdio_func_disable(device, func, reset); + if (status == QDF_STATUS_SUCCESS) + device->is_disabled = true; + + cleanup_hif_scatter_resources(device); + + HIF_EXIT(); + + return status; +} + +/** + * hif_enable_func() - Enable SDIO function + * + * @ol_sc: HIF object pointer + * @device: HIF device pointer + * @sdio_func: SDIO function pointer + * @resume: If this is called from resume or probe + * + * Return: 0 in case of success, else error value + */ +QDF_STATUS hif_enable_func(struct hif_softc *ol_sc, struct hif_sdio_dev *device, + struct sdio_func *func, bool resume) +{ + int ret = QDF_STATUS_SUCCESS; + + HIF_ENTER(); + + if (!device) { + HIF_ERROR("%s: HIF device is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + if (hif_sdio_func_enable(ol_sc, func)) + return QDF_STATUS_E_FAILURE; + + /* create async I/O thread */ + if (!device->async_task && device->is_disabled) { + device->async_shutdown = 0; + device->async_task = kthread_create(async_task, + (void *)device, + "AR6K Async"); + if (IS_ERR(device->async_task)) { + HIF_ERROR("%s: Error creating async task", + __func__); + return QDF_STATUS_E_FAILURE; + } + device->is_disabled = false; + wake_up_process(device->async_task); + } + + if (!resume) + ret = hif_sdio_probe(ol_sc, func, device); + + HIF_EXIT(); + + return ret; } #endif /* CONFIG_SDIO_TRANSFER_MAILBOX */ diff --git a/hif/src/sdio/transfer/mailbox.h b/hif/src/sdio/transfer/mailbox.h index 9123d872e7..3e59132152 100644 --- a/hif/src/sdio/transfer/mailbox.h +++ b/hif/src/sdio/transfer/mailbox.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2016-2019 The Linux Foundation. All rights reserved. * * * @@ -146,11 +146,6 @@ */ #define HIF_DUMMY_SPACE_MASK 0xFFFF0000 -/* - * data written into the dummy space will not put into the final mbox FIFO - */ -#define HIF_DUMMY_SPACE_MASK 0xFFFF0000 - PREPACK struct MBOX_IRQ_PROC_REGISTERS { uint8_t host_int_status; uint8_t cpu_int_status; @@ -190,4 +185,9 @@ struct devRegisters { #define DEV_REGISTERS_SIZE (sizeof(struct MBOX_IRQ_PROC_REGISTERS) + \ sizeof(struct MBOX_IRQ_ENABLE_REGISTERS) + \ sizeof(struct MBOX_COUNTER_REGISTERS)) + +void hif_dev_dump_registers(struct hif_sdio_device *pdev, + struct MBOX_IRQ_PROC_REGISTERS *irq_proc, + struct MBOX_IRQ_ENABLE_REGISTERS *irq_en, + struct MBOX_COUNTER_REGISTERS *mbox_regs); #endif /* _MAILBOX_H_ */ diff --git a/hif/src/sdio/transfer/transfer.c b/hif/src/sdio/transfer/transfer.c index 07ebf52b95..a7837c8f28 100644 --- a/hif/src/sdio/transfer/transfer.c +++ b/hif/src/sdio/transfer/transfer.c @@ -91,7 +91,8 @@ QDF_STATUS hif_dev_send_buffer(struct hif_sdio_device *pdev, uint32_t xfer_id, unsigned char *pData; struct hif_sendContext *sctx; uint32_t request = hif_get_send_buffer_flags(pdev); - uint32_t padded_length, addr = 0; + uint32_t padded_length; + unsigned long addr = 0; int frag_count = 0, i, count, head_len; if (hif_get_send_address(pdev, pipe, &addr)) { @@ -381,7 +382,7 @@ QDF_STATUS hif_dev_process_trailer(struct hif_sdio_device *pdev, HTC_RECORD_HDR *record; HTC_LOOKAHEAD_REPORT *look_ahead; - HIF_INFO("%s: length:%d", __func__, length); + HIF_INFO_HI("%s: length:%d", __func__, length); orig_buffer = buffer; orig_length = length; @@ -515,7 +516,7 @@ QDF_STATUS hif_dev_process_trailer(struct hif_sdio_device *pdev, debug_dump_bytes(orig_buffer, orig_length, "BAD Recv Trailer"); - HIF_INFO("%s: status = %d", __func__, status); + HIF_INFO_HI("%s: status = %d", __func__, status); return status; } diff --git a/hif/src/sdio/transfer/transfer.h b/hif/src/sdio/transfer/transfer.h index 4a72838513..73d6b53692 100644 --- a/hif/src/sdio/transfer/transfer.h +++ b/hif/src/sdio/transfer/transfer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * * * @@ -65,13 +65,8 @@ struct hif_sendContext { unsigned int head_data_len; }; -void hif_dev_dump_registers(struct hif_sdio_device *pdev, - struct MBOX_IRQ_PROC_REGISTERS *irq_proc, - struct MBOX_IRQ_ENABLE_REGISTERS *irq_en, - struct MBOX_COUNTER_REGISTERS *mbox_regs); - int hif_get_send_address(struct hif_sdio_device *pdev, - uint8_t pipe, uint32_t *addr); + uint8_t pipe, unsigned long *addr); QDF_STATUS hif_dev_alloc_and_prepare_rx_packets(struct hif_sdio_device *pdev, uint32_t look_aheads[], @@ -104,7 +99,11 @@ static inline uint32_t hif_get_send_buffer_flags(struct hif_sdio_device *pdev) return 0; } #elif defined(CONFIG_SDIO_TRANSFER_ADMA) -#error "Error - Not implemented yet" +static inline uint32_t hif_get_send_buffer_flags(struct hif_sdio_device *pdev) +{ + /* ADAM-TODO */ + return (uint32_t)HIF_WR_ASYNC_BLOCK_FIX; +} #endif #endif /* __TRANSFER_H__ */