| /* |
| * Platform Dependent file for Samsung Exynos |
| * |
| * Copyright (C) 2020, Broadcom. |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed to you |
| * under the terms of the GNU General Public License version 2 (the "GPL"), |
| * available at http://www.broadcom.com/licenses/GPLv2.php, with the |
| * following added to such license: |
| * |
| * As a special exception, the copyright holders of this software give you |
| * permission to link this software with independent modules, and to copy and |
| * distribute the resulting executable under terms of your choice, provided that |
| * you also meet, for each linked independent module, the terms and conditions of |
| * the license of that module. An independent module is a module which is not |
| * derived from this software. The special exception does not apply to any |
| * modifications of the software. |
| * |
| * |
| * <<Broadcom-WL-IPTag/Open:>> |
| * |
| * $Id$ |
| */ |
| #include <linux/device.h> |
| #include <linux/gpio.h> |
| #include <linux/of_gpio.h> |
| #include <linux/delay.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/slab.h> |
| #include <linux/workqueue.h> |
| #include <linux/poll.h> |
| #include <linux/miscdevice.h> |
| #include <linux/sched.h> |
| #include <linux/module.h> |
| #include <linux/fs.h> |
| #include <linux/list.h> |
| #include <linux/io.h> |
| #include <linux/workqueue.h> |
| #include <linux/unistd.h> |
| #include <linux/bug.h> |
| #include <linux/skbuff.h> |
| #include <linux/init.h> |
| #include <linux/platform_device.h> |
| #if defined(CONFIG_SOC_EXYNOS8895) || defined(CONFIG_SOC_EXYNOS9810) || \ |
| defined(CONFIG_SOC_EXYNOS9820) || defined(CONFIG_SOC_EXYNOS9830) || \ |
| defined(CONFIG_SOC_EXYNOS2100) || defined(CONFIG_SOC_EXYNOS1000) |
| #include <linux/exynos-pci-ctrl.h> |
| #endif /* CONFIG_SOC_EXYNOS8895 || CONFIG_SOC_EXYNOS9810 || |
| * CONFIG_SOC_EXYNOS9820 || CONFIG_SOC_EXYNOS9830 || |
| * CONFIG_SOC_EXYNOS2100 || CONFIG_SOC_EXYNOS1000 |
| */ |
| |
| #if defined(CONFIG_64BIT) |
| #include <asm-generic/gpio.h> |
| #endif /* CONFIG_64BIT */ |
| |
| #ifdef BCMDHD_MODULAR |
| #if IS_ENABLED(CONFIG_SEC_SYSFS) |
| #include <linux/sec_sysfs.h> |
| #endif /* CONFIG_SEC_SYSFS */ |
| #if IS_ENABLED(CONFIG_DRV_SAMSUNG) |
| #include <linux/sec_class.h> |
| #endif /* CONFIG_SEC_SYSFS */ |
| #else |
| #if defined(CONFIG_SEC_SYSFS) |
| #include <linux/sec_sysfs.h> |
| #elif defined(CONFIG_DRV_SAMSUNG) |
| #include <linux/sec_class.h> |
| #endif /* CONFIG_SEC_SYSFS */ |
| #endif /* BCMDHD_MODULAR */ |
| #include <linux/wlan_plat.h> |
| |
| #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE) |
| #define PINCTL_DELAY 150 |
| #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */ |
| |
| #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM |
| extern void dhd_exit_wlan_mem(void); |
| extern int dhd_init_wlan_mem(void); |
| extern void *dhd_wlan_mem_prealloc(int section, unsigned long size); |
| #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ |
| |
| #define WIFI_TURNON_DELAY 200 |
| static int wlan_pwr_on = -1; |
| |
| #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE |
| static int wlan_host_wake_irq = 0; |
| static unsigned int wlan_host_wake_up = -1; |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ |
| |
| #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE) |
| extern struct device *mmc_dev_for_wlan; |
| #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */ |
| |
| #ifdef CONFIG_BCMDHD_PCIE |
| extern int pcie_ch_num; |
| extern void exynos_pcie_pm_resume(int); |
| extern void exynos_pcie_pm_suspend(int); |
| #endif /* CONFIG_BCMDHD_PCIE */ |
| |
| #if defined(CONFIG_SOC_EXYNOS7870) || defined(CONFIG_SOC_EXYNOS9110) |
| extern struct mmc_host *wlan_mmc; |
| extern void mmc_ctrl_power(struct mmc_host *host, bool onoff); |
| #endif /* SOC_EXYNOS7870 || CONFIG_SOC_EXYNOS9110 */ |
| |
| static int |
| dhd_wlan_power(int onoff) |
| { |
| #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE) |
| struct pinctrl *pinctrl = NULL; |
| #endif /* CONFIG_MACH_A7LTE || ONFIG_NOBLESSE */ |
| |
| printk(KERN_INFO"%s Enter: power %s\n", __FUNCTION__, onoff ? "on" : "off"); |
| |
| #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE) |
| if (onoff) { |
| pinctrl = devm_pinctrl_get_select(mmc_dev_for_wlan, "sdio_wifi_on"); |
| if (IS_ERR(pinctrl)) |
| printk(KERN_INFO "%s WLAN SDIO GPIO control error\n", __FUNCTION__); |
| msleep(PINCTL_DELAY); |
| } |
| #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */ |
| |
| if (gpio_direction_output(wlan_pwr_on, onoff)) { |
| printk(KERN_ERR "%s failed to control WLAN_REG_ON to %s\n", |
| __FUNCTION__, onoff ? "HIGH" : "LOW"); |
| return -EIO; |
| } |
| |
| #if defined(CONFIG_MACH_A7LTE) || defined(CONFIG_NOBLESSE) |
| if (!onoff) { |
| pinctrl = devm_pinctrl_get_select(mmc_dev_for_wlan, "sdio_wifi_off"); |
| if (IS_ERR(pinctrl)) |
| printk(KERN_INFO "%s WLAN SDIO GPIO control error\n", __FUNCTION__); |
| } |
| #endif /* CONFIG_MACH_A7LTE || CONFIG_NOBLESSE */ |
| |
| #if defined(CONFIG_SOC_EXYNOS7870) || defined(CONFIG_SOC_EXYNOS9110) |
| if (wlan_mmc) |
| mmc_ctrl_power(wlan_mmc, onoff); |
| #endif /* SOC_EXYNOS7870 || CONFIG_SOC_EXYNOS9110 */ |
| return 0; |
| } |
| |
| static int |
| dhd_wlan_reset(int onoff) |
| { |
| return 0; |
| } |
| |
| #ifndef CONFIG_BCMDHD_PCIE |
| extern void (*notify_func_callback)(void *dev_id, int state); |
| extern void *mmc_host_dev; |
| #endif /* !CONFIG_BCMDHD_PCIE */ |
| |
| static int |
| dhd_wlan_set_carddetect(int val) |
| { |
| #ifndef CONFIG_BCMDHD_PCIE |
| pr_err("%s: notify_func=%p, mmc_host_dev=%p, val=%d\n", |
| __FUNCTION__, notify_func_callback, mmc_host_dev, val); |
| |
| if (notify_func_callback) { |
| notify_func_callback(mmc_host_dev, val); |
| } else { |
| pr_warning("%s: Nobody to notify\n", __FUNCTION__); |
| } |
| #else |
| if (val) { |
| exynos_pcie_pm_resume(pcie_ch_num); |
| } else { |
| exynos_pcie_pm_suspend(pcie_ch_num); |
| } |
| #endif /* CONFIG_BCMDHD_PCIE */ |
| |
| return 0; |
| } |
| |
| int __init |
| dhd_wlan_init_gpio(void) |
| { |
| const char *wlan_node = "samsung,brcm-wlan"; |
| struct device_node *root_node = NULL; |
| struct device *wlan_dev; |
| |
| wlan_dev = sec_device_create(NULL, "wlan"); |
| |
| root_node = of_find_compatible_node(NULL, NULL, wlan_node); |
| if (!root_node) { |
| WARN(1, "failed to get device node of bcm4354\n"); |
| return -ENODEV; |
| } |
| |
| /* ========== WLAN_PWR_EN ============ */ |
| wlan_pwr_on = of_get_gpio(root_node, 0); |
| if (!gpio_is_valid(wlan_pwr_on)) { |
| WARN(1, "Invalied gpio pin : %d\n", wlan_pwr_on); |
| return -ENODEV; |
| } |
| |
| if (gpio_request(wlan_pwr_on, "WLAN_REG_ON")) { |
| WARN(1, "fail to request gpio(WLAN_REG_ON)\n"); |
| return -ENODEV; |
| } |
| #ifdef CONFIG_BCMDHD_PCIE |
| gpio_direction_output(wlan_pwr_on, 1); |
| msleep(WIFI_TURNON_DELAY); |
| #else |
| gpio_direction_output(wlan_pwr_on, 0); |
| #endif /* CONFIG_BCMDHD_PCIE */ |
| gpio_export(wlan_pwr_on, 1); |
| if (wlan_dev) |
| gpio_export_link(wlan_dev, "WLAN_REG_ON", wlan_pwr_on); |
| |
| #ifdef CONFIG_BCMDHD_PCIE |
| exynos_pcie_pm_resume(pcie_ch_num); |
| #endif /* CONFIG_BCMDHD_PCIE */ |
| |
| #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE |
| /* ========== WLAN_HOST_WAKE ============ */ |
| wlan_host_wake_up = of_get_gpio(root_node, 1); |
| if (!gpio_is_valid(wlan_host_wake_up)) { |
| WARN(1, "Invalied gpio pin : %d\n", wlan_host_wake_up); |
| return -ENODEV; |
| } |
| |
| if (gpio_request(wlan_host_wake_up, "WLAN_HOST_WAKE")) { |
| WARN(1, "fail to request gpio(WLAN_HOST_WAKE)\n"); |
| return -ENODEV; |
| } |
| gpio_direction_input(wlan_host_wake_up); |
| gpio_export(wlan_host_wake_up, 1); |
| if (wlan_dev) |
| gpio_export_link(wlan_dev, "WLAN_HOST_WAKE", wlan_host_wake_up); |
| |
| wlan_host_wake_irq = gpio_to_irq(wlan_host_wake_up); |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_BCMDHD_OOB_HOST_WAKE) && defined(CONFIG_BCMDHD_GET_OOB_STATE) |
| int |
| dhd_get_wlan_oob_gpio(void) |
| { |
| return gpio_is_valid(wlan_host_wake_up) ? |
| gpio_get_value(wlan_host_wake_up) : -1; |
| } |
| EXPORT_SYMBOL(dhd_get_wlan_oob_gpio); |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE && CONFIG_BCMDHD_GET_OOB_STATE */ |
| |
| struct resource dhd_wlan_resources = { |
| .name = "bcmdhd_wlan_irq", |
| .start = 0, |
| .end = 0, |
| .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE | |
| #ifdef CONFIG_BCMDHD_PCIE |
| IORESOURCE_IRQ_HIGHEDGE, |
| #else |
| IORESOURCE_IRQ_HIGHLEVEL, |
| #endif /* CONFIG_BCMDHD_PCIE */ |
| }; |
| EXPORT_SYMBOL(dhd_wlan_resources); |
| |
| struct wifi_platform_data dhd_wlan_control = { |
| .set_power = dhd_wlan_power, |
| .set_reset = dhd_wlan_reset, |
| .set_carddetect = dhd_wlan_set_carddetect, |
| #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM |
| .mem_prealloc = dhd_wlan_mem_prealloc, |
| #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ |
| }; |
| EXPORT_SYMBOL(dhd_wlan_control); |
| |
| int __init |
| dhd_wlan_init(void) |
| { |
| int ret; |
| |
| printk(KERN_INFO "%s: START.......\n", __FUNCTION__); |
| ret = dhd_wlan_init_gpio(); |
| if (ret < 0) { |
| printk(KERN_ERR "%s: failed to initiate GPIO, ret=%d\n", |
| __FUNCTION__, ret); |
| goto fail; |
| } |
| |
| #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE |
| dhd_wlan_resources.start = wlan_host_wake_irq; |
| dhd_wlan_resources.end = wlan_host_wake_irq; |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ |
| |
| #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM |
| ret = dhd_init_wlan_mem(); |
| if (ret < 0) { |
| printk(KERN_ERR "%s: failed to alloc reserved memory," |
| " ret=%d\n", __FUNCTION__, ret); |
| } |
| #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ |
| |
| fail: |
| return ret; |
| } |
| |
| int |
| dhd_wlan_deinit(void) |
| { |
| #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE |
| gpio_free(wlan_host_wake_up); |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ |
| gpio_free(wlan_pwr_on); |
| |
| #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM |
| dhd_exit_wlan_mem(); |
| #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ |
| return 0; |
| } |
| |
| #ifndef BCMDHD_MODULAR |
| #if defined(CONFIG_MACH_UNIVERSAL7420) || defined(CONFIG_SOC_EXYNOS8890) || \ |
| defined(CONFIG_SOC_EXYNOS8895) || defined(CONFIG_SOC_EXYNOS9810) || \ |
| defined(CONFIG_SOC_EXYNOS9820) || defined(CONFIG_SOC_EXYNOS9830) |
| #if defined(CONFIG_DEFERRED_INITCALLS) |
| deferred_module_init(dhd_wlan_init); |
| #else |
| late_initcall(dhd_wlan_init); |
| #endif /* CONFIG_DEFERRED_INITCALLS */ |
| #else |
| device_initcall(dhd_wlan_init); |
| #endif /* CONFIG Exynos PCIE Platforms */ |
| #endif /* !BCMDHD_MODULAR */ |