temp revert rk change
[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_RST       TEGRA_GPIO_PU2
25
26 #define ATAG_STINGRAY_MAC       0x57464d41
27 #define ATAG_STINGRAY_MAC_DEBUG
28
29 #define PREALLOC_WLAN_NUMBER_OF_SECTIONS        4
30 #define PREALLOC_WLAN_NUMBER_OF_BUFFERS         160
31 #define PREALLOC_WLAN_SECTION_HEADER            24
32
33 #define WLAN_SECTION_SIZE_0     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
34 #define WLAN_SECTION_SIZE_1     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
35 #define WLAN_SECTION_SIZE_2     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512)
36 #define WLAN_SECTION_SIZE_3     (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024)
37
38 #define WLAN_SKB_BUF_NUM        16
39
40 static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
41
42 typedef struct wifi_mem_prealloc_struct {
43         void *mem_ptr;
44         unsigned long size;
45 } wifi_mem_prealloc_t;
46
47 static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = {
48         { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) },
49         { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) },
50         { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) },
51         { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) }
52 };
53
54 static void *stingray_wifi_mem_prealloc(int section, unsigned long size)
55 {
56         if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS)
57                 return wlan_static_skb;
58         if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS))
59                 return NULL;
60         if (wifi_mem_array[section].size < size)
61                 return NULL;
62         return wifi_mem_array[section].mem_ptr;
63 }
64
65 int __init stingray_init_wifi_mem(void)
66 {
67         int i;
68
69         for(i=0;( i < WLAN_SKB_BUF_NUM );i++) {
70                 if (i < (WLAN_SKB_BUF_NUM/2))
71                         wlan_static_skb[i] = dev_alloc_skb(4096);
72                 else
73                         wlan_static_skb[i] = dev_alloc_skb(8192);
74         }
75         for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) {
76                 wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size,
77                                                         GFP_KERNEL);
78                 if (wifi_mem_array[i].mem_ptr == NULL)
79                         return -ENOMEM;
80         }
81         return 0;
82 }
83
84 static struct resource stingray_wifi_resources[] = {
85         [0] = {
86                 .name           = "bcm4329_wlan_irq",
87                 .start          = TEGRA_GPIO_TO_IRQ(STINGRAY_WLAN_IRQ),
88                 .end            = TEGRA_GPIO_TO_IRQ(STINGRAY_WLAN_IRQ),
89                 .flags          = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
90         },
91 };
92
93 /* BCM4329 returns wrong sdio_vsn(1) when we read cccr,
94  * we use predefined value (sdio_vsn=2) here to initial sdio driver well
95   */
96 static struct embedded_sdio_data stingray_wifi_emb_data = {
97         .cccr   = {
98                 .sdio_vsn       = 2,
99                 .multi_block    = 1,
100                 .low_speed      = 0,
101                 .wide_bus       = 0,
102                 .high_power     = 1,
103                 .high_speed     = 1,
104         },
105 };
106
107 static int stingray_wifi_cd = 0; /* WIFI virtual 'card detect' status */
108 static void (*wifi_status_cb)(int card_present, void *dev_id);
109 static void *wifi_status_cb_devid;
110
111 static int stingray_wifi_status_register(
112                 void (*callback)(int card_present, void *dev_id),
113                 void *dev_id)
114 {
115         if (wifi_status_cb)
116                 return -EAGAIN;
117         wifi_status_cb = callback;
118         wifi_status_cb_devid = dev_id;
119         return 0;
120 }
121
122 static unsigned int stingray_wifi_status(struct device *dev)
123 {
124         return stingray_wifi_cd;
125 }
126
127 struct tegra_sdhci_platform_data stingray_wifi_data = {
128         .clk_id = NULL,
129         .force_hs = 0,
130         .mmc_data = {
131                 .ocr_mask               = MMC_VDD_165_195,
132                 .built_in               = 1,
133                 .status                 = stingray_wifi_status,
134                 .register_status_notify = stingray_wifi_status_register,
135                 .embedded_sdio          = &stingray_wifi_emb_data,
136         },
137         .cd_gpio = -1,
138         .wp_gpio = -1,
139         .power_gpio = -1,
140 };
141
142 static int stingray_wifi_set_carddetect(int val)
143 {
144         pr_debug("%s: %d\n", __func__, val);
145         stingray_wifi_cd = val;
146         if (wifi_status_cb) {
147                 wifi_status_cb(val, wifi_status_cb_devid);
148         } else
149                 pr_warning("%s: Nobody to notify\n", __func__);
150         return 0;
151 }
152
153 static int stingray_wifi_power_state;
154
155 static int stingray_wifi_power(int on)
156 {
157         pr_debug("%s: %d\n", __func__, on);
158
159         mdelay(100);
160         change_power_brcm_4329(on);
161         mdelay(100);
162         gpio_set_value(STINGRAY_WLAN_RST, on);
163         mdelay(200);
164
165         stingray_wifi_power_state = on;
166         return 0;
167 }
168
169 static int stingray_wifi_reset_state;
170
171 static int stingray_wifi_reset(int on)
172 {
173         pr_debug("%s: do nothing\n", __func__);
174         stingray_wifi_reset_state = on;
175         return 0;
176 }
177
178 static unsigned char stingray_mac_addr[IFHWADDRLEN] = { 0,0x90,0x4c,0,0,0 };
179
180 static int __init parse_tag_wlan_mac(const struct tag *tag)
181 {
182         unsigned char *dptr = (unsigned char *)(&tag->u);
183         unsigned size;
184 #ifdef ATAG_STINGRAY_MAC_DEBUG
185         unsigned i;
186 #endif
187
188         size = min((tag->hdr.size - 2) * sizeof(__u32), (unsigned)IFHWADDRLEN);
189 #ifdef ATAG_STINGRAY_MAC_DEBUG
190         printk("WiFi MAC Addr [%d] = 0x%x\n", tag->hdr.size, tag->hdr.tag);
191         for(i=0;(i < size);i++) {
192                 printk(" %02x", dptr[i]);
193         }
194         printk("\n");
195 #endif
196         memcpy(stingray_mac_addr, dptr, size);
197         return 0;
198 }
199
200 __tagtable(ATAG_STINGRAY_MAC, parse_tag_wlan_mac);
201
202 static int stingray_wifi_get_mac_addr(unsigned char *buf)
203 {
204         uint rand_mac;
205
206         if (!buf)
207                 return -EINVAL;
208
209         if ((stingray_mac_addr[4] == 0) && (stingray_mac_addr[5] == 0)) {
210                 srandom32((uint)jiffies);
211                 rand_mac = random32();
212                 stingray_mac_addr[3] = (unsigned char)rand_mac;
213                 stingray_mac_addr[4] = (unsigned char)(rand_mac >> 8);
214                 stingray_mac_addr[5] = (unsigned char)(rand_mac >> 16);
215         }
216         memcpy(buf, stingray_mac_addr, IFHWADDRLEN);
217         return 0;
218 }
219
220 /* Customized Locale table : OPTIONAL feature */
221 #define WLC_CNTRY_BUF_SZ        4
222 typedef struct cntry_locales_custom {
223         char iso_abbrev[WLC_CNTRY_BUF_SZ];
224         char custom_locale[WLC_CNTRY_BUF_SZ];
225         int  custom_locale_rev;
226 } cntry_locales_custom_t;
227
228 static cntry_locales_custom_t stingray_wifi_translate_custom_table[] = {
229 /* Table should be filled out based on custom platform regulatory requirement */
230         {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */
231         {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */
232         {"EU", "EU",  5}, /* input ISO "EU" to : EU regrev 05 */
233         {"FR", "EU",  5},
234         {"DE", "EU",  5},
235         {"IR", "EU",  5},
236         {"UK", "EU",  5}, /* input ISO "UK" to : EU regrev 05 */
237         {"KR", "XY",  3},
238         {"AU", "XY",  3},
239         {"CN", "XY",  3}, /* input ISO "CN" to : XY regrev 03 */
240         {"TW", "XY",  3},
241         {"AR", "XY",  3},
242 };
243
244 static void *stingray_wifi_get_country_code(char *ccode)
245 {
246         int size = ARRAY_SIZE(stingray_wifi_translate_custom_table);
247         int i;
248
249         if (!ccode)
250                 return NULL;
251
252         for (i = 0; i < size; i++)
253                 if (strcmp(ccode, stingray_wifi_translate_custom_table[i].iso_abbrev) == 0)
254                         return &stingray_wifi_translate_custom_table[i];
255         return NULL;
256 }
257
258 static struct wifi_platform_data stingray_wifi_control = {
259         .set_power      = stingray_wifi_power,
260         .set_reset      = stingray_wifi_reset,
261         .set_carddetect = stingray_wifi_set_carddetect,
262         .mem_prealloc   = stingray_wifi_mem_prealloc,
263         .get_mac_addr   = stingray_wifi_get_mac_addr,
264         .get_country_code = stingray_wifi_get_country_code,
265 };
266
267 static struct platform_device stingray_wifi_device = {
268         .name           = "bcm4329_wlan",
269         .id             = 1,
270         .num_resources  = ARRAY_SIZE(stingray_wifi_resources),
271         .resource       = stingray_wifi_resources,
272         .dev            = {
273                 .platform_data = &stingray_wifi_control,
274         },
275 };
276
277 static void __init stingray_wlan_gpio(void)
278 {
279         tegra_gpio_enable(STINGRAY_WLAN_RST);
280         gpio_request(STINGRAY_WLAN_RST, "wlan_rst");
281         gpio_direction_output(STINGRAY_WLAN_RST, 0);
282
283         tegra_gpio_enable(STINGRAY_WLAN_IRQ);
284         gpio_request(STINGRAY_WLAN_IRQ, "wlan_irq");
285         gpio_direction_input(STINGRAY_WLAN_IRQ);
286 }
287
288 int __init stingray_wlan_init(void)
289 {
290         pr_debug("%s: start\n", __func__);
291         stingray_wlan_gpio();
292         stingray_init_wifi_mem();
293         return platform_device_register(&stingray_wifi_device);
294 }