1 /*
2 * Platform Dependent file for Samsung Exynos
3 *
4 * Copyright (C) 2020, Broadcom.
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 *
21 * <<Broadcom-WL-IPTag/Open:>>
22 *
23 * $Id$
24 */
25 #include <linux/device.h>
26 #include <linux/gpio.h>
27 #include <linux/of_gpio.h>
28 #include <linux/delay.h>
29 #include <linux/interrupt.h>
30 #include <linux/irq.h>
31 #include <linux/slab.h>
32 #include <linux/workqueue.h>
33 #include <linux/poll.h>
34 #include <linux/miscdevice.h>
35 #include <linux/sched.h>
36 #include <linux/module.h>
37 #include <linux/fs.h>
38 #include <linux/list.h>
39 #include <linux/io.h>
40 #include <linux/workqueue.h>
41 #include <linux/unistd.h>
42 #include <linux/bug.h>
43 #include <linux/skbuff.h>
44 #include <linux/init.h>
45 #include <linux/platform_device.h>
46 #if defined(CONFIG_SOC_EXYNOS8895) || defined(CONFIG_SOC_EXYNOS9810) || \
47 defined(CONFIG_SOC_EXYNOS9820) || defined(CONFIG_SOC_EXYNOS9830) || \
48 defined(CONFIG_SOC_EXYNOS2100) || defined(CONFIG_SOC_EXYNOS1000)
49 #include <linux/exynos-pci-ctrl.h>
50 #endif /* CONFIG_SOC_EXYNOS8895 || CONFIG_SOC_EXYNOS9810 ||
51 * CONFIG_SOC_EXYNOS9820 || CONFIG_SOC_EXYNOS9830 ||
52 * CONFIG_SOC_EXYNOS2100 || CONFIG_SOC_EXYNOS1000
53 */
54
55 #if defined(CONFIG_64BIT)
56 #include <asm-generic/gpio.h>
57 #endif /* CONFIG_64BIT */
58
59 #ifdef BCMDHD_MODULAR
60 #if IS_ENABLED(CONFIG_SEC_SYSFS)
61 #include <linux/sec_sysfs.h>
62 #endif /* CONFIG_SEC_SYSFS */
63 #if IS_ENABLED(CONFIG_DRV_SAMSUNG)
64 #include <linux/sec_class.h>
65 #endif /* CONFIG_SEC_SYSFS */
66 #else
67 #if defined(CONFIG_SEC_SYSFS)
68 #include <linux/sec_sysfs.h>
69 #elif defined(CONFIG_DRV_SAMSUNG)
70 #include <linux/sec_class.h>
71 #endif /* CONFIG_SEC_SYSFS */
72 #endif /* BCMDHD_MODULAR */
73 #include <linux/wlan_plat.h>
74
75 #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE)
76 #define PINCTL_DELAY 150
77 #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */
78
79 #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM
80 extern void dhd_exit_wlan_mem(void);
81 extern int dhd_init_wlan_mem(void);
82 extern void *dhd_wlan_mem_prealloc(int section, unsigned long size);
83 #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */
84
85 #define WIFI_TURNON_DELAY 200
86 static int wlan_pwr_on = -1;
87
88 #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE
89 static int wlan_host_wake_irq = 0;
90 static unsigned int wlan_host_wake_up = -1;
91 #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */
92
93 #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE)
94 extern struct device *mmc_dev_for_wlan;
95 #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */
96
97 #ifdef CONFIG_BCMDHD_PCIE
98 extern int pcie_ch_num;
99 extern void exynos_pcie_pm_resume(int);
100 extern void exynos_pcie_pm_suspend(int);
101 #endif /* CONFIG_BCMDHD_PCIE */
102
103 #if defined(CONFIG_SOC_EXYNOS7870) || defined(CONFIG_SOC_EXYNOS9110)
104 extern struct mmc_host *wlan_mmc;
105 extern void mmc_ctrl_power(struct mmc_host *host, bool onoff);
106 #endif /* SOC_EXYNOS7870 || CONFIG_SOC_EXYNOS9110 */
107
108 static int
dhd_wlan_power(int onoff)109 dhd_wlan_power(int onoff)
110 {
111 #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE)
112 struct pinctrl *pinctrl = NULL;
113 #endif /* CONFIG_MACH_A7LTE || ONFIG_NOBLESSE */
114
115 printk(KERN_INFO"%s Enter: power %s\n", __FUNCTION__, onoff ? "on" : "off");
116
117 #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE)
118 if (onoff) {
119 pinctrl = devm_pinctrl_get_select(mmc_dev_for_wlan, "sdio_wifi_on");
120 if (IS_ERR(pinctrl))
121 printk(KERN_INFO "%s WLAN SDIO GPIO control error\n", __FUNCTION__);
122 msleep(PINCTL_DELAY);
123 }
124 #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */
125
126 if (gpio_direction_output(wlan_pwr_on, onoff)) {
127 printk(KERN_ERR "%s failed to control WLAN_REG_ON to %s\n",
128 __FUNCTION__, onoff ? "HIGH" : "LOW");
129 return -EIO;
130 }
131
132 #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE)
133 if (!onoff) {
134 pinctrl = devm_pinctrl_get_select(mmc_dev_for_wlan, "sdio_wifi_off");
135 if (IS_ERR(pinctrl))
136 printk(KERN_INFO "%s WLAN SDIO GPIO control error\n", __FUNCTION__);
137 }
138 #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */
139
140 #if defined(CONFIG_SOC_EXYNOS7870) || defined(CONFIG_SOC_EXYNOS9110)
141 if (wlan_mmc)
142 mmc_ctrl_power(wlan_mmc, onoff);
143 #endif /* SOC_EXYNOS7870 || CONFIG_SOC_EXYNOS9110 */
144 return 0;
145 }
146
147 static int
dhd_wlan_reset(int onoff)148 dhd_wlan_reset(int onoff)
149 {
150 return 0;
151 }
152
153 #ifndef CONFIG_BCMDHD_PCIE
154 extern void (*notify_func_callback)(void *dev_id, int state);
155 extern void *mmc_host_dev;
156 #endif /* !CONFIG_BCMDHD_PCIE */
157
158 static int
dhd_wlan_set_carddetect(int val)159 dhd_wlan_set_carddetect(int val)
160 {
161 #ifndef CONFIG_BCMDHD_PCIE
162 pr_err("%s: notify_func=%p, mmc_host_dev=%p, val=%d\n",
163 __FUNCTION__, notify_func_callback, mmc_host_dev, val);
164
165 if (notify_func_callback) {
166 notify_func_callback(mmc_host_dev, val);
167 } else {
168 pr_warning("%s: Nobody to notify\n", __FUNCTION__);
169 }
170 #else
171 if (val) {
172 exynos_pcie_pm_resume(pcie_ch_num);
173 } else {
174 exynos_pcie_pm_suspend(pcie_ch_num);
175 }
176 #endif /* CONFIG_BCMDHD_PCIE */
177
178 return 0;
179 }
180
181 int __init
dhd_wlan_init_gpio(void)182 dhd_wlan_init_gpio(void)
183 {
184 const char *wlan_node = "samsung,brcm-wlan";
185 struct device_node *root_node = NULL;
186 struct device *wlan_dev;
187
188 wlan_dev = sec_device_create(NULL, "wlan");
189
190 root_node = of_find_compatible_node(NULL, NULL, wlan_node);
191 if (!root_node) {
192 WARN(1, "failed to get device node of bcm4354\n");
193 return -ENODEV;
194 }
195
196 /* ========== WLAN_PWR_EN ============ */
197 wlan_pwr_on = of_get_gpio(root_node, 0);
198 if (!gpio_is_valid(wlan_pwr_on)) {
199 WARN(1, "Invalied gpio pin : %d\n", wlan_pwr_on);
200 return -ENODEV;
201 }
202
203 if (gpio_request(wlan_pwr_on, "WLAN_REG_ON")) {
204 WARN(1, "fail to request gpio(WLAN_REG_ON)\n");
205 return -ENODEV;
206 }
207 #ifdef CONFIG_BCMDHD_PCIE
208 gpio_direction_output(wlan_pwr_on, 1);
209 msleep(WIFI_TURNON_DELAY);
210 #else
211 gpio_direction_output(wlan_pwr_on, 0);
212 #endif /* CONFIG_BCMDHD_PCIE */
213 gpio_export(wlan_pwr_on, 1);
214 if (wlan_dev)
215 gpio_export_link(wlan_dev, "WLAN_REG_ON", wlan_pwr_on);
216
217 #ifdef CONFIG_BCMDHD_PCIE
218 exynos_pcie_pm_resume(pcie_ch_num);
219 #endif /* CONFIG_BCMDHD_PCIE */
220
221 #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE
222 /* ========== WLAN_HOST_WAKE ============ */
223 wlan_host_wake_up = of_get_gpio(root_node, 1);
224 if (!gpio_is_valid(wlan_host_wake_up)) {
225 WARN(1, "Invalied gpio pin : %d\n", wlan_host_wake_up);
226 return -ENODEV;
227 }
228
229 if (gpio_request(wlan_host_wake_up, "WLAN_HOST_WAKE")) {
230 WARN(1, "fail to request gpio(WLAN_HOST_WAKE)\n");
231 return -ENODEV;
232 }
233 gpio_direction_input(wlan_host_wake_up);
234 gpio_export(wlan_host_wake_up, 1);
235 if (wlan_dev)
236 gpio_export_link(wlan_dev, "WLAN_HOST_WAKE", wlan_host_wake_up);
237
238 wlan_host_wake_irq = gpio_to_irq(wlan_host_wake_up);
239 #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */
240
241 return 0;
242 }
243
244 #if defined(CONFIG_BCMDHD_OOB_HOST_WAKE) && defined(CONFIG_BCMDHD_GET_OOB_STATE)
245 int
dhd_get_wlan_oob_gpio(void)246 dhd_get_wlan_oob_gpio(void)
247 {
248 return gpio_is_valid(wlan_host_wake_up) ?
249 gpio_get_value(wlan_host_wake_up) : -1;
250 }
251 EXPORT_SYMBOL(dhd_get_wlan_oob_gpio);
252 #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE && CONFIG_BCMDHD_GET_OOB_STATE */
253
254 struct resource dhd_wlan_resources = {
255 .name = "bcmdhd_wlan_irq",
256 .start = 0,
257 .end = 0,
258 .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE |
259 #ifdef CONFIG_BCMDHD_PCIE
260 IORESOURCE_IRQ_HIGHEDGE,
261 #else
262 IORESOURCE_IRQ_HIGHLEVEL,
263 #endif /* CONFIG_BCMDHD_PCIE */
264 };
265 EXPORT_SYMBOL(dhd_wlan_resources);
266
267 struct wifi_platform_data dhd_wlan_control = {
268 .set_power = dhd_wlan_power,
269 .set_reset = dhd_wlan_reset,
270 .set_carddetect = dhd_wlan_set_carddetect,
271 #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM
272 .mem_prealloc = dhd_wlan_mem_prealloc,
273 #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */
274 };
275 EXPORT_SYMBOL(dhd_wlan_control);
276
277 int __init
dhd_wlan_init(void)278 dhd_wlan_init(void)
279 {
280 int ret;
281
282 printk(KERN_INFO "%s: START.......\n", __FUNCTION__);
283 ret = dhd_wlan_init_gpio();
284 if (ret < 0) {
285 printk(KERN_ERR "%s: failed to initiate GPIO, ret=%d\n",
286 __FUNCTION__, ret);
287 goto fail;
288 }
289
290 #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE
291 dhd_wlan_resources.start = wlan_host_wake_irq;
292 dhd_wlan_resources.end = wlan_host_wake_irq;
293 #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */
294
295 #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM
296 ret = dhd_init_wlan_mem();
297 if (ret < 0) {
298 printk(KERN_ERR "%s: failed to alloc reserved memory,"
299 " ret=%d\n", __FUNCTION__, ret);
300 }
301 #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */
302
303 fail:
304 return ret;
305 }
306
307 int
dhd_wlan_deinit(void)308 dhd_wlan_deinit(void)
309 {
310 #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE
311 gpio_free(wlan_host_wake_up);
312 #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */
313 gpio_free(wlan_pwr_on);
314
315 #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM
316 dhd_exit_wlan_mem();
317 #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */
318 return 0;
319 }
320
321 #ifndef BCMDHD_MODULAR
322 #if defined(CONFIG_MACH_UNIVERSAL7420) || defined(CONFIG_SOC_EXYNOS8890) || \
323 defined(CONFIG_SOC_EXYNOS8895) || defined(CONFIG_SOC_EXYNOS9810) || \
324 defined(CONFIG_SOC_EXYNOS9820) || defined(CONFIG_SOC_EXYNOS9830)
325 #if defined(CONFIG_DEFERRED_INITCALLS)
326 deferred_module_init(dhd_wlan_init);
327 #else
328 late_initcall(dhd_wlan_init);
329 #endif /* CONFIG_DEFERRED_INITCALLS */
330 #else
331 device_initcall(dhd_wlan_init);
332 #endif /* CONFIG Exynos PCIE Platforms */
333 #endif /* !BCMDHD_MODULAR */
334