diff options
Diffstat (limited to 'drivers/platform/sprd/dhd_adapter.c')
| -rw-r--r-- | drivers/platform/sprd/dhd_adapter.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/drivers/platform/sprd/dhd_adapter.c b/drivers/platform/sprd/dhd_adapter.c new file mode 100644 index 00000000..aa5637c0 --- /dev/null +++ b/drivers/platform/sprd/dhd_adapter.c @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications 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 <asm/mach-types.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/skbuff.h> +#include <linux/wlan_plat.h> +#include <soc/sprd/board.h> +#include <soc/sprd/hardware.h> +#include <linux/regulator/consumer.h> +#include <soc/sprd/regulator.h> +#include <linux/export.h> +#include <linux/clk.h> +#include <linux/fs.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdhci.h> +#include <soc/sprd/pinmap.h> + +#define GET_WIFI_MAC_ADDR_FROM_NV_ITEM 1 + +#define WLAN_STATIC_SCAN_BUF0 5 +#define WLAN_STATIC_SCAN_BUF1 6 +#define WLAN_STATIC_DHD_INFO_BUF 7 +#define WLAN_SCAN_BUF_SIZE (64 * 1024) +#define WLAN_DHD_INFO_BUF_SIZE (16 * 1024) +#define PREALLOC_WLAN_SEC_NUM 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 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_SKB_BUF_NUM 17 +#define IFHWADDRLEN 6 + +#define CUSTOMER_MAC_FILE "/data/misc/wifi/wifimac.txt" + +extern void * dhd_os_open_image(char *filename); +extern int dhd_os_get_image_block(char *buf, int len, void *image); +extern void dhd_os_close_image(void *image); +extern int sdhci_wifi_detect_isbusy(void); + +static struct mmc_host * wlan_mmc =NULL; +static int wlan_device_cd = 0; /* WIFI virtual 'card detect' status */ +static int wlan_device_power_state; +static int wlan_device_reset_state; +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; +static struct regulator *wlan_regulator_18 = NULL; + +int wlan_device_power(int on); +int wlan_device_reset(int on); +int wlan_device_set_carddetect(int on); + +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 wlan_mem_array[PREALLOC_WLAN_SEC_NUM] = { + { 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) } +}; + +void *wlan_static_scan_buf0; +void *wlan_static_scan_buf1; +void *wlan_static_dhd_info_buf; + +static void * open_image(char *filename) +{ + struct file *fp; + + fp = filp_open(filename, O_RDONLY, 0); + /* + * 2.6.11 (FC4) supports filp_open() but later revs don't? + * Alternative: + * fp = open_namei(AT_FDCWD, filename, O_RD, 0); + * ??? + */ + if (IS_ERR(fp)) + fp = NULL; + + return fp; +} + +static int get_image_block(char *buf, int len, void *image) +{ + struct file *fp = (struct file *)image; + int rdlen; + + if (!image) + return 0; + + rdlen = kernel_read(fp, fp->f_pos, buf, len); + if (rdlen > 0) + fp->f_pos += rdlen; + + return rdlen; +} + +static void close_image(void *image) +{ + if (image) + filp_close((struct file *)image, NULL); +} + +static void *wlan_device_mem_prealloc(int section, unsigned long size) +{ + if (section == PREALLOC_WLAN_SEC_NUM) + return wlan_static_skb; + + if (section == WLAN_STATIC_SCAN_BUF0) + return wlan_static_scan_buf0; + + if (section == WLAN_STATIC_SCAN_BUF1) + return wlan_static_scan_buf1; + + if (section == WLAN_STATIC_DHD_INFO_BUF) { + if (size > WLAN_DHD_INFO_BUF_SIZE) { + pr_err("request DHD_INFO size(%lu) is bigger than static size(%d).\n", size, WLAN_DHD_INFO_BUF_SIZE); + return NULL; + } + return wlan_static_dhd_info_buf; + } + + if ((section < 0) || (section > PREALLOC_WLAN_SEC_NUM)) + return NULL; + + if (wlan_mem_array[section].size < size) + return NULL; + + return wlan_mem_array[section].mem_ptr; +} + +int __init init_wifi_mem(void) +{ + int i; + int j; + + for (i = 0; i < 8; i++) { + wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_1PAGE_BUFSIZE); + if (!wlan_static_skb[i]) + goto err_skb_alloc; + } + + for (; i < 16; i++) { + wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_2PAGE_BUFSIZE); + if (!wlan_static_skb[i]) + goto err_skb_alloc; + } + + wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_4PAGE_BUFSIZE); + if (!wlan_static_skb[i]) + goto err_skb_alloc; + + for (i = 0 ; i < PREALLOC_WLAN_SEC_NUM ; i++) { + wlan_mem_array[i].mem_ptr = + kmalloc(wlan_mem_array[i].size, GFP_KERNEL); + + if (!wlan_mem_array[i].mem_ptr) + goto err_mem_alloc; + } + + wlan_static_scan_buf0 = kmalloc(WLAN_SCAN_BUF_SIZE, GFP_KERNEL); + if (!wlan_static_scan_buf0) + goto err_mem_alloc; + + wlan_static_scan_buf1 = kmalloc(WLAN_SCAN_BUF_SIZE, GFP_KERNEL); + if (!wlan_static_scan_buf1) + goto err_mem_alloc; + + wlan_static_dhd_info_buf = kmalloc(WLAN_DHD_INFO_BUF_SIZE, GFP_KERNEL); + if (!wlan_static_dhd_info_buf) + goto err_mem_alloc; + + printk(KERN_INFO"%s: WIFI MEM Allocated\n", __func__); + return 0; + + err_mem_alloc: + pr_err("Failed to mem_alloc for WLAN\n"); + for (j = 0 ; j < i ; j++) + kfree(wlan_mem_array[j].mem_ptr); + + 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; +} + +/* Control the BT_VDDIO and WLAN_VDDIO +Always power on According to spec +*/ +static int wlan_ldo_enable(void) +{ + int err; + +#ifdef CONFIG_ARCH_SCX35 + /*temp config for clk_aux0, waiting for SC8830 pin config*/ +// pinmap_set(0x400, 0x0101); + + wlan_regulator_18 = regulator_get(NULL, "vdd18"); +#else + wlan_regulator_18 = regulator_get(NULL, "vddsd1"); +#endif + + if (IS_ERR(wlan_regulator_18)) { + pr_err("can't get wlan 1.8V regulator\n"); + return -1; + } + + err = regulator_set_voltage(wlan_regulator_18,1800000,1800000); + if (err){ + pr_err("can't set wlan to 1.8V.\n"); + return -1; + } + + regulator_set_mode(wlan_regulator_18, REGULATOR_MODE_STANDBY); + regulator_enable(wlan_regulator_18); + return 0; +} + +static void wlan_clk_init(void) +{ + struct clk *wlan_clk; + struct clk *clk_parent; +/*8825ea/openphone use aux0 garda use aux1*/ +#ifdef CONFIG_MACH_GARDA + wlan_clk = clk_get(NULL, "clk_aux1"); + if (IS_ERR(wlan_clk)) { + printk("clock: failed to get clk_aux1\n"); + } +#else + wlan_clk = clk_get(NULL, "clk_aux0"); + if (IS_ERR(wlan_clk)) { + printk("clock: failed to get clk_aux0\n"); + } +#endif + clk_parent = clk_get(NULL, "ext_32k"); + if (IS_ERR(clk_parent)) { + printk("failed to get parent ext_32k\n"); + } + + clk_set_parent(wlan_clk, clk_parent); + clk_set_rate(wlan_clk, 32000); + clk_prepare_enable(wlan_clk); +} + +int wlan_device_power(int on) +{ + int i; + pr_info("%s:%d \n", __func__, on); + + if(on) { + #if 0 + for (i = 0; i <= 200; i++) { + //if(!sdhci_wifi_detect_isbusy()) + break; + msleep(100); + } + #else +// if(wlan_mmc) +// flush_delayed_work(&wlan_mmc->detect); + #endif + printk("%s after delay %d times (100ms)\n", __func__, i); + /* enable SDIO clock */ + #ifdef CONFIG_WLAN_SDIO + //sdhci_device_attach(1); + #endif + gpio_direction_output(GPIO_WIFI_SHUTDOWN, 0); + mdelay(10); + gpio_direction_output(GPIO_WIFI_SHUTDOWN, 1); +// mdelay(200); + } + else { + /* disale SDIO clock */ + #ifdef CONFIG_WLAN_SDIO + //sdhci_device_attach(0); + #endif + gpio_direction_output(GPIO_WIFI_SHUTDOWN, 0); + + } + wlan_device_power_state = on; + return 0; +} + +int wlan_device_reset(int on) +{ + pr_info("%s: do nothing\n", __func__); + wlan_device_reset_state = on; + return 0; +} + + +int wlan_device_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; +} + +EXPORT_SYMBOL(wlan_device_status_register); + +static __used unsigned int wlan_device_status(struct device *dev) +{ + return wlan_device_cd; +} + +int wlan_device_set_carddetect(int val) +{ + pr_info("%s: %d\n", __func__, val); + mdelay(100); +#if 0 + wlan_device_cd = val; + if (wifi_status_cb) { + wifi_status_cb(val, wifi_status_cb_devid); + } else + pr_warning("%s: Nobody to notify\n", __func__); +#endif + +#ifdef CONFIG_WLAN_SDIO +// sdhci_bus_scan(); + if(wlan_mmc) { + mmc_detect_change(wlan_mmc, 0); + } else { + pr_info("%s wlan_mmc is null,carddetect failed \n ",__func__); + } +#endif + return 0; +} + + + +#ifdef GET_WIFI_MAC_ADDR_FROM_NV_ITEM +static unsigned char wlan_mac_addr[IFHWADDRLEN] = { 0x11,0x22,0x33,0x44,0x55,0x66 }; + +static unsigned char char2bin( char m) +{ + if (( m >= 'a') && (m <= 'f')) { + return m - 'a' + 10; + } + if (( m >= 'A') && (m <= 'F')) { + return m - 'A' + 10; + } + if (( m >= '0') && (m <= '9')) { + return m - '0'; + } + return 0; +} + +static int wlan_device_get_mac_addr(unsigned char *buf) +{ + + char macaddr[20]; + int mac_len = 0; + void *fp; + + if (!buf){ + pr_err("%s, null parameter !!\n", __func__); + return -EFAULT; + } + fp = open_image(CUSTOMER_MAC_FILE); + if (fp == NULL) + return -EFAULT; + mac_len = get_image_block(macaddr, 17, fp); + if (mac_len < 17) + return -EFAULT; + + + wlan_mac_addr[0] = (unsigned char)((char2bin(macaddr[0]) << 4) | char2bin(macaddr[1])); + wlan_mac_addr[1] = (unsigned char)((char2bin(macaddr[3]) << 4) | char2bin(macaddr[4])); + wlan_mac_addr[2] = (unsigned char)((char2bin(macaddr[6]) << 4) | char2bin(macaddr[7])); + wlan_mac_addr[3] = (unsigned char)((char2bin(macaddr[9]) << 4) | char2bin(macaddr[10])); + wlan_mac_addr[4] = (unsigned char)((char2bin(macaddr[12]) << 4) | char2bin(macaddr[13])); + wlan_mac_addr[5] = (unsigned char)((char2bin(macaddr[15]) << 4) | char2bin(macaddr[16])); + + memcpy(buf, wlan_mac_addr, IFHWADDRLEN); + pr_info("wifi mac: %x:%x:%x:%x:%x:%x\n", wlan_mac_addr[0], wlan_mac_addr[1], wlan_mac_addr[2], wlan_mac_addr[3], wlan_mac_addr[4], wlan_mac_addr[5]); + + close_image(fp); + + return 0; + +} +#endif + +static struct wifi_platform_data wlan_device_control = { + .set_power = wlan_device_power, + .set_reset = wlan_device_reset, + .set_carddetect = wlan_device_set_carddetect, + .mem_prealloc = wlan_device_mem_prealloc, +#ifdef GET_WIFI_MAC_ADDR_FROM_NV_ITEM + .get_mac_addr = wlan_device_get_mac_addr, +#endif +}; + +static struct resource wlan_resources[] = { +#ifdef CONFIG_WLAN_SDIO + [0] = { + .start = SPRD_SDIO1_PHYS, + .end = SPRD_SDIO1_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +#else + [0] = { + .start = SPRD_SPI0_PHYS, + .end = SPRD_SPI0_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +#endif + [1] = { + .name = "bcmdhd_wlan_irq", + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +static struct platform_device sprd_wlan_device = { + .name = "bcmdhd_wlan", + .id = 1, + .dev = { + .platform_data = &wlan_device_control, + }, + .resource = wlan_resources, + .num_resources = ARRAY_SIZE(wlan_resources), +}; + +void wlan_host_get(void) { + struct sdhci_host *host; +#ifndef CONFIG_OF + extern struct platform_device sprd_sdio1_device; + host = platform_get_drvdata(&sprd_sdio1_device); +#else + struct platform_device * sdio1_device = to_platform_device(bus_find_device_by_name(&platform_bus_type, NULL, "sdio_wifi")); + host = platform_get_drvdata(sdio1_device); +#endif +// BUG_ON(!host->mmc); +// wlan_mmc = host->mmc; + wlan_mmc = container_of(host, struct mmc_host, private); + BUG_ON(!wlan_mmc); +} + +static int __init wlan_device_init(void) +{ + int ret; + wlan_host_get(); + init_wifi_mem(); +// wlan_ldo_enable();//NOTE:check this function!If enble this, it cause V2.8 jump from 2.6v to 2.8v when sleep! + wlan_clk_init(); + gpio_request(GPIO_WIFI_IRQ, "oob_irq"); + gpio_direction_input(GPIO_WIFI_IRQ); + + wlan_resources[1].start = gpio_to_irq(GPIO_WIFI_IRQ); + wlan_resources[1].end = gpio_to_irq(GPIO_WIFI_IRQ); + + gpio_request(GPIO_WIFI_SHUTDOWN,"wifi_pwd"); + gpio_direction_output(GPIO_WIFI_SHUTDOWN, 0); + ret = platform_device_register(&sprd_wlan_device); + + return ret; +} + +late_initcall(wlan_device_init); + +//MODULE_DESCRIPTION("Broadcomm wlan driver"); +//MODULE_LICENSE("GPL"); + |
