| /* |
| * Platform Dependent file |
| * |
| * Copyright (C) 1999-2018, 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. |
| * |
| * Notwithstanding the above, under no circumstances may you combine this |
| * software in any way with any other Broadcom software provided under a license |
| * other than the GPL, without Broadcom's express prior written consent. |
| * |
| * <<Broadcom-WL-IPTag/Open:>> |
| * |
| * $Id$ |
| * |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/platform_device.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/gpio.h> |
| #include <linux/skbuff.h> |
| #if defined(CONFIG_WIFI_CONTROL_FUNC) |
| #include <linux/wlan_plat.h> |
| #else |
| #include <dhd_linux.h> |
| #endif |
| #include <linux/fcntl.h> |
| #include <linux/fs.h> |
| #include <linux/of_gpio.h> |
| #include <dhd_dbg.h> |
| #include <dhd.h> |
| |
| #ifdef DHD_COREDUMP |
| #include <linux/platform_data/sscoredump.h> |
| #endif /* DHD_COREDUMP */ |
| |
| #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM |
| extern int dhd_init_wlan_mem_43752(void); |
| extern void *dhd_wlan_mem_prealloc_43752(int section, unsigned long size); |
| #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ |
| |
| #define WLAN_REG_ON_GPIO 491 |
| #define WLAN_HOST_WAKE_GPIO 493 |
| |
| static int wlan_reg_on = -1; |
| #define DHD_DT_COMPAT_ENTRY "android,bcmdhd_wlan" |
| #define WIFI_WL_REG_ON_PROPNAME "wl_reg_on" |
| |
| static int wlan_host_wake_up = -1; |
| #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE |
| static int wlan_host_wake_irq = 0; |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ |
| #define WIFI_WLAN_HOST_WAKE_PROPNAME "wl_host_wake" |
| |
| #if defined(CONFIG_SOC_EXYNOS9810) || defined(CONFIG_SOC_EXYNOS9820) || \ |
| defined(CONFIG_SOC_GS101) |
| #define EXYNOS_PCIE_RC_ONOFF |
| int pcie_ch_num = -1; |
| extern void exynos_pcie_pm_resume(int); |
| extern void exynos_pcie_pm_suspend(int); |
| #endif /* CONFIG_SOC_EXYNOS9810 || CONFIG_SOC_EXYNOS9820 || CONFIG_SOC_GS101 */ |
| |
| #ifdef DHD_COREDUMP |
| #define DEVICE_NAME "wlan_43752" |
| |
| static void sscd_release(struct device *dev); |
| static struct sscd_platform_data sscd_pdata; |
| static struct platform_device sscd_dev = { |
| .name = DEVICE_NAME, |
| .driver_override = SSCD_NAME, |
| .id = -1, |
| .dev = { |
| .platform_data = &sscd_pdata, |
| .release = sscd_release, |
| }, |
| }; |
| |
| static void sscd_release(struct device *dev) |
| { |
| DHD_INFO(("%s: enter\n", __FUNCTION__)); |
| } |
| |
| /* trigger coredump */ |
| static int |
| dhd_set_coredump(const char *buf, int buf_len, const char *info) |
| { |
| struct sscd_platform_data *pdata = dev_get_platdata(&sscd_dev.dev); |
| struct sscd_segment seg; |
| |
| if (pdata->sscd_report) { |
| memset(&seg, 0, sizeof(seg)); |
| seg.addr = (void *) buf; |
| seg.size = buf_len; |
| pdata->sscd_report(&sscd_dev, &seg, 1, 0, info); |
| } |
| return 0; |
| } |
| #endif /* DHD_COREDUMP */ |
| |
| #ifdef GET_CUSTOM_MAC_ENABLE |
| |
| #define CDB_PATH "/chosen/config" |
| #define WIFI_MAC "wlan_mac1" |
| static u8 wlan_mac[6] = {0}; |
| |
| static int |
| dhd_wlan_get_mac_addr(unsigned char *buf) |
| { |
| if (memcmp(wlan_mac, "\0\0\0\0\0\0", 6)) { |
| memcpy(buf, wlan_mac, sizeof(wlan_mac)); |
| return 0; |
| } |
| return -EIO; |
| } |
| |
| int |
| dhd_wlan_init_mac_addr(void) |
| { |
| u8 mac[6] = {0}; |
| unsigned int size; |
| unsigned char *mac_addr = NULL; |
| struct device_node *node; |
| unsigned int mac_found = 0; |
| |
| node = of_find_node_by_path(CDB_PATH); |
| if (!node) { |
| DHD_ERROR(("CDB Node not created under %s\n", CDB_PATH)); |
| return -ENODEV; |
| } else { |
| mac_addr = (unsigned char *) |
| of_get_property(node, WIFI_MAC, &size); |
| } |
| |
| /* In case Missing Provisioned MAC Address, exit with error */ |
| if (!mac_addr) { |
| DHD_ERROR(("Missing Provisioned MAC address\n")); |
| return -EINVAL; |
| } |
| |
| /* Start decoding MAC Address |
| * Note that 2 formats are supported for now |
| * AA:BB:CC:DD:EE:FF (with separating colons) and |
| * AABBCCDDEEFF (without separating colons) */ |
| if (sscanf(mac_addr, |
| "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", |
| &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], |
| &mac[5]) == 6) { |
| mac_found = 1; |
| } else if (sscanf(mac_addr, |
| "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", |
| &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], |
| &mac[5]) == 6) { |
| mac_found = 1; |
| } |
| |
| /* Make sure Address decoding succeeds */ |
| if (!mac_found) { |
| DHD_ERROR(("Invalid format for Provisioned MAC Address\n")); |
| return -EINVAL; |
| } |
| |
| /* Make sure Provisioned MAC Address is globally Administered */ |
| if (mac[0] & 2) { |
| DHD_ERROR(("Invalid Provisioned MAC Address\n")); |
| return -EINVAL; |
| } |
| |
| memcpy(wlan_mac, mac, sizeof(mac)); |
| return 0; |
| } |
| #endif /* GET_CUSTOM_MAC_ENABLE */ |
| |
| int |
| dhd_wifi_init_gpio(void) |
| { |
| int gpio_reg_on_val; |
| /* ========== WLAN_PWR_EN ============ */ |
| 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) { |
| DHD_ERROR(("failed to get device node of BRCM WLAN\n")); |
| return -ENODEV; |
| } |
| |
| wlan_reg_on = of_get_named_gpio(root_node, WIFI_WL_REG_ON_PROPNAME, 0); |
| if (!gpio_is_valid(wlan_reg_on)) { |
| DHD_ERROR(("Invalid gpio pin : %d\n", wlan_reg_on)); |
| return -ENODEV; |
| } |
| |
| /* ========== WLAN_PWR_EN ============ */ |
| DHD_INFO(("%s: gpio_wlan_power : %d\n", __FUNCTION__, wlan_reg_on)); |
| |
| #ifdef EXYNOS_PCIE_RC_ONOFF |
| if (of_property_read_u32(root_node, "ch-num", &pcie_ch_num)) { |
| DHD_INFO(("%s: Failed to parse the channel number\n", __FUNCTION__)); |
| return -EINVAL; |
| } |
| /* ========== WLAN_PCIE_NUM ============ */ |
| DHD_INFO(("%s: pcie_ch_num : %d\n", __FUNCTION__, pcie_ch_num)); |
| #endif /* EXYNOS_PCIE_RC_ONOFF */ |
| |
| /* |
| * For reg_on, gpio_request will fail if the gpio is configured to output-high |
| * in the dts using gpio-hog, so do not return error for failure. |
| */ |
| if (gpio_request_one(wlan_reg_on, GPIOF_OUT_INIT_HIGH, "WL_REG_ON")) { |
| DHD_ERROR(("%s: Failed to request gpio %d for WL_REG_ON, " |
| "might have configured in the dts\n", |
| __FUNCTION__, wlan_reg_on)); |
| } else { |
| DHD_ERROR(("%s: gpio_request WL_REG_ON done - WLAN_EN: GPIO %d\n", |
| __FUNCTION__, wlan_reg_on)); |
| } |
| |
| gpio_reg_on_val = gpio_get_value(wlan_reg_on); |
| DHD_INFO(("%s: Initial WL_REG_ON: [%d]\n", |
| __FUNCTION__, gpio_get_value(wlan_reg_on))); |
| |
| if (gpio_reg_on_val == 0) { |
| DHD_INFO(("%s: WL_REG_ON is LOW, drive it HIGH\n", __FUNCTION__)); |
| if (gpio_direction_output(wlan_reg_on, 1)) { |
| DHD_ERROR(("%s: WL_REG_ON is failed to pull up\n", __FUNCTION__)); |
| return -EIO; |
| } |
| } |
| |
| DHD_ERROR(("%s: WL_REG_ON is pulled up\n", __FUNCTION__)); |
| |
| /* Wait for WIFI_TURNON_DELAY due to power stability */ |
| msleep(WIFI_TURNON_DELAY); |
| #ifdef EXYNOS_PCIE_RC_ONOFF |
| exynos_pcie_pm_resume(pcie_ch_num); |
| #endif /* EXYNOS_PCIE_RC_ONOFF */ |
| #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); |
| DHD_INFO(("%s: gpio_wlan_host_wake : %d\n", __FUNCTION__, wlan_host_wake_up)); |
| |
| if (gpio_request_one(wlan_host_wake_up, GPIOF_IN, "WLAN_HOST_WAKE")) { |
| DHD_ERROR(("%s: Failed to request gpio %d for WLAN_HOST_WAKE\n", |
| __FUNCTION__, wlan_host_wake_up)); |
| return -ENODEV; |
| } else { |
| DHD_ERROR(("%s: gpio_request WLAN_HOST_WAKE done" |
| " - WLAN_HOST_WAKE: GPIO %d\n", |
| __FUNCTION__, wlan_host_wake_up)); |
| } |
| |
| if (gpio_direction_input(wlan_host_wake_up)) { |
| DHD_ERROR(("%s: Failed to set WL_HOST_WAKE gpio direction\n", __FUNCTION__)); |
| } |
| |
| wlan_host_wake_irq = gpio_to_irq(wlan_host_wake_up); |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ |
| return 0; |
| } |
| |
| int |
| dhd_wlan_power_43752(int onoff) |
| { |
| DHD_INFO(("------------------------------------------------\n")); |
| DHD_INFO(("------------------------------------------------\n")); |
| DHD_INFO(("%s Enter: power %s\n", __func__, onoff ? "on" : "off")); |
| |
| if (onoff) { |
| if (gpio_direction_output(wlan_reg_on, 1)) { |
| DHD_ERROR(("%s: WL_REG_ON is failed to pull up\n", __FUNCTION__)); |
| return -EIO; |
| } |
| if (gpio_get_value(wlan_reg_on)) { |
| DHD_INFO(("WL_REG_ON on-step-2 : [%d]\n", |
| gpio_get_value(wlan_reg_on))); |
| } else { |
| DHD_ERROR(("[%s] gpio value is 0. We need reinit.\n", __func__)); |
| if (gpio_direction_output(wlan_reg_on, 1)) { |
| DHD_ERROR(("%s: WL_REG_ON is " |
| "failed to pull up\n", __func__)); |
| } |
| } |
| #ifdef EXYNOS_PCIE_RC_ONOFF |
| exynos_pcie_pm_resume(pcie_ch_num); |
| #endif /* EXYNOS_PCIE_RC_ONOFF */ |
| } else { |
| #ifdef EXYNOS_PCIE_RC_ONOFF |
| exynos_pcie_pm_suspend(pcie_ch_num); |
| #endif /* EXYNOS_PCIE_RC_ONOFF */ |
| if (gpio_direction_output(wlan_reg_on, 0)) { |
| DHD_ERROR(("%s: WL_REG_ON is failed to pull up\n", __FUNCTION__)); |
| return -EIO; |
| } |
| if (gpio_get_value(wlan_reg_on)) { |
| DHD_INFO(("WL_REG_ON on-step-2 : [%d]\n", |
| gpio_get_value(wlan_reg_on))); |
| } |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL(dhd_wlan_power_43752); |
| |
| static int |
| dhd_wlan_reset(int onoff) |
| { |
| return 0; |
| } |
| |
| static int |
| dhd_wlan_set_carddetect(int val) |
| { |
| return 0; |
| } |
| |
| #ifdef BCMSDIO |
| static int dhd_wlan_get_wake_irq(void) |
| { |
| return gpio_to_irq(wlan_host_wake_up); |
| } |
| #endif /* BCMSDIO */ |
| |
| #if defined(CONFIG_BCMDHD_OOB_HOST_WAKE) && defined(CONFIG_BCMDHD_GET_OOB_STATE) |
| int |
| dhd_get_wlan_oob_gpio_43752(void) |
| { |
| return gpio_is_valid(wlan_host_wake_up) ? |
| gpio_get_value(wlan_host_wake_up) : -1; |
| } |
| EXPORT_SYMBOL(dhd_get_wlan_oob_gpio_43752); |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE && CONFIG_BCMDHD_GET_OOB_STATE */ |
| |
| struct resource dhd_wlan_resources_43752 = { |
| .name = "bcmdhd_wlan_irq", |
| .start = 0, /* Dummy */ |
| .end = 0, /* Dummy */ |
| .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE | |
| IORESOURCE_IRQ_HIGHEDGE, |
| }; |
| EXPORT_SYMBOL(dhd_wlan_resources_43752); |
| |
| struct wifi_platform_data dhd_wlan_control_43752 = { |
| .set_power = dhd_wlan_power_43752, |
| .set_reset = dhd_wlan_reset, |
| .set_carddetect = dhd_wlan_set_carddetect, |
| #ifdef DHD_COREDUMP |
| .set_coredump = dhd_set_coredump, |
| #endif /* DHD_COREDUMP */ |
| #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM |
| .mem_prealloc = dhd_wlan_mem_prealloc_43752, |
| #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ |
| #ifdef GET_CUSTOM_MAC_ENABLE |
| .get_mac_addr = dhd_wlan_get_mac_addr, |
| #endif /* GET_CUSTOM_MAC_ENABLE */ |
| #ifdef BCMSDIO |
| .get_wake_irq = dhd_wlan_get_wake_irq, |
| #endif // endif |
| }; |
| EXPORT_SYMBOL(dhd_wlan_control_43752); |
| |
| int |
| dhd_wlan_init(void) |
| { |
| int ret; |
| |
| DHD_INFO(("%s: START.......\n", __FUNCTION__)); |
| |
| #ifdef DHD_COREDUMP |
| platform_device_register(&sscd_dev); |
| #endif /* DHD_COREDUMP */ |
| |
| ret = dhd_wifi_init_gpio(); |
| if (ret < 0) { |
| DHD_ERROR(("%s: failed to initiate GPIO, ret=%d\n", |
| __FUNCTION__, ret)); |
| goto fail; |
| } |
| #ifdef CONFIG_BCMDHD_OOB_HOST_WAKE |
| dhd_wlan_resources_43752.start = wlan_host_wake_irq; |
| dhd_wlan_resources_43752.end = wlan_host_wake_irq; |
| #endif /* CONFIG_BCMDHD_OOB_HOST_WAKE */ |
| |
| #ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM |
| ret = dhd_init_wlan_mem_43752(); |
| if (ret < 0) { |
| DHD_ERROR(("%s: failed to alloc reserved memory," |
| " ret=%d\n", __FUNCTION__, ret)); |
| } |
| #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */ |
| |
| #ifdef GET_CUSTOM_MAC_ENABLE |
| dhd_wlan_init_mac_addr(); |
| #endif /* GET_CUSTOM_MAC_ENABLE */ |
| |
| fail: |
| DHD_ERROR(("%s: FINISH.......\n", __FUNCTION__)); |
| return ret; |
| } |
| |
| int |
| dhd_wlan_deinit(void) |
| { |
| if (gpio_is_valid(wlan_host_wake_up)) { |
| gpio_free(wlan_host_wake_up); |
| } |
| if (gpio_is_valid(wlan_reg_on)) { |
| gpio_free(wlan_reg_on); |
| } |
| |
| #ifdef DHD_COREDUMP |
| platform_device_unregister(&sscd_dev); |
| #endif /* DHD_COREDUMP */ |
| |
| return 0; |
| } |
| #ifndef BCMDHD_MODULAR |
| /* Required only for Built-in DHD */ |
| device_initcall(dhd_wlan_init); |
| #endif /* BCMDHD_MODULAR */ |