qcacmn: Support IPQ4019 driver probe and detach
Add AHB bus type support. Change-Id: I968f3ae06bb63a2116a9d24d75957f397a0e8c50 Acked-by: Balamurugan Mahalingam <bmahalin@codeaurora.org> CRs-Fixed: 1009050
This commit is contained in:

committed by
Gerrit - the friendly Code Review server

parent
abd0077413
commit
3db96a43b6
@@ -364,7 +364,7 @@
|
||||
/* PLL end */
|
||||
|
||||
#define FW_CPU_PLL_CONFIG \
|
||||
(sc->targetdef->d_FW_CPU_PLL_CONFIG)
|
||||
(scn->targetdef->d_FW_CPU_PLL_CONFIG)
|
||||
|
||||
#define WIFICMN_PCIE_BAR_REG_ADDRESS \
|
||||
(sc->targetdef->d_WIFICMN_PCIE_BAR_REG_ADDRESS)
|
||||
|
39
hif/src/dispatcher/ahb_api.h
Normal file
39
hif/src/dispatcher/ahb_api.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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 __AHB_API_H
|
||||
#define __AHB_API_H
|
||||
|
||||
QDF_STATUS hif_ahb_open(struct hif_softc *hif_ctx,
|
||||
enum qdf_bus_type bus_type);
|
||||
void hif_ahb_close(struct hif_softc *hif_ctx);
|
||||
|
||||
|
||||
void hif_ahb_disable_isr(struct hif_softc *hif_ctx);
|
||||
void hif_ahb_nointrs(struct hif_softc *scn);
|
||||
QDF_STATUS hif_ahb_enable_bus(struct hif_softc *ol_sc,
|
||||
struct device *dev, void *bdev,
|
||||
const hif_bus_id *bid,
|
||||
enum hif_enable_type type);
|
||||
void hif_ahb_disable_bus(struct hif_softc *scn);
|
||||
int hif_ahb_bus_configure(struct hif_softc *scn);
|
||||
void hif_ahb_irq_disable(struct hif_softc *scn, int ce_id);
|
||||
void hif_ahb_irq_enable(struct hif_softc *scn, int ce_id);
|
||||
int hif_ahb_dump_registers(struct hif_softc *scn);
|
||||
|
||||
#endif
|
@@ -86,6 +86,8 @@ int hif_bus_get_context_size(enum qdf_bus_type bus_type)
|
||||
switch (bus_type) {
|
||||
case QDF_BUS_TYPE_PCI:
|
||||
return hif_pci_get_context_size();
|
||||
case QDF_BUS_TYPE_AHB:
|
||||
return hif_ahb_get_context_size();
|
||||
case QDF_BUS_TYPE_SNOC:
|
||||
return hif_snoc_get_context_size();
|
||||
default:
|
||||
@@ -114,6 +116,9 @@ QDF_STATUS hif_bus_open(struct hif_softc *hif_sc,
|
||||
case QDF_BUS_TYPE_SNOC:
|
||||
status = hif_initialize_snoc_ops(&hif_sc->bus_ops);
|
||||
break;
|
||||
case QDF_BUS_TYPE_AHB:
|
||||
status = hif_initialize_ahb_ops(&hif_sc->bus_ops);
|
||||
break;
|
||||
default:
|
||||
status = QDF_STATUS_E_NOSUPPORT;
|
||||
break;
|
||||
|
@@ -100,4 +100,31 @@ static inline int hif_pci_get_context_size(void)
|
||||
}
|
||||
#endif /* HIF_PCI */
|
||||
|
||||
#ifdef HIF_AHB
|
||||
QDF_STATUS hif_initialize_ahb_ops(struct hif_bus_ops *bus_ops);
|
||||
int hif_ahb_get_context_size(void);
|
||||
#else
|
||||
/**
|
||||
* hif_initialize_ahb_ops() - dummy for when ahb not supported
|
||||
*
|
||||
* Return: QDF_STATUS_E_NOSUPPORT
|
||||
*/
|
||||
static inline QDF_STATUS hif_initialize_ahb_ops(struct hif_bus_ops *bus_ops)
|
||||
{
|
||||
HIF_ERROR("%s: not supported", __func__);
|
||||
return QDF_STATUS_E_NOSUPPORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_ahb_get_context_size() - dummy for when ahb not supported
|
||||
*
|
||||
* Return: 0 as an invalid size to indicate no support
|
||||
*/
|
||||
static inline int hif_ahb_get_context_size(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _MULTIBUS_H_ */
|
||||
|
70
hif/src/dispatcher/multibus_ahb.c
Normal file
70
hif/src/dispatcher/multibus_ahb.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 "hif.h"
|
||||
#include "hif_main.h"
|
||||
#include "multibus.h"
|
||||
#include "ce_main.h"
|
||||
#include "ahb_api.h"
|
||||
#include "dummy.h"
|
||||
|
||||
/**
|
||||
* hif_initialize_ahb_ops() - initialize the ahb ops
|
||||
* @bus_ops: hif_bus_ops table pointer to initialize
|
||||
*
|
||||
* This function will assign the set of callbacks that needs
|
||||
* to be called for ipq4019 platform
|
||||
*
|
||||
* Return: QDF_STATUS_SUCCESS
|
||||
*/
|
||||
QDF_STATUS hif_initialize_ahb_ops(struct hif_bus_ops *bus_ops)
|
||||
{
|
||||
bus_ops->hif_bus_open = &hif_ahb_open;
|
||||
bus_ops->hif_bus_close = &hif_ahb_close;
|
||||
bus_ops->hif_bus_prevent_linkdown = &hif_dummy_bus_prevent_linkdown;
|
||||
bus_ops->hif_reset_soc = &hif_dummy_reset_soc;
|
||||
bus_ops->hif_bus_suspend = &hif_dummy_bus_suspend;
|
||||
bus_ops->hif_bus_resume = &hif_dummy_bus_resume;
|
||||
bus_ops->hif_target_sleep_state_adjust =
|
||||
&hif_dummy_target_sleep_state_adjust;
|
||||
|
||||
bus_ops->hif_disable_isr = &hif_ahb_disable_isr;
|
||||
bus_ops->hif_nointrs = &hif_ahb_nointrs;
|
||||
bus_ops->hif_enable_bus = &hif_ahb_enable_bus;
|
||||
bus_ops->hif_disable_bus = &hif_ahb_disable_bus;
|
||||
bus_ops->hif_bus_configure = &hif_ahb_bus_configure;
|
||||
bus_ops->hif_irq_disable = &hif_ahb_irq_disable;
|
||||
bus_ops->hif_irq_enable = &hif_ahb_irq_enable;
|
||||
bus_ops->hif_dump_registers = &hif_ahb_dump_registers;
|
||||
bus_ops->hif_enable_power_management =
|
||||
&hif_dummy_enable_power_management;
|
||||
bus_ops->hif_disable_power_management =
|
||||
&hif_dummy_disable_power_management;
|
||||
|
||||
return QDF_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_ahb_get_context_size() - return the size of the snoc context
|
||||
*
|
||||
* Return the size of the context. (0 for invalid bus)
|
||||
*/
|
||||
int hif_ahb_get_context_size(void)
|
||||
{
|
||||
return sizeof(struct HIF_CE_state);
|
||||
}
|
@@ -487,7 +487,7 @@ struct hif_opaque_softc *hif_open(qdf_device_t qdf_ctx, uint32_t mode,
|
||||
qdf_atomic_init(&scn->link_suspended);
|
||||
qdf_atomic_init(&scn->tasklet_from_intr);
|
||||
qdf_mem_copy(&scn->callbacks, cbk, sizeof(struct hif_driver_state_callbacks));
|
||||
|
||||
scn->bus_type = bus_type;
|
||||
status = hif_bus_open(scn, bus_type);
|
||||
if (status != QDF_STATUS_SUCCESS) {
|
||||
HIF_ERROR("%s: hif_bus_open error = %d, bus_type = %d",
|
||||
@@ -804,6 +804,12 @@ int hif_get_device_type(uint32_t device_id,
|
||||
HIF_INFO(" *********** AR900B *************\n");
|
||||
break;
|
||||
|
||||
case IPQ4019_DEVICE_ID:
|
||||
*hif_type = HIF_TYPE_IPQ4019;
|
||||
*target_type = TARGET_TYPE_IPQ4019;
|
||||
HIF_INFO(" *********** IPQ4019 *************\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
HIF_ERROR("%s: Unsupported device ID!", __func__);
|
||||
ret = -ENODEV;
|
||||
|
@@ -90,6 +90,7 @@
|
||||
#define AR900B_DEVICE_ID (0x0040)
|
||||
#define QCA9984_DEVICE_ID (0x0046)
|
||||
#define QCA9888_DEVICE_ID (0x0056)
|
||||
#define IPQ4019_DEVICE_ID (0x12ef)
|
||||
|
||||
#define HIF_GET_PCI_SOFTC(scn) ((struct hif_pci_softc *)scn)
|
||||
#define HIF_GET_CE_STATE(scn) ((struct HIF_CE_state *)scn)
|
||||
|
@@ -176,7 +176,7 @@ static void pci_dispatch_interrupt(struct hif_softc *scn)
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t hif_pci_interrupt_handler(int irq, void *arg)
|
||||
irqreturn_t hif_pci_interrupt_handler(int irq, void *arg)
|
||||
{
|
||||
struct hif_pci_softc *sc = (struct hif_pci_softc *)arg;
|
||||
struct hif_softc *scn = HIF_GET_SOFTC(sc);
|
||||
@@ -891,7 +891,7 @@ static void hif_init_reschedule_tasklet_work(struct hif_pci_softc *sc)
|
||||
static void hif_init_reschedule_tasklet_work(struct hif_pci_softc *sc) { }
|
||||
#endif /* CONFIG_SLUB_DEBUG_ON */
|
||||
|
||||
static void wlan_tasklet(unsigned long data)
|
||||
void wlan_tasklet(unsigned long data)
|
||||
{
|
||||
struct hif_pci_softc *sc = (struct hif_pci_softc *)data;
|
||||
struct hif_softc *scn = HIF_GET_SOFTC(sc);
|
||||
@@ -2170,7 +2170,7 @@ end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wlan_tasklet_msi(unsigned long data)
|
||||
void wlan_tasklet_msi(unsigned long data)
|
||||
{
|
||||
struct hif_tasklet_entry *entry = (struct hif_tasklet_entry *)data;
|
||||
struct hif_pci_softc *sc = (struct hif_pci_softc *) entry->hif_handler;
|
||||
@@ -2338,6 +2338,9 @@ static int hif_pci_configure_legacy_irq(struct hif_pci_softc *sc)
|
||||
HIF_ERROR("%s: request_irq failed, ret = %d", __func__, ret);
|
||||
goto end;
|
||||
}
|
||||
/* Use sc->irq instead of sc->pdev-irq
|
||||
platform_device pdev doesn't have an irq field */
|
||||
sc->irq = sc->pdev->irq;
|
||||
/* Use Legacy PCI Interrupts */
|
||||
hif_write32_mb(sc->mem+(SOC_CORE_BASE_ADDRESS |
|
||||
PCIE_INTR_ENABLE_ADDRESS),
|
||||
@@ -2372,12 +2375,14 @@ void hif_pci_nointrs(struct hif_softc *scn)
|
||||
if (sc->num_msi_intrs > 0) {
|
||||
/* MSI interrupt(s) */
|
||||
for (i = 0; i < sc->num_msi_intrs; i++) {
|
||||
free_irq(sc->pdev->irq + i, sc);
|
||||
free_irq(sc->irq + i, sc);
|
||||
}
|
||||
sc->num_msi_intrs = 0;
|
||||
} else {
|
||||
/* Legacy PCI line interrupt */
|
||||
free_irq(sc->pdev->irq, sc);
|
||||
/* Legacy PCI line interrupt
|
||||
Use sc->irq instead of sc->pdev-irq
|
||||
platform_device pdev doesn't have an irq field */
|
||||
free_irq(sc->irq, sc);
|
||||
}
|
||||
ce_unregister_irq(hif_state, 0xfff);
|
||||
scn->request_irq_done = false;
|
||||
@@ -3262,8 +3267,16 @@ void hif_target_dump_access_log(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HIF_AHB
|
||||
int hif_ahb_configure_legacy_irq(struct hif_pci_softc *sc)
|
||||
{
|
||||
QDF_BUG(0);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* hif_configure_irq(): configure interrupt
|
||||
* hif_configure_irq() - configure interrupt
|
||||
*
|
||||
* This function configures interrupt(s)
|
||||
*
|
||||
@@ -3287,7 +3300,14 @@ int hif_configure_irq(struct hif_softc *scn)
|
||||
goto end;
|
||||
}
|
||||
/* MSI failed. Try legacy irq */
|
||||
ret = hif_pci_configure_legacy_irq(sc);
|
||||
switch (scn->target_info.target_type) {
|
||||
case TARGET_TYPE_IPQ4019:
|
||||
ret = hif_ahb_configure_legacy_irq(sc);
|
||||
break;
|
||||
default:
|
||||
ret = hif_pci_configure_legacy_irq(sc);
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
HIF_ERROR("%s: hif_pci_configure_legacy_irq error = %d",
|
||||
__func__, ret);
|
||||
|
@@ -147,6 +147,8 @@ struct hif_pci_softc {
|
||||
bool hif_pci_targ_is_present(struct hif_softc *scn, void *__iomem *mem);
|
||||
int hif_configure_irq(struct hif_softc *sc);
|
||||
void hif_pci_cancel_deferred_target_sleep(struct hif_softc *scn);
|
||||
void wlan_tasklet(unsigned long data);
|
||||
irqreturn_t hif_pci_interrupt_handler(int irq, void *arg);
|
||||
|
||||
/*
|
||||
* A firmware interrupt to the Host is indicated by the
|
||||
|
401
hif/src/snoc/if_ahb.c
Normal file
401
hif/src/snoc/if_ahb.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: if_ahb.c
|
||||
*
|
||||
* c file for ahb specific implementations.
|
||||
*/
|
||||
|
||||
#include "hif.h"
|
||||
#include "hif_main.h"
|
||||
#include "hif_debug.h"
|
||||
#include "hif_io32.h"
|
||||
#include "ce_main.h"
|
||||
#include "ce_tasklet.h"
|
||||
#include "if_ahb.h"
|
||||
#include "if_pci.h"
|
||||
#include "ahb_api.h"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
||||
#define IRQF_DISABLED 0x00000020
|
||||
#endif
|
||||
/**
|
||||
* hif_disable_isr() - disable isr
|
||||
*
|
||||
* This function disables isr and kills tasklets
|
||||
*
|
||||
* @hif_ctx: struct hif_softc
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
void hif_ahb_disable_isr(struct hif_softc *scn)
|
||||
{
|
||||
struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn);
|
||||
|
||||
hif_nointrs(scn);
|
||||
ce_tasklet_kill(scn);
|
||||
tasklet_kill(&sc->intr_tq);
|
||||
qdf_atomic_set(&scn->active_tasklet_cnt, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_dump_registers() - dump bus debug registers
|
||||
* @scn: struct hif_opaque_softc
|
||||
*
|
||||
* This function dumps hif bus debug registers
|
||||
*
|
||||
* Return: 0 for success or error code
|
||||
*/
|
||||
int hif_ahb_dump_registers(struct hif_softc *hif_ctx)
|
||||
{
|
||||
int status;
|
||||
struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
|
||||
|
||||
status = hif_dump_ce_registers(scn);
|
||||
if (status)
|
||||
HIF_ERROR("%s: Dump CE Registers Failed status %d", __func__,
|
||||
status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_ahb_close() - hif_bus_close
|
||||
* @scn: pointer to the hif context.
|
||||
*
|
||||
* This is a callback function for hif_bus_close.
|
||||
*
|
||||
*
|
||||
* Return: n/a
|
||||
*/
|
||||
void hif_ahb_close(struct hif_softc *scn)
|
||||
{
|
||||
hif_ce_close(scn);
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_bus_open() - hif_ahb open
|
||||
* @hif_ctx: hif context
|
||||
* @bus_type: bus type
|
||||
*
|
||||
* This is a callback function for hif_bus_open.
|
||||
*
|
||||
* Return: n/a
|
||||
*/
|
||||
QDF_STATUS hif_ahb_open(struct hif_softc *hif_ctx, enum qdf_bus_type bus_type)
|
||||
{
|
||||
|
||||
struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(hif_ctx);
|
||||
|
||||
qdf_spinlock_create(&sc->irq_lock);
|
||||
return hif_ce_open(hif_ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_bus_configure() - Configure the bus
|
||||
* @scn: pointer to the hif context.
|
||||
*
|
||||
* This function configure the ahb bus
|
||||
*
|
||||
* return: 0 for success. nonzero for failure.
|
||||
*/
|
||||
int hif_ahb_bus_configure(struct hif_softc *scn)
|
||||
{
|
||||
return hif_pci_bus_configure(scn);
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_configure_msi_ahb - Configure MSI interrupts
|
||||
* @sc : pointer to the hif context
|
||||
*
|
||||
* return: 0 for success. nonzero for failure.
|
||||
*/
|
||||
|
||||
int hif_configure_msi_ahb(struct hif_pci_softc *sc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_ahb_configure_legacy_irq() - Configure Legacy IRQ
|
||||
* @sc: pointer to the hif context.
|
||||
*
|
||||
* This function registers the irq handler and enables legacy interrupts
|
||||
*
|
||||
* return: 0 for success. nonzero for failure.
|
||||
*/
|
||||
int hif_ahb_configure_legacy_irq(struct hif_pci_softc *sc)
|
||||
{
|
||||
int ret = 0;
|
||||
struct hif_softc *scn = HIF_GET_SOFTC(sc);
|
||||
struct platform_device *pdev = (struct platform_device *)sc->pdev;
|
||||
int irq = 0;
|
||||
|
||||
/* do not support MSI or MSI IRQ failed */
|
||||
tasklet_init(&sc->intr_tq, wlan_tasklet, (unsigned long)sc);
|
||||
irq = platform_get_irq_byname(pdev, "legacy");
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "Unable to get irq\n");
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
ret = request_irq(irq, hif_pci_interrupt_handler,
|
||||
IRQF_DISABLED, "wlan_ahb", sc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ath_request_irq failed\n");
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
sc->irq = irq;
|
||||
|
||||
/* Use Legacy PCI Interrupts */
|
||||
hif_write32_mb(sc->mem+(SOC_CORE_BASE_ADDRESS |
|
||||
PCIE_INTR_ENABLE_ADDRESS),
|
||||
PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL);
|
||||
/* read once to flush */
|
||||
hif_read32_mb(sc->mem+(SOC_CORE_BASE_ADDRESS |
|
||||
PCIE_INTR_ENABLE_ADDRESS)
|
||||
);
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_target_sync() : ensure the target is ready
|
||||
* @scn: hif control structure
|
||||
*
|
||||
* Informs fw that we plan to use legacy interupts so that
|
||||
* it can begin booting. Ensures that the fw finishes booting
|
||||
* before continuing. Should be called before trying to write
|
||||
* to the targets other registers for the first time.
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
int hif_target_sync_ahb(struct hif_softc *scn)
|
||||
{
|
||||
hif_write32_mb(scn->mem + FW_INDICATOR_ADDRESS, FW_IND_HOST_READY);
|
||||
if (HAS_FW_INDICATOR) {
|
||||
int wait_limit = 500;
|
||||
int fw_ind = 0;
|
||||
|
||||
while (1) {
|
||||
fw_ind = hif_read32_mb(scn->mem +
|
||||
FW_INDICATOR_ADDRESS);
|
||||
if (fw_ind & FW_IND_INITIALIZED)
|
||||
break;
|
||||
if (wait_limit-- < 0)
|
||||
break;
|
||||
hif_write32_mb(scn->mem+(SOC_CORE_BASE_ADDRESS |
|
||||
PCIE_INTR_ENABLE_ADDRESS),
|
||||
PCIE_INTR_FIRMWARE_MASK);
|
||||
qdf_mdelay(10);
|
||||
}
|
||||
if (wait_limit < 0) {
|
||||
HIF_TRACE("%s: FW signal timed out", __func__);
|
||||
return -EIO;
|
||||
} else {
|
||||
HIF_TRACE("%s: Got FW signal, retries = %x", __func__,
|
||||
500-wait_limit);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_disable_bus() - Disable the bus
|
||||
* @scn : pointer to the hif context
|
||||
*
|
||||
* This function disables the bus and helds the target in reset state
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void hif_ahb_disable_bus(struct hif_softc *scn)
|
||||
{
|
||||
struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn);
|
||||
void __iomem *mem;
|
||||
struct platform_device *pdev = (struct platform_device *)sc->pdev;
|
||||
|
||||
/*Disable WIFI clock input*/
|
||||
hif_ahb_clk_enable_disable(&pdev->dev, 0);
|
||||
|
||||
hif_ahb_device_reset(scn);
|
||||
mem = (void __iomem *)sc->mem;
|
||||
if (mem) {
|
||||
devm_iounmap(&pdev->dev, mem);
|
||||
sc->mem = NULL;
|
||||
}
|
||||
scn->mem = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_enable_bus() - Enable the bus
|
||||
* @dev: dev
|
||||
* @bdev: bus dev
|
||||
* @bid: bus id
|
||||
* @type: bus type
|
||||
*
|
||||
* This function enables the radio bus by enabling necessary
|
||||
* clocks and waits for the target to get ready to proceed futher
|
||||
*
|
||||
* Return: QDF_STATUS
|
||||
*/
|
||||
QDF_STATUS hif_ahb_enable_bus(struct hif_softc *ol_sc,
|
||||
struct device *dev, void *bdev,
|
||||
const hif_bus_id *bid,
|
||||
enum hif_enable_type type)
|
||||
{
|
||||
int ret = 0;
|
||||
int hif_type;
|
||||
int target_type;
|
||||
const struct platform_device_id *id = (struct platform_device_id *)bid;
|
||||
struct platform_device *pdev = bdev;
|
||||
struct hif_target_info *tgt_info = NULL;
|
||||
struct resource *memres = NULL;
|
||||
void __iomem *mem = NULL;
|
||||
uint32_t revision_id = 0;
|
||||
struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(ol_sc);
|
||||
|
||||
sc->pdev = (struct pci_dev *)pdev;
|
||||
sc->dev = &pdev->dev;
|
||||
sc->devid = id->driver_data;
|
||||
|
||||
ret = hif_get_device_type(id->driver_data, revision_id,
|
||||
&hif_type, &target_type);
|
||||
if (ret < 0) {
|
||||
HIF_ERROR("%s: invalid device ret %d id %d revision_id %d",
|
||||
__func__, ret, (int)id->driver_data, revision_id);
|
||||
return QDF_STATUS_E_FAILURE;
|
||||
}
|
||||
|
||||
memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!memres) {
|
||||
HIF_INFO("%s: Failed to get IORESOURCE_MEM\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = dma_set_mask(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
HIF_INFO("ath: 32-bit DMA not available\n");
|
||||
goto err_cleanup1;
|
||||
}
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
HIF_ERROR("%s: failed to set dma mask error = %d",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Arrange for access to Target SoC registers. */
|
||||
mem = devm_ioremap_resource(&pdev->dev, memres);
|
||||
if (IS_ERR(mem)) {
|
||||
HIF_INFO("ath: ioremap error\n");
|
||||
ret = PTR_ERR(mem);
|
||||
goto err_cleanup1;
|
||||
}
|
||||
|
||||
sc->mem = mem;
|
||||
ol_sc->mem = mem;
|
||||
ol_sc->mem_pa = memres->start;
|
||||
tgt_info = hif_get_target_info_handle((struct hif_opaque_softc *)ol_sc);
|
||||
|
||||
tgt_info->target_type = target_type;
|
||||
hif_register_tbl_attach(ol_sc, hif_type);
|
||||
target_register_tbl_attach(ol_sc, target_type);
|
||||
|
||||
if (hif_ahb_enable_radio(sc, pdev, id) != 0) {
|
||||
HIF_INFO("error in enabling soc\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (hif_target_sync_ahb(ol_sc) < 0) {
|
||||
ret = -EIO;
|
||||
goto err_target_sync;
|
||||
}
|
||||
HIF_TRACE("%s: X - hif_type = 0x%x, target_type = 0x%x",
|
||||
__func__, hif_type, target_type);
|
||||
|
||||
return QDF_STATUS_SUCCESS;
|
||||
err_target_sync:
|
||||
HIF_INFO("Error: Disabling target\n");
|
||||
hif_ahb_disable_bus(ol_sc);
|
||||
err_cleanup1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hif_reset_soc() - reset soc
|
||||
*
|
||||
* @hif_ctx: HIF context
|
||||
*
|
||||
* This function resets soc and helds the
|
||||
* target in reset state
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
/* Function to reset SoC */
|
||||
void hif_ahb_reset_soc(struct hif_opaque_softc *hif_ctx)
|
||||
{
|
||||
struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(hif_ctx);
|
||||
hif_ahb_device_reset((struct hif_softc *)sc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hif_nointrs() - disable IRQ
|
||||
*
|
||||
* @scn: struct hif_softc
|
||||
*
|
||||
* This function stops interrupt(s)
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
void hif_ahb_nointrs(struct hif_softc *scn)
|
||||
{
|
||||
hif_pci_nointrs(scn);
|
||||
}
|
||||
|
||||
/**
|
||||
* ce_irq_enable() - enable copy engine IRQ
|
||||
* @scn: struct hif_softc
|
||||
* @ce_id: ce_id
|
||||
*
|
||||
* This function enables the interrupt for the radio.
|
||||
*
|
||||
* Return: N/A
|
||||
*/
|
||||
void hif_ahb_irq_enable(struct hif_softc *scn, int ce_id)
|
||||
{
|
||||
hif_pci_irq_enable(scn, ce_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* ce_irq_disable() - disable copy engine IRQ
|
||||
* @scn: struct hif_softc
|
||||
* @ce_id: ce_id
|
||||
*
|
||||
* Return: N/A
|
||||
*/
|
||||
void hif_ahb_irq_disable(struct hif_softc *scn, int ce_id)
|
||||
{
|
||||
|
||||
}
|
49
hif/src/snoc/if_ahb.h
Normal file
49
hif/src/snoc/if_ahb.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: if_ahb.h
|
||||
*
|
||||
* h file for ahb specific implementations.
|
||||
*/
|
||||
|
||||
#ifndef __IF_AHB_H
|
||||
#define __IF_AHB_H
|
||||
|
||||
#define GCC_BASE 0x1800000
|
||||
#define GCC_SIZE 0x60000
|
||||
#define GCC_FEPLL_PLL_DIV 0x2f020
|
||||
#define GCC_FEPLL_PLL_CLK_WIFI_0_SEL_MASK 0x00000300
|
||||
#define GCC_FEPLL_PLL_CLK_WIFI_0_SEL_SHIFT 8
|
||||
#define GCC_FEPLL_PLL_CLK_WIFI_1_SEL_MASK 0x00003000
|
||||
#define GCC_FEPLL_PLL_CLK_WIFI_1_SEL_SHIFT 12
|
||||
|
||||
|
||||
/* These registers are outsize Wifi space. */
|
||||
/* TBD: Should we add these offsets as device tree properties? */
|
||||
#define TCSR_BASE 0x1900000
|
||||
#define TCSR_SIZE 0x80000
|
||||
#define TCSR_WIFI0_GLB_CFG 0x49000
|
||||
#define TCSR_WIFI1_GLB_CFG 0x49004
|
||||
#define TCSR_WCSS0_HALTREQ 0x52000
|
||||
#define TCSR_WCSS1_HALTREQ 0x52004
|
||||
#define TCSR_WCSS0_HALTACK 0x52010
|
||||
#define TCSR_WCSS1_HALTACK 0x52014
|
||||
#define ATH_AHB_RESET_WAIT_MAX 10 /* Ms */
|
||||
#endif
|
||||
|
372
hif/src/snoc/if_ahb_reset.c
Normal file
372
hif/src/snoc/if_ahb_reset.c
Normal file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: if_ahb_reset.c
|
||||
*
|
||||
* c file for ahb ipq4019 specific implementations.
|
||||
*/
|
||||
|
||||
#include "hif.h"
|
||||
#include "hif_main.h"
|
||||
#include "hif_debug.h"
|
||||
#include "hif_io32.h"
|
||||
#include "ce_main.h"
|
||||
#include "ce_tasklet.h"
|
||||
#include "ahb_api.h"
|
||||
#include "if_ahb.h"
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/**
|
||||
* clk_enable_disable() - Enable/disable clock
|
||||
* @dev : pointer to device structure
|
||||
* @str : clock name
|
||||
* @enable : should be true, if the clock needs to be enabled
|
||||
* should be false, if the clock needs to be enabled
|
||||
*
|
||||
* This is a helper function for hif_ahb_clk_enable_disable to enable
|
||||
* disable clocks.
|
||||
* clk_prepare_enable will enable the clock
|
||||
* clk_disable_unprepare will disable the clock
|
||||
*
|
||||
* Return: zero on success, non-zero incase of error.
|
||||
*/
|
||||
|
||||
static int clk_enable_disable(struct device *dev, const char *str, int enable)
|
||||
{
|
||||
struct clk *clk_t = NULL;
|
||||
int ret;
|
||||
|
||||
clk_t = clk_get(dev, str);
|
||||
if (IS_ERR(clk_t)) {
|
||||
HIF_INFO("%s: Failed to get %s clk %ld\n",
|
||||
__func__, str, PTR_ERR(clk_t));
|
||||
return -EFAULT;
|
||||
}
|
||||
if (TRUE == enable) {
|
||||
/* Prepare and Enable clk */
|
||||
ret = clk_prepare_enable(clk_t);
|
||||
if (ret) {
|
||||
HIF_INFO("%s: err enabling clk %s , error:%d\n",
|
||||
__func__, str, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* Disable and unprepare clk */
|
||||
clk_disable_unprepare(clk_t);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hif_ahb_clk_enable_disable() - Enable/disable ahb clock
|
||||
* @dev : pointer to device structure
|
||||
* @enable : should be true, if the clock needs to be enabled
|
||||
* should be false, if the clock needs to be enabled
|
||||
*
|
||||
* This functions helps to enable/disable all the necesasary clocks
|
||||
* for bus access.
|
||||
*
|
||||
* Return: zero on success, non-zero incase of error
|
||||
*/
|
||||
int hif_ahb_clk_enable_disable(struct device *dev, int enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_enable_disable(dev, "wifi_wcss_cmd", enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_enable_disable(dev, "wifi_wcss_ref", enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_enable_disable(dev, "wifi_wcss_rtc", enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hif_enable_radio() - Enable the target radio.
|
||||
* @sc : pointer to the hif context
|
||||
*
|
||||
* This function helps to release the target from reset state
|
||||
*
|
||||
* Return : zero on success, non-zero incase of error.
|
||||
*/
|
||||
int hif_ahb_enable_radio(struct hif_pci_softc *sc,
|
||||
struct platform_device *pdev,
|
||||
const struct platform_device_id *id)
|
||||
{
|
||||
struct reset_control *reset_ctl = NULL;
|
||||
uint32_t msi_addr, msi_base, wifi_core_id;
|
||||
struct hif_softc *scn = HIF_GET_SOFTC(sc);
|
||||
struct device_node *dev_node = pdev->dev.of_node;
|
||||
bool msienable = false;
|
||||
int ret = 0;
|
||||
|
||||
ret = of_property_read_u32(dev_node, "qca,msi_addr", &msi_addr);
|
||||
if (ret) {
|
||||
HIF_INFO("%s: Unable to get msi_addr - error:%d\n",
|
||||
__func__, ret);
|
||||
return -EIO;
|
||||
}
|
||||
ret = of_property_read_u32(dev_node, "qca,msi_base", &msi_base);
|
||||
if (ret) {
|
||||
HIF_INFO("%s: Unable to get msi_base - error:%d\n",
|
||||
__func__, ret);
|
||||
return -EIO;
|
||||
}
|
||||
ret = of_property_read_u32(dev_node, "core-id", &wifi_core_id);
|
||||
if (ret) {
|
||||
HIF_INFO("%s: Unable to get core-id - error:%d\n",
|
||||
__func__, ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Program the above values into Wifi scratch regists */
|
||||
if (msienable) {
|
||||
hif_write32_mb(sc->mem + FW_AXI_MSI_ADDR, msi_addr);
|
||||
hif_write32_mb(sc->mem + FW_AXI_MSI_DATA, msi_base);
|
||||
}
|
||||
|
||||
/* TBD: Temporary changes. Frequency should be
|
||||
retrieved through clk_xxx once kernel GCC driver is available */
|
||||
{
|
||||
void __iomem *mem_gcc;
|
||||
uint32_t clk_sel;
|
||||
uint32_t gcc_fepll_pll_div;
|
||||
uint32_t wifi_cpu_freq[4] = {266700000, 250000000, 222200000,
|
||||
200000000};
|
||||
uint32_t current_freq = 0;
|
||||
|
||||
/* Enable WIFI clock input */
|
||||
if (scn->target_info.target_type == TARGET_TYPE_IPQ4019) {
|
||||
ret = hif_ahb_clk_enable_disable(&pdev->dev, 1);
|
||||
if (ret) {
|
||||
HIF_INFO("%s:Error while enabling clock :%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
mem_gcc = ioremap_nocache(GCC_BASE, GCC_SIZE);
|
||||
if (IS_ERR(mem_gcc)) {
|
||||
HIF_INFO("%s: GCC ioremap failed\n", __func__);
|
||||
return PTR_ERR(mem_gcc);
|
||||
}
|
||||
gcc_fepll_pll_div = hif_read32_mb(mem_gcc + GCC_FEPLL_PLL_DIV);
|
||||
clk_sel = (wifi_core_id == 0) ? ((gcc_fepll_pll_div &
|
||||
GCC_FEPLL_PLL_CLK_WIFI_0_SEL_MASK) >>
|
||||
GCC_FEPLL_PLL_CLK_WIFI_0_SEL_SHIFT) :
|
||||
((gcc_fepll_pll_div & GCC_FEPLL_PLL_CLK_WIFI_1_SEL_MASK)
|
||||
>> GCC_FEPLL_PLL_CLK_WIFI_1_SEL_SHIFT);
|
||||
current_freq = wifi_cpu_freq[clk_sel];
|
||||
|
||||
HIF_INFO("Wifi%d CPU frequency %u\n", wifi_core_id,
|
||||
current_freq);
|
||||
hif_write32_mb(sc->mem + FW_CPU_PLL_CONFIG, gcc_fepll_pll_div);
|
||||
iounmap(mem_gcc);
|
||||
}
|
||||
|
||||
/* De-assert radio cold reset */
|
||||
reset_ctl = reset_control_get(&pdev->dev, "wifi_radio_cold");
|
||||
if (IS_ERR(reset_ctl)) {
|
||||
HIF_INFO("%s: Failed to get radio cold reset control\n",
|
||||
__func__);
|
||||
ret = PTR_ERR(reset_ctl);
|
||||
goto err_reset;
|
||||
}
|
||||
reset_control_deassert(reset_ctl);
|
||||
reset_control_put(reset_ctl);
|
||||
|
||||
/* De-assert radio warm reset */
|
||||
reset_ctl = reset_control_get(&pdev->dev, "wifi_radio_warm");
|
||||
if (IS_ERR(reset_ctl)) {
|
||||
HIF_INFO("%s: Failed to get radio warm reset control\n",
|
||||
__func__);
|
||||
ret = PTR_ERR(reset_ctl);
|
||||
goto err_reset;
|
||||
}
|
||||
reset_control_deassert(reset_ctl);
|
||||
reset_control_put(reset_ctl);
|
||||
|
||||
/* De-assert radio srif reset */
|
||||
reset_ctl = reset_control_get(&pdev->dev, "wifi_radio_srif");
|
||||
if (IS_ERR(reset_ctl)) {
|
||||
HIF_INFO("%s: Failed to get radio srif reset control\n",
|
||||
__func__);
|
||||
ret = PTR_ERR(reset_ctl);
|
||||
goto err_reset;
|
||||
}
|
||||
reset_control_deassert(reset_ctl);
|
||||
reset_control_put(reset_ctl);
|
||||
|
||||
/* De-assert target CPU reset */
|
||||
reset_ctl = reset_control_get(&pdev->dev, "wifi_cpu_init");
|
||||
if (IS_ERR(reset_ctl)) {
|
||||
HIF_INFO("%s: Failed to get cpu init reset control", __func__);
|
||||
ret = PTR_ERR(reset_ctl);
|
||||
goto err_reset;
|
||||
}
|
||||
reset_control_deassert(reset_ctl);
|
||||
reset_control_put(reset_ctl);
|
||||
|
||||
return 0;
|
||||
|
||||
err_reset:
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
|
||||
/* "wifi_core_warm" is the other reset type */
|
||||
#define AHB_RESET_TYPE "wifi_core_cold"
|
||||
|
||||
/**
|
||||
* hif_ahb_device_reset() - Disable the radio and held the radio is reset state.
|
||||
* @scn : pointer to the hif context
|
||||
*
|
||||
* This function will hold the target in reset state.
|
||||
* Will be called while unload the driver or any graceful unload path.
|
||||
*
|
||||
* Return : n/a.
|
||||
*/
|
||||
void hif_ahb_device_reset(struct hif_softc *scn)
|
||||
{
|
||||
struct reset_control *resetctl = NULL;
|
||||
struct reset_control *core_resetctl = NULL;
|
||||
struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn);
|
||||
struct platform_device *pdev = (struct platform_device *)(sc->pdev);
|
||||
uint32_t glb_cfg_offset;
|
||||
uint32_t haltreq_offset;
|
||||
uint32_t haltack_offset;
|
||||
void __iomem *mem_tcsr;
|
||||
uint32_t wifi_core_id;
|
||||
uint32_t reg_value;
|
||||
int wait_limit = ATH_AHB_RESET_WAIT_MAX;
|
||||
|
||||
|
||||
wifi_core_id = hif_read32_mb(sc->mem + WLAN_SUBSYSTEM_CORE_ID_ADDRESS);
|
||||
glb_cfg_offset = (wifi_core_id == 0) ? TCSR_WIFI0_GLB_CFG :
|
||||
TCSR_WIFI1_GLB_CFG;
|
||||
haltreq_offset = (wifi_core_id == 0) ? TCSR_WCSS0_HALTREQ :
|
||||
TCSR_WCSS1_HALTREQ;
|
||||
haltack_offset = (wifi_core_id == 0) ? TCSR_WCSS0_HALTACK :
|
||||
TCSR_WCSS1_HALTACK;
|
||||
|
||||
mem_tcsr = ioremap_nocache(TCSR_BASE, TCSR_SIZE);
|
||||
if (IS_ERR(mem_tcsr)) {
|
||||
HIF_INFO("%s: TCSR ioremap failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
reg_value = hif_read32_mb(mem_tcsr + haltreq_offset);
|
||||
hif_write32_mb(mem_tcsr + haltreq_offset, reg_value | 0x1);
|
||||
/* Wait for halt ack before asserting reset */
|
||||
while (wait_limit) {
|
||||
|
||||
if (hif_read32_mb(mem_tcsr + haltack_offset) & 0x1)
|
||||
break;
|
||||
|
||||
A_MDELAY(1);
|
||||
wait_limit--;
|
||||
}
|
||||
|
||||
reg_value = hif_read32_mb(mem_tcsr + glb_cfg_offset);
|
||||
hif_write32_mb(mem_tcsr + glb_cfg_offset, reg_value | (1 << 25));
|
||||
|
||||
core_resetctl = reset_control_get(&pdev->dev, AHB_RESET_TYPE);
|
||||
if (IS_ERR(core_resetctl)) {
|
||||
HIF_INFO("Failed to get wifi core cold reset control\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset wifi core */
|
||||
reset_control_assert(core_resetctl);
|
||||
|
||||
/* TBD: Check if we should also assert other bits (radio_cold, radio_
|
||||
warm, radio_srif, cpu_ini) */
|
||||
A_MDELAY(1); /* TBD: Get reqd delay from HW team */
|
||||
|
||||
/* Assert radio cold reset */
|
||||
resetctl = reset_control_get(&pdev->dev, "wifi_radio_cold");
|
||||
if (IS_ERR(resetctl)) {
|
||||
HIF_INFO("%s: Failed to get radio cold reset control\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
reset_control_assert(resetctl);
|
||||
A_MDELAY(1); /* TBD: Get reqd delay from HW team */
|
||||
reset_control_put(resetctl);
|
||||
|
||||
/* Assert radio warm reset */
|
||||
resetctl = reset_control_get(&pdev->dev, "wifi_radio_warm");
|
||||
if (IS_ERR(resetctl)) {
|
||||
HIF_INFO("%s: Failed to get radio warm reset control\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
reset_control_assert(resetctl);
|
||||
A_MDELAY(1); /* TBD: Get reqd delay from HW team */
|
||||
reset_control_put(resetctl);
|
||||
|
||||
/* Assert radio srif reset */
|
||||
resetctl = reset_control_get(&pdev->dev, "wifi_radio_srif");
|
||||
if (IS_ERR(resetctl)) {
|
||||
HIF_INFO("%s: Failed to get radio srif reset control\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
reset_control_assert(resetctl);
|
||||
A_MDELAY(1); /* TBD: Get reqd delay from HW team */
|
||||
reset_control_put(resetctl);
|
||||
|
||||
/* Assert target CPU reset */
|
||||
resetctl = reset_control_get(&pdev->dev, "wifi_cpu_init");
|
||||
if (IS_ERR(resetctl)) {
|
||||
HIF_INFO("%s: Failed to get cpu init reset control", __func__);
|
||||
return;
|
||||
}
|
||||
reset_control_assert(resetctl);
|
||||
A_MDELAY(10); /* TBD: Get reqd delay from HW team */
|
||||
reset_control_put(resetctl);
|
||||
|
||||
/* Clear gbl_cfg and haltreq before clearing Wifi core reset */
|
||||
reg_value = hif_read32_mb(mem_tcsr + haltreq_offset);
|
||||
hif_write32_mb(mem_tcsr + haltreq_offset, reg_value & ~0x1);
|
||||
reg_value = hif_read32_mb(mem_tcsr + glb_cfg_offset);
|
||||
hif_write32_mb(mem_tcsr + glb_cfg_offset, reg_value & ~(1 << 25));
|
||||
|
||||
/* de-assert wifi core reset */
|
||||
reset_control_deassert(core_resetctl);
|
||||
|
||||
A_MDELAY(1); /* TBD: Get reqd delay from HW team */
|
||||
|
||||
/* TBD: Check if we should de-assert other bits here */
|
||||
reset_control_put(core_resetctl);
|
||||
iounmap(mem_tcsr);
|
||||
HIF_INFO("Reset complete for wifi core id : %d\n", wifi_core_id);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user