Merge remote branch 'tegra/android-tegra-2.6.36' into android-tegra-moto-2.6.36
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-tegra / board-stingray-wifi.c
1 /* linux/arch/arm/mach-msm/board-stingray-wifi.c
2 */
3 #include <linux/kernel.h>
4 #include <linux/init.h>
5 #include <linux/platform_device.h>
6 #include <linux/delay.h>
7 #include <linux/err.h>
8 #include <asm/mach-types.h>
9 #include <asm/gpio.h>
10 #include <asm/io.h>
11 #include <asm/setup.h>
12 #include <linux/if.h>
13 #include <linux/skbuff.h>
14 #include <linux/wlan_plat.h>
15 #include <mach/sdhci.h>
16
17 #include <linux/random.h>
18 #include <linux/jiffies.h>
19
20 #include "board-stingray.h"
21 #include "gpio-names.h"
22
23 #define STINGRAY_WLAN_IRQ       TEGRA_GPIO_PU5
24 #define STINGRAY_WLAN_PWR       TEGRA_GPIO_PU4
25 #define STINGRAY_WLAN_RST       TEGRA_GPIO_PU2
26
27 #define ATAG_STINGRAY_MAC       0x57464d41
28 #define ATAG_STINGRAY_MAC_DEBUG
29
30 #define PREALLOC_WLAN_NUMBER_OF_SECTIONS        4
31 #define PREALLOC_WLAN_NUMBER_OF_BUFFERS         160
32 #define PREALLOC_WLAN_SECTION_HEADER            24
33
34 #define WLAN_SECTION_SIZE_0     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
35 #define WLAN_SECTION_SIZE_1     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
36 #define WLAN_SECTION_SIZE_2     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512)
37 #define WLAN_SECTION_SIZE_3     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024)
38
39 #define WLAN_SKB_BUF_NUM        16
40
41 static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
42
43 typedef struct wifi_mem_prealloc_struct {
44         void *mem_ptr;
45         unsigned long size;
46 } wifi_mem_prealloc_t;
47
48 static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = {
49         { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) },
50         { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) },
51         { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) },
52         { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) }
53 };
54
55 static void *stingray_wifi_mem_prealloc(int section, unsigned long size)
56 {
57         if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS)
58                 return wlan_static_skb;
59         if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS))
60                 return NULL;
61         if (wifi_mem_array[section].size < size)
62                 return NULL;
63         return wifi_mem_array[section].mem_ptr;
64 }
65
66 int __init stingray_init_wifi_mem(void)
67 {
68         int i;
69
70         for(i=0;( i < WLAN_SKB_BUF_NUM );i++) {
71                 if (i < (WLAN_SKB_BUF_NUM/2))
72                         wlan_static_skb[i] = dev_alloc_skb(4096);
73                 else
74                         wlan_static_skb[i] = dev_alloc_skb(8192);
75         }
76         for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) {
77                 wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size,
78                                                         GFP_KERNEL);
79                 if (wifi_mem_array[i].mem_ptr == NULL)
80                         return -ENOMEM;
81         }
82         return 0;
83 }
84
85 static struct resource stingray_wifi_resources[] = {
86         [0] = {
87                 .name           = "bcm4329_wlan_irq",
88                 .start          = TEGRA_GPIO_TO_IRQ(STINGRAY_WLAN_IRQ),
89                 .end            = TEGRA_GPIO_TO_IRQ(STINGRAY_WLAN_IRQ),
90                 .flags          = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
91         },
92 };
93
94 /* BCM4329 returns wrong sdio_vsn(1) when we read cccr,
95  * we use predefined value (sdio_vsn=2) here to initial sdio driver well
96   */
97 static struct embedded_sdio_data stingray_wifi_emb_data = {
98         .cccr   = {
99                 .sdio_vsn       = 2,
100                 .multi_block    = 1,
101                 .low_speed      = 0,
102                 .wide_bus       = 0,
103                 .high_power     = 1,
104                 .high_speed     = 1,
105         },
106 };
107
108 static int stingray_wifi_cd = 0; /* WIFI virtual 'card detect' status */
109 static void (*wifi_status_cb)(int card_present, void *dev_id);
110 static void *wifi_status_cb_devid;
111
112 static int stingray_wifi_status_register(
113                 void (*callback)(int card_present, void *dev_id),
114                 void *dev_id)
115 {
116         if (wifi_status_cb)
117                 return -EAGAIN;
118         wifi_status_cb = callback;
119         wifi_status_cb_devid = dev_id;
120         return 0;
121 }
122
123 static unsigned int stingray_wifi_status(struct device *dev)
124 {
125         return stingray_wifi_cd;
126 }
127
128 struct tegra_sdhci_platform_data stingray_wifi_data = {
129         .clk_id = NULL,
130         .force_hs = 0,
131         .mmc_data = {
132                 .ocr_mask               = MMC_VDD_165_195,
133                 .built_in               = 1,
134                 .status                 = stingray_wifi_status,
135                 .register_status_notify = stingray_wifi_status_register,
136                 .embedded_sdio          = &stingray_wifi_emb_data,
137         },
138         .cd_gpio = -1,
139         .wp_gpio = -1,
140         .power_gpio = -1,
141 };
142
143 static int stingray_wifi_set_carddetect(int val)
144 {
145         pr_debug("%s: %d\n", __func__, val);
146         stingray_wifi_cd = val;
147         if (wifi_status_cb) {
148                 wifi_status_cb(val, wifi_status_cb_devid);
149         } else
150                 pr_warning("%s: Nobody to notify\n", __func__);
151         return 0;
152 }
153
154 static int stingray_wifi_power_state;
155
156 static int stingray_wifi_power(int on)
157 {
158         pr_debug("%s: %d\n", __func__, on);
159
160         mdelay(100);
161         gpio_set_value(STINGRAY_WLAN_PWR, on);
162         mdelay(100);
163         gpio_set_value(STINGRAY_WLAN_RST, on);
164         mdelay(200);
165
166         stingray_wifi_power_state = on;
167         return 0;
168 }
169
170 static int stingray_wifi_reset_state;
171
172 static int stingray_wifi_reset(int on)
173 {
174         pr_debug("%s: do nothing\n", __func__);
175         stingray_wifi_reset_state = on;
176         return 0;
177 }
178
179 static unsigned char stingray_mac_addr[IFHWADDRLEN] = { 0,0x90,0x4c,0,0,0 };
180
181 static int __init parse_tag_wlan_mac(const struct tag *tag)
182 {
183         unsigned char *dptr = (unsigned char *)(&tag->u);
184         unsigned size;
185 #ifdef ATAG_STINGRAY_MAC_DEBUG
186         unsigned i;
187 #endif
188
189         size = min((tag->hdr.size - 2) * sizeof(__u32), (unsigned)IFHWADDRLEN);
190 #ifdef ATAG_STINGRAY_MAC_DEBUG
191         printk("WiFi MAC Addr [%d] = 0x%x\n", tag->hdr.size, tag->hdr.tag);
192         for(i=0;(i < size);i++) {
193                 printk(" %02x", dptr[i]);
194         }
195         printk("\n");
196 #endif
197         memcpy(stingray_mac_addr, dptr, size);
198         return 0;
199 }
200
201 __tagtable(ATAG_STINGRAY_MAC, parse_tag_wlan_mac);
202
203 static int stingray_wifi_get_mac_addr(unsigned char *buf)
204 {
205         uint rand_mac;
206
207         if (!buf)
208                 return -EINVAL;
209
210         if ((stingray_mac_addr[4] == 0) && (stingray_mac_addr[5] == 0)) {
211                 srandom32((uint)jiffies);
212                 rand_mac = random32();
213                 stingray_mac_addr[3] = (unsigned char)rand_mac;
214                 stingray_mac_addr[4] = (unsigned char)(rand_mac >> 8);
215                 stingray_mac_addr[5] = (unsigned char)(rand_mac >> 16);
216         }
217         memcpy(buf, stingray_mac_addr, IFHWADDRLEN);
218         return 0;
219 }
220
221 static struct wifi_platform_data stingray_wifi_control = {
222         .set_power      = stingray_wifi_power,
223         .set_reset      = stingray_wifi_reset,
224         .set_carddetect = stingray_wifi_set_carddetect,
225         .mem_prealloc   = stingray_wifi_mem_prealloc,
226         .get_mac_addr   = stingray_wifi_get_mac_addr,
227 };
228
229 static struct platform_device stingray_wifi_device = {
230         .name           = "bcm4329_wlan",
231         .id             = 1,
232         .num_resources  = ARRAY_SIZE(stingray_wifi_resources),
233         .resource       = stingray_wifi_resources,
234         .dev            = {
235                 .platform_data = &stingray_wifi_control,
236         },
237 };
238
239 static void __init stingray_wlan_gpio(void)
240 {
241         tegra_gpio_enable(STINGRAY_WLAN_PWR);
242         gpio_request(STINGRAY_WLAN_PWR, "wlan_pwr");
243         gpio_direction_output(STINGRAY_WLAN_PWR, 0);
244
245         tegra_gpio_enable(STINGRAY_WLAN_RST);
246         gpio_request(STINGRAY_WLAN_RST, "wlan_rst");
247         gpio_direction_output(STINGRAY_WLAN_RST, 0);
248
249         tegra_gpio_enable(STINGRAY_WLAN_IRQ);
250         gpio_request(STINGRAY_WLAN_IRQ, "wlan_irq");
251         gpio_direction_input(STINGRAY_WLAN_IRQ);
252 }
253
254 int __init stingray_wlan_init(void)
255 {
256         pr_debug("%s: start\n", __func__);
257         stingray_wlan_gpio();
258         stingray_init_wifi_mem();
259         return platform_device_register(&stingray_wifi_device);
260 }