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
Šī revīzija ir iekļauta:
Houston Hoffman
2016-05-05 19:54:39 -07:00
revīziju iesūtīja Gerrit - the friendly Code Review server
vecāks abd0077413
revīzija 3db96a43b6
12 mainīti faili ar 1002 papildinājumiem un 10 dzēšanām

Parādīt failu

@@ -364,7 +364,7 @@
/* PLL end */ /* PLL end */
#define FW_CPU_PLL_CONFIG \ #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 \ #define WIFICMN_PCIE_BAR_REG_ADDRESS \
(sc->targetdef->d_WIFICMN_PCIE_BAR_REG_ADDRESS) (sc->targetdef->d_WIFICMN_PCIE_BAR_REG_ADDRESS)

39
hif/src/dispatcher/ahb_api.h Parasts fails
Parādīt failu

@@ -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

Parādīt failu

@@ -86,6 +86,8 @@ int hif_bus_get_context_size(enum qdf_bus_type bus_type)
switch (bus_type) { switch (bus_type) {
case QDF_BUS_TYPE_PCI: case QDF_BUS_TYPE_PCI:
return hif_pci_get_context_size(); return hif_pci_get_context_size();
case QDF_BUS_TYPE_AHB:
return hif_ahb_get_context_size();
case QDF_BUS_TYPE_SNOC: case QDF_BUS_TYPE_SNOC:
return hif_snoc_get_context_size(); return hif_snoc_get_context_size();
default: default:
@@ -114,6 +116,9 @@ QDF_STATUS hif_bus_open(struct hif_softc *hif_sc,
case QDF_BUS_TYPE_SNOC: case QDF_BUS_TYPE_SNOC:
status = hif_initialize_snoc_ops(&hif_sc->bus_ops); status = hif_initialize_snoc_ops(&hif_sc->bus_ops);
break; break;
case QDF_BUS_TYPE_AHB:
status = hif_initialize_ahb_ops(&hif_sc->bus_ops);
break;
default: default:
status = QDF_STATUS_E_NOSUPPORT; status = QDF_STATUS_E_NOSUPPORT;
break; break;

Parādīt failu

@@ -100,4 +100,31 @@ static inline int hif_pci_get_context_size(void)
} }
#endif /* HIF_PCI */ #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_ */ #endif /* _MULTIBUS_H_ */

Parādīt failu

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

Parādīt failu

@@ -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->link_suspended);
qdf_atomic_init(&scn->tasklet_from_intr); qdf_atomic_init(&scn->tasklet_from_intr);
qdf_mem_copy(&scn->callbacks, cbk, sizeof(struct hif_driver_state_callbacks)); qdf_mem_copy(&scn->callbacks, cbk, sizeof(struct hif_driver_state_callbacks));
scn->bus_type = bus_type;
status = hif_bus_open(scn, bus_type); status = hif_bus_open(scn, bus_type);
if (status != QDF_STATUS_SUCCESS) { if (status != QDF_STATUS_SUCCESS) {
HIF_ERROR("%s: hif_bus_open error = %d, bus_type = %d", 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"); HIF_INFO(" *********** AR900B *************\n");
break; break;
case IPQ4019_DEVICE_ID:
*hif_type = HIF_TYPE_IPQ4019;
*target_type = TARGET_TYPE_IPQ4019;
HIF_INFO(" *********** IPQ4019 *************\n");
break;
default: default:
HIF_ERROR("%s: Unsupported device ID!", __func__); HIF_ERROR("%s: Unsupported device ID!", __func__);
ret = -ENODEV; ret = -ENODEV;

Parādīt failu

@@ -90,6 +90,7 @@
#define AR900B_DEVICE_ID (0x0040) #define AR900B_DEVICE_ID (0x0040)
#define QCA9984_DEVICE_ID (0x0046) #define QCA9984_DEVICE_ID (0x0046)
#define QCA9888_DEVICE_ID (0x0056) #define QCA9888_DEVICE_ID (0x0056)
#define IPQ4019_DEVICE_ID (0x12ef)
#define HIF_GET_PCI_SOFTC(scn) ((struct hif_pci_softc *)scn) #define HIF_GET_PCI_SOFTC(scn) ((struct hif_pci_softc *)scn)
#define HIF_GET_CE_STATE(scn) ((struct HIF_CE_state *)scn) #define HIF_GET_CE_STATE(scn) ((struct HIF_CE_state *)scn)

Parādīt failu

@@ -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_pci_softc *sc = (struct hif_pci_softc *)arg;
struct hif_softc *scn = HIF_GET_SOFTC(sc); 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) { } static void hif_init_reschedule_tasklet_work(struct hif_pci_softc *sc) { }
#endif /* CONFIG_SLUB_DEBUG_ON */ #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_pci_softc *sc = (struct hif_pci_softc *)data;
struct hif_softc *scn = HIF_GET_SOFTC(sc); struct hif_softc *scn = HIF_GET_SOFTC(sc);
@@ -2170,7 +2170,7 @@ end:
return ret; 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_tasklet_entry *entry = (struct hif_tasklet_entry *)data;
struct hif_pci_softc *sc = (struct hif_pci_softc *) entry->hif_handler; 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); HIF_ERROR("%s: request_irq failed, ret = %d", __func__, ret);
goto end; 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 */ /* Use Legacy PCI Interrupts */
hif_write32_mb(sc->mem+(SOC_CORE_BASE_ADDRESS | hif_write32_mb(sc->mem+(SOC_CORE_BASE_ADDRESS |
PCIE_INTR_ENABLE_ADDRESS), PCIE_INTR_ENABLE_ADDRESS),
@@ -2372,12 +2375,14 @@ void hif_pci_nointrs(struct hif_softc *scn)
if (sc->num_msi_intrs > 0) { if (sc->num_msi_intrs > 0) {
/* MSI interrupt(s) */ /* MSI interrupt(s) */
for (i = 0; i < sc->num_msi_intrs; i++) { 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; sc->num_msi_intrs = 0;
} else { } else {
/* Legacy PCI line interrupt */ /* Legacy PCI line interrupt
free_irq(sc->pdev->irq, sc); 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); ce_unregister_irq(hif_state, 0xfff);
scn->request_irq_done = false; scn->request_irq_done = false;
@@ -3262,8 +3267,16 @@ void hif_target_dump_access_log(void)
} }
#endif #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) * This function configures interrupt(s)
* *
@@ -3287,7 +3300,14 @@ int hif_configure_irq(struct hif_softc *scn)
goto end; goto end;
} }
/* MSI failed. Try legacy irq */ /* 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) { if (ret < 0) {
HIF_ERROR("%s: hif_pci_configure_legacy_irq error = %d", HIF_ERROR("%s: hif_pci_configure_legacy_irq error = %d",
__func__, ret); __func__, ret);

Parādīt failu

@@ -147,6 +147,8 @@ struct hif_pci_softc {
bool hif_pci_targ_is_present(struct hif_softc *scn, void *__iomem *mem); bool hif_pci_targ_is_present(struct hif_softc *scn, void *__iomem *mem);
int hif_configure_irq(struct hif_softc *sc); int hif_configure_irq(struct hif_softc *sc);
void hif_pci_cancel_deferred_target_sleep(struct hif_softc *scn); 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 * A firmware interrupt to the Host is indicated by the

401
hif/src/snoc/if_ahb.c Parasts fails
Parādīt failu

@@ -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 Parasts fails
Parādīt failu

@@ -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 Parasts fails
Parādīt failu

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