blob: 65d4fbde837bf55c526d596bbfb2f161d70806f3 [file] [log] [blame] [edit]
/*
* Copyright (C) 2011 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <asm/mach-types.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <linux/if.h>
#include <linux/skbuff.h>
#include <linux/wlan_plat.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/fixed.h>
#include <plat/mmc.h>
#include <linux/random.h>
#include <linux/jiffies.h>
#include "hsmmc.h"
#include "control.h"
#include "mux.h"
#include "board-tuna.h"
#define GPIO_WLAN_PMENA 104
#define GPIO_WLAN_IRQ 2
#define ATAG_TUNA_MAC 0x57464d41
/* #define ATAG_TUNA_MAC_DEBUG */
#ifdef CONFIG_DHD_USE_STATIC_BUF
#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4
#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160
#define PREALLOC_WLAN_SECTION_HEADER 24
#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512)
#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024)
#define WLAN_SKB_BUF_NUM 16
static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
typedef struct wifi_mem_prealloc_struct {
void *mem_ptr;
unsigned long size;
} wifi_mem_prealloc_t;
static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = {
{ NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) },
{ NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) },
{ NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) },
{ NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) }
};
static void *tuna_wifi_mem_prealloc(int section, unsigned long size)
{
if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS)
return wlan_static_skb;
if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS))
return NULL;
if (wifi_mem_array[section].size < size)
return NULL;
return wifi_mem_array[section].mem_ptr;
}
#endif
int __init tuna_init_wifi_mem(void)
{
#ifdef CONFIG_DHD_USE_STATIC_BUF
int i;
for(i=0;( i < WLAN_SKB_BUF_NUM );i++) {
if (i < (WLAN_SKB_BUF_NUM/2))
wlan_static_skb[i] = dev_alloc_skb(4096);
else
wlan_static_skb[i] = dev_alloc_skb(8192);
}
for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) {
wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size,
GFP_KERNEL);
if (wifi_mem_array[i].mem_ptr == NULL)
return -ENOMEM;
}
#endif
return 0;
}
static struct resource tuna_wifi_resources[] = {
[0] = {
.name = "bcmdhd_wlan_irq",
.start = OMAP_GPIO_IRQ(GPIO_WLAN_IRQ),
.end = OMAP_GPIO_IRQ(GPIO_WLAN_IRQ),
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
},
};
#if 0
/* BCM4329 returns wrong sdio_vsn(1) when we read cccr,
* we use predefined value (sdio_vsn=2) here to initial sdio driver well
*/
static struct embedded_sdio_data tuna_wifi_emb_data = {
.cccr = {
.sdio_vsn = 2,
.multi_block = 1,
.low_speed = 0,
.wide_bus = 0,
.high_power = 1,
.high_speed = 1,
},
};
#endif
static int tuna_wifi_cd = 0; /* WIFI virtual 'card detect' status */
static void (*wifi_status_cb)(int card_present, void *dev_id);
static void *wifi_status_cb_devid;
static struct regulator *clk32kaudio_reg;
static int tuna_wifi_status_register(
void (*callback)(int card_present, void *dev_id),
void *dev_id)
{
if (wifi_status_cb)
return -EAGAIN;
wifi_status_cb = callback;
wifi_status_cb_devid = dev_id;
return 0;
}
static unsigned int tuna_wifi_status(struct device *dev)
{
return tuna_wifi_cd;
}
struct mmc_platform_data tuna_wifi_data = {
.ocr_mask = MMC_VDD_165_195 | MMC_VDD_20_21,
.built_in = 1,
.status = tuna_wifi_status,
.card_present = 0,
.register_status_notify = tuna_wifi_status_register,
};
static int tuna_wifi_set_carddetect(int val)
{
pr_debug("%s: %d\n", __func__, val);
tuna_wifi_cd = val;
if (wifi_status_cb) {
wifi_status_cb(val, wifi_status_cb_devid);
} else
pr_warning("%s: Nobody to notify\n", __func__);
return 0;
}
static int tuna_wifi_power_state;
struct fixed_voltage_data {
struct regulator_desc desc;
struct regulator_dev *dev;
int microvolts;
int gpio;
unsigned startup_delay;
bool enable_high;
bool is_enabled;
};
static struct regulator_consumer_supply tuna_vmmc5_supply = {
.supply = "vmmc",
.dev_name = "omap_hsmmc.4",
};
static struct regulator_init_data tuna_vmmc5 = {
.constraints = {
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = 1,
.consumer_supplies = &tuna_vmmc5_supply,
};
static struct fixed_voltage_config tuna_vwlan = {
.supply_name = "vwl1271",
.microvolts = 2000000, /* 2.0V */
.gpio = GPIO_WLAN_PMENA,
.startup_delay = 70000, /* 70msec */
.enable_high = 1,
.enabled_at_boot = 0,
.init_data = &tuna_vmmc5,
};
static struct platform_device omap_vwlan_device = {
.name = "reg-fixed-voltage",
.id = 1,
.dev = {
.platform_data = &tuna_vwlan,
},
};
static int tuna_wifi_power(int on)
{
int ret = 0;
pr_debug("%s: %d\n", __func__, on);
if (!clk32kaudio_reg) {
clk32kaudio_reg = regulator_get(0, "clk32kaudio");
if (IS_ERR(clk32kaudio_reg)) {
pr_err("%s: clk32kaudio reg not found!\n", __func__);
clk32kaudio_reg = NULL;
}
}
if (clk32kaudio_reg && on && !tuna_wifi_power_state) {
ret = regulator_enable(clk32kaudio_reg);
if (ret)
pr_err("%s: regulator_enable failed: %d\n", __func__,
ret);
}
msleep(300);
gpio_set_value(GPIO_WLAN_PMENA, on);
msleep(200);
if (clk32kaudio_reg && !on && tuna_wifi_power_state) {
ret = regulator_disable(clk32kaudio_reg);
if (ret)
pr_err("%s: regulator_disable failed: %d\n", __func__,
ret);
}
tuna_wifi_power_state = on;
return ret;
}
static int tuna_wifi_reset_state;
static int tuna_wifi_reset(int on)
{
pr_debug("%s: do nothing\n", __func__);
tuna_wifi_reset_state = on;
return 0;
}
static unsigned char tuna_mac_addr[IFHWADDRLEN] = { 0,0x90,0x4c,0,0,0 };
static int __init tuna_mac_addr_setup(char *str)
{
char macstr[IFHWADDRLEN*3];
char *macptr = macstr;
char *token;
int i = 0;
if (!str)
return 0;
pr_debug("wlan MAC = %s\n", str);
if (strlen(str) >= sizeof(macstr))
return 0;
strcpy(macstr, str);
while ((token = strsep(&macptr, ":")) != NULL) {
unsigned long val;
int res;
if (i >= IFHWADDRLEN)
break;
res = strict_strtoul(token, 0x10, &val);
if (res < 0)
return 0;
tuna_mac_addr[i++] = (u8)val;
}
return 1;
}
__setup("androidboot.macaddr=", tuna_mac_addr_setup);
static int tuna_wifi_get_mac_addr(unsigned char *buf)
{
int type = omap4_tuna_get_type();
uint rand_mac;
if (type != TUNA_TYPE_TORO)
return -EINVAL;
if (!buf)
return -EFAULT;
if ((tuna_mac_addr[4] == 0) && (tuna_mac_addr[5] == 0)) {
srandom32((uint)jiffies);
rand_mac = random32();
tuna_mac_addr[3] = (unsigned char)rand_mac;
tuna_mac_addr[4] = (unsigned char)(rand_mac >> 8);
tuna_mac_addr[5] = (unsigned char)(rand_mac >> 16);
}
memcpy(buf, tuna_mac_addr, IFHWADDRLEN);
return 0;
}
/* Customized Locale table : OPTIONAL feature */
#define WLC_CNTRY_BUF_SZ 4
typedef struct cntry_locales_custom {
char iso_abbrev[WLC_CNTRY_BUF_SZ];
char custom_locale[WLC_CNTRY_BUF_SZ];
int custom_locale_rev;
} cntry_locales_custom_t;
static cntry_locales_custom_t tuna_wifi_translate_custom_table[] = {
/* Table should be filled out based on custom platform regulatory requirement */
{"", "XY", 4}, /* universal */
{"US", "US", 69}, /* input ISO "US" to : US regrev 69 */
{"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */
{"EU", "EU", 5}, /* European union countries */
{"AT", "EU", 5},
{"BE", "EU", 5},
{"BG", "EU", 5},
{"CY", "EU", 5},
{"CZ", "EU", 5},
{"DK", "EU", 5},
{"EE", "EU", 5},
{"FI", "EU", 5},
{"FR", "EU", 5},
{"DE", "EU", 5},
{"GR", "EU", 5},
{"HU", "EU", 5},
{"IE", "EU", 5},
{"IT", "EU", 5},
{"LV", "EU", 5},
{"LI", "EU", 5},
{"LT", "EU", 5},
{"LU", "EU", 5},
{"MT", "EU", 5},
{"NL", "EU", 5},
{"PL", "EU", 5},
{"PT", "EU", 5},
{"RO", "EU", 5},
{"SK", "EU", 5},
{"SI", "EU", 5},
{"ES", "EU", 5},
{"SE", "EU", 5},
{"GB", "EU", 5}, /* input ISO "GB" to : EU regrev 05 */
{"IL", "IL", 0},
{"CH", "CH", 0},
{"TR", "TR", 0},
{"NO", "NO", 0},
{"KR", "KR", 25},
{"AU", "XY", 3},
{"CN", "CN", 0},
{"TW", "XY", 3},
{"AR", "XY", 3},
{"MX", "XY", 3},
{"JP", "EU", 0},
{"BR", "KR", 25}
};
static void *tuna_wifi_get_country_code(char *ccode)
{
int size = ARRAY_SIZE(tuna_wifi_translate_custom_table);
int i;
if (!ccode)
return NULL;
for (i = 0; i < size; i++)
if (strcmp(ccode, tuna_wifi_translate_custom_table[i].iso_abbrev) == 0)
return &tuna_wifi_translate_custom_table[i];
return &tuna_wifi_translate_custom_table[0];
}
static struct wifi_platform_data tuna_wifi_control = {
.set_power = tuna_wifi_power,
.set_reset = tuna_wifi_reset,
.set_carddetect = tuna_wifi_set_carddetect,
#ifdef CONFIG_DHD_USE_STATIC_BUF
.mem_prealloc = tuna_wifi_mem_prealloc,
#else
.mem_prealloc = NULL,
#endif
.get_mac_addr = tuna_wifi_get_mac_addr,
.get_country_code = tuna_wifi_get_country_code,
};
static struct platform_device tuna_wifi_device = {
.name = "bcmdhd_wlan",
.id = 1,
.num_resources = ARRAY_SIZE(tuna_wifi_resources),
.resource = tuna_wifi_resources,
.dev = {
.platform_data = &tuna_wifi_control,
},
};
static void __init tuna_wlan_gpio(void)
{
pr_debug("%s: start\n", __func__);
/* WLAN SDIO: MMC5 CMD */
omap_mux_init_signal("sdmmc5_cmd", OMAP_PIN_INPUT_PULLUP);
/* WLAN SDIO: MMC5 CLK */
omap_mux_init_signal("sdmmc5_clk", OMAP_PIN_INPUT_PULLUP);
/* WLAN SDIO: MMC5 DAT[0-3] */
omap_mux_init_signal("sdmmc5_dat0", OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc5_dat1", OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc5_dat2", OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc5_dat3", OMAP_PIN_INPUT_PULLUP);
/* WLAN OOB - BCM4330 - GPIO 16 or GPIO 2 */
omap_mux_init_signal("sim_reset.gpio_wk2", OMAP_PIN_INPUT);
omap_mux_init_signal("kpd_row1.safe_mode", 0);
/* WLAN PMENA - GPIO 104 */
omap_mux_init_signal("gpmc_ncs7.gpio_104", OMAP_PIN_OUTPUT);
/* Enable power to gpio_wk0-gpio_wk2 */
omap4_ctrl_wk_pad_writel(0xb0000000,
OMAP4_CTRL_MODULE_PAD_WKUP_CONTROL_USIMIO);
/* gpio_enable(GPIO_WLAN_IRQ); */
gpio_request(GPIO_WLAN_IRQ, "wlan_irq");
gpio_direction_input(GPIO_WLAN_IRQ);
}
int __init tuna_wlan_init(void)
{
pr_debug("%s: start\n", __func__);
tuna_wlan_gpio();
tuna_init_wifi_mem();
platform_device_register(&omap_vwlan_device);
return platform_device_register(&tuna_wifi_device);
}