blob: af02f143e1c9941330364dceebe20dced94af4f8 [file] [log] [blame]
/*
* 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.
*
*
* <<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>
#include <linux/mmc/host.h>
#ifdef BCMPCIE
#include <linux/msm_pcie.h>
#endif /* BCMPCIE */
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/of_gpio.h>
#include <dhd_plat.h>
#if IS_ENABLED(CONFIG_PCI_MSM)
#include <linux/msm_pcie.h>
#else
#include <mach/msm_pcie.h>
#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 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"
#elif defined(CONFIG_ARCH_SDM660)
#define WIFI_WL_REG_ON_PROPNAME "qcom,wlreg_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)
#define MSM_PCIE_CH_NUM 0
#else
#define MSM_PCIE_CH_NUM 1
#endif /* MSM PCIE Platforms */
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"
#elif defined(CONFIG_ARCH_SDM660)
#define WIFI_WLAN_HOST_WAKE_PROPNAME "gpios"
#else
#define WIFI_WLAN_HOST_WAKE_PROPNAME "wlan-host-wake-gpio"
#endif /* CUSTOMER_HW2 */
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;
char* dhd_get_device_dt_name(void)
{
return DHD_DT_COMPAT_ENTRY;
}
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;
}
// dump the DTS to confirm
{
struct property * pp = root_node->properties;
while (NULL != pp) {
char str[128] = {0};
memset(str, 0, sizeof(str));
memcpy(str, pp->value, pp->length);
printk("%s: name='%s', len=%d, val='%s'\n", __FUNCTION__, pp->name, pp->length, str);
pp = pp->next;
}
}
/*
// extra test
{
const char * name = NULL;
name="pinctrl-0";
printk(KERN_INFO "%s: gpio_wlan_power('%s') : %d\n", __FUNCTION__, name, of_get_named_gpio(root_node, name, 0));
name="pinctrl-1";
printk(KERN_INFO "%s: gpio_wlan_power('%s') : %d\n", __FUNCTION__, name, of_get_named_gpio(root_node, name, 0));
name="gpios";
printk(KERN_INFO "%s: gpio_wlan_power('%s') : %d\n", __FUNCTION__, name, of_get_named_gpio(root_node, name, 0));
}
*/
/* ========== 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('%s'): %d\n", __FUNCTION__, WIFI_WL_REG_ON_PROPNAME, wlan_reg_on);
// keep the device power untouched, and power on when detect with enumerate action
if (gpio_request(wlan_reg_on, "WL_REG_ON")) {
printk(KERN_ERR "%s: Failed 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);
}
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);
/* ========== 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('%s'): %d\n", __FUNCTION__, WIFI_WLAN_HOST_WAKE_PROPNAME, wlan_host_wake_up);
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);
}
gpio_direction_input(wlan_host_wake_up);
wlan_host_wake_irq = gpio_to_irq(wlan_host_wake_up);
printk(KERN_ERR "%s: gpio_request WLAN_HOST_WAKE IRQ number %d\n",
__FUNCTION__, wlan_host_wake_irq);
#ifdef BCMPCIE
printk(KERN_INFO "%s: Call msm_pcie_enumerate\n", __FUNCTION__);
msm_pcie_enumerate(MSM_PCIE_CH_NUM);
#endif /* BCMPCIE */
return 0;
}
int dhd_wifi_deinit_gpio(void)
{
if (wlan_reg_on) {
gpio_free(wlan_reg_on);
}
if (wlan_host_wake_up) {
gpio_free(wlan_host_wake_up);
}
return 0;
}
int
dhd_wlan_power(int onoff)
{
printk("------------------------------------------------");
printk("------------------------------------------------\n");
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__);
}
}
/* Wait for WIFI_TURNON_DELAY due to power stability */
msleep(WIFI_TURNON_DELAY);
} 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;
}
// add for card detect
#if defined(BCMSDIO) && defined(BCMDHD_MODULAR) && defined(ENABLE_INSMOD_NO_FW_LOAD)
#if defined(CONFIG_ARCH_SDM660)
extern void wifi_card_detect(bool device_present ,bool power_up_status);
#else // defined(CONFIG_ARCH_SDM660)
extern int wifi_card_detect(void);
#endif // defined(CONFIG_ARCH_SDM660)
#endif // BCMSDIO && BCMDHD_MODULAR && ENABLE_INSMOD_NO_FW_LOAD
static int
dhd_wlan_set_carddetect(int val)
{
#ifdef BCMPCIE
printk(KERN_INFO "%s: Call msm_pcie_enumerate\n", __FUNCTION__);
msm_pcie_enumerate(MSM_PCIE_CH_NUM);
#endif /* BCMPCIE */
// add for card detect
#if defined(BCMSDIO) && defined(BCMDHD_MODULAR) && defined(ENABLE_INSMOD_NO_FW_LOAD)
int ret = 0;
int device_present = 0;
static int power_up_status = 0;
#if defined(CONFIG_ARCH_SDM660)
// create a true-vaue table for this via guessing the logical
// input-value output-value
// is_power_on val(on/off) power_up_status device_present
// first_power_on 1 1 0 1
// lock_power_on 1 1 1 1
// turn_off 0 0 0 0
// ??? 1 0
device_present = val;
if (0 == device_present) { // off
power_up_status = 0;
}
wifi_card_detect(device_present, power_up_status);
// flip over according to the true-value table
if (device_present) { // on
if (0 == power_up_status) {
power_up_status = 1;
}
}
#else // defined(CONFIG_ARCH_SDM660)
ret = wifi_card_detect();
#endif // defined(CONFIG_ARCH_SDM660)
if (0 > ret) {
printk("%s-%d: * error hapen, ret=%d (ignore when remove)\n", __FUNCTION__, __LINE__, ret);
}
#endif // BCMSDIO && BCMDHD_MODULAR && ENABLE_INSMOD_NO_FW_LOAD
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, /* Dummy */
.end = 0, /* Dummy */
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE |
#ifdef BCMPCIE
IORESOURCE_IRQ_HIGHEDGE,
#else
IORESOURCE_IRQ_HIGHLEVEL,
#endif /* BCMPCIE */
};
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;
}
dhd_wlan_resources.start = wlan_host_wake_irq;
dhd_wlan_resources.end = wlan_host_wake_irq;
#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.......ret=%d\n", __FUNCTION__, ret);
if (0 > ret) {
dhd_wifi_deinit_gpio();
}
return ret;
}
int
dhd_wlan_deinit(void)
{
gpio_free(wlan_host_wake_up);
gpio_free(wlan_reg_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_ARCH_MSM8996) || defined(CONFIG_ARCH_MSM8998) || \
defined(CONFIG_ARCH_SDM845) || defined(CONFIG_ARCH_SM8150) || \
defined(CONFIG_ARCH_KONA) || defined(CONFIG_ARCH_LAHAINA)
#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 */