blob: 01e1f5482f9023ebb74ab5c623e9fa472f1d492e [file] [log] [blame]
/*
* 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 */