blob: 93eb8a973be7247524cbde4bbeffe768e7c725a3 [file] [log] [blame]
/*
* Customer code to add GPIO control during WLAN start/stop
*
* Copyright 1999-2016, Broadcom Corporation
* All rights reserved,
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided by the copyright holder "as is" and any express or
* implied warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose are disclaimed. In no event
* shall copyright holder be liable for any direct, indirect, incidental, special,
* exemplary, or consequential damages (including, but not limited to, procurement
* of substitute goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether in
* contract, strict liability, or tort (including negligence or otherwise) arising
* in any way out of the use of this software, even if advised of the possibility
* of such damage
*
*
* <<Broadcom-WL-IPTag/Open:>>
*
* $Id: dhd_gpio.c 591129 2015-10-07 05:22:14Z $
*/
#include <osl.h>
#include <dhd_linux.h>
#include <linux/gpio.h>
#include <dhd_dbg.h>
#ifdef CUSTOMER_HW_PLATFORM
#include <plat/sdhci.h>
#define sdmmc_channel sdmmc_device_mmc0
#endif /* CUSTOMER_HW_PLATFORM */
#if defined(BUS_POWER_RESTORE) && defined(BCMSDIO)
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#endif /* defined(BUS_POWER_RESTORE) && defined(BCMSDIO) */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
#include <linux/amlogic/aml_gpio_consumer.h>
extern int wifi_irq_trigger_level(void);
#endif
#ifdef CUSTOMER_HW_AMLOGIC
extern void sdio_reinit(void);
extern void extern_wifi_set_enable(int is_on);
extern void pci_remove_reinit(unsigned int vid, unsigned int pid, int delBus);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
extern int wifi_irq_num(void);
#endif
#endif
extern u8 *wifi_get_mac(void);
#ifdef CONFIG_DHD_USE_STATIC_BUF
#endif /* CONFIG_DHD_USE_STATIC_BUF */
static int gpio_wl_reg_on = -1; // WL_REG_ON is input pin of WLAN module
#ifdef CUSTOMER_OOB
static int gpio_wl_host_wake = -1; // WL_HOST_WAKE is output pin of WLAN module
#endif
static int
dhd_wlan_set_power(bool on
#ifdef BUS_POWER_RESTORE
, wifi_adapter_info_t *adapter
#endif /* BUS_POWER_RESTORE */
)
{
int err = 0;
if (on) {
DHD_ERROR(("======== PULL WL_REG_ON(%d) HIGH! ========\n", gpio_wl_reg_on));
if (gpio_wl_reg_on >= 0) {
err = gpio_direction_output(gpio_wl_reg_on, 1);
if (err) {
DHD_ERROR(("%s: WL_REG_ON didn't output high\n", __FUNCTION__));
return -EIO;
}
}
#ifdef CUSTOMER_HW_AMLOGIC
#ifdef BCMSDIO
extern_wifi_set_enable(0);
mdelay(200);
extern_wifi_set_enable(1);
mdelay(200);
// sdio_reinit();
#endif
#endif
#if defined(BUS_POWER_RESTORE)
#if defined(BCMSDIO)
if (adapter->sdio_func && adapter->sdio_func->card && adapter->sdio_func->card->host) {
DHD_INFO(("======== mmc_power_restore_host! ========\n"));
mmc_power_restore_host(adapter->sdio_func->card->host);
}
#elif defined(BCMPCIE)
OSL_SLEEP(50); /* delay needed to be able to restore PCIe configuration registers */
if (adapter->pci_dev) {
DHD_ERROR(("======== pci_set_power_state PCI_D0! ========\n"));
pci_set_power_state(adapter->pci_dev, PCI_D0);
if (adapter->pci_saved_state)
pci_load_and_free_saved_state(adapter->pci_dev, &adapter->pci_saved_state);
pci_restore_state(adapter->pci_dev);
err = pci_enable_device(adapter->pci_dev);
if (err < 0)
DHD_ERROR(("%s: PCI enable device failed", __FUNCTION__));
pci_set_master(adapter->pci_dev);
}
#endif /* BCMPCIE */
#endif /* BUS_POWER_RESTORE */
/* Lets customer power to get stable */
mdelay(100);
} else {
#if defined(BUS_POWER_RESTORE)
#if defined(BCMSDIO)
if (adapter->sdio_func && adapter->sdio_func->card && adapter->sdio_func->card->host) {
DHD_ERROR(("======== mmc_power_save_host! ========\n"));
mmc_power_save_host(adapter->sdio_func->card->host);
}
#elif defined(BCMPCIE)
if (adapter->pci_dev) {
DHD_ERROR(("======== pci_set_power_state PCI_D3hot! ========\n"));
pci_save_state(adapter->pci_dev);
adapter->pci_saved_state = pci_store_saved_state(adapter->pci_dev);
if (pci_is_enabled(adapter->pci_dev))
pci_disable_device(adapter->pci_dev);
pci_set_power_state(adapter->pci_dev, PCI_D3hot);
}
#endif /* BCMPCIE */
#endif /* BUS_POWER_RESTORE */
DHD_ERROR(("======== PULL WL_REG_ON(%d) LOW! ========\n", gpio_wl_reg_on));
if (gpio_wl_reg_on >= 0) {
err = gpio_direction_output(gpio_wl_reg_on, 0);
if (err) {
DHD_ERROR(("%s: WL_REG_ON didn't output low\n", __FUNCTION__));
return -EIO;
}
}
#ifdef CUSTOMER_HW_AMLOGIC
// extern_wifi_set_enable(0);
// mdelay(200);
#endif
}
return err;
}
static int dhd_wlan_set_reset(int onoff)
{
return 0;
}
static int dhd_wlan_set_carddetect(bool present)
{
int err = 0;
#if !defined(BUS_POWER_RESTORE)
if (present) {
#if defined(BCMSDIO)
DHD_INFO(("======== Card detection to detect SDIO card! ========\n"));
#ifdef CUSTOMER_HW_PLATFORM
err = sdhci_force_presence_change(&sdmmc_channel, 1);
#endif /* CUSTOMER_HW_PLATFORM */
#ifdef CUSTOMER_HW_AMLOGIC
sdio_reinit();
#endif
#endif
} else {
#if defined(BCMSDIO)
DHD_INFO(("======== Card detection to remove SDIO card! ========\n"));
#ifdef CUSTOMER_HW_PLATFORM
err = sdhci_force_presence_change(&sdmmc_channel, 0);
#endif /* CUSTOMER_HW_PLATFORM */
#ifdef CUSTOMER_HW_AMLOGIC
extern_wifi_set_enable(0);
mdelay(200);
#endif
#elif defined(BCMPCIE)
DHD_INFO(("======== Card detection to remove PCIE card! ========\n"));
extern_wifi_set_enable(0);
mdelay(200);
#endif
}
#endif /* BUS_POWER_RESTORE */
return err;
}
static int dhd_wlan_get_mac_addr(unsigned char *buf)
{
int err = 0;
DHD_INFO(("======== %s ========\n", __FUNCTION__));
#ifdef EXAMPLE_GET_MAC
/* EXAMPLE code */
{
struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}};
bcopy((char *)&ea_example, buf, sizeof(struct ether_addr));
}
#endif /* EXAMPLE_GET_MAC */
#ifdef EXAMPLE_GET_MAC_VER2
/* EXAMPLE code */
{
char mac[6] = {0x00,0x11,0x22,0x33,0x44,0xFF};
char macpad[56]= {
0x00,0xaa,0x9c,0x84,0xc7,0xbc,0x9b,0xf6,
0x02,0x33,0xa9,0x4d,0x5c,0xb4,0x0a,0x5d,
0xa8,0xef,0xb0,0xcf,0x8e,0xbf,0x24,0x8a,
0x87,0x0f,0x6f,0x0d,0xeb,0x83,0x6a,0x70,
0x4a,0xeb,0xf6,0xe6,0x3c,0xe7,0x5f,0xfc,
0x0e,0xa7,0xb3,0x0f,0x00,0xe4,0x4a,0xaf,
0x87,0x08,0x16,0x6d,0x3a,0xe3,0xc7,0x80};
bcopy(mac, buf, sizeof(mac));
bcopy(macpad, buf+6, sizeof(macpad));
}
#endif /* EXAMPLE_GET_MAC_VER2 */
bcopy((char *)wifi_get_mac(), buf, sizeof(struct ether_addr));
if (buf[0] == 0xff) {
DHD_ERROR(("custom wifi mac is not set\n"));
err = -1;
}
return err;
}
#ifdef CONFIG_DHD_USE_STATIC_BUF
extern void *bcmdhd_mem_prealloc(int section, unsigned long size);
void* bcm_wlan_prealloc(int section, unsigned long size)
{
void *alloc_ptr = NULL;
alloc_ptr = bcmdhd_mem_prealloc(section, size);
if (alloc_ptr) {
DHD_INFO(("success alloc section %d, size %ld\n", section, size));
if (size != 0L)
bzero(alloc_ptr, size);
return alloc_ptr;
}
DHD_ERROR(("can't alloc section %d\n", section));
return NULL;
}
#endif
#if !defined(WL_WIRELESS_EXT)
struct cntry_locales_custom {
char iso_abbrev[WLC_CNTRY_BUF_SZ]; /* ISO 3166-1 country abbreviation */
char custom_locale[WLC_CNTRY_BUF_SZ]; /* Custom firmware locale */
int32 custom_locale_rev; /* Custom local revisin default -1 */
};
#endif
static struct cntry_locales_custom brcm_wlan_translate_custom_table[] = {
/* Table should be filled out based on custom platform regulatory requirement */
{"", "WW", 0}, /* Universal if Country code is unknown or empty */
{"FT", "ALL", 0},
};
#ifdef CUSTOM_FORCE_NODFS_FLAG
struct cntry_locales_custom brcm_wlan_translate_nodfs_table[] = {
{"", "XT", 50}, /* Universal if Country code is unknown or empty */
{"US", "US", 0},
};
#endif
static void *dhd_wlan_get_country_code(char *ccode
#ifdef CUSTOM_FORCE_NODFS_FLAG
, u32 flags
#endif
)
{
struct cntry_locales_custom *locales;
int size;
int i;
if (!ccode)
return NULL;
#ifdef CUSTOM_FORCE_NODFS_FLAG
if (flags & WLAN_PLAT_NODFS_FLAG) {
locales = brcm_wlan_translate_nodfs_table;
size = ARRAY_SIZE(brcm_wlan_translate_nodfs_table);
} else {
#endif
locales = brcm_wlan_translate_custom_table;
size = ARRAY_SIZE(brcm_wlan_translate_custom_table);
#ifdef CUSTOM_FORCE_NODFS_FLAG
}
#endif
for (i = 0; i < size; i++)
if (strcmp(ccode, locales[i].iso_abbrev) == 0)
return &locales[i];
return NULL;
}
struct resource dhd_wlan_resources[] = {
[0] = {
.name = "bcmdhd_wlan_irq",
.start = 0, /* Dummy */
.end = 0, /* Dummy */
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE
| IORESOURCE_IRQ_HIGHLEVEL, /* Dummy */
},
};
struct wifi_platform_data dhd_wlan_control = {
.set_power = dhd_wlan_set_power,
.set_reset = dhd_wlan_set_reset,
.set_carddetect = dhd_wlan_set_carddetect,
.get_mac_addr = dhd_wlan_get_mac_addr,
#ifdef CONFIG_DHD_USE_STATIC_BUF
.mem_prealloc = bcm_wlan_prealloc,
#endif /* CONFIG_DHD_USE_STATIC_BUF */
.get_country_code = dhd_wlan_get_country_code,
};
int dhd_wlan_init_gpio(void)
{
int err = 0;
#ifdef CUSTOMER_OOB
int host_oob_irq = -1;
uint host_oob_irq_flags = 0;
#endif
/* Please check your schematic and fill right GPIO number which connected to
* WL_REG_ON and WL_HOST_WAKE.
*/
gpio_wl_reg_on = -1;
#ifdef CUSTOMER_OOB
gpio_wl_host_wake = -1;
#endif
#if defined(BCMPCIE)
DHD_INFO(("======== Card detection to detect PCIE card! ========\n"));
pci_remove_reinit(0x14e4, 0x43ec, 1);
#endif
DHD_INFO(("%s: GPIO(WL_REG_ON) = %d\n", __FUNCTION__, gpio_wl_reg_on));
if (gpio_wl_reg_on >= 0) {
err = gpio_request(gpio_wl_reg_on, "WL_REG_ON");
if (err < 0) {
DHD_ERROR(("%s: Faiiled to request gpio %d for WL_REG_ON\n",
__FUNCTION__, gpio_wl_reg_on));
gpio_wl_reg_on = -1;
}
}
#ifdef CUSTOMER_OOB
DHD_INFO(("%s: GPIO(WL_HOST_WAKE) = %d\n", __FUNCTION__, gpio_wl_host_wake));
if (gpio_wl_host_wake >= 0) {
err = gpio_request(gpio_wl_host_wake, "bcmdhd");
if (err < 0) {
DHD_ERROR(("%s: gpio_request failed\n", __FUNCTION__));
return -1;
}
err = gpio_direction_input(gpio_wl_host_wake);
if (err < 0) {
DHD_ERROR(("%s: gpio_direction_input failed\n", __FUNCTION__));
gpio_free(gpio_wl_host_wake);
return -1;
}
host_oob_irq = gpio_to_irq(gpio_wl_host_wake);
if (host_oob_irq < 0) {
DHD_ERROR(("%s: gpio_to_irq failed\n", __FUNCTION__));
gpio_free(gpio_wl_host_wake);
return -1;
}
}
#ifdef CUSTOMER_HW_AMLOGIC
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
host_oob_irq = INT_GPIO_4;
#else
host_oob_irq = wifi_irq_num();
#endif
#endif
DHD_INFO(("%s: host_oob_irq: %d\n", __FUNCTION__, host_oob_irq));
#ifdef HW_OOB
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
if (wifi_irq_trigger_level() == GPIO_IRQ_LOW)
host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_SHAREABLE;
else
host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
#else
#ifdef HW_OOB_LOW_LEVEL
host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_SHAREABLE;
#else
host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
#endif
#endif
#else
host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_SHAREABLE;
#endif
dhd_wlan_resources[0].start = dhd_wlan_resources[0].end = host_oob_irq;
dhd_wlan_resources[0].flags = host_oob_irq_flags;
DHD_INFO(("%s: host_oob_irq_flags=0x%x\n", __FUNCTION__, host_oob_irq_flags));
#endif /* CUSTOMER_OOB */
return 0;
}
static void dhd_wlan_deinit_gpio(void)
{
if (gpio_wl_reg_on >= 0) {
DHD_INFO(("%s: gpio_free(WL_REG_ON %d)\n", __FUNCTION__, gpio_wl_reg_on));
gpio_free(gpio_wl_reg_on);
gpio_wl_reg_on = -1;
}
#ifdef CUSTOMER_OOB
if (gpio_wl_host_wake >= 0) {
DHD_INFO(("%s: gpio_free(WL_HOST_WAKE %d)\n", __FUNCTION__, gpio_wl_host_wake));
gpio_free(gpio_wl_host_wake);
gpio_wl_host_wake = -1;
}
#endif /* CUSTOMER_OOB */
}
int dhd_wlan_init_plat_data(void)
{
int err = 0;
DHD_INFO(("======== %s ========\n", __FUNCTION__));
err = dhd_wlan_init_gpio();
return err;
}
void dhd_wlan_deinit_plat_data(wifi_adapter_info_t *adapter)
{
DHD_INFO(("======== %s ========\n", __FUNCTION__));
dhd_wlan_deinit_gpio();
}