1316 lines
39 KiB
C
1316 lines
39 KiB
C
/*
|
|
* Copyright (c) 202X Amlogic, Inc. All rights reserved.
|
|
*
|
|
* This source code is subject to the terms and conditions defined in the
|
|
* file 'LICENSE' which is part of this source code package.
|
|
*
|
|
* Description:
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/io.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/reboot.h>
|
|
#include <linux/time.h>
|
|
#include <linux/ktime.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/semaphore.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/hrtimer.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pm_wakeup.h>
|
|
#include <linux/pm_wakeirq.h>
|
|
#include <linux/amlogic/pm.h>
|
|
#include <linux/input.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/version.h>
|
|
#include <linux/pci.h>
|
|
|
|
#include "common.h"
|
|
#include "w2_bt_entry.h"
|
|
#include "w2_pcie_bt.h"
|
|
#include "rc_list.h"
|
|
|
|
#define AML_BT_NOTE "stpbt"
|
|
#define AML_BT_CONFIG_NAME "aml/aml_bt.conf"
|
|
#define AML_BT_FIRMWARE_NAME "aml/w2_bt_fw_uart.bin"
|
|
#define AML_BT_REVC_FIRMWARE_NAME "aml/w2_bt_revC_fw_uart.bin"
|
|
#define AML_BT_FIRMWARE_TXT_NAME "aml/w2_bt_fw_uart.txt"
|
|
#define AML_BT_REVC_FIRMWARE_TXT_NAME "aml/w2_bt_revC_fw_uart.txt"
|
|
|
|
#define ICCM_SIZE 0x40000
|
|
#define DCCM_SIZE 0x20000
|
|
#define ICCM_ROM_SIZE 256*1024
|
|
#define DOWNLOAD_SIZE 4
|
|
#define ICCM_RAM_BASE (0x000000)
|
|
#define DCCM_RAM_BASE (0xd00000)
|
|
#define BT_ICCM_AHB_BASE 0x00300000
|
|
#define BT_DCCM_AHB_BASE 0x00400000
|
|
#define DEBUG_DCCM_ADDR 0x40a4f8 //Store information about the firmware crash
|
|
|
|
#ifndef BIT
|
|
#define BIT(_n) (1 << (_n))
|
|
#endif
|
|
|
|
#define REG_DEV_RESET 0xf03058
|
|
#define REG_PMU_POWER_CFG 0xf03040
|
|
#define REG_RAM_PD_SHUTDWONW_SW 0xf03050
|
|
#define REG_FW_MODE 0xf000e0
|
|
#define REG_FW_PC 0x200034
|
|
#define REG_DF_A194 0xf06308
|
|
|
|
#define BIT_PHY 1
|
|
#define BIT_MAC (1 << 1)
|
|
#define BIT_CPU (1 << 2)
|
|
#define BIT_RF_NUM 28
|
|
#define BT_SINK_MODE 25
|
|
|
|
#define CHIP_BT_PMU_REG_BASE (0xf03000)
|
|
#define RG_BT_PMU_A11 (CHIP_BT_PMU_REG_BASE + 0x2c)
|
|
#define RG_BT_PMU_A12 (CHIP_BT_PMU_REG_BASE + 0x30)
|
|
#define RG_BT_PMU_A13 (CHIP_BT_PMU_REG_BASE + 0x34)
|
|
#define RG_BT_PMU_A14 (CHIP_BT_PMU_REG_BASE + 0x38)
|
|
#define RG_BT_PMU_A15 (CHIP_BT_PMU_REG_BASE + 0x3c)
|
|
#define RG_BT_PMU_A16 (CHIP_BT_PMU_REG_BASE + 0x40)
|
|
#define RG_BT_PMU_A17 (CHIP_BT_PMU_REG_BASE + 0x44)
|
|
#define RG_BT_PMU_A18 (CHIP_BT_PMU_REG_BASE + 0x48)
|
|
#define RG_BT_PMU_A20 (CHIP_BT_PMU_REG_BASE + 0x50)
|
|
#define RG_BT_PMU_A22 (CHIP_BT_PMU_REG_BASE + 0x58)
|
|
|
|
#define CHIP_INTF_REG_BASE (0xf00000)
|
|
#define RG_AON_A15 (CHIP_INTF_REG_BASE + 0x3c)
|
|
#define RG_AON_A16 (CHIP_INTF_REG_BASE + 0x40)
|
|
#define RG_AON_A17 (CHIP_INTF_REG_BASE + 0x44)
|
|
#define RG_AON_A24 (CHIP_INTF_REG_BASE + 0x60)
|
|
#define RG_AON_A30 (CHIP_INTF_REG_BASE + 0x78)
|
|
#define RG_AON_A52 (CHIP_INTF_REG_BASE + 0xd0)
|
|
#define RG_AON_A53 (CHIP_INTF_REG_BASE + 0xd4)
|
|
/*********amlbt_tool*********/
|
|
#define RG_AON_A55 (CHIP_INTF_REG_BASE + 0xdc)
|
|
#define RG_AON_A56 (CHIP_INTF_REG_BASE + 0xe0)
|
|
#define RG_AON_A57 (CHIP_INTF_REG_BASE + 0xe4)
|
|
#define RG_AON_A58 (CHIP_INTF_REG_BASE + 0xe8)
|
|
#define RG_AON_A59 (CHIP_INTF_REG_BASE + 0xec)
|
|
#define RG_AON_A60 (CHIP_INTF_REG_BASE + 0xf0)
|
|
#define RG_AON_A61 (CHIP_INTF_REG_BASE + 0xf4)
|
|
#define RG_AON_A62 (CHIP_INTF_REG_BASE + 0xf8)
|
|
|
|
// pmu status
|
|
#define PMU_PWR_OFF 0x0
|
|
#define PMU_PWR_XOSC 0x1
|
|
#define PMU_XOSC_WAIT 0x2
|
|
#define PMU_XOSC_DPLL 0x3
|
|
#define PMU_DPLL_WAIT 0x4
|
|
#define PMU_DPLL_ACT 0x5
|
|
#define PMU_ACT_MODE 0x6
|
|
#define PMU_ACT_SLEEP 0x7
|
|
#define PMU_SLEEP_MODE 0x8
|
|
#define PMU_SLEEP_WAKE 0x9
|
|
#define PMU_WAKE_WAIT 0xa
|
|
#define PMU_WAKE_XOSC 0xb
|
|
|
|
#define AML_ADDR_CPU 0
|
|
#define AML_ADDR_AON 1
|
|
#define AML_ADDR_MAC 2
|
|
|
|
struct aml_plat_pci {
|
|
struct usb_device *usb_dev;
|
|
struct auc_hif_ops *hif_ops;
|
|
|
|
struct device *dev;
|
|
struct aml_hif_sdio_ops *hif_sdio_ops;
|
|
|
|
struct pci_dev *pci_dev;
|
|
|
|
bool enabled;
|
|
|
|
int (*enable)(void *aml_hw);
|
|
int (*disable)(void *aml_hw);
|
|
void (*deinit)(struct aml_plat_pci *aml_plat_pci);
|
|
u8* (*get_address)(struct aml_plat_pci *aml_plat_pci, int addr_name,
|
|
unsigned int offset);
|
|
void (*ack_irq)(struct aml_plat_pci *aml_plat_pci);
|
|
int (*get_config_reg)(struct aml_plat_pci *aml_plat_pci, const u32 **list);
|
|
|
|
u8 priv[0] __aligned(sizeof(void *));
|
|
};
|
|
|
|
extern struct aml_plat_pci *g_aml_plat_pci;
|
|
extern struct aml_pm_type g_wifi_pm;
|
|
extern uint32_t aml_pci_read_for_bt(int base, u32 offset);
|
|
extern void aml_pci_write_for_bt(u32 val, int base, u32 offset);
|
|
extern int aml_pci_insmod(void);
|
|
extern void aml_pci_rmmod(void);
|
|
extern unsigned char g_pci_driver_insmoded;
|
|
#ifdef CONFIG_AMLOGIC_GX_SUSPEND
|
|
extern unsigned int get_resume_method(void);
|
|
#endif
|
|
extern unsigned int g_aml_device_id;
|
|
|
|
//extern unsigned char aml_wifi_detect_bt_status __attribute__((weak));
|
|
|
|
static w2_pcie_bt_t pcie_bt = {0};
|
|
//static void amlbt_shutdown_func(void);
|
|
|
|
static int amlbt_pcie_probe(struct platform_device *dev);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
|
|
static int amlbt_pcie_remove(struct platform_device *dev);
|
|
#else
|
|
static void amlbt_pcie_remove(struct platform_device *dev);
|
|
#endif
|
|
static int amlbt_pcie_suspend(struct platform_device *dev, pm_message_t state);
|
|
static int amlbt_pcie_resume(struct platform_device *dev);
|
|
static void amlbt_pcie_shutdown(struct platform_device *dev);
|
|
|
|
static int amlbt_pcie_fops_open(struct inode *inode, struct file *file);
|
|
static int amlbt_pcie_fops_close(struct inode *inode, struct file *file);
|
|
static long amlbt_ioctl(struct file* filp, unsigned int cmd, unsigned long arg);
|
|
#ifdef CONFIG_COMPAT
|
|
static long amlbt_compat_ioctl(struct file* filp, unsigned int cmd, unsigned long arg);
|
|
#endif
|
|
static void amlbt_w2p_resume_work(struct work_struct *work);
|
|
|
|
static void amlbt_wake_func(struct work_struct *work)
|
|
{
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
unsigned int key = aml_pci_read_for_bt(AML_ADDR_AON, RG_AON_A17);
|
|
|
|
BTF("key %#x\n", key);
|
|
if (key & BIT(5)) //bit 5 power key, bit 6 netfix
|
|
{
|
|
key &= ~BIT(5);
|
|
aml_pci_write_for_bt(key, AML_ADDR_AON, RG_AON_A17);
|
|
input_event(p_pcie->input_dev, EV_KEY, KEY_POWER, 1);
|
|
input_sync(p_pcie->input_dev);
|
|
input_event(p_pcie->input_dev, EV_KEY, KEY_POWER, 0);
|
|
input_sync(p_pcie->input_dev);
|
|
p_pcie->irq_handle = 0;
|
|
BTF("%s input power key %#x\n", __func__, aml_pci_read_for_bt(AML_ADDR_AON, RG_AON_A17));
|
|
}
|
|
else if (key & BIT(6))
|
|
{
|
|
key &= ~BIT(6);
|
|
aml_pci_write_for_bt(key, AML_ADDR_AON, RG_AON_A17);
|
|
input_event(p_pcie->input_dev, EV_KEY, KEY_NETFLIX, 1);
|
|
input_sync(p_pcie->input_dev);
|
|
input_event(p_pcie->input_dev, EV_KEY, KEY_NETFLIX, 0);
|
|
input_sync(p_pcie->input_dev);
|
|
p_pcie->irq_handle = 0;
|
|
BTF("%s input Netflix key %#x \n", __func__, aml_pci_read_for_bt(AML_ADDR_AON, RG_AON_A17));
|
|
}
|
|
}
|
|
|
|
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
|
{
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
if (p_pcie->irq_handle)
|
|
{
|
|
BTF("irq\n");
|
|
amlbt_intf_queue_work(p_pcie->wake_work_wq, &p_pcie->wake_work);
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int amlbt_register_interrupt_gpio(w2_pcie_bt_t *p_bt)
|
|
{
|
|
struct device_node *node;
|
|
int gpio_num;
|
|
int irq;
|
|
int ret;
|
|
|
|
BTF("%s %d\n", __func__, p_bt->irq);
|
|
|
|
node = of_find_node_by_name(NULL, "aml_bt");
|
|
if (!node)
|
|
{
|
|
BTE("Failed to find device node\n");
|
|
return -1;
|
|
}
|
|
|
|
gpio_num = of_get_named_gpio(node, "btwakeup-gpios", 0);
|
|
if (gpio_num < 0)
|
|
{
|
|
BTE("Failed to get GPIO\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = gpio_request(gpio_num, "btwakeup_gpio");
|
|
if (ret)
|
|
{
|
|
BTE("Failed to request gpio %d: %d\n", gpio_num, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = gpio_direction_input(gpio_num);
|
|
if (ret)
|
|
{
|
|
BTE("Failed to set gpio %d as input: %d\n", gpio_num, ret);
|
|
gpio_free(gpio_num);
|
|
return ret;
|
|
}
|
|
p_bt->gpio_num = gpio_num;
|
|
|
|
irq = gpio_to_irq(gpio_num);
|
|
if (irq < 0)
|
|
{
|
|
BTE("Failed to get IRQ for GPIO\n");
|
|
gpio_free(gpio_num);
|
|
return -1;
|
|
}
|
|
|
|
ret = request_irq(irq, gpio_irq_handler, IRQF_TRIGGER_FALLING, "usb_gpio_irq", p_bt->dev_device);
|
|
if (ret)
|
|
{
|
|
pr_err("Failed to request IRQ %d: %d\n", irq, ret);
|
|
gpio_free(gpio_num);
|
|
return -1;
|
|
}
|
|
|
|
p_bt->irq = irq;
|
|
BTF("gpio_num %d irq %d success!\n", gpio_num, irq);
|
|
return 0;
|
|
}
|
|
|
|
static void amlbt_unregister_interrupt_gpio(w2_pcie_bt_t *p_bt)
|
|
{
|
|
BTF("%s %d\n", __func__, p_bt->irq);
|
|
|
|
if (p_bt->irq > 0)
|
|
{
|
|
free_irq(p_bt->irq, p_bt->dev_device);
|
|
p_bt->irq = -1;
|
|
}
|
|
|
|
if (p_bt->gpio_num > 0)
|
|
{
|
|
gpio_free(p_bt->gpio_num);
|
|
p_bt->gpio_num = -1;
|
|
}
|
|
}
|
|
|
|
static int amlbt_input_device_init(struct platform_device *pdev)
|
|
{
|
|
int err;
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
p_pcie->input_dev = input_allocate_device();
|
|
|
|
BTF("%s \n", __func__);
|
|
if (!p_pcie->input_dev)
|
|
{
|
|
BTF("input_allocate_device failed:");
|
|
return -EINVAL;
|
|
}
|
|
set_bit(EV_KEY, p_pcie->input_dev->evbit);
|
|
set_bit(KEY_POWER, p_pcie->input_dev->keybit);
|
|
set_bit(KEY_NETFLIX, p_pcie->input_dev->keybit);
|
|
|
|
p_pcie->input_dev->name = INPUT_NAME;
|
|
p_pcie->input_dev->phys = INPUT_PHYS;
|
|
p_pcie->input_dev->dev.parent = &pdev->dev;
|
|
p_pcie->input_dev->id.bustype = BUS_ISA;
|
|
p_pcie->input_dev->id.vendor = 0x0001;
|
|
p_pcie->input_dev->id.product = 0x0001;
|
|
p_pcie->input_dev->id.version = 0x0100;
|
|
p_pcie->input_dev->rep[REP_DELAY] = 0xffffffff;
|
|
p_pcie->input_dev->rep[REP_PERIOD] = 0xffffffff;
|
|
p_pcie->input_dev->keycodesize = sizeof(unsigned short);
|
|
p_pcie->input_dev->keycodemax = 0x1ff;
|
|
err = input_register_device(p_pcie->input_dev);
|
|
if (err < 0)
|
|
{
|
|
pr_err("input_register_device failed: %d\n", err);
|
|
input_free_device(p_pcie->input_dev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void amlbt_input_device_deinit(void)
|
|
{
|
|
w2_pcie_bt_t *p_sdio = &pcie_bt;
|
|
input_unregister_device(p_sdio->input_dev);
|
|
p_sdio->input_dev = NULL;
|
|
}
|
|
|
|
|
|
static void amlbt_dev_release(struct device *dev)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static struct platform_device amlbt_pcie_device =
|
|
{
|
|
.name = "pcie_bt",
|
|
.id = -1,
|
|
.dev = {
|
|
.release = &amlbt_dev_release,
|
|
}
|
|
};
|
|
|
|
static struct platform_driver amlbt_pcie_driver =
|
|
{
|
|
.probe = amlbt_pcie_probe,
|
|
.remove = amlbt_pcie_remove,
|
|
.suspend = amlbt_pcie_suspend,
|
|
.resume = amlbt_pcie_resume,
|
|
.shutdown = amlbt_pcie_shutdown,
|
|
|
|
.driver = {
|
|
.name = "pcie_bt",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static const struct file_operations amlbt_sdio_fops =
|
|
{
|
|
.open = amlbt_pcie_fops_open,
|
|
.release = amlbt_pcie_fops_close,
|
|
.write = NULL,
|
|
.read = NULL,
|
|
.unlocked_ioctl = amlbt_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = amlbt_compat_ioctl,
|
|
#endif
|
|
.poll = NULL,
|
|
.fasync = NULL
|
|
};
|
|
|
|
|
|
static void amlbt_aon_addr_bit_set(unsigned int addr, unsigned int bit)
|
|
{
|
|
unsigned int reg_value = 0;
|
|
|
|
reg_value = aml_pci_read_for_bt(AML_ADDR_AON, addr);
|
|
BTI("%#x: %#x\n", addr, reg_value);
|
|
reg_value |= BIT(bit);
|
|
aml_pci_write_for_bt(reg_value, AML_ADDR_AON, addr);
|
|
BTI("%#x: %#x", addr, aml_pci_read_for_bt(AML_ADDR_AON, addr));
|
|
}
|
|
|
|
static void amlbt_aon_addr_bit_clr(unsigned int addr, unsigned int bit)
|
|
{
|
|
unsigned int reg_value = 0;
|
|
|
|
reg_value = aml_pci_read_for_bt(AML_ADDR_AON, addr);
|
|
BTI("%#x: %#x\n", addr, reg_value);
|
|
reg_value &= ~BIT(bit);
|
|
aml_pci_write_for_bt(reg_value, AML_ADDR_AON, addr);
|
|
BTI("%#x: %#x", addr, aml_pci_read_for_bt(AML_ADDR_AON, addr));
|
|
}
|
|
|
|
static unsigned int amlbt_aon_addr_bit_get(unsigned int addr, unsigned int bit)
|
|
{
|
|
unsigned int reg_value = 0;
|
|
unsigned int bit_value = 0;
|
|
|
|
reg_value = aml_pci_read_for_bt(AML_ADDR_AON, addr);
|
|
|
|
bit_value = (reg_value >> bit) & 0x1;
|
|
BTI("get %#x bit%#d: %#x\n", addr, bit, bit_value);
|
|
|
|
return bit_value;
|
|
}
|
|
|
|
static unsigned int amlbt_fw_pmu_sleep_get(void)
|
|
{
|
|
unsigned int reg_value = 0;
|
|
|
|
reg_value = aml_pci_read_for_bt(AML_ADDR_AON, RG_BT_PMU_A15);
|
|
BTI("%s PMU FSM %#x\n", __func__, (reg_value & 0xF));
|
|
|
|
if (((reg_value & 0xF) == PMU_SLEEP_MODE) || ((reg_value & 0xF) == PMU_ACT_SLEEP))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static int amlbt_wake_fw(void)
|
|
{
|
|
unsigned int reg_value = 0;
|
|
|
|
reg_value = aml_pci_read_for_bt(AML_ADDR_AON, RG_BT_PMU_A16);
|
|
reg_value &= ~BIT(0);
|
|
reg_value |= BIT(1);
|
|
aml_pci_write_for_bt(reg_value, AML_ADDR_AON, RG_BT_PMU_A16);
|
|
reg_value = aml_pci_read_for_bt(AML_ADDR_AON, RG_BT_PMU_A16);
|
|
|
|
BTI("%s RG_BT_PMU_A16 %#x\n", __func__, reg_value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlbt_powersave_clear(void)
|
|
{
|
|
// set bt open flag
|
|
//amlbt_aon_addr_bit_set(RG_AON_A24, 24);
|
|
|
|
// clear shutdown bit
|
|
amlbt_aon_addr_bit_clr(RG_AON_A24, 27);
|
|
|
|
// clear suspend bit
|
|
amlbt_aon_addr_bit_clr(RG_AON_A24, 26);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_int_value(char *start, const char *key, int *value)
|
|
{
|
|
size_t key_len = strlen(key);
|
|
if (strncmp(start, key, key_len) == 0 && start[key_len] == '=')
|
|
{
|
|
*value = simple_strtol(start + key_len + 1, NULL, 10);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int amlbt_load_conf(w2_pcie_bt_t *p_bt)
|
|
{
|
|
int ret = 0;
|
|
const struct firmware *fw_entry = NULL;
|
|
char *data;
|
|
size_t len, pos = 0;
|
|
|
|
BTI("Firmware load:%s\n", AML_BT_CONFIG_NAME);
|
|
ret = request_firmware(&fw_entry, AML_BT_CONFIG_NAME, p_bt->dev_device);
|
|
if (ret)
|
|
{
|
|
BTE("%s:%d Failed to load config file: %d\n", __func__, __LINE__, ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!fw_entry || !fw_entry->data)
|
|
{
|
|
BTE("Failed to load conf or data is empty\n");
|
|
release_firmware(fw_entry);
|
|
return -EINVAL;
|
|
}
|
|
|
|
data = (char *)fw_entry->data;
|
|
len = fw_entry->size;
|
|
|
|
// Manual parsing loop
|
|
while (pos < len) {
|
|
char *line_start = data + pos;
|
|
char *line_end = strchr(line_start, '\n'); // Find end of line
|
|
if (!line_end) {
|
|
line_end = data + len; // If no newline, this is the last line
|
|
}
|
|
|
|
*line_end = '\0'; // Null-terminate the current line
|
|
|
|
// Parse known keys
|
|
if (parse_int_value(line_start, "BtAntenna", &p_bt->antenna)) {
|
|
BTI("Parsed BtAntenna: %d\n", p_bt->antenna);
|
|
} else if (parse_int_value(line_start, "FirmwareMode", &p_bt->fw_mode)) {
|
|
BTI("Parsed FirmwareMode: %d\n", p_bt->fw_mode);
|
|
} else if (parse_int_value(line_start, "BtSink", &p_bt->bt_sink)) {
|
|
BTI("Parsed BtSink: %d\n", p_bt->bt_sink);
|
|
} else if (parse_int_value(line_start, "PtaEnable", &p_bt->pta_enable)) {
|
|
BTI("Parsed PtaEnable: %d\n", p_bt->pta_enable);
|
|
} else if (parse_int_value(line_start, "ChangePinMux", &p_bt->pin_mux)) {
|
|
BTI("Parsed ChangePinMux: %d\n", p_bt->pin_mux);
|
|
} else if (parse_int_value(line_start, "BrDigitGain", &p_bt->br_digit_gain)) {
|
|
BTI("Parsed BrDigitGain: %d\n", p_bt->br_digit_gain);
|
|
} else if (parse_int_value(line_start, "EdrDigitGain", &p_bt->edr_digit_gain)) {
|
|
BTI("Parsed EdrDigitGain: %d\n", p_bt->edr_digit_gain);
|
|
} else if (parse_int_value(line_start, "Btfwlog", &p_bt->fw_log)) {
|
|
BTI("Parsed Btfwlog: %d\n", p_bt->fw_log);
|
|
} else if (parse_int_value(line_start, "Btlog", &p_bt->driver_log)) {
|
|
BTI("Parsed Btlog: %d\n", p_bt->driver_log);
|
|
} else if (parse_int_value(line_start, "Btfactory", &p_bt->factory)) {
|
|
BTI("Parsed Btfactory: %d\n", p_bt->factory);
|
|
} else if (parse_int_value(line_start, "Btsystem", &p_bt->system)) {
|
|
BTI("Parsed Btsystem: %d\n", p_bt->system);
|
|
}else if (parse_int_value(line_start, "BtIsolation", &p_bt->isolation)) {
|
|
BTI("Parsed isolatvalue: %d\n", p_bt->isolation);
|
|
}
|
|
/* else {
|
|
BTI("unknown key in configuration file: %s\n", line_start);
|
|
}*/
|
|
|
|
// Move to the next line
|
|
pos = (line_end - data) + 1;
|
|
}
|
|
|
|
release_firmware(fw_entry);
|
|
return 0;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
|
|
static int amlbt_bind_bus(void)
|
|
{
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
if (g_aml_plat_pci->pci_dev == NULL)
|
|
{
|
|
BTE("aml_priv_to_func(7) is NULL");
|
|
return -ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
if (p_pcie->link == NULL)
|
|
{
|
|
p_pcie->link = device_link_add(&amlbt_pcie_device.dev, &g_aml_plat_pci->pci_dev->dev, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
|
|
if (p_pcie->link == NULL)
|
|
{
|
|
BTE("Failed to create device link");
|
|
return -ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
BTI("Success to create device link");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BTI("p_pcie->link is ready %#x \n", p_pcie->link);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct device_link *find_device_link(struct device *consumer, struct device *supplier)
|
|
{
|
|
struct device_link *link;
|
|
|
|
list_for_each_entry(link, &consumer->links.suppliers, c_node)
|
|
{
|
|
if (link->supplier == supplier)
|
|
{
|
|
return link;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void amlbt_unbind_bus(void)
|
|
{
|
|
struct device_link *link = NULL;
|
|
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
if (p_pcie->link != NULL)
|
|
{
|
|
if (g_aml_plat_pci->pci_dev && device_is_registered(&g_aml_plat_pci->pci_dev->dev))
|
|
{
|
|
link = find_device_link(&amlbt_pcie_device.dev, &g_aml_plat_pci->pci_dev->dev);
|
|
BTI("find_device_link : %#x", (unsigned long)link);
|
|
}
|
|
if (link != NULL && link == p_pcie->link)
|
|
{
|
|
device_link_del(p_pcie->link);
|
|
BTI("Success to del device link");
|
|
}
|
|
p_pcie->link = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void amlbt_pcie_res_deinit(w2_pcie_bt_t *p_pcie)
|
|
{
|
|
p_pcie->irq_handle = 0;
|
|
p_pcie->firmware_start = 0;
|
|
|
|
if (p_pcie->resume_wq != NULL)
|
|
{
|
|
flush_workqueue(p_pcie->resume_wq);
|
|
destroy_workqueue(p_pcie->resume_wq);
|
|
p_pcie->resume_wq = NULL;
|
|
}
|
|
|
|
if (p_pcie->wake_work_wq != NULL)
|
|
{
|
|
flush_workqueue(p_pcie->wake_work_wq);
|
|
destroy_workqueue(p_pcie->wake_work_wq);
|
|
p_pcie->wake_work_wq = NULL;
|
|
}
|
|
|
|
BTF("%s finished \n", __func__);
|
|
}
|
|
|
|
static int amlbt_pcie_res_init(w2_pcie_bt_t *p_pcie)
|
|
{
|
|
BTF("%s \n", __func__);
|
|
|
|
p_pcie->irq_handle = 0;
|
|
p_pcie->shutdown_value = 0;
|
|
|
|
INIT_WORK(&p_pcie->resume_work, amlbt_w2p_resume_work);
|
|
INIT_WORK(&p_pcie->wake_work, amlbt_wake_func);
|
|
|
|
p_pcie->resume_wq = create_singlethread_workqueue("resume_wq");
|
|
if (p_pcie->resume_wq == NULL)
|
|
{
|
|
BTE("%s create_resume_wq fail\n", __func__);
|
|
}
|
|
|
|
p_pcie->wake_work_wq = create_singlethread_workqueue("wake_work_wq");
|
|
if (p_pcie->wake_work_wq == NULL)
|
|
{
|
|
BTE("%s create wake_work_wq fail\n", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void amlbt_w2p_resume_work(struct work_struct *work)
|
|
{
|
|
int wait_cnt = 0;
|
|
unsigned int reg = 0;
|
|
int retry_cnt = 0;
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
while (atomic_read(&g_wifi_pm.bus_suspend_cnt) != 0)
|
|
{
|
|
usleep_range(10000, 10000);
|
|
wait_cnt++;
|
|
if (wait_cnt > 100)
|
|
{
|
|
BTF("wifi resume failed!!!!, %#x\n", atomic_read(&g_wifi_pm.bus_suspend_cnt));
|
|
return ;
|
|
}
|
|
}
|
|
|
|
//forbid fw sleep
|
|
amlbt_aon_addr_bit_set(RG_AON_A24, 25);
|
|
usleep_range(1000, 1000);
|
|
// wake bt fw
|
|
wake_retry:
|
|
if (amlbt_fw_pmu_sleep_get() == TRUE)
|
|
{
|
|
usleep_range(1000, 1000);
|
|
amlbt_wake_fw();
|
|
}
|
|
wait_cnt = 0;
|
|
// wait bt fw wake done
|
|
//fw will clear bit after wake done
|
|
do
|
|
{
|
|
reg = amlbt_aon_addr_bit_get(RG_AON_A17, 29);
|
|
usleep_range(10000, 10000);
|
|
if (wait_cnt++ > 5)//wait 50ms
|
|
{
|
|
BTE("%s wake fw failed\n", __func__);
|
|
if (retry_cnt++ < 3)
|
|
goto wake_retry;
|
|
break;
|
|
}
|
|
} while (reg);
|
|
#ifdef CONFIG_AMLOGIC_GX_SUSPEND
|
|
BTI("get_resume_method %d, %d\n", get_resume_method(), BT_WAKEUP);
|
|
if (get_resume_method() != BT_WAKEUP &&
|
|
get_resume_method() != REMOTE_CUS_WAKEUP &&
|
|
get_resume_method() != REMOTE_WAKEUP)
|
|
{
|
|
p_pcie->irq_handle = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int amlbt_pcie_create_device(w2_pcie_bt_t *p_pcie)
|
|
{
|
|
int ret = 0;
|
|
int cdevErr = 0;
|
|
dev_t dev = 0;
|
|
|
|
BTF("%s \n", __func__);
|
|
|
|
ret = alloc_chrdev_region(&dev, 0, 1, AML_BT_NOTE);
|
|
if (ret)
|
|
{
|
|
BTE("fail to allocate chrdev\n");
|
|
return ret;
|
|
}
|
|
|
|
p_pcie->dev_major = MAJOR(dev);
|
|
BTI("major number:%d\n", p_pcie->dev_major);
|
|
cdev_init(&p_pcie->dev_cdev, &amlbt_sdio_fops);
|
|
p_pcie->dev_cdev.owner = THIS_MODULE;
|
|
|
|
cdevErr = cdev_add(&p_pcie->dev_cdev, dev, 1);
|
|
if (cdevErr)
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
BTI("driver(major %d) installed.\n", p_pcie->dev_major);
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
|
p_pcie->dev_class = class_create(THIS_MODULE, AML_BT_NOTE);
|
|
#else
|
|
p_pcie->dev_class = class_create(AML_BT_NOTE);
|
|
#endif
|
|
|
|
if (IS_ERR(p_pcie->dev_class))
|
|
{
|
|
BTE("class create fail, error code(%ld)\n", PTR_ERR(p_pcie->dev_class));
|
|
goto err1;
|
|
}
|
|
|
|
p_pcie->dev_device = device_create(p_pcie->dev_class, NULL, dev, NULL, AML_BT_NOTE);
|
|
if (IS_ERR(p_pcie->dev_device))
|
|
{
|
|
BTE("device create fail, error code(%ld)\n", PTR_ERR(p_pcie->dev_device));
|
|
goto err2;
|
|
}
|
|
|
|
BTI("%s: BT_major %d\n", __func__, p_pcie->dev_major);
|
|
BTI("%s: dev id %d\n", __func__, dev);
|
|
|
|
return 0;
|
|
|
|
err2:
|
|
if (p_pcie->dev_class)
|
|
{
|
|
class_destroy(p_pcie->dev_class);
|
|
p_pcie->dev_class = NULL;
|
|
}
|
|
|
|
err1:
|
|
|
|
error:
|
|
if (cdevErr == 0)
|
|
cdev_del(&p_pcie->dev_cdev);
|
|
|
|
if (ret == 0)
|
|
unregister_chrdev_region(dev, 1);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int amlbt_pcie_destroy_device(w2_pcie_bt_t *p_pcie)
|
|
{
|
|
dev_t dev = MKDEV(p_pcie->dev_major, 0);
|
|
|
|
BTF("%s dev id %d\n", __func__, dev);
|
|
|
|
if (p_pcie->dev_device)
|
|
{
|
|
device_destroy(p_pcie->dev_class, dev);
|
|
p_pcie->dev_device = NULL;
|
|
}
|
|
if (p_pcie->dev_class)
|
|
{
|
|
class_destroy(p_pcie->dev_class);
|
|
p_pcie->dev_class = NULL;
|
|
}
|
|
cdev_del(&p_pcie->dev_cdev);
|
|
|
|
unregister_chrdev_region(dev, 1);
|
|
|
|
BTI("%s driver removed.\n", AML_BT_NOTE);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
|
|
static void bt_earlysuspend(struct early_suspend *h)
|
|
{
|
|
BTF("%s \n", __func__);
|
|
}
|
|
|
|
static void bt_lateresume(struct early_suspend *h)
|
|
{
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
//clear suspend bit
|
|
//if (p_pcie->firmware_start)
|
|
//{
|
|
amlbt_aon_addr_bit_clr(RG_AON_A24, 26);
|
|
//}
|
|
|
|
BTF("%s \n", __func__);
|
|
}
|
|
|
|
static void amlbt_register_early_suspend(struct platform_device *dev)
|
|
{
|
|
BTF("%s \n", __func__);
|
|
|
|
pcie_bt.early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
|
|
pcie_bt.early_suspend.suspend = bt_earlysuspend;
|
|
pcie_bt.early_suspend.resume = bt_lateresume;
|
|
pcie_bt.early_suspend.param = dev;
|
|
register_early_suspend(&pcie_bt.early_suspend);
|
|
}
|
|
|
|
static void amlbt_unregister_early_suspend(struct platform_device *dev)
|
|
{
|
|
BTF("%s \n", __func__);
|
|
|
|
unregister_early_suspend(&pcie_bt.early_suspend);
|
|
}
|
|
#endif
|
|
|
|
static int amlbt_pcie_probe(struct platform_device *dev)
|
|
{
|
|
BTF("%s \n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
|
|
static int amlbt_pcie_remove(struct platform_device *dev)
|
|
#else
|
|
static void amlbt_pcie_remove(struct platform_device *dev)
|
|
#endif
|
|
{
|
|
BTF("%s \n", __func__);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int amlbt_pcie_suspend(struct platform_device *dev, pm_message_t state)
|
|
{
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
BTF("%s \n", __func__);
|
|
//if (p_pcie->firmware_start)
|
|
//{
|
|
if (atomic_read(&g_wifi_pm.bus_suspend_cnt) == 0)
|
|
{
|
|
amlbt_write_rclist_to_firmware();
|
|
amlbt_aon_addr_bit_set(RG_AON_A24, 26);//set suspend bit
|
|
amlbt_aon_addr_bit_clr(RG_AON_A24, 25);//allow fw sleep
|
|
}
|
|
else
|
|
{
|
|
BTF("%s failed bus_suspend_cnt %#x\n", __func__, atomic_read(&g_wifi_pm.bus_suspend_cnt));
|
|
}
|
|
pcie_bt.irq_handle = 0;
|
|
//}
|
|
BTI("%s \n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int amlbt_pcie_resume(struct platform_device *dev)
|
|
{
|
|
int wait_cnt = 0;
|
|
unsigned reg = 0;
|
|
int retry_cnt = 0;
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
BTF("%s \n", __func__);
|
|
//if (p_pcie->firmware_start)
|
|
//{
|
|
//wait usb bus ready
|
|
BTF("g_wifi_pm.bus_suspend_cnt:%#x\n", g_wifi_pm.bus_suspend_cnt);
|
|
if (atomic_read(&g_wifi_pm.bus_suspend_cnt) != 0)
|
|
{
|
|
amlbt_intf_queue_work(p_pcie->resume_wq, &p_pcie->resume_work);
|
|
}
|
|
else
|
|
{
|
|
//forbid fw sleep
|
|
amlbt_aon_addr_bit_set(RG_AON_A24, 25);
|
|
usleep_range(1000, 1000);
|
|
// wake bt fw
|
|
wake_retry:
|
|
if (amlbt_fw_pmu_sleep_get() == TRUE)
|
|
{
|
|
usleep_range(1000, 1000);
|
|
amlbt_wake_fw();
|
|
}
|
|
wait_cnt = 0;
|
|
// wait bt fw wake done
|
|
//fw will clear bit after wake done
|
|
do
|
|
{
|
|
reg = amlbt_aon_addr_bit_get(RG_AON_A17, 29);
|
|
usleep_range(10000, 10000);
|
|
if (wait_cnt++ > 5)//wait 50ms
|
|
{
|
|
BTE("%s wake fw failed\n", __func__);
|
|
if (retry_cnt++ < 3)
|
|
goto wake_retry;
|
|
break;
|
|
}
|
|
} while (reg);
|
|
#ifdef CONFIG_AMLOGIC_GX_SUSPEND
|
|
BTF("get_resume_method %d, %d\n", get_resume_method(), BT_WAKEUP);
|
|
if (get_resume_method() != BT_WAKEUP &&
|
|
get_resume_method() != REMOTE_CUS_WAKEUP &&
|
|
get_resume_method() != REMOTE_WAKEUP)
|
|
{
|
|
p_pcie->irq_handle = 1;
|
|
}
|
|
#endif
|
|
}
|
|
//}
|
|
BTF("%s end\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static void amlbt_pcie_shutdown(struct platform_device *dev)
|
|
{
|
|
BTF("%s \n", __func__);
|
|
//if (pcie_bt.shutdown_value)
|
|
//{
|
|
amlbt_write_rclist_to_firmware();
|
|
amlbt_aon_addr_bit_set(RG_AON_A24, 27);
|
|
//}
|
|
}
|
|
|
|
static void amlbt_show_fw_debug_info(void)
|
|
{
|
|
unsigned int value1 = 0, value2 = 0, value3 = 0;
|
|
unsigned int i;
|
|
|
|
BTI("PMU 0x00f03040:%#x \n", aml_pci_read_for_bt(AML_ADDR_AON, REG_PMU_POWER_CFG));
|
|
usleep_range(10000, 10000);
|
|
value1 = aml_pci_read_for_bt(AML_ADDR_CPU, REG_FW_PC);
|
|
value1 = (value1 >> 6);
|
|
BTI("pc1 0x200034:%#x\n", value1);
|
|
usleep_range(10000, 10000);
|
|
value2 = aml_pci_read_for_bt(AML_ADDR_CPU, REG_FW_PC);
|
|
value2 = (value2 >> 6);
|
|
BTI("pc2 0x200034:%#x\n", value2);
|
|
usleep_range(10000, 10000);
|
|
value3 = aml_pci_read_for_bt(AML_ADDR_CPU, REG_FW_PC);
|
|
value3 = (value3 >> 6);
|
|
BTI("pc3 0x200034:%#x\n", value3);
|
|
|
|
if (value1 == value2 && value2 == value3)
|
|
{
|
|
for (i = 0; i < 32; i++) //32 *int
|
|
{
|
|
value1 = aml_pci_read_for_bt(AML_ADDR_CPU, DEBUG_DCCM_ADDR + i*4);
|
|
BTI("%#x: %#x \n", DEBUG_DCCM_ADDR+i*4, value1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int amlbt_pcie_download_firmware(w2_pcie_bt_t *p_bt)
|
|
{
|
|
int ret = 0;
|
|
unsigned int offset = 0;
|
|
unsigned int remain_len = 0;
|
|
unsigned int iccm_base_addr = BT_ICCM_AHB_BASE + ICCM_ROM_SIZE;
|
|
unsigned int dccm_base_addr = BT_DCCM_AHB_BASE;
|
|
unsigned int check_buf = 0;
|
|
unsigned int val = 0;
|
|
|
|
remain_len = ICCM_SIZE;
|
|
|
|
//to do download bt fw
|
|
BTI("amlbt_sdio_download_firmware:iccm size %#x, remain_len %#x\n", ICCM_SIZE, remain_len);
|
|
|
|
BTI("iccm start [%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n",
|
|
p_bt->iccm_buf[0], p_bt->iccm_buf[1], p_bt->iccm_buf[2], p_bt->iccm_buf[3],
|
|
p_bt->iccm_buf[4], p_bt->iccm_buf[5], p_bt->iccm_buf[6], p_bt->iccm_buf[7]);
|
|
BTI("dccm start [%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n",
|
|
p_bt->dccm_buf[0], p_bt->dccm_buf[1], p_bt->dccm_buf[2], p_bt->dccm_buf[3],
|
|
p_bt->dccm_buf[4], p_bt->dccm_buf[5], p_bt->dccm_buf[6], p_bt->dccm_buf[7]);
|
|
|
|
while (offset < ICCM_SIZE)
|
|
{
|
|
val = ((p_bt->iccm_buf[offset+3]<<24)|(p_bt->iccm_buf[offset+2]<<16)|(p_bt->iccm_buf[offset+1]<<8)|p_bt->iccm_buf[offset]);
|
|
aml_pci_write_for_bt(val, AML_ADDR_CPU, iccm_base_addr);
|
|
check_buf = aml_pci_read_for_bt(AML_ADDR_CPU, iccm_base_addr);
|
|
if (check_buf != val)
|
|
{
|
|
BTE("Firmware iccm check error! val:%#x, check_buf:%#x, offset %#x\n", val, check_buf, offset);
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
offset += DOWNLOAD_SIZE;
|
|
remain_len -= DOWNLOAD_SIZE;
|
|
iccm_base_addr += DOWNLOAD_SIZE;
|
|
}
|
|
|
|
BTI("Firmware iccm check pass, offset %#x\n", offset);
|
|
offset = 0;
|
|
remain_len = DCCM_SIZE;
|
|
//to do download bt fw
|
|
BTI("amlbt_sdio_download_firmware:dccm size %#x, remain_len %#x\n", DCCM_SIZE, remain_len);
|
|
while (offset < DCCM_SIZE)
|
|
{
|
|
val = ((p_bt->dccm_buf[offset+3]<<24)|(p_bt->dccm_buf[offset+2]<<16)|(p_bt->dccm_buf[offset+1]<<8)|p_bt->dccm_buf[offset]);
|
|
aml_pci_write_for_bt(val, AML_ADDR_CPU, dccm_base_addr);
|
|
check_buf = aml_pci_read_for_bt(AML_ADDR_CPU, dccm_base_addr);
|
|
if (check_buf != val)
|
|
{
|
|
BTE("Firmware dccm check error! val:%#x, check_buf:%#x, offset %#x\n", val, check_buf, offset);
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
offset += DOWNLOAD_SIZE;
|
|
remain_len -= DOWNLOAD_SIZE;
|
|
dccm_base_addr += DOWNLOAD_SIZE;
|
|
}
|
|
BTI("Firmware dccm check pass, offset %#x\n", offset);
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int amlbt_load_firmware(w2_pcie_bt_t *p_bt)
|
|
{
|
|
int ret = 0;
|
|
unsigned int reg = 0;
|
|
const struct firmware *fw_entry = NULL;
|
|
unsigned int iccm_size;
|
|
unsigned int dccm_size;
|
|
const char *fw_name = NULL;
|
|
|
|
fw_name = ((g_aml_device_id == W2p_C_PRODUCT_AMLOGIC_EFUSE) ?
|
|
AML_BT_REVC_FIRMWARE_NAME : AML_BT_FIRMWARE_NAME);
|
|
BTI("Firmware load:%s g_aml_device_id %#x\n", fw_name, g_aml_device_id);
|
|
ret = request_firmware(&fw_entry, fw_name, p_bt->dev_device);
|
|
if (ret)
|
|
{
|
|
BTE("%s:%d Failed to load firmware: %d\n", __func__, __LINE__, ret);
|
|
return ret;
|
|
}
|
|
BTI("Firmware [%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n",
|
|
fw_entry->data[0], fw_entry->data[1], fw_entry->data[2], fw_entry->data[3],
|
|
fw_entry->data[4], fw_entry->data[5], fw_entry->data[6], fw_entry->data[7]);
|
|
iccm_size = ((fw_entry->data[3]<<24)|(fw_entry->data[2]<<16)|(fw_entry->data[1]<<8)|(fw_entry->data[0]));
|
|
dccm_size = ((fw_entry->data[7]<<24)|(fw_entry->data[6]<<16)|(fw_entry->data[5]<<8)|(fw_entry->data[4]));
|
|
|
|
BTI("Firmware loaded successfully, iccm_size: %#x, dccm_size:%#x\n", iccm_size - ICCM_ROM_SIZE, dccm_size);
|
|
|
|
p_bt->iccm_buf = &fw_entry->data[ICCM_ROM_SIZE + 8];
|
|
p_bt->dccm_buf = &fw_entry->data[iccm_size + 8];
|
|
|
|
BTI("pmu %#x\n",aml_pci_read_for_bt(AML_ADDR_AON, 0xf03078));
|
|
//aml_pci_write_for_bt(((BIT_PHY|BIT_MAC|BIT_CPU)<<16)|(BIT_PHY|BIT_MAC|BIT_CPU), AML_ADDR_AON, REG_DEV_RESET);
|
|
//usleep_range(1000, 1000);
|
|
//aml_pci_write_for_bt((BIT_CPU<<16)|BIT_CPU, AML_ADDR_AON, REG_DEV_RESET);
|
|
//BTI("hold bt cpu\n");
|
|
ret = amlbt_pcie_download_firmware(p_bt);
|
|
release_firmware(fw_entry);
|
|
if (ret != 0)
|
|
{
|
|
BTE("Download firmware failed!!\n");
|
|
return ret;
|
|
}
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, REG_DF_A194);
|
|
reg &= 0xfffffffc;
|
|
reg |= (p_bt->isolation & 0x3);
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, REG_DF_A194);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, REG_FW_MODE);
|
|
reg &= 0xfffffffc;
|
|
reg |= (p_bt->fw_mode & 0x3);
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, REG_FW_MODE);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, REG_PMU_POWER_CFG);
|
|
reg &= 0xedffffff;
|
|
reg |= ((p_bt->antenna << BIT_RF_NUM)|(p_bt->bt_sink << BT_SINK_MODE));
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, REG_PMU_POWER_CFG);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, RG_AON_A53);
|
|
reg &= 0xf74f0000;
|
|
reg |= ((p_bt->pin_mux << 20) | (p_bt->factory << 21) | (p_bt->system << 23) | (p_bt->pta_enable << 27));
|
|
reg |= (((p_bt->edr_digit_gain & 0xff) << 8) | (p_bt->br_digit_gain & 0xff));
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, RG_AON_A53);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, RG_AON_A59);
|
|
reg &= 0xfffffffc;
|
|
reg |= (p_bt->fw_log & 0x3);
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, RG_AON_A59);
|
|
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, REG_DEV_RESET);
|
|
BTI("start bt cpu ok!\n");
|
|
usleep_range(50000, 50000);
|
|
BTI("pc1:%#x\n", aml_pci_read_for_bt(AML_ADDR_CPU, REG_FW_PC));
|
|
usleep_range(10000, 10000);
|
|
BTI("pc2:%#x\n", aml_pci_read_for_bt(AML_ADDR_CPU, REG_FW_PC));
|
|
usleep_range(10000, 10000);
|
|
BTI("pc3:%#x\n", aml_pci_read_for_bt(AML_ADDR_CPU, REG_FW_PC));
|
|
p_bt->iccm_buf = NULL;
|
|
p_bt->dccm_buf = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int amlbt_apply_fw_config(w2_pcie_bt_t *p_bt)
|
|
{
|
|
unsigned int reg = 0;
|
|
|
|
if (!p_bt) {
|
|
BTE("%s: p_bt is NULL\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
BTF("%s: after download send bt configurations\n", __func__);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, REG_DF_A194);
|
|
reg &= 0xfffffffc;
|
|
reg |= (p_bt->isolation & 0x3);
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, REG_DF_A194);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, REG_PMU_POWER_CFG);
|
|
reg &= 0xedffffff;
|
|
reg |= ((p_bt->antenna << BIT_RF_NUM)|(p_bt->bt_sink << BT_SINK_MODE));
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, REG_PMU_POWER_CFG);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, RG_AON_A53);
|
|
reg &= 0xf74f0000;
|
|
reg |= ((p_bt->pin_mux << 20) | (p_bt->factory << 21) | (p_bt->system << 23) | (p_bt->pta_enable << 27));
|
|
reg |= (((p_bt->edr_digit_gain & 0xff) << 8) | (p_bt->br_digit_gain & 0xff));
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, RG_AON_A53);
|
|
|
|
reg = aml_pci_read_for_bt(AML_ADDR_AON, RG_AON_A59);
|
|
reg &= 0xfffffffc;
|
|
reg |= (p_bt->fw_log & 0x3);
|
|
aml_pci_write_for_bt(reg, AML_ADDR_AON, RG_AON_A59);
|
|
|
|
BTF("BT configurations applied successfully\n");
|
|
return 0;
|
|
}
|
|
|
|
static int amlbt_pcie_fops_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = 0;
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
|
|
BTF("%s, version:%s \n", __func__, AML_W2P_VERSION);
|
|
|
|
file->private_data = &pcie_bt;
|
|
amlbt_load_conf(&pcie_bt);
|
|
amlbt_apply_fw_config(&pcie_bt);
|
|
#if 0
|
|
amlbt_load_conf(&pcie_bt);
|
|
amlbt_pcie_res_init(p_pcie);
|
|
//clear fw lowpower
|
|
amlbt_powersave_clear();
|
|
ret = amlbt_load_firmware(&pcie_bt);
|
|
if (ret != 0)
|
|
{
|
|
BTI("amlbt_load_firmware failed!\n");
|
|
amlbt_pcie_res_deinit(&pcie_bt);
|
|
return -EIO;
|
|
}
|
|
pcie_bt.firmware_start = 1;
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
|
|
amlbt_bind_bus();
|
|
#endif
|
|
//amlbt_register_interrupt_gpio(&pcie_bt);
|
|
#endif
|
|
return nonseekable_open(inode, file);
|
|
}
|
|
|
|
static int amlbt_pcie_fops_close(struct inode *inode, struct file *file)
|
|
{
|
|
w2_pcie_bt_t *p_pcie = &pcie_bt;
|
|
BTF("%s, version:%s \n", __func__, AML_W2P_VERSION);
|
|
#if 0
|
|
amlbt_show_fw_debug_info();
|
|
amlbt_pcie_res_deinit(p_pcie);
|
|
//aml_pci_write_for_bt(0, AML_ADDR_AON, RG_AON_A15);
|
|
//amlbt_unregister_interrupt_gpio(&pcie_bt);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static long amlbt_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case IOCTL_SET_BT_SHUTDOWN:
|
|
{
|
|
if (copy_from_user(&pcie_bt.shutdown_value, (unsigned int __user *)arg, sizeof(pcie_bt.shutdown_value)) != 0)
|
|
{
|
|
BTE("IOCTL_SET_BT_SHUTDOWN copy error\n");
|
|
return -EFAULT;
|
|
}
|
|
BTI("IOCTL_SET_BT_SHUTDOWN %#x\n", pcie_bt.shutdown_value);
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long amlbt_compat_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
long ret = 0;
|
|
|
|
ret = amlbt_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int amlbt_w2p_init(void)
|
|
{
|
|
int ret = 0;
|
|
struct platform_device *p_device = &amlbt_pcie_device;
|
|
struct platform_driver *p_driver = &amlbt_pcie_driver;
|
|
|
|
BTF("%s, version:%s", __func__, AML_W2P_VERSION);
|
|
|
|
ret = platform_device_register(p_device);
|
|
if (ret)
|
|
{
|
|
dev_err(&p_device->dev, "platform_device_register failed!\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = platform_driver_register(p_driver);
|
|
if (ret)
|
|
{
|
|
dev_err(&p_device->dev, "platform_driver_register failed!\n");
|
|
return ret;
|
|
}
|
|
amlbt_pcie_create_device(&pcie_bt);
|
|
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
|
|
amlbt_register_early_suspend(p_device);
|
|
#endif
|
|
amlbt_input_device_init(p_device);
|
|
amlbt_rc_list_init(pcie_bt.dev_device, NULL, NULL, NULL, NULL);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
|
|
amlbt_bind_bus();
|
|
#endif
|
|
amlbt_register_interrupt_gpio(&pcie_bt);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void amlbt_w2p_exit(void)
|
|
{
|
|
struct platform_device *p_device = &amlbt_pcie_device;
|
|
struct platform_driver *p_driver = &amlbt_pcie_driver;
|
|
|
|
BTF("%s, log level:%d \n", __func__, g_dbg_level);
|
|
|
|
amlbt_unregister_interrupt_gpio(&pcie_bt);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
|
|
amlbt_unbind_bus();
|
|
#endif
|
|
amlbt_rc_list_deinit(pcie_bt.dev_device);
|
|
amlbt_input_device_deinit();
|
|
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
|
|
amlbt_unregister_early_suspend(p_device);
|
|
#endif
|
|
amlbt_pcie_destroy_device(&pcie_bt);
|
|
|
|
platform_device_unregister(p_device);
|
|
platform_driver_unregister(p_driver);
|
|
}
|
|
|