blob: 0e387401f4de3f67e158dd06c0667606a62d9c55 [file] [log] [blame]
/*
* Custom file for dealing platform specific resouce
*
* Copyright (C) 1999-2015, Broadcom Corporation
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a license
* other than the GPL, without Broadcom's express prior written consent.
*
* $Id: dhd_custom_msm.c 520105 2014-12-10 07:22:01Z $
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/skbuff.h>
#include <linux/wlan_plat.h>
#include <linux/mmc/host.h>
#include <linux/if.h>
#if defined(CONFIG_ARCH_MSM)
#if defined(CONFIG_64BIT)
#include <linux/msm_pcie.h>
#else
#include <mach/msm_pcie.h>
#endif
#endif
#include <linux/of_gpio.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#define BCM_DBG pr_debug
static int gpio_wl_reg_on = -1;
static int brcm_wake_irq = -1;
#if !defined(CONFIG_WIFI_CONTROL_FUNC)
#define WLAN_PLAT_NODFS_FLAG 0x01
#define WLAN_PLAT_AP_FLAG 0x02
#endif
#ifdef CONFIG_DHD_USE_STATIC_BUF
enum dhd_prealloc_index {
DHD_PREALLOC_PROT = 0,
DHD_PREALLOC_RXBUF,
DHD_PREALLOC_DATABUF,
DHD_PREALLOC_OSL_BUF,
DHD_PREALLOC_SKB_BUF,
DHD_PREALLOC_WIPHY_ESCAN0 = 5,
DHD_PREALLOC_WIPHY_ESCAN1 = 6,
DHD_PREALLOC_DHD_INFO = 7,
DHD_PREALLOC_DHD_WLFC_INFO = 8,
DHD_PREALLOC_IF_FLOW_LKUP = 9,
DHD_PREALLOC_FLOWRING = 10,
DHD_PREALLOC_MAX
};
#define STATIC_BUF_MAX_NUM 20
#define STATIC_BUF_SIZE (PAGE_SIZE*2)
#define DHD_PREALLOC_PROT_SIZE (512)
#define DHD_PREALLOC_WIPHY_ESCAN0_SIZE (64 * 1024)
#define DHD_PREALLOC_DHD_INFO_SIZE (24 * 1024)
#ifdef CONFIG_64BIT
#define DHD_PREALLOC_IF_FLOW_LKUP_SIZE (20 * 1024 * 2)
#else
#define DHD_PREALLOC_IF_FLOW_LKUP_SIZE (20 * 1024)
#endif
#define DHD_PREALLOC_OSL_BUF_SIZE (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE)
#define WLAN_SCAN_BUF_SIZE (64 * 1024)
#if defined(CONFIG_64BIT)
#define WLAN_DHD_INFO_BUF_SIZE (24 * 1024)
#define WLAN_DHD_WLFC_BUF_SIZE (64 * 1024)
#define WLAN_DHD_IF_FLOW_LKUP_SIZE (64 * 1024)
#else
#define WLAN_DHD_INFO_BUF_SIZE (16 * 1024)
#define WLAN_DHD_WLFC_BUF_SIZE (16 * 1024)
#define WLAN_DHD_IF_FLOW_LKUP_SIZE (20 * 1024)
#endif /* CONFIG_64BIT */
#define WLAN_DHD_MEMDUMP_SIZE (800 * 1024)
#define PREALLOC_WLAN_SEC_NUM 4
#define PREALLOC_WLAN_BUF_NUM 160
#define PREALLOC_WLAN_SECTION_HEADER 24
#ifdef CONFIG_BCMDHD_PCIE
#define DHD_SKB_1PAGE_BUFSIZE (PAGE_SIZE*1)
#define DHD_SKB_2PAGE_BUFSIZE (PAGE_SIZE*2)
#define DHD_SKB_4PAGE_BUFSIZE (PAGE_SIZE*4)
#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_BUF_NUM * 128)
#define WLAN_SECTION_SIZE_1 0
#define WLAN_SECTION_SIZE_2 0
#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_BUF_NUM * 1024)
#define DHD_SKB_1PAGE_BUF_NUM 0
#define DHD_SKB_2PAGE_BUF_NUM 64
#define DHD_SKB_4PAGE_BUF_NUM 0
#else
#define DHD_SKB_HDRSIZE 336
#define DHD_SKB_1PAGE_BUFSIZE ((PAGE_SIZE*1)-DHD_SKB_HDRSIZE)
#define DHD_SKB_2PAGE_BUFSIZE ((PAGE_SIZE*2)-DHD_SKB_HDRSIZE)
#define DHD_SKB_4PAGE_BUFSIZE ((PAGE_SIZE*4)-DHD_SKB_HDRSIZE)
#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_BUF_NUM * 128)
#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_BUF_NUM * 128)
#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_BUF_NUM * 512)
#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_BUF_NUM * 1024)
#define DHD_SKB_1PAGE_BUF_NUM 8
#define DHD_SKB_2PAGE_BUF_NUM 8
#define DHD_SKB_4PAGE_BUF_NUM 1
#endif /* CONFIG_BCMDHD_PCIE */
#define WLAN_SKB_1_2PAGE_BUF_NUM ((DHD_SKB_1PAGE_BUF_NUM) + \
(DHD_SKB_2PAGE_BUF_NUM))
#define WLAN_SKB_BUF_NUM ((WLAN_SKB_1_2PAGE_BUF_NUM) + \
(DHD_SKB_4PAGE_BUF_NUM))
void *wlan_static_prot = NULL;
void *wlan_static_scan_buf0 = NULL;
void *wlan_static_scan_buf1 = NULL;
void *wlan_static_dhd_info_buf = NULL;
void *wlan_static_if_flow_lkup = NULL;
void *wlan_static_osl_buf = NULL;
static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
static void *dhd_wlan_mem_prealloc(int section, unsigned long size)
{
if (section == DHD_PREALLOC_PROT)
return wlan_static_prot;
if (section == DHD_PREALLOC_SKB_BUF)
return wlan_static_skb;
if (section == DHD_PREALLOC_WIPHY_ESCAN0)
return wlan_static_scan_buf0;
if (section == DHD_PREALLOC_WIPHY_ESCAN1)
return wlan_static_scan_buf1;
if (section == DHD_PREALLOC_OSL_BUF) {
if (size > DHD_PREALLOC_OSL_BUF_SIZE) {
pr_err("request OSL_BUF(%lu) is bigger than static size(%ld).\n",
size, DHD_PREALLOC_OSL_BUF_SIZE);
return NULL;
}
return wlan_static_osl_buf;
}
if (section == DHD_PREALLOC_DHD_INFO) {
if (size > DHD_PREALLOC_DHD_INFO_SIZE) {
pr_err("request DHD_INFO size(%lu) is bigger than static size(%d).\n",
size, DHD_PREALLOC_DHD_INFO_SIZE);
return NULL;
}
return wlan_static_dhd_info_buf;
}
if (section == DHD_PREALLOC_IF_FLOW_LKUP) {
if (size > DHD_PREALLOC_IF_FLOW_LKUP_SIZE) {
pr_err("request DHD_IF_FLOW_LKUP size(%lu) is bigger than static size(%d).\n",
size, DHD_PREALLOC_IF_FLOW_LKUP_SIZE);
return NULL;
}
return wlan_static_if_flow_lkup;
}
if ((section < 0) || (section > DHD_PREALLOC_MAX))
pr_err("request section id(%d) is out of max index %d\n",
section, DHD_PREALLOC_MAX);
return NULL;
}
static int dhd_init_wlan_mem(void)
{
int i;
int j;
for (i = 0; i < DHD_SKB_1PAGE_BUF_NUM; i++) {
wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_1PAGE_BUFSIZE);
if (!wlan_static_skb[i]) {
goto err_skb_alloc;
}
}
for (i = DHD_SKB_1PAGE_BUF_NUM; i < WLAN_SKB_1_2PAGE_BUF_NUM; i++) {
wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_2PAGE_BUFSIZE);
if (!wlan_static_skb[i]) {
goto err_skb_alloc;
}
}
#if !defined(CONFIG_BCMDHD_PCIE)
wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_4PAGE_BUFSIZE);
if (!wlan_static_skb[i]) {
goto err_skb_alloc;
}
#endif /* !CONFIG_BCMDHD_PCIE */
wlan_static_prot = kmalloc(DHD_PREALLOC_PROT_SIZE, GFP_KERNEL);
if (!wlan_static_prot) {
pr_err("Failed to alloc wlan_static_prot\n");
goto err_mem_alloc;
}
wlan_static_osl_buf = kmalloc(DHD_PREALLOC_OSL_BUF_SIZE, GFP_KERNEL);
if (!wlan_static_osl_buf) {
pr_err("Failed to alloc wlan_static_osl_buf\n");
goto err_mem_alloc;
}
wlan_static_scan_buf0 = kmalloc(DHD_PREALLOC_WIPHY_ESCAN0_SIZE, GFP_KERNEL);
if (!wlan_static_scan_buf0) {
pr_err("Failed to alloc wlan_static_scan_buf0\n");
goto err_mem_alloc;
}
wlan_static_dhd_info_buf = kmalloc(DHD_PREALLOC_DHD_INFO_SIZE, GFP_KERNEL);
if (!wlan_static_dhd_info_buf) {
pr_err("Failed to alloc wlan_static_dhd_info_buf\n");
goto err_mem_alloc;
}
#ifdef CONFIG_BCMDHD_PCIE
wlan_static_if_flow_lkup = kmalloc(DHD_PREALLOC_IF_FLOW_LKUP_SIZE, GFP_KERNEL);
if (!wlan_static_if_flow_lkup) {
pr_err("Failed to alloc wlan_static_if_flow_lkup\n");
goto err_mem_alloc;
}
#endif /* CONFIG_BCMDHD_PCIE */
return 0;
err_mem_alloc:
if (wlan_static_prot)
kfree(wlan_static_prot);
if (wlan_static_dhd_info_buf)
kfree(wlan_static_dhd_info_buf);
if (wlan_static_scan_buf1)
kfree(wlan_static_scan_buf1);
if (wlan_static_scan_buf0)
kfree(wlan_static_scan_buf0);
if (wlan_static_osl_buf)
kfree(wlan_static_osl_buf);
#ifdef CONFIG_BCMDHD_PCIE
if (wlan_static_if_flow_lkup)
kfree(wlan_static_if_flow_lkup);
#endif
pr_err("Failed to mem_alloc for WLAN\n");
i = WLAN_SKB_BUF_NUM;
err_skb_alloc:
pr_err("Failed to skb_alloc for WLAN\n");
for (j = 0; j < i; j++) {
dev_kfree_skb(wlan_static_skb[j]);
}
return -ENOMEM;
}
#endif /* CONFIG_DHD_USE_STATIC_BUF */
int dhd_wifi_init_gpio(void)
{
int wl_reg_on,wl_host_wake;
char *wlan_node = "android,bcmdhd_wlan";
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, wlan_node);
if (!np) {
WARN(1, "failed to get device node of BRCM WLAN\n");
return -ENODEV;
}
/* get wlan_reg_on */
wl_reg_on = of_get_named_gpio(np, "wl_reg_on", 0);
if (wl_reg_on >= 0) {
gpio_wl_reg_on = wl_reg_on;
BCM_DBG("%s: gpio_wl_reg_on:%d.\n", __FUNCTION__, gpio_wl_reg_on);
}
/* get host_wake irq */
wl_host_wake = of_get_named_gpio(np, "wl_host_wake", 0);
if (wl_host_wake >= 0) {
BCM_DBG("%s: wl_host_wake:%d.\n", __FUNCTION__, wl_host_wake);
brcm_wake_irq = gpio_to_irq(wl_host_wake);
}
if (gpio_request(gpio_wl_reg_on, "WL_REG_ON"))
pr_err("%s: Faiiled to request gpio %d for WL_REG_ON\n",
__func__, gpio_wl_reg_on);
else
pr_err("%s: gpio_request WL_REG_ON done\n", __func__);
if (gpio_direction_output(gpio_wl_reg_on, 1))
pr_err("%s: WL_REG_ON failed to pull up\n", __func__);
else
BCM_DBG("%s: WL_REG_ON is pulled up\n", __func__);
if (gpio_get_value(gpio_wl_reg_on))
BCM_DBG("%s: Initial WL_REG_ON: [%d]\n",
__func__, gpio_get_value(gpio_wl_reg_on));
return 0;
}
int dhd_wlan_power(int on)
{
pr_info("%s Enter: power %s\n", __func__, on ? "on" : "off");
if (on) {
if (gpio_direction_output(gpio_wl_reg_on, 1)) {
pr_err("%s: WL_REG_ON didn't output high\n", __func__);
return -EIO;
}
if (!gpio_get_value(gpio_wl_reg_on))
pr_err("[%s] gpio didn't set high.\n", __func__);
} else {
if (gpio_direction_output(gpio_wl_reg_on, 0)) {
pr_err("%s: WL_REG_ON didn't output low\n", __func__);
return -EIO;
}
}
return 0;
}
EXPORT_SYMBOL(dhd_wlan_power);
static int dhd_wlan_reset(int onoff)
{
return 0;
}
static int dhd_wlan_set_carddetect(int val)
{
return 0;
}
/* Customized Locale table : OPTIONAL feature */
#define WLC_CNTRY_BUF_SZ 4
struct cntry_locales_custom {
char iso_abbrev[WLC_CNTRY_BUF_SZ];
char custom_locale[WLC_CNTRY_BUF_SZ];
int custom_locale_rev;
};
static struct cntry_locales_custom brcm_wlan_translate_custom_table[] = {
/* Table should be filled out based on custom platform regulatory requirement */
{"", "XT", 49}, /* Universal if Country code is unknown or empty */
{"US", "US", 176},
{"AE", "AE", 1},
{"AR", "AR", 21},
{"AT", "AT", 4},
{"AU", "AU", 40},
{"BE", "BE", 4},
{"BG", "BG", 4},
{"BN", "BN", 4},
{"BR", "BR", 4},
{"CA", "US", 176}, /* Previousely was CA/31 */
{"CH", "CH", 4},
{"CY", "CY", 4},
{"CZ", "CZ", 4},
{"DE", "DE", 7},
{"DK", "DK", 4},
{"EE", "EE", 4},
{"ES", "ES", 4},
{"FI", "FI", 4},
{"FR", "FR", 5},
{"GB", "GB", 6},
{"GR", "GR", 4},
{"HK", "HK", 2},
{"HR", "HR", 4},
{"HU", "HU", 4},
{"IE", "IE", 5},
{"IN", "IN", 28},
{"IS", "IS", 4},
{"IT", "IT", 4},
{"ID", "ID", 5},
{"JP", "JP", 86},
{"KR", "KR", 57},
{"KW", "KW", 5},
{"LI", "LI", 4},
{"LT", "LT", 4},
{"LU", "LU", 3},
{"LV", "LV", 4},
{"MA", "MA", 2},
{"MT", "MT", 4},
{"MX", "MX", 20},
{"MY", "MY", 16},
{"NL", "NL", 4},
{"NO", "NO", 4},
{"NZ", "NZ", 4},
{"PL", "PL", 4},
{"PT", "PT", 4},
{"PY", "PY", 2},
{"RO", "RO", 4},
{"RU", "RU", 13},
{"SE", "SE", 4},
{"SG", "SG", 19},
{"SI", "SI", 4},
{"SK", "SK", 4},
{"TH", "TH", 5},
{"TR", "TR", 7},
{"TW", "TW", 1},
{"VN", "VN", 4},
};
struct cntry_locales_custom brcm_wlan_translate_nodfs_table[] = {
{"", "XT", 50}, /* Universal if Country code is unknown or empty */
{"US", "US", 177},
{"AU", "AU", 41},
{"BR", "BR", 18},
{"CA", "US", 177},
{"CH", "E0", 33},
{"CY", "E0", 33},
{"CZ", "E0", 33},
{"DE", "E0", 33},
{"DK", "E0", 33},
{"EE", "E0", 33},
{"ES", "E0", 33},
{"EU", "E0", 33},
{"FI", "E0", 33},
{"FR", "E0", 33},
{"GB", "E0", 33},
{"GR", "E0", 33},
{"HK", "SG", 20},
{"HR", "E0", 33},
{"HU", "E0", 33},
{"IE", "E0", 33},
{"IN", "IN", 29},
{"ID", "ID", 5},
{"IS", "E0", 33},
{"IT", "E0", 33},
{"JP", "JP", 87},
{"KR", "KR", 79},
{"KW", "KW", 5},
{"LI", "E0", 33},
{"LT", "E0", 33},
{"LU", "E0", 33},
{"LV", "LV", 4},
{"MA", "MA", 2},
{"MT", "E0", 33},
{"MY", "MY", 17},
{"MX", "US", 177},
{"NL", "E0", 33},
{"NO", "E0", 33},
{"PL", "E0", 33},
{"PT", "E0", 33},
{"RO", "E0", 33},
{"SE", "E0", 33},
{"SG", "SG", 20},
{"SI", "E0", 33},
{"SK", "E0", 33},
{"SZ", "E0", 33},
{"TH", "TH", 9},
{"TW", "TW", 60},
};
static void *dhd_wlan_get_country_code(char *ccode, u32 flags)
{
struct cntry_locales_custom *locales;
int size;
int i;
if (!ccode)
return NULL;
if (flags & WLAN_PLAT_NODFS_FLAG) {
locales = brcm_wlan_translate_nodfs_table;
size = ARRAY_SIZE(brcm_wlan_translate_nodfs_table);
} else {
locales = brcm_wlan_translate_custom_table;
size = ARRAY_SIZE(brcm_wlan_translate_custom_table);
}
for (i = 0; i < size; i++)
if (strcmp(ccode, locales[i].iso_abbrev) == 0)
return &locales[i];
return &locales[0];
}
static unsigned char brcm_mac_addr[IFHWADDRLEN] = { 0, 0x90, 0x4c, 0, 0, 0 };
static int __init dhd_mac_addr_setup(char *str)
{
char macstr[IFHWADDRLEN*3];
char *macptr = macstr;
char *token;
int i = 0;
if (!str)
return 0;
BCM_DBG("wlan MAC = %s\n", str);
if (strlen(str) >= sizeof(macstr))
return 0;
strlcpy(macstr, str, sizeof(macstr));
while (((token = strsep(&macptr, ":")) != NULL) && (i < IFHWADDRLEN)) {
unsigned long val;
int res;
res = kstrtoul(token, 0x10, &val);
if (res < 0)
break;
brcm_mac_addr[i++] = (u8)val;
}
if (i < IFHWADDRLEN && strlen(macstr)==IFHWADDRLEN*2) {
/* try again with wrong format (sans colons) */
u64 mac;
if (kstrtoull(macstr, 0x10, &mac) < 0)
return 0;
for (i=0; i<IFHWADDRLEN; i++)
brcm_mac_addr[IFHWADDRLEN-1-i] = (u8)((0xFF)&(mac>>(i*8)));
}
return i==IFHWADDRLEN ? 1:0;
}
__setup("androidboot.wifimacaddr=", dhd_mac_addr_setup);
static int dhd_wifi_get_mac_addr(unsigned char *buf)
{
uint rand_mac;
if (!buf)
return -EFAULT;
if ((brcm_mac_addr[4] == 0) && (brcm_mac_addr[5] == 0)) {
prandom_seed((uint)jiffies);
rand_mac = prandom_u32();
brcm_mac_addr[3] = (unsigned char)rand_mac;
brcm_mac_addr[4] = (unsigned char)(rand_mac >> 8);
brcm_mac_addr[5] = (unsigned char)(rand_mac >> 16);
}
memcpy(buf, brcm_mac_addr, IFHWADDRLEN);
return 0;
}
static int dhd_wlan_get_wake_irq(void)
{
return brcm_wake_irq;
}
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 */
},
};
EXPORT_SYMBOL(dhd_wlan_resources);
struct wifi_platform_data dhd_wlan_control = {
.set_power = dhd_wlan_power,
.set_reset = dhd_wlan_reset,
.set_carddetect = dhd_wlan_set_carddetect,
.get_mac_addr = dhd_wifi_get_mac_addr,
#ifdef CONFIG_DHD_USE_STATIC_BUF
.mem_prealloc = dhd_wlan_mem_prealloc,
#endif
.get_wake_irq = dhd_wlan_get_wake_irq,
.get_country_code = dhd_wlan_get_country_code,
};
EXPORT_SYMBOL(dhd_wlan_control);
int __init dhd_wlan_init(void)
{
int ret;
printk(KERN_INFO "%s: START\n", __FUNCTION__);
#ifdef CONFIG_DHD_USE_STATIC_BUF
ret = dhd_init_wlan_mem();
#endif
ret = dhd_wifi_init_gpio();
dhd_wlan_resources[0].start = dhd_wlan_resources[0].end =
brcm_wake_irq;
return ret;
}
void __exit dhd_wlan_exit(void)
{
#ifdef CONFIG_DHD_USE_STATIC_BUF
int i;
for (i = 0; i < DHD_SKB_1PAGE_BUF_NUM; i++) {
if (wlan_static_skb[i])
dev_kfree_skb(wlan_static_skb[i]);
}
for (i = DHD_SKB_1PAGE_BUF_NUM; i < WLAN_SKB_1_2PAGE_BUF_NUM; i++) {
if (wlan_static_skb[i])
dev_kfree_skb(wlan_static_skb[i]);
}
#if !defined(CONFIG_BCMDHD_PCIE)
if (wlan_static_skb[i])
dev_kfree_skb(wlan_static_skb[i]);
#endif /* !CONFIG_BCMDHD_PCIE */
if (wlan_static_prot)
kfree(wlan_static_prot);
if (wlan_static_osl_buf)
kfree(wlan_static_osl_buf);
if (wlan_static_scan_buf0)
kfree(wlan_static_scan_buf0);
if (wlan_static_dhd_info_buf)
kfree(wlan_static_dhd_info_buf);
if (wlan_static_scan_buf1)
kfree(wlan_static_scan_buf1);
#ifdef CONFIG_BCMDHD_PCIE
if (wlan_static_if_flow_lkup)
kfree(wlan_static_if_flow_lkup);
#endif
#endif /* CONFIG_BCMDHD_USE_STATIC_BUF */
return;
}