1、电路图兼容速鼎WIFI和BT模块
2、涉及到的接口和管脚如下:
WIFI涉及到的管脚:
power管脚:
VCC_WIFI(wlan_power)<---->aldo1
IO_VDD(wlan_io_regulator)<---->aldo1
使能管脚:
WIFI_PDn(PL07)
WIFI_WAKE(PL05)
数据接口:
SD_CMD<---->PG01
SD_CLK<---->PG00
SD_D0 <---->PG02
SD_D1 <---->PG03
SD_D2 <---->PG04
SD_D3 <---->PG05
时钟频率:使用低速1500000
BT涉及到的管脚:
控制管脚:
AP_WAKE_BT<---->PL06
BT_LDO_EN<----->PL07
BT_POWER <------>VCC_CTP(CLDO1-3.3V)
通信管脚:
UART1_TXD<----->PG06
UART1_RXD<----->PG07
UART1_RTS<----->PG08
UART1_CTS<----->PG09
PCM蓝牙音频通路管脚(对应I2S1,需要配置成PCM模式):
PCM_SYNC<------->PG10
PCM_BCLK<------->PG11
PCM_DO <------->PG12
PCO_DI <------->PG13
32K_OUT <------->PG14
3、WIFI部分的系统管脚配置
lichee\tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
;sdio1配置
[sdc1]
sdc1_used = 1
bus-width = 4
sdc1_clk = port:PG00<2><1><0><default>
sdc1_cmd = port:PG01<2><1><0><default>
sdc1_d0 = port:PG02<2><1><0><default>
sdc1_d1 = port:PG03<2><1><0><default>
sdc1_d2 = port:PG04<2><1><0><default>
sdc1_d3 = port:PG05<2><1><0><default>
;non-removable =
;cd-gpios =
;sunxi-power-save-mode =
ctl-spec-caps = 0x1
sunxi-power-save-mode =
sunxi-dis-signal-vol-sw =
;sd-uhs-sdr50 =
;sd-uhs-ddr50 =
;sd-uhs-sdr104 =
;cap-sdio-irq =
keep-power-in-suspend =
ignore-pm-notify =
;max-frequency = 150000000
min-frequency = 150000
;WIFI和BT管脚配置
;--------------------------------------------------------------------------------
;wlan configuration
;wlan_used: 0-not use, 1- use
;wlan_busnum: sdio/usb index
;clocks: external low power clock input (32.768KHz)
;wlan_power: input supply voltage
;wlan_io_regulator: wlan/sdio I/O voltage
;wlan_regon: power up/down internal regulators used by wifi section
;wlan_hostwake: wlan to wake-up host
;wlan_clk_gpio: wlan low power clock output pin
;--------------------------------------------------------------------------------
[wlan]
compatible = "allwinner,sunxi-wlan"
wlan_used = 1
wlan_busnum = 1
;wlan_usbnum = 2
;clocks = "&clk_outa"
wlan_power = "vcc-wifi"
;wlan_power_ext = "dvdd-csi"
;wlan_io_regulator = "vcc-io-wifi"
wlan_io_regulator = "vcc-wifi"
wlan_regon = port:PL04<1><default><default><0>
wlan_hostwake = port:PL05<6><default><default><0>
;wlan_clk_gpio = port:PI12<4><default><default><0>
;--------------------------------------------------------------------------------
;bluetooth configuration
;bt_used: 0- no used, 1- used
;clocks: external low power clock input (32.768KHz)
;bt_power: input supply voltage
;bt_io_regulator: bluetooth I/O voltage
;bt_rst_n: power up/down internal regulators used by BT section
;--------------------------------------------------------------------------------
[bt]
bt_used = 1
compatible = "allwinner,sunxi-bt"
bt_power = "vcc-ctp"
;clocks = 0x55
bt_rst_n = port:PL07<1><default><default><0>
ap_wakeup_bt = port:PL06<1><default><1><0>
4、WIFI部分的上电时序,BT部分后面再写
struct sunxi_wlan_platdata {
int bus_index;
struct regulator *wlan_power;
struct regulator *wlan_power_ext;
struct regulator *io_regulator;
struct clk *lpo;
int gpio_wlan_regon;
int gpio_wlan_hostwake;
int gpio_chip_en;
char *wlan_power_name;
char *wlan_power_ext_name;
char *io_regulator_name;
int power_state;
int enable_state;
struct platform_device *pdev;
};
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sunxi-gpio.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include <linux/pm_runtime.h>
#include <linux/etherdevice.h>
#include <linux/crypto.h>
#include <linux/miscdevice.h>
#include <linux/capability.h>
#include "sunxi-rfkill.h"
#define USE_HOSC_24M
static struct sunxi_wlan_platdata *wlan_data = NULL;
//static char *project_name = CONFIG_PROJECT_NAME;
static int g_wifi_module = 0; //default xradio
static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off);
static int sunxi_wlan_enable(struct sunxi_wlan_platdata *data, bool enable);
static DEFINE_MUTEX(sunxi_wlan_mutex);
unsigned int wlan_power = 0;
void sunxi_wlan_set_enable(bool enable)
{
struct platform_device *pdev;
int ret = 0;
if (!wlan_data)
return;
pdev = wlan_data->pdev;
mutex_lock(&sunxi_wlan_mutex);
if (enable != wlan_data->enable_state) {
ret = sunxi_wlan_enable(wlan_data, enable);
if (ret)
dev_err(&pdev->dev, "set enable failed\n");
}
mutex_unlock(&sunxi_wlan_mutex);
wlan_power = enable;
dev_info(&pdev->dev,"%s,%d\n",__func__,enable);
}
EXPORT_SYMBOL(wlan_power);
EXPORT_SYMBOL_GPL(sunxi_wlan_set_enable);
void sunxi_wlan_set_power(bool on_off)
{
struct platform_device *pdev;
int ret = 0;
if (!wlan_data)
return;
pdev = wlan_data->pdev;
mutex_lock(&sunxi_wlan_mutex);
if(on_off != wlan_data->power_state){
ret = sunxi_wlan_on(wlan_data, on_off);
if(ret)
dev_err(&pdev->dev,"set power failed\n");
}
mutex_unlock(&sunxi_wlan_mutex);
}
EXPORT_SYMBOL_GPL(sunxi_wlan_set_power);
//nwd-zhaojr add for recognize wifi module
void sunxi_wlan_set_module(int flag)
{
printk("nwd-wangtao set wifi module : %s\n", flag?"xr829":"xradio");
g_wifi_module = flag;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_set_module);
int sunxi_wlan_get_bus_index(void)
{
struct platform_device *pdev;
pr_err("%s ---sunxi_wlan_get_bus_index()\n", __FUNCTION__);
if (!wlan_data)
return -EINVAL;
pdev = wlan_data->pdev;
dev_info(&pdev->dev,"bus_index: %d\n",wlan_data->bus_index);
return wlan_data->bus_index;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_get_bus_index);
int sunxi_wlan_get_oob_irq(void)
{
struct platform_device *pdev;
int host_oob_irq = 0;
if (!wlan_data || !gpio_is_valid(wlan_data->gpio_wlan_hostwake))
return 0;
pdev = wlan_data->pdev;
host_oob_irq = gpio_to_irq(wlan_data->gpio_wlan_hostwake);
if (IS_ERR_VALUE(host_oob_irq))
dev_err(&pdev->dev,"map gpio [%d] to virq failed, errno = %d\n",
wlan_data->gpio_wlan_hostwake,host_oob_irq);
return host_oob_irq;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_get_oob_irq);
int sunxi_wlan_get_oob_irq_flags(void)
{
int oob_irq_flags;
if (!wlan_data)
return 0;
oob_irq_flags = (IRQF_TRIGGER_HIGH | IRQF_SHARED | IRQF_NO_SUSPEND);
return oob_irq_flags;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_get_oob_irq_flags);
static int sunxi_wlan_enable(struct sunxi_wlan_platdata *data, bool enable)
{
struct platform_device *pdev = data->pdev;
struct device *dev = &pdev->dev;
if (gpio_is_valid(data->gpio_wlan_regon)) {
if (enable) {
gpio_direction_output(data->gpio_wlan_regon, 1);
} else {
gpio_direction_output(data->gpio_wlan_regon, 0);
}
dev_info(dev, "set wlan enable: %s\n", enable ? "1" : "0");
}
wlan_data->enable_state = enable;
return 0;
}
static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off)
{
struct platform_device *pdev = data->pdev;
struct device *dev = &pdev->dev;
int ret = 0;
pr_err("%s ---sunxi_wlan_on()\n", __FUNCTION__);
pr_err("sunxi_wlan_on()--regulator--:on_off: %d\n",on_off);
//if (!on_off && gpio_is_valid(data->gpio_wlan_regon))
// gpio_set_value(data->gpio_wlan_regon, 0);
if (data->wlan_power_name) {
data->wlan_power = regulator_get(dev, data->wlan_power_name);
if (!IS_ERR(data->wlan_power)) {
if (on_off) {
regulator_set_voltage(data->wlan_power,
(int)3300*1000,
(int)3300*1000);
ret = regulator_enable(data->wlan_power);
if (ret < 0) {
dev_err(dev, "regulator wlan_power enable failed\n");
regulator_put(data->wlan_power);
return ret;
}
ret = regulator_get_voltage(data->wlan_power);
if (ret < 0) {
dev_err(dev, "regulator wlan_power get voltage failed\n");
regulator_put(data->wlan_power);
return ret;
}
dev_info(dev, "check wlan wlan_power voltage: %d\n",
ret);
} else {
ret = regulator_disable(data->wlan_power);
if (ret < 0) {
dev_err(dev, "regulator wlan_power disable failed\n");
regulator_put(data->wlan_power);
return ret;
}
}
regulator_put(data->wlan_power);
}
}
#if 0 //no use
if (data->wlan_power_ext_name) {
data->wlan_power_ext = regulator_get(dev, data->wlan_power_ext_name);
if (!IS_ERR(data->wlan_power_ext)) {
if (on_off) {
regulator_set_voltage(data->wlan_power_ext,
(int)3300*1000,
(int)3300*1000);
//(int)1800*1000,
//(int)1800*1000);
ret = regulator_enable(data->wlan_power_ext);
if (ret < 0) {
dev_err(dev, "regulator wlan_power_ext enable failed\n");
regulator_put(data->wlan_power_ext);
//return ret;
}
ret = regulator_get_voltage(data->wlan_power_ext);
if (ret < 0) {
dev_err(dev, "regulator wlan_power_ext get voltage "
"failed\n");
regulator_put(data->wlan_power_ext);
//return ret;
}
dev_info(dev, "check wlan BT power wlan_power_ext voltage: %d\n",
ret);
} else {
ret = regulator_disable(data->wlan_power_ext);
if (ret < 0) {
dev_err(dev, "regulator wlan_power_ext disable failed\n");
regulator_put(data->wlan_power_ext);
//return ret;
}
}
regulator_put(data->wlan_power_ext);
}
}
#endif
if(data->io_regulator_name){
data->io_regulator = regulator_get(dev, data->io_regulator_name);
if (!IS_ERR(data->io_regulator)) {
if(on_off){
ret = regulator_enable(data->io_regulator);
if (ret < 0){
dev_err(dev, "regulator io_regulator enable failed\n");
regulator_put(data->io_regulator);
return ret;
}
ret = regulator_get_voltage(data->io_regulator);
if (ret < 0){
dev_err(dev, "regulator io_regulator get voltage failed\n");
regulator_put(data->io_regulator);
return ret;
}
dev_info(dev, "check wlan io_regulator voltage: %d\n",ret);
}else{
ret = regulator_disable(data->io_regulator);
if (ret < 0){
dev_err(dev, "regulator io_regulator disable failed\n");
regulator_put(data->io_regulator);
return ret;
}
}
regulator_put(data->io_regulator);
}
}
// if (on_off && gpio_is_valid(data->gpio_wlan_regon)) {
// mdelay(10);
// gpio_set_value(data->gpio_wlan_regon, 1);
// }
wlan_data->power_state = on_off;
return 0;
}
static ssize_t power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
pr_err("%s ---power_state_show()\n", __FUNCTION__);
return sprintf(buf, "%d\n", wlan_data->power_state);
}
static ssize_t power_state_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
pr_err("%s ---power_state_store()\n", __FUNCTION__);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
err = kstrtoul(buf, 0, &state);
if (err)
return err;
if (state > 1)
return -EINVAL;
pr_err("power_state_store():state: %d\n",state);
mutex_lock(&sunxi_wlan_mutex);
if (state != wlan_data->power_state) {
err = sunxi_wlan_on(wlan_data, state);
if (err)
dev_err(dev, "set power failed\n");
}
mutex_unlock(&sunxi_wlan_mutex);
return count;
}
static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR | S_IWGRP,
power_state_show, power_state_store);
static ssize_t enable_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", wlan_data->enable_state);
}
static ssize_t enable_state_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
err = kstrtoul(buf, 0, &state);
if (err)
return err;
if (state > 1)
return -EINVAL;
mutex_lock(&sunxi_wlan_mutex);
if (state != wlan_data->enable_state) {
err = sunxi_wlan_enable(wlan_data, state);
if (err)
dev_err(dev,"set enable failed\n");
}
mutex_unlock(&sunxi_wlan_mutex);
return count;
}
//static DEVICE_ATTR(enable_state, S_IWUGO | S_IRUGO,
// enable_state_show, enable_state_store);
static DEVICE_ATTR(enable_state, S_IRUGO | S_IWUSR | S_IWGRP,
enable_state_show, enable_state_store);
static ssize_t scan_device_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
int bus = wlan_data->bus_index;
pr_err("%s ---scan_device_store()\n", __FUNCTION__);
err = kstrtoul(buf, 0, &state);
if (err)
return err;
//sunxi_wl_chipen_set(0, state);
dev_info(dev, "scan_device_store()::start scan device on bus_index: %d state:%d\n",
wlan_data->bus_index,state);
if (bus < 0) {
dev_err(dev, "scan device fail!\n");
return -1;
}
sunxi_mmc_rescan_card(bus);
return count;
}
static DEVICE_ATTR(scan_device, S_IRUGO | S_IWUSR | S_IWGRP,
NULL, scan_device_store);
extern void sunxi_mmc_remove_sdio(unsigned ids);
static ssize_t remove_card_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int bus = wlan_data->bus_index;
dev_info(dev, "start remove card on bus_index: %d\n",
wlan_data->bus_index);
if (bus < 0) {
dev_err(dev, "remove card fail!\n");
return -1;
}
sunxi_mmc_remove_sdio(bus);
return count;
}
static DEVICE_ATTR(remove_card, S_IRUGO | S_IWUSR | S_IWGRP,
NULL, remove_card_store);
static struct attribute *misc_attributes[] = {
&dev_attr_enable_state.attr,
&dev_attr_scan_device.attr,
&dev_attr_remove_card.attr,
&dev_attr_power_state.attr,
NULL,
};
static struct attribute_group misc_attribute_group = {
.name = "rf-ctrl",
.attrs = misc_attributes,
};
static struct miscdevice sunxi_wlan_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "sunxi-wlan",
};
static char wifi_mac_str[18] = {0};
void sunxi_wlan_chipid_mac_address(u8 *mac)
{
#define MD5_SIZE 16
#define CHIP_SIZE 16
struct crypto_hash *tfm;
struct hash_desc desc;
struct scatterlist sg;
u8 result[MD5_SIZE];
u8 chipid[CHIP_SIZE];
int i = 0;
int ret = -1;
memset(chipid, 0, sizeof(chipid));
memset(result, 0, sizeof(result));
sunxi_get_soc_chipid((u8 *)chipid);
tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm)) {
pr_err("Failed to alloc md5\n");
return;
}
desc.tfm = tfm;
desc.flags = 0;
ret = crypto_hash_init(&desc);
if (ret < 0) {
pr_err("crypto_hash_init() failed\n");
goto out;
}
sg_init_one(&sg, chipid, sizeof(chipid) - 1);
ret = crypto_hash_update(&desc, &sg, sizeof(chipid) - 1);
if (ret < 0) {
pr_err("crypto_hash_update() failed for id\n");
goto out;
}
crypto_hash_final(&desc, result);
if (ret < 0) {
pr_err("crypto_hash_final() failed for result\n");
goto out;
}
/* Choose md5 result's [0][2][4][6][8][10] byte as mac address */
for (i = 0; i < 6; i++)
mac[i] = result[2*i];
mac[0] &= 0xfe; /* clear multicast bit */
mac[0] &= 0xfd; /* clear local assignment bit (IEEE802) */
out:
crypto_free_hash(tfm);
}
EXPORT_SYMBOL(sunxi_wlan_chipid_mac_address);
void sunxi_wlan_custom_mac_address(u8 *mac)
{
int i;
char *p = wifi_mac_str;
u8 mac_addr[ETH_ALEN] = {0};
if (0 == strlen(p))
return;
for (i = 0; i < ETH_ALEN; i++, p++)
mac_addr[i] = simple_strtoul(p, &p, 16);
memcpy(mac, mac_addr, sizeof(mac_addr));
}
EXPORT_SYMBOL(sunxi_wlan_custom_mac_address);
#ifndef MODULE
static int __init set_wlan_mac_addr(char *str)
{
char *p = str;
if (str != NULL && *str)
strlcpy(wifi_mac_str, p, 18);
return 0;
}
__setup("wifi_mac=", set_wlan_mac_addr);
#endif
static int sunxi_wlan_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct sunxi_wlan_platdata *data;
struct gpio_config config;
u32 val;
const char *power, *io_regulator, *power_ext;
int ret = 0;
#ifdef USE_HOSC_24M
void __iomem *vaddr;
#endif
pr_err("%s ---sunxi_wlan_probe()\n", __FUNCTION__);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!dev)
return -ENOMEM;
data->pdev = pdev;
wlan_data = data;
data->bus_index = -1;
if (!of_property_read_u32(np, "wlan_busnum", &val)) {
switch (val) {
case 0:
case 1:
case 2:
data->bus_index = val;
break;
default:
dev_err(dev, "unsupported wlan_busnum (%u)\n", val);
return -EINVAL;
}
}
dev_info(dev, "wlan_busnum (%u)\n", val);
if (of_property_read_string(np, "wlan_power", &power)) {
dev_warn(dev, "Missing wlan_power.\n");
} else {
data->wlan_power_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if (!data->wlan_power_name)
return -ENOMEM;
strcpy(data->wlan_power_name, power);
}
dev_info(dev, "wlan_power_name (%s)\n", data->wlan_power_name);
#if 0 //no use
if (of_property_read_string(np, "wlan_power_ext", &power_ext)) {
dev_warn(dev, "Missing wlan_power_ext.\n");
} else {
data->wlan_power_ext_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if (!data->wlan_power_ext_name)
return -ENOMEM;
strcpy(data->wlan_power_ext_name, power_ext);
}
dev_info(dev, "wlan_power_ext_name (%s)\n", data->wlan_power_ext_name);
#endif
if (of_property_read_string(np, "wlan_io_regulator", &io_regulator)) {
dev_warn(dev, "Missing wlan_io_regulator.\n");
} else {
data->io_regulator_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if (!data->io_regulator_name)
return -ENOMEM;
strcpy(data->io_regulator_name, io_regulator);
}
dev_info(dev, "io_regulator_name (%s)\n", data->io_regulator_name);
data->gpio_wlan_regon = of_get_named_gpio_flags(np, "wlan_regon",
0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_wlan_regon)) {
dev_err(dev, "get gpio wlan_regon failed\n");
} else {
dev_info(dev, "wlan_regon gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_wlan_regon,
"wlan_regon");
if (ret < 0) {
dev_err(dev, "can't request wlan_regon gpio %d\n",
data->gpio_wlan_regon);
return ret;
}
ret = gpio_direction_output(data->gpio_wlan_regon, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction wlan_regon gpio %d\n",
data->gpio_wlan_regon);
return ret;
}
}
#if 0 //no use
data->gpio_chip_en = of_get_named_gpio_flags(np, "chip_en",
0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_chip_en)) {
dev_err(dev, "get gpio chip_en failed\n");
} else {
dev_info(dev, "chip_en gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_chip_en, "chip_en");
if (ret < 0) {
dev_err(dev, "can't request chip_en gpio %d\n",
data->gpio_chip_en);
return ret;
}
ret = gpio_direction_output(data->gpio_chip_en, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction chip_en gpio %d\n",
data->gpio_chip_en);
return ret;
}
}
#endif
data->gpio_wlan_hostwake = of_get_named_gpio_flags(np, "wlan_hostwake",
0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_wlan_hostwake)) {
dev_err(dev, "get gpio wlan_hostwake failed\n");
} else {
dev_info(dev,
"wlan_hostwake gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_wlan_hostwake,
"wlan_hostwake");
if (ret < 0) {
dev_err(dev, "can't request wlan_hostwake gpio %d\n",
data->gpio_wlan_hostwake);
return ret;
}
gpio_direction_input(data->gpio_wlan_hostwake);
if (ret < 0) {
dev_err(dev,
"can't request input direction wlan_hostwake gpio %d\n",
data->gpio_wlan_hostwake);
return ret;
}
#ifdef CONFIG_PM
ret = enable_gpio_wakeup_src(data->gpio_wlan_hostwake);
if (ret < 0) {
dev_err(dev, "can't enable wakeup src for wlan_hostwake %d\n",
data->gpio_wlan_hostwake);
return ret;
}
#endif
}
#if 0 //no use
data->lpo = devm_clk_get(dev, NULL);
if (IS_ERR_OR_NULL(data->lpo)) {
dev_warn(dev, "clk not config\n");
} else {
ret = clk_prepare_enable(data->lpo);
if (ret < 0)
dev_warn(dev, "can't enable clk\n");
}
#endif
#ifdef USE_HOSC_24M
vaddr = ioremap(0x03001f20, sizeof(unsigned long));
val = readl(vaddr);
val |= 0x80000000;
writel(val, vaddr);
val = readl(vaddr);
iounmap(vaddr);
dev_info(dev, "HOSC_24M reg val is: 0x%08x\n", val);
#endif
ret = misc_register(&sunxi_wlan_dev);
if (ret) {
dev_err(dev, "sunxi-wlan register driver as misc device error!\n");
return ret;
}
ret = sysfs_create_group(&sunxi_wlan_dev.this_device->kobj,
&misc_attribute_group);
if (ret) {
dev_err(dev, "sunxi-wlan register sysfs create group failed!\n");
return ret;
}
data->power_state = 0;
data->enable_state = 0;
if (1 != data->power_state) {
sunxi_wlan_set_power(1);
}
return 0;
}
static int sunxi_wlan_remove(struct platform_device *pdev)
{
misc_deregister(&sunxi_wlan_dev);
sysfs_remove_group(&(sunxi_wlan_dev.this_device->kobj),
&misc_attribute_group);
if (!IS_ERR_OR_NULL(wlan_data->lpo))
clk_disable_unprepare(wlan_data->lpo);
return 0;
}
static int wlan_pm_suspend(struct device *dev)
{
//struct sunxi_wlan_platdata *platdata = dev_get_drvdata(dev);
printk("CONFIG_PM:enter wlan_pm_suspend.\n");
if (pm_runtime_suspended(dev))
return 0;
//if (hw_ver == HW_VER_V1_0 || hw_ver == HW_VER_V4_0) {
// //return 0;
//}
//sunxi_wlan_set_enable(0);
sunxi_wlan_set_power(0);
return 0;
}
static int wlan_pm_resume(struct device *dev)
{
//struct sunxi_wlan_platdata *platdata = dev_get_drvdata(dev);
printk("CONFIG_PM:enter wlan_pm_resume.\n");
if (pm_runtime_suspended(dev))
return 0;
//if (hw_ver == HW_VER_V1_0 || hw_ver == HW_VER_V4_0) {
// //return 0;
//}
sunxi_wlan_set_power(1);
//sunxi_wlan_set_enable(1);
return 0;
}
static struct dev_pm_ops wlan_pm_ops = {
.suspend = wlan_pm_suspend,
.resume = wlan_pm_resume,
};
static const struct of_device_id sunxi_wlan_ids[] = {
{ .compatible = "allwinner,sunxi-wlan" },
{ /* Sentinel */ }
};
static struct platform_driver sunxi_wlan_driver = {
.probe = sunxi_wlan_probe,
.remove = sunxi_wlan_remove,
.driver = {
.owner = THIS_MODULE,
.name = "sunxi-wlan",
.pm = &wlan_pm_ops,
.of_match_table = sunxi_wlan_ids,
},
};
module_platform_driver(sunxi_wlan_driver);
MODULE_DESCRIPTION("sunxi wlan driver");
MODULE_LICENSE(GPL);
5、XR829驱动的整合
将全志给的linux4.9的驱动放到lichee\linux-4.9\drivers\net\wireless\xr829这个目录,修改
lichee\linux-4.9\drivers\net\wireless\Makefile
lichee\linux-4.9\drivers\net\wireless\Kconfig
这两个文件,注意跟公版XR819模块的兼容,如下:
lichee\linux-4.9\drivers\net\wireless\Kconfig
menuconfig WLAN
bool "Wireless LAN"
depends on !S390
depends on NET
select WIRELESS
default y
---help---
This section contains all the pre 802.11 and 802.11 wireless
device drivers. For a complete list of drivers and documentation
on them refer to the wireless wiki:
http://wireless.kernel.org/en/users/Drivers
//添加配置选项
config XR829_SUDING_DRIVERS_CHOOSE
tristate "Choose XR829 and suding wifi&&bt support"
---help---
Choose XR829 or suding configuration
config XR819_DRIVERS_CHOOSE
tristate "Choose XR819 wifi&&bt support"
---help---
Choose XR819 configuration
if WLAN
..............................................
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/ralink/Kconfig"
source "drivers/net/wireless/realtek/Kconfig"
source "drivers/net/wireless/rsi/Kconfig"
source "drivers/net/wireless/st/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zydas/Kconfig"
//根据不同的配置选择加载不同模块的配置文件
if XR829_SUDING_DRIVERS_CHOOSE
source "drivers/net/wireless/xr829/Kconfig"
endif #XR829_SUDING_DRIVERS_CHOOSE
if XR819_DRIVERS_CHOOSE
source "drivers/net/wireless/xradio/Kconfig"
endif #XR819_DRIVERS_CHOOSE
lichee\linux-4.9\drivers\net\wireless\Makefile
obj-$(CONFIG_BCMDHD) += bcmdhd/
//添加如下两行
obj-$(CONFIG_XR829_SUDING_DRIVERS_CHOOSE) += xr829/
obj-$(CONFIG_XR819_DRIVERS_CHOOSE) += xradio/
修改:
lichee\linux-4.9\drivers\net\wireless\xr829\Kconfig
menuconfig XR_WLAN
tristate "XRadio WLAN support"
default n
select CFG80211
select AVERAGE
select CRYPTO
select CRYPTO_ARC4
---help---
To compile this code as a module, choose M here.
if XR_WLAN
#source "drivers/net/wireless/xr/umac/Kconfig"
#source "drivers/net/wireless/xradio/wlan/Kconfig"//修改成:
source "drivers/net/wireless/xr829/umac/Kconfig"
source "drivers/net/wireless/xr829/wlan/Kconfig"
endif # XR_WLAN
修改:
lichee\linux-4.9\drivers\net\wireless\xr829\Makefile
#NOSTDINC_FLAGS := -I$(srctree)/drivers/net/wireless/xradio/include/ //修改成
NOSTDINC_FLAGS := -I$(srctree)/drivers/net/wireless/xr829/include/
obj-$(CONFIG_XR_WLAN) += umac/
obj-$(CONFIG_XR_WLAN) += wlan/
clean-files += Module.symvers Module.markers modules modules.order
在文件
lichee\linux-4.9\arch\arm\configs\sun8iw17p1_android_k23x3-wb_defconfig中添加
...........................
CONFIG_WLAN=y
CONFIG_XR829_SUDING_DRIVERS_CHOOSE=y //添加这个配置,选择XR829的配置
CONFIG_WLAN_VENDOR_ADMTEK=y
CONFIG_WLAN_VENDOR_ATH=y
...............................
6、android平台OS的配置
android\device\softwinner\k23x3-wb\BoardConfig.mk
BOARD_WIFI_VENDOR := xradio
##BOARD_WIFI_VENDOR := QCOM
..............................
#1.4 xradio wifi config
ifeq ($(BOARD_WIFI_VENDOR), xradio)
# WiFi Configuration
WPA_SUPPLICANT_VERSION := VER_0_8_X
BOARD_WPA_SUPPLICANT_DRIVER := NL80211
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_xr
BOARD_HOSTAPD_DRIVER := NL80211
BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_xr
BOARD_WLAN_DEVICE := xradio
WIFI_DRIVER_MODULE_PATH := "/system/vendor/modules/xradio_wlan.ko"
WIFI_DRIVER_MODULE_NAME := "xradio_wlan"
WIFI_DRIVER_MODULE_ARG := ""
include hardware/xradio/wlan/kernel-firmware/xradio-wlan.mk
endif
其它涉及到的文件:
device\softwinner\common\init.wireless.xradio.rc
android\hardware\xradio\
直接使用全志发布的公版代码即可。
以上的文件修改之后,编译整个系统,系统启动后打开WIFI基本就OK了。
二、BT部分的调试
1、android os平台部分的配置
android\device\softwinner\common\init.wireless.xradio.rc
在文件中添加:
# to observe dnsmasq.leases file for dhcp information of soft ap.
chown dhcp system /data/misc/dhcp
#for XR829 by zhjr add
#setprop persist.sys.i2s 1
setprop persist.bt.sco.uart 1 //蓝牙电话走串口
chmod 0666 /sys/class/bt_power/power_state
chmod 0666 /sys/class/bt_power/uart_enable
#end by zhaojr
android\device\softwinner\k23x3-wb\BoardConfig.mk
修改:
BOARD_WIFI_VENDOR := xradio
##BOARD_WIFI_VENDOR := QCOM
..................................
#1.4 xradio wifi config
ifeq ($(BOARD_WIFI_VENDOR), xradio)
# WiFi Configuration
WPA_SUPPLICANT_VERSION := VER_0_8_X
BOARD_WPA_SUPPLICANT_DRIVER := NL80211
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_xr
BOARD_HOSTAPD_DRIVER := NL80211
BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_xr
BOARD_WLAN_DEVICE := xradio
WIFI_DRIVER_MODULE_PATH := "/system/vendor/modules/xradio_wlan.ko"
WIFI_DRIVER_MODULE_NAME := "xradio_wlan"
WIFI_DRIVER_MODULE_ARG := ""
include hardware/xradio/wlan/kernel-firmware/xradio-wlan.mk
endif
其他部分已经在WIFI中配置OK.
固件部分配置:
android\hardware\xradio\wlan\kernel-firmware\xradio-wlan.mk 添加:
hardware/xradio/wlan/kernel-firmware/boot_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/boot_xr829.bin \
hardware/xradio/wlan/kernel-firmware/fw_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/fw_xr829.bin \
hardware/xradio/wlan/kernel-firmware/sdd_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/sdd_xr829.bin \
hardware/xradio/wlan/kernel-firmware/etf_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/etf_xr829.bin \
hardware/xradio/wlan/kernel-firmware/fw_xr829_bt.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/fw_xr829_bt.bin \
hardware/xradio/wlan/kernel-firmware/fw_xr829_bt_sco_hci_uart.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/fw_xr829_bt_sco_hci_uart.bin \
hardware/xradio/wlan/kernel-firmware/fw_xr829_bt.bin:/system/etc/firmware/fw_xr829_bt.bin \
hardware/xradio/wlan/kernel-firmware/fw_xr829_bt_sco_hci_uart.bin:/system/etc/firmware/fw_xr829_bt_sco_hci_uart.bin //这个主要针对bt声音通道的固件
2、内核部分的配置修改:
lichee\linux-4.9\arch\arm\configs\sun8iw17p1_android_k23x3-wb_defconfig
添加以下配置:
CONFIG_WLAN=y
CONFIG_XR829_SUDING_DRIVERS_CHOOSE=y //便于XR829的上电时序和XR829模块驱动
# CONFIG_XR819_DRIVERS_CHOOSE is not set
CONFIG_WLAN_VENDOR_ADMTEK=y
CONFIG_WLAN_VENDOR_ATH=y
# CONFIG_ATH_DEBUG is not set
# CONFIG_ATH6KL is not set
CONFIG_WLAN_VENDOR_ATMEL=y
CONFIG_WLAN_VENDOR_BROADCOM=y
# CONFIG_BRCMFMAC is not set
CONFIG_WLAN_VENDOR_CISCO=y
CONFIG_WLAN_VENDOR_INTEL=y
CONFIG_WLAN_VENDOR_INTERSIL=y
# CONFIG_HOSTAP is not set
CONFIG_WLAN_VENDOR_MARVELL=y
# CONFIG_LIBERTAS is not set
# CONFIG_MWIFIEX is not set
CONFIG_WLAN_VENDOR_MEDIATEK=y
CONFIG_WLAN_VENDOR_RALINK=y
CONFIG_WLAN_VENDOR_REALTEK=y
CONFIG_WLAN_VENDOR_RSI=y
CONFIG_WLAN_VENDOR_ST=y
CONFIG_WLAN_VENDOR_TI=y
CONFIG_WLAN_VENDOR_ZYDAS=y
# CONFIG_USB_ZD1201 is not set
#CONFIG_XR_WLAN=m
#CONFIG_XRMAC=m
#CONFIG_XRMAC_RC_DEFAULT="minstrel_ht"
# CONFIG_XRMAC_RC_PID is not set
#CONFIG_XRMAC_RC_MINSTREL=y
#CONFIG_XRMAC_RC_MINSTREL_HT=y
#CONFIG_XRMAC_DEBUGFS=y
#CONFIG_XRADIO=m
#CONFIG_XRADIO_SDIO=y
#CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES=y
# CONFIG_XRADIO_USE_GPIO_IRQ is not set
#CONFIG_XRADIO_SUSPEND_POWER_OFF=y
# CONFIG_XRADIO_EXTEND_SUSPEND is not set
#
# Driver debug features
#
CONFIG_XR_WLAN=m
CONFIG_XRMAC=m
CONFIG_XRMAC_RC_DEFAULT="minstrel_ht"
# CONFIG_XRMAC_RC_PID is not set
CONFIG_XRMAC_RC_MINSTREL=y
CONFIG_XRMAC_RC_MINSTREL_HT=y
CONFIG_XRMAC_DEBUGFS=y
CONFIG_XRADIO=m
CONFIG_XRADIO_SDIO=y
CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES=y
CONFIG_XRADIO_USE_GPIO_IRQ=y //走GPIO驱动
CONFIG_XRADIO_SUSPEND_POWER_OFF=y
# CONFIG_XRADIO_EXTEND_SUSPEND is not set
#
# Driver debug features
#
CONFIG_XRADIO_DEBUG=y
CONFIG_XRADIO_ETF=y
# CONFIG_XRADIO_DUMP_ON_ERROR is not set
CONFIG_XRADIO_DEBUGFS=y
# CONFIG_USB_NET_RNDIS_WLAN is not set
# CONFIG_BCMDHD is not set
# CONFIG_RTL8723CS is not set
# CONFIG_RTL8723BS_VQ0 is not set
3、系统部分的配置和上电时序
3.1系统配置
lichee\tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
bt模块的管脚配置(请结合原理图来配置):
;--------------------------------------------------------------------------------
;bluetooth configuration
;bt_used: 0- no used, 1- used
;clocks: external low power clock input (32.768KHz)
;bt_power: input supply voltage
;bt_io_regulator: bluetooth I/O voltage
;bt_rst_n: power up/down internal regulators used by BT section
;--------------------------------------------------------------------------------
[bt]
bt_used = 1
compatible = "allwinner,sunxi-bt"
bt_power = "vcc-ctp"
;clocks = 0x55
bt_rst_n = port:PL07<1><default><default><0>
ap_wakeup_bt = port:PL06<1><default><1><0>
音频的I2S1的PCM通道不要配置,保留速鼎模块的配置即可,最后XR829模块不用I2S1的PCM通道,走的是串口到主芯片,然后根据切源管理之后到声卡0到MCU的方式出声音(包括蓝牙电话和音乐)
3.2、上电时序
lichee\linux-4.9\drivers\misc\sunxi-rf\sunxi-bluetooth.c(跟速鼎模块共用一个上电时序)
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sunxi-gpio.h>
#include <linux/pm_runtime.h>
#include "sunxi-rfkill.h"
static struct sunxi_bt_platdata *bluetooth_data = NULL;
static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off);
static DEFINE_MUTEX(sunxi_bluetooth_mutex);
static int set_bt_uart_enable(struct sunxi_bt_platdata *platdata, int enable)
{
int gpio_tx = GPIOG(6);
int gpio_rx = GPIOG(7);
int UART_FUNC = 2;
char pin_name_tx[SUNXI_PIN_NAME_MAX_LEN];
char pin_name_rx[SUNXI_PIN_NAME_MAX_LEN];
long unsigned int config_set;
long unsigned int config_get;
sunxi_gpio_to_name(gpio_tx, pin_name_tx);
sunxi_gpio_to_name(gpio_rx, pin_name_rx);
if (0 == enable) {
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
SUNXI_PIN_OUTPUT_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
SUNXI_PIN_DATA_LOW);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
SUNXI_PIN_OUTPUT_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
pin_name_rx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
SUNXI_PIN_DATA_LOW);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
pin_name_rx);
}
} else if (1 == enable) {
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
SUNXI_PIN_PULL_UP);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
pin_name_rx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
SUNXI_PIN_PULL_UP);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
pin_name_rx);
}
}
platdata->uart_enable = enable;
return 0;
}
static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off)
{
struct platform_device *pdev = data->pdev;
struct device *dev = &pdev->dev;
int ret = 0;
pr_err("%s ---sunxi_bt_on()\n", __FUNCTION__);
#if 0
if (!on_off && gpio_is_valid(data->gpio_bt_rst))
gpio_set_value(data->gpio_bt_rst, 0);
#endif
if (data->bt_power_name) {
data->bt_power = regulator_get(dev, data->bt_power_name);
if (!IS_ERR(data->bt_power)) {
if (on_off) {
regulator_set_voltage(data->bt_power,
(int)3300*1000,
(int)3300*1000);
ret = regulator_enable(data->bt_power);
if (ret < 0) {
dev_err(dev, "regulator bt_power enable failed\n");
regulator_put(data->bt_power);
return ret;
}
ret = regulator_get_voltage(data->bt_power);
if (ret < 0) {
dev_err(dev, "regulator bt_power get voltage failed\n");
regulator_put(data->bt_power);
return ret;
}
dev_info(dev, "check bluetooth bt_power voltage: %d\n", ret);
} else {
ret = regulator_disable(data->bt_power);
if (ret < 0) {
dev_err(dev, "regulator bt_power disable failed\n");
regulator_put(data->bt_power);
return ret;
}
}
regulator_put(data->bt_power);
}
}
#if 0
if(data->io_regulator_name){
data->io_regulator = regulator_get(dev, data->io_regulator_name);
if (!IS_ERR(data->io_regulator)) {
if (on_off) {
regulator_set_voltage(data->io_regulator,
(int)3300*1000,
(int)3300*1000);
ret = regulator_enable(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator enable failed\n");
regulator_put(data->io_regulator);
return ret;
}
ret = regulator_get_voltage(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator get voltage failed\n");
regulator_put(data->io_regulator);
return ret;
}
dev_info(dev, "check bluetooth io_regulator voltage: %d\n",ret);
}else{
ret = regulator_disable(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator disable failed\n");
regulator_put(data->io_regulator);
return ret;
}
}
regulator_put(data->io_regulator);
}
}
if (on_off && gpio_is_valid(data->gpio_bt_rst)) {
mdelay(10);
gpio_set_value(data->gpio_bt_rst, 1);
}
#endif
data->power_state = on_off;
return 0;
}
static int sunxi_bt_set_block(void *data, bool blocked)
{
struct sunxi_bt_platdata *platdata = data;
struct platform_device *pdev = platdata->pdev;
int ret;
if (blocked != platdata->power_state) {
dev_warn(&pdev->dev, "block state already is %d\n", blocked);
return 0;
}
dev_info(&pdev->dev, "set block: %d\n", blocked);
//ret = sunxi_bt_on(platdata, !blocked);
//if (ret) {
// dev_err(&pdev->dev, "set block failed\n");
// return ret;
//}
//sunxi_wl_chipen_set(1, !blocked);
return 0;
}
static const struct rfkill_ops sunxi_bt_rfkill_ops = {
.set_block = sunxi_bt_set_block,
};
static ssize_t power_state_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", bluetooth_data->power_state);
}
void bt_xr829_enable(int enable);
static ssize_t power_state_store(struct class *class,
struct class_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
pr_err("%s ---power_state_store()\n", __FUNCTION__);
err = kstrtoul(buf, 0, &state);
if (err)
return err;
bluetooth_data->power_state = state;
pr_err("%s ---bt_xr829_enable = %ld\n",__FUNCTION__,state);
if(state)
bt_xr829_enable(1);
else
bt_xr829_enable(0);
return count;
#if 0
if (state > 1)
return count;
mutex_lock(&sunxi_bluetooth_mutex);
if (state != bluetooth_data->power_state) {
dev_info(&bluetooth_data->pdev->dev, "set power: %s\n", state ? "on" : "off");
err = sunxi_bt_on(bluetooth_data, state);
if (err) {
dev_err(&bluetooth_data->pdev->dev, "set power failed\n");
}
}
sunxi_wl_chipen_set(1, state);
mutex_unlock(&sunxi_bluetooth_mutex);
return count;
#endif
}
static ssize_t uart_enable_store(struct class *class,
struct class_attribute *attr, const char *buf, size_t count)
{
unsigned long enable;
int err;
err = kstrtoul(buf, 0, &enable);
if (err)
return err;
if (enable > 1)
return count;
pr_err("%s ---enable:%d\n", __FUNCTION__,enable);
if (enable != bluetooth_data->uart_enable) {
dev_info(&bluetooth_data->pdev->dev, "bt uart: %s\n", enable ? "on" : "off");
err = set_bt_uart_enable(bluetooth_data, enable);
if(0==err)
{
if (0 == enable){
bluetooth_data->uart_enable = 0;
} else {
bluetooth_data->uart_enable = 1;
}
}
else{
dev_err(&bluetooth_data->pdev->dev, "set uart failed\n");
}
}
return count;
}
static struct class_attribute bt_attribute_group[] = {
__ATTR(power_state, S_IRUGO | S_IWUSR | S_IWGRP,
power_state_show, power_state_store),
__ATTR(uart_enable, S_IWUSR | S_IWGRP,
NULL, uart_enable_store),
__ATTR_NULL
};
static struct class bt_power_class = {
.name = "bt_power",
.class_attrs = bt_attribute_group,
};
static int bt_xr829_reset = 0;
static int bt_xr829_wake = 0;
void bt_xr829_enable(int enable)
{
pr_err("%s aa enable=%d\n",__FUNCTION__,enable);
if(enable){
gpio_direction_output(bt_xr829_reset, 1);
gpio_direction_output(bt_xr829_wake, 1);
}else{
gpio_direction_output(bt_xr829_wake, 0);
gpio_direction_output(bt_xr829_reset, 0);
}
}
static int sunxi_bt_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct sunxi_bt_platdata *data;
struct gpio_config config;
const char *power, *io_regulator;
int ret = 0;
dev_info(dev, "sunxi_bt_probe()\n");
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!dev)
return -ENOMEM;
data->pdev = pdev;
bluetooth_data = data;
if (of_property_read_string(np, "bt_power", &power)) {
dev_warn(dev, "Missing bt_power.\n");
} else {
data->bt_power_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if (!data->bt_power_name)
return -ENOMEM;
else
strcpy(data->bt_power_name,power);
}
dev_info(dev, "bt_power_name (%s)\n", data->bt_power_name);
bt_xr829_reset = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(bt_xr829_reset)) {
pr_err("get gpio bt_rst failed\n");
} else {
pr_err("bt_rst gpio=%d gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",bt_xr829_reset,
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, bt_xr829_reset, "bt_xr829_reset");
if (ret < 0) {
dev_err(dev, "can't request bt_rst gpio %d\n",
bt_xr829_reset);
return ret;
}
ret = gpio_direction_output(bt_xr829_reset, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction bt_rst gpio %d\n",
bt_xr829_reset);
return ret;
}
gpio_set_value(data->gpio_bt_rst, 0);
}
bt_xr829_wake = of_get_named_gpio_flags(np, "ap_wakeup_bt", 0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(bt_xr829_wake)) {
pr_err("get gpio bt_xr829_wake failed\n");
} else {
pr_err("bt_xr829_wake gpio=%d gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",bt_xr829_wake,
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, bt_xr829_wake, "ap_wakeup_bt");
if (ret < 0) {
dev_err(dev, "can't request bt_xr829_wake gpio %d\n",
bt_xr829_wake);
return ret;
}
ret = gpio_direction_output(bt_xr829_wake, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction bt_xr829_wake %d\n",
bt_xr829_wake);
return ret;
}
}
#if 0
if (of_property_read_string(np, "bt_io_regulator", &io_regulator)) {
dev_warn(dev, "Missing bt_io_regulator.\n");
} else {
data->io_regulator_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if (!data->io_regulator_name)
return -ENOMEM;
else
strcpy(data->io_regulator_name,io_regulator);
}
dev_info(dev, "io_regulator_name (%s)\n", data->io_regulator_name);
data->gpio_bt_rst = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_bt_rst)) {
dev_err(dev, "get gpio bt_rst failed\n");
} else {
dev_info(dev, "bt_rst gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_bt_rst, "bt_rst");
if (ret < 0) {
dev_err(dev, "can't request bt_rst gpio %d\n",
data->gpio_bt_rst);
return ret;
}
ret = gpio_direction_output(data->gpio_bt_rst, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction bt_rst gpio %d\n",
data->gpio_bt_rst);
return ret;
}
}
data->lpo = devm_clk_get(dev, NULL);
if (IS_ERR_OR_NULL(data->lpo)) {
dev_warn(dev, "clk not config\n");
} else {
ret = clk_prepare_enable(data->lpo);
if (ret < 0)
dev_warn(dev, "can't enable clk\n");
}
#endif
data->rfkill = rfkill_alloc("sunxi-bt", dev, RFKILL_TYPE_BLUETOOTH,
&sunxi_bt_rfkill_ops, data);
if (!data->rfkill) {
ret = -ENOMEM;
goto failed_alloc;
}
rfkill_set_states(data->rfkill, true, false);
ret = rfkill_register(data->rfkill);
if (ret){
goto fail_rfkill;
}
platform_set_drvdata(pdev, data);
class_register(&bt_power_class);
data->power_state = 0;
data->uart_enable = 1;
pm_runtime_set_active(dev);
pm_runtime_get(dev);
pm_runtime_enable(dev);
if (1 != data->power_state) {
sunxi_bt_on(data, 1);
}
return 0;
fail_rfkill:
if (data->rfkill)
rfkill_destroy(data->rfkill);
failed_alloc:
//if (!IS_ERR_OR_NULL(data->lpo)) {
// clk_disable_unprepare(data->lpo);
// clk_put(data->lpo);
//}
return ret;
}
static int sunxi_bt_remove(struct platform_device *pdev)
{
struct sunxi_bt_platdata *data = platform_get_drvdata(pdev);
struct rfkill *rfk = data->rfkill;
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
class_unregister(&bt_power_class);
platform_set_drvdata(pdev, NULL);
if(rfk){
rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
//if (!IS_ERR_OR_NULL(data->lpo)) {
// clk_disable_unprepare(data->lpo);
// clk_put(data->lpo);
//}
return 0;
}
static int bt_pm_suspend(struct device *dev)
{
struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);
printk("CONFIG_PM:enter bt_pm_suspend.\n");
if (pm_runtime_suspended(dev))
return 0;
if (platdata) {
if (0 != platdata->power_state) {
sunxi_bt_on(platdata, 0);
}
}
return 0;
}
static int bt_pm_resume(struct device *dev)
{
struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);
printk("CONFIG_PM:enter bt_pm_resume.\n");
if (pm_runtime_suspended(dev))
return 0;
if (platdata) {
if (1 != platdata->power_state) {
sunxi_bt_on(platdata, 1);
}
}
return 0;
}
static struct dev_pm_ops bt_pm_ops = {
.suspend = bt_pm_suspend,
.resume = bt_pm_resume,
};
static const struct of_device_id sunxi_bt_ids[] = {
{ .compatible = "allwinner,sunxi-bt" },
{ /* Sentinel */ }
};
static struct platform_driver sunxi_bt_driver = {
.probe = sunxi_bt_probe,
.remove = sunxi_bt_remove,
.driver = {
.owner = THIS_MODULE,
.name = "sunxi-bt",
.pm = &bt_pm_ops,
.of_match_table = sunxi_bt_ids,
},
};
module_platform_driver(sunxi_bt_driver);
MODULE_DESCRIPTION("sunxi bluetooth driver");
MODULE_LICENSE(GPL);
lichee\linux-4.9\drivers\misc\sunxi-rf\sunxi-rfkill.h
/*
* drivers/misc/sunxi-rf/sunxi-rfkill.h
*
* Copyright (c) 2014 softwinner.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SUNXI_RFKILL_H
#define __SUNXI_RFKILL_H
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/rfkill.h>
struct sunxi_bt_platdata {
struct regulator *bt_power;
struct regulator *io_regulator;
struct clk *lpo;
int gpio_bt_rst;
char *bt_power_name;
char *io_regulator_name;
int power_state;
int uart_enable;
struct rfkill *rfkill;
struct platform_device *pdev;
};
struct sunxi_wlan_platdata {
int bus_index;
struct regulator *wlan_power;
struct regulator *wlan_power_ext;
struct regulator *io_regulator;
struct clk *lpo;
int gpio_wlan_regon;
int gpio_wlan_hostwake;
int gpio_chip_en;
char *wlan_power_name;
char *wlan_power_ext_name;
char *io_regulator_name;
int power_state;
int enable_state;
struct platform_device *pdev;
};
extern void sunxi_wl_chipen_set(int dev, int on_off);
extern void sunxi_wlan_set_power(bool on_off);
extern int sunxi_wlan_get_bus_index(void);
extern int sunxi_wlan_get_oob_irq(void);
extern int sunxi_wlan_get_oob_irq_flags(void);
extern int enable_gpio_wakeup_src(int para);
extern void sunxi_mmc_rescan_card(unsigned ids);
extern int sunxi_get_soc_chipid(uint8_t *chipid);
#endif /* SUNXI_RFKILL_H */
最后将蓝牙协议栈供应商给的gocsdk放到/system/bin下,如下:
adb push gocsdk /system/bin/
adb shell
chmod 777 /system/bin/gocsdk
sync
之后重新上电即可
系统起来之后,到蓝牙电话界面打开,手机连接车机即可,之后打开WIFI连接到路由器,进行协议栈的注册授权,之后就可以使用蓝牙音乐和蓝牙电话了,注意授权只在第一次刷机之后运行的情况,没有刷机是不需要每次都授权的。