/* * Platform Dependent file for Qualcomm MSM/APQ * * Copyright (C) 2021, 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. * * * <> * * $Id$ * */ #include #include #include #include #include #include #include #include #ifdef CONFIG_BCMDHD_PCIE #include #endif /* CONFIG_BCMDHD_PCIE */ #include #include #include #include #if IS_ENABLED(CONFIG_PCI_MSM) #include #else #include #endif /* CONFIG_PCI_MSM */ #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 MSM_PCIE_VENDOR_ID 0x17cb #if defined(CONFIG_ARCH_APQ8084) #define MSM_PCIE_DEVICE_ID 0x0101 #elif defined(CONFIG_ARCH_MSM8994) #define MSM_PCIE_DEVICE_ID 0x0300 #elif defined(CONFIG_ARCH_MSM8996) #define MSM_PCIE_DEVICE_ID 0x0104 #elif defined(CONFIG_ARCH_MSM8998) #define MSM_PCIE_DEVICE_ID 0x0105 #elif defined(CONFIG_ARCH_SDM845) || defined(CONFIG_ARCH_SM8150) || \ defined(CONFIG_ARCH_KONA) || defined(CONFIG_ARCH_LAHAINA) || \ defined(CONFIG_ARCH_WAIPIO) #define MSM_PCIE_DEVICE_ID 0x0106 #else #error "Not supported platform" #endif #define WIFI_TURNON_DELAY 200 static int wlan_reg_on = -1; #define DHD_DT_COMPAT_ENTRY "android,bcmdhd_wlan" #ifdef CUSTOMER_HW2 #define WIFI_WL_REG_ON_PROPNAME "wl_reg_on" #else #define WIFI_WL_REG_ON_PROPNAME "wlan-en-gpio" #endif /* CUSTOMER_HW2 */ #if defined(CONFIG_ARCH_MSM8996) || defined(CONFIG_ARCH_MSM8998) || \ defined(CONFIG_ARCH_SDM845) || defined(CONFIG_ARCH_SM8150) || defined(CONFIG_ARCH_KONA) \ || defined(CONFIG_ARCH_LAHAINA) || defined(CONFIG_ARCH_WAIPIO) #define MSM_PCIE_CH_NUM 0 #else #define MSM_PCIE_CH_NUM 1 #endif /* MSM PCIE Platforms */ #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE static int wlan_host_wake_up = -1; static int wlan_host_wake_irq = 0; #ifdef CUSTOMER_HW2 #define WIFI_WLAN_HOST_WAKE_PROPNAME "wl_host_wake" #else #define WIFI_WLAN_HOST_WAKE_PROPNAME "wlan-host-wake-gpio" #endif /* CUSTOMER_HW2 */ #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ #include typedef struct dhd_plat_info { struct msm_pcie_register_event pcie_event; struct msm_pcie_notify pcie_notify; struct pci_dev *pdev; } dhd_plat_info_t; static dhd_pcie_event_cb_t g_pfn = NULL; uint32 dhd_plat_get_info_size(void) { return sizeof(dhd_plat_info_t); } void plat_pcie_notify_cb(struct msm_pcie_notify *pcie_notify) { struct pci_dev *pdev; if (pcie_notify == NULL) { pr_err("%s(): Invalid argument to Platform layer call back \r\n", __func__); return; } if (g_pfn) { pdev = (struct pci_dev *)pcie_notify->user; pr_err("%s(): Invoking DHD call back with pdev %p \r\n", __func__, pdev); (*(g_pfn))(pdev); } else { pr_err("%s(): Driver Call back pointer is NULL \r\n", __func__); } return; } int dhd_plat_pcie_register_event(void *plat_info, struct pci_dev *pdev, dhd_pcie_event_cb_t pfn) { dhd_plat_info_t *p = plat_info; if ((p == NULL) || (pdev == NULL) || (pfn == NULL)) { pr_err("%s(): Invalid argument p %p, pdev %p, pfn %p\r\n", __func__, p, pdev, pfn); return -1; } g_pfn = pfn; p->pdev = pdev; p->pcie_event.events = MSM_PCIE_EVENT_LINKDOWN; p->pcie_event.user = pdev; p->pcie_event.mode = MSM_PCIE_TRIGGER_CALLBACK; p->pcie_event.callback = plat_pcie_notify_cb; p->pcie_event.options = MSM_PCIE_CONFIG_NO_RECOVERY; msm_pcie_register_event(&p->pcie_event); pr_err("%s(): Registered Event PCIe event pdev %p \r\n", __func__, pdev); return 0; } void dhd_plat_pcie_deregister_event(void *plat_info) { dhd_plat_info_t *p = plat_info; if (p) { msm_pcie_deregister_event(&p->pcie_event); } return; } int dhd_wifi_init_gpio(void) { char *wlan_node = DHD_DT_COMPAT_ENTRY; struct device_node *root_node = NULL; root_node = of_find_compatible_node(NULL, NULL, wlan_node); if (!root_node) { WARN(1, "failed to get device node of BRCM WLAN\n"); return -ENODEV; } /* ========== WLAN_PWR_EN ============ */ wlan_reg_on = of_get_named_gpio(root_node, WIFI_WL_REG_ON_PROPNAME, 0); printk(KERN_INFO "%s: gpio_wlan_power : %d\n", __FUNCTION__, wlan_reg_on); if (gpio_request_one(wlan_reg_on, GPIOF_OUT_INIT_LOW, "WL_REG_ON")) { printk(KERN_ERR "%s: Faiiled to request gpio %d for WL_REG_ON\n", __FUNCTION__, wlan_reg_on); } else { printk(KERN_ERR "%s: gpio_request WL_REG_ON done - WLAN_EN: GPIO %d\n", __FUNCTION__, wlan_reg_on); } if (gpio_direction_output(wlan_reg_on, 1)) { printk(KERN_ERR "%s: WL_REG_ON failed to pull up\n", __FUNCTION__); } else { printk(KERN_ERR "%s: WL_REG_ON is pulled up\n", __FUNCTION__); } if (gpio_get_value(wlan_reg_on)) { printk(KERN_INFO "%s: Initial WL_REG_ON: [%d]\n", __FUNCTION__, gpio_get_value(wlan_reg_on)); } /* Wait for WIFI_TURNON_DELAY due to power stability */ msleep(WIFI_TURNON_DELAY); #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE /* ========== WLAN_HOST_WAKE ============ */ wlan_host_wake_up = of_get_named_gpio(root_node, WIFI_WLAN_HOST_WAKE_PROPNAME, 0); printk(KERN_INFO "%s: gpio_wlan_host_wake : %d\n", __FUNCTION__, wlan_host_wake_up); #ifndef CUSTOMER_HW2 if (gpio_request_one(wlan_host_wake_up, GPIOF_IN, "WLAN_HOST_WAKE")) { printk(KERN_ERR "%s: Faiiled to request gpio %d for WLAN_HOST_WAKE\n", __FUNCTION__, wlan_host_wake_up); return -ENODEV; } else { printk(KERN_ERR "%s: gpio_request WLAN_HOST_WAKE done" " - WLAN_HOST_WAKE: GPIO %d\n", __FUNCTION__, wlan_host_wake_up); } #endif /* !CUSTOMER_HW2 */ gpio_direction_input(wlan_host_wake_up); wlan_host_wake_irq = gpio_to_irq(wlan_host_wake_up); #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ #ifdef CONFIG_BCMDHD_PCIE printk(KERN_INFO "%s: Call msm_pcie_enumerate\n", __FUNCTION__); msm_pcie_enumerate(MSM_PCIE_CH_NUM); #endif /* CONFIG_BCMDHD_PCIE */ return 0; } int dhd_wlan_power(int onoff) { printk(KERN_INFO"%s Enter: power %s\n", __func__, onoff ? "on" : "off"); if (onoff) { if (gpio_direction_output(wlan_reg_on, 1)) { printk(KERN_ERR "%s: WL_REG_ON is failed to pull up\n", __FUNCTION__); return -EIO; } if (gpio_get_value(wlan_reg_on)) { printk(KERN_INFO"WL_REG_ON on-step-2 : [%d]\n", gpio_get_value(wlan_reg_on)); } else { printk("[%s] gpio value is 0. We need reinit.\n", __func__); if (gpio_direction_output(wlan_reg_on, 1)) { printk(KERN_ERR "%s: WL_REG_ON is " "failed to pull up\n", __func__); } } } else { if (gpio_direction_output(wlan_reg_on, 0)) { printk(KERN_ERR "%s: WL_REG_ON is failed to pull up\n", __FUNCTION__); return -EIO; } if (gpio_get_value(wlan_reg_on)) { printk(KERN_INFO"WL_REG_ON on-step-2 : [%d]\n", gpio_get_value(wlan_reg_on)); } } return 0; } EXPORT_SYMBOL(dhd_wlan_power); static int dhd_wlan_reset(int onoff) { return 0; } static int dhd_wlan_set_carddetect(int val) { #ifdef CONFIG_BCMDHD_PCIE printk(KERN_INFO "%s: Call msm_pcie_enumerate\n", __FUNCTION__); msm_pcie_enumerate(MSM_PCIE_CH_NUM); #endif /* CONFIG_BCMDHD_PCIE */ 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); int dhd_get_wlan_oob_gpio_number(void) { return gpio_is_valid(wlan_host_wake_up) ? wlan_host_wake_up : -1; } EXPORT_SYMBOL(dhd_get_wlan_oob_gpio_number); #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE && CONFIG_BCMDHD_GET_OOB_STATE */ struct resource dhd_wlan_resources = { .name = "bcmdhd_wlan_irq", .start = 0, /* Dummy */ .end = 0, /* Dummy */ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE | IORESOURCE_IRQ_HIGHLEVEL, }; 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 dhd_wlan_init(void) { int ret; printk(KERN_INFO"%s: START.......\n", __FUNCTION__); ret = dhd_wifi_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: printk(KERN_INFO"%s: FINISH.......\n", __FUNCTION__); 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_reg_on); #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM dhd_exit_wlan_mem(); #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ return 0; } uint32 dhd_plat_get_rc_vendor_id(void) { return MSM_PCIE_VENDOR_ID; } uint32 dhd_plat_get_rc_device_id(void) { return MSM_PCIE_DEVICE_ID; } #ifndef BCMDHD_MODULAR #if defined(CONFIG_ARCH_MSM8996) || defined(CONFIG_ARCH_MSM8998) || \ defined(CONFIG_ARCH_SDM845) || defined(CONFIG_ARCH_SM8150) || defined(CONFIG_ARCH_KONA) \ || defined(CONFIG_ARCH_LAHAINA) || defined(CONFIG_ARCH_WAIPIO) #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 /* MSM PCIE Platforms */ #endif /* !BCMDHD_MODULAR */