/* * arch/arm/mach-wmt/wmt_rfkill_bluetooth.c * Copyright (c) RubbitXiao RubbitXiao@wondermedia.com.cn * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * wonderMedia bluetooth "driver" * */ #include #include #include #include #include #include #include #include #include //for readb/writeb #include //WMT_MMAP_OFFSET #include //for pwm #include #define DRV_NAME "wmt-bt" static int pwren_num = -1; static int active_level = -1; static int gpio_int_num = -1; static int src_32K =1; enum LEVEL { LOW = 0, HIGH, }; enum POWER_CTRL { POWER_DIS = 0, POWER_EN, }; extern int wmt_getsyspara(char *varname, char *varval, int *varlen); void pwm_set_enable(int no,int enable); void pwm_set_freq(int no,unsigned int freq); void pwm_set_level(int no,unsigned int level); int bluetooth_power_ctrl(int gpio_num, int enable,int active_level); //configure pwm1/2 pin void config_pwm_pin(int pwm_num,int enable) { int val; if(enable) { val = readb(0xd8110200+WMT_MMAP_OFFSET); val &= ~(1 << 7); writeb(val, 0xd8110200+WMT_MMAP_OFFSET); }else{ val = readb(0xd8110200+WMT_MMAP_OFFSET); val |= (1 << 7); writeb(val, 0xd8110200+WMT_MMAP_OFFSET); } } static struct pwm_device * g_pwm =NULL; DEFINE_SPINLOCK(pwm_lock); static volatile int g_pwm_enable = 0x00; static int enable_pwm_32KHz(int pwm_num,int enable) { if(enable) { if(!g_pwm){ g_pwm = pwm_request(pwm_num,"bluetooth 32KHz"); if(!g_pwm){ printk("can not request pwm1 for bluetooth mtk6622\n"); return -ENOMEM; } } //pwm_config(g_pwm, 15625, 31250);// configuration output 32KHZ pwm_config(g_pwm, 15258, 30517);// configuration output 32768HZ pwm_enable(g_pwm); config_pwm_pin(pwm_num,0x01); printk("enable 32khz output\n"); mdelay(200); }else{ pwm_disable(g_pwm); config_pwm_pin(pwm_num,0x00); printk("disable 32khz output\n"); } return 0; } /* * TODO: should atomis operation */ int enable_32Khz(int enable) { int val; val = readb(0xd8110203+WMT_MMAP_OFFSET); if(enable) { val |= (1 << 4); }else { val &= ~(1 << 4); } writeb(val, 0xd8110203+WMT_MMAP_OFFSET); return 0; } static void realease_pwm(void) { if(g_pwm) pwm_free(g_pwm); g_pwm = NULL; } int pwm_32KHZ_control(int enable) { unsigned long flags; spin_lock_irqsave(&pwm_lock, flags); if(enable){ enable_pwm_32KHz(src_32K,1); }else{ enable_pwm_32KHz(src_32K,0); } printk("enable:%d\n",enable); spin_unlock_irqrestore(&pwm_lock, flags); } EXPORT_SYMBOL(pwm_32KHZ_control); /* Bluetooth control */ static void bt_enable(int on) { if (on) { printk("bt_enable on:%d\n",on); if(src_32K == 4){ enable_32Khz(1); }else{ pwm_32KHZ_control(1); } mdelay(50); /* Power on the chip */ //gpio_set_value(pwren_num, 1);,& bluetooth_power_ctrl(pwren_num, POWER_EN,active_level); mdelay(100); } else { printk("bt_enable on:%d\n",on); //gpio_set_value(pwren_num, 0); bluetooth_power_ctrl(pwren_num, POWER_DIS,active_level); mdelay(50); if(src_32K == 4){ //enable_32Khz(0); }else{ //pwm_32KHZ_control(0); } mdelay(100); } } static int bt_set_block(void *data, bool blocked) { bt_enable(!blocked); return 0; } static const struct rfkill_ops bt_rfkill_ops = { .set_block = bt_set_block, }; #include "test_skeleton.c" //for test_skeleton_proc_init() int bluetooth_power_ctrl(int gpio_num, int enable,int active_level) { if(enable){ /*for power enable gpio pin*/ if(active_level) gpio_direction_output(gpio_num, HIGH); else gpio_direction_output(gpio_num, LOW); }else{ if(active_level) gpio_direction_output(gpio_num, LOW); else gpio_direction_output(gpio_num, HIGH); } return 0; } static int __devinit bt_probe(struct platform_device *pdev) { struct rfkill *rfk; int ret = 0; char buf[256]; int varlen = 256; /* * wmt.bt.brcm formater as follows: * pwren_num:active_level:gpio_int_num:src_32K * for example: 151:1:0 * src_32K: * 1:pwm1 * 2:pwm2 * 3:pwm3 * 4:susgpio3 cpu output 32KHz * * for 8723bs_bt modules on SK_WAVE7_FT5316_BET-CT070040 gpio14 as bt power enable * setenv wmt.bt.brcm 14:1:0:4 * for ap6330 bt modules on 30KT susgpio2 as bt power enable * setenv wmt.bt.brcm 151:1:0:4 */ if(wmt_getsyspara("wmt.bt.brcm",buf,&varlen) == 0) { sscanf(buf,"%d:%d:%d:%d",&pwren_num,&active_level,&gpio_int_num,&src_32K); printk("use customized value:p%d,a%d,i%d,32K src:%d for wmt rfkill bluetooth\n",pwren_num,active_level,gpio_int_num,src_32K); } else { pwren_num = WMT_PIN_GP62_WAKEUP2; active_level = 0x01; gpio_int_num = -1; src_32K = 1; printk("use default value:p%d,a%d,i%d for wmt rfkill bluetooth\n",pwren_num,active_level,gpio_int_num); } #if 1 /*for power enable gpio pin*/ ret = gpio_request(pwren_num, "rfkill bluetooth power/reset pin"); if(ret < 0) { printk("request gpio:%x failed!!! for rfkill bluetooth\n",pwren_num); goto err_rfk_alloc; }else{ printk("request gpio:%d for rfkill bluetooth success!!!\n",pwren_num); } ret = gpio_request(WMT_PIN_GP1_GPIO14, "bt_wakeup"); if (ret < 0) printk("reuqest bt_wakeup gpio failed!\n"); else printk("reuqest bt_wakeup gpio sucessful!\n"); gpio_direction_output(WMT_PIN_GP1_GPIO14, 1); #endif bluetooth_power_ctrl(pwren_num, POWER_DIS, active_level); #if 0 /*for bt wakeup gpio pin*/ ret = gpio_request(WMT_PIN_GP1_GPIO14, "bluetooth wakeup pin"); if(ret < 0) { printk("request gpio:%x failed!!! bluetooth wakeup pin\n",WMT_PIN_GP1_GPIO14); goto err_rfk_alloc; }else{ printk("request gpio:%d for bluetooth wakeup pin success!!!\n",WMT_PIN_GP1_GPIO14); } gpio_direction_output(WMT_PIN_GP1_GPIO14, HIGH); #endif /* Configures BT serial port GPIOs */ //this jobs is doned in uart serial drivers #if 0 //first disable bluetooth module during system booting stage. if(active_level) gpio_direction_output(pwren_num, LOW); else gpio_direction_output(pwren_num, HIGH); //enable_32Khz(0x00); //disable susgpio3 ouput 32Khz, which lead to system hangup and can not enter suspend. #endif if(!g_pwm && src_32K < 4){ printk("begint to pwm request\n"); g_pwm = pwm_request(src_32K,"bluetooth 32KHz"); if(!g_pwm){ printk("can not request pwm1 for bluetooth mtk6622\n"); ret = -ENOMEM; goto err_rfk_alloc; } }else{ enable_32Khz(1); } rfk = rfkill_alloc(DRV_NAME, &pdev->dev, RFKILL_TYPE_BLUETOOTH, &bt_rfkill_ops, NULL); if (!rfk) { ret = -ENOMEM; goto err_rfk_alloc; } ret = rfkill_register(rfk); if (ret) goto err_rfkill; platform_set_drvdata(pdev, rfk); test_skeleton_proc_init(); return 0; err_rfkill: rfkill_destroy(rfk); err_rfk_alloc: return ret; } static int bt_remove(struct platform_device *pdev) { struct rfkill *rfk = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); if (rfk) { rfkill_unregister(rfk); rfkill_destroy(rfk); } rfk = NULL; bt_enable(0); gpio_free(pwren_num); //gpio_free(WMT_PIN_GP1_GPIO9); gpio_free(WMT_PIN_GP1_GPIO14); if(src_32K<4) realease_pwm(); deinit_proc_init(); return 0; } static int bt_suspend(struct platform_device *pdev, pm_message_t state) { pwm_32KHZ_control(0); if(src_32K<4) realease_pwm(); printk("bt_suspend"); return 0; } static int bt_resume(struct platform_device *pdev) { pwm_32KHZ_control(1); printk("bt_resume"); return 0; } static struct platform_driver bt_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, .probe = bt_probe, .remove = bt_remove, .suspend = bt_suspend, .resume = bt_resume, }; static void bt_device_release(struct device * dev) {} static struct platform_device device_bluetooth = { .name = DRV_NAME, .id = -1, .dev = { .release = bt_device_release, } }; static int __init bt_init(void) { platform_device_register(&device_bluetooth); return platform_driver_register(&bt_driver); } static void __exit bt_exit(void) { platform_driver_unregister(&bt_driver); platform_device_unregister(&device_bluetooth); } module_init(bt_init); module_exit(bt_exit); MODULE_AUTHOR("RubbitXiao RubbitXiao@wondermedia.com.cn"); MODULE_DESCRIPTION("Driver for the common wondermedia bluetooth chip"); MODULE_LICENSE("GPL");