MTK 驅動開發---Vibrator

參考

MTK 驅動開發(33)—Vibrator


概述

pmic mt6357爲振動器提供ldo穩壓電源。


主要文件

  • 驅動
drivers/misc/mediatek/vibrator/mt6765/vibrator.c
drivers/misc/mediatek/vibrator/vibrator_drv.c
  • DTS
    kernel-4.9/arch/arm64/boot/dts/mediatek/M50.dts:
&odm {
...
        vibrator0:vibrator@0 {
                compatible = "mediatek,vibrator";
                vib_timer = <25>; //持續時間
                vib_limit = <9>; //最短持續時間限定
                vib_vol= <9>;  //LDO電壓2.8v
        };

}
  • 分析如下
vib_vol:
 *  hw->vib_vol:  Voltage selection
 * 4'b0000 :1.2V
 * 4'b0001 :1.3V
 * 4'b0010 :1.5V
 * 4'b0100 :1.8V
 * 4'b0101 :2.0V
 * 4'b1001 :2.8V
 * 4'b1011 :3.0V
 * 4'b1101 :3.3V
 默認值是9則選擇2.8v
 
 vib_timer :
 
 根據:
 static void vibrator_enable(unsigned int dur, unsigned int activate)
{
            ...
 #ifdef CUST_VIBR_LIMIT
		if (dur > hw->vib_limit && dur < hw->vib_timer)
#else
		if (dur >= 10 && dur < hw->vib_timer)
#endif
			dur = hw->vib_timer;
			...
ktime_set(dur / 1000, (dur % 1000) * 1000000)			
 }
 可知這個就是持續時間的設定,單位是毫秒,則爲25ms。
 
 vib_limit:
 
 mt6765/vibrator.h
18:#define CUST_VIBR_LIMIT

 以上代碼定義了CUST_VIBR_LIMIT可知,這個是持續時間最小值限定。

驅動分析

數據結構
struct mt_vibr {
	struct workqueue_struct *vibr_queue;
	struct work_struct vibr_work;
	struct hrtimer vibr_timer;
	int ldo_state;
	int shutdown_flag;
	atomic_t vibr_dur; //原子操作
	spinlock_t vibr_lock;
	atomic_t vibr_state;  //原子操作
};
probe分析
static int vib_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct mt_vibr *vibr;

	init_vibr_oc_handler(vibrator_oc_handler); //初始化pmic中斷函數爲vibrator_oc_handler

	vibr = devm_kzalloc(&pdev->dev, sizeof(*vibr), GFP_KERNEL);
	if (!vibr)
		return -ENOMEM;

	ret = devm_led_classdev_register(&pdev->dev, &led_vibr);
	if (ret < 0) {
		pr_err(VIB_TAG "led class register fail\n");
		return ret;
	}

	vibr->vibr_queue = create_singlethread_workqueue(VIB_DEVICE); //創建工作隊列
	if (!vibr->vibr_queue) {
		pr_err(VIB_TAG "unable to create workqueue\n");
		return -ENODATA;
	}

	INIT_WORK(&vibr->vibr_work, update_vibrator);   //創建工作隊列函數update_vibrator
	spin_lock_init(&vibr->vibr_lock);
	vibr->shutdown_flag = 0;
	atomic_set(&vibr->vibr_state, 0);
	hrtimer_init(&vibr->vibr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	vibr->vibr_timer.function = vibrator_timer_func;  //創建定時器,用於震動時間的控制。

	dev_set_drvdata(&pdev->dev, vibr);
	g_mt_vib = vibr;
	init_cust_vibrator_dtsi(pdev); //讀取dts
	vibr_power_set();  //給振動器上電

	pr_debug(VIB_TAG "probe done\n");

	return 0;
}
  • 工作隊列線程:
static void update_vibrator(struct work_struct *work)
{
	struct mt_vibr *vibr = container_of(work, struct mt_vibr, vibr_work);

	if (atomic_read(&vibr->vibr_state) == 0)
		vibr_Disable();
	else
		vibr_Enable();
}

static int vibr_Enable(void)
{
	if (!g_mt_vib->ldo_state) {
		vibr_Enable_HW();
		g_mt_vib->ldo_state = 1;
	}
	return 0;
}

void vibr_Enable_HW(void)
{
#ifdef CONFIG_MTK_PMIC_CHIP_MT6357
	pmic_set_register_value(PMIC_RG_LDO_VIBR_EN, 1); //使能LDO_VIBR
#endif
	mdelay(OC_INTR_INIT_DELAY);
	pmic_enable_interrupt(INT_VIBR_OC, 1, "vibr");
}

static int vibr_Disable(void)
{
	if (g_mt_vib->ldo_state) {
		vibr_Disable_HW();
		g_mt_vib->ldo_state = 0;
	}
	return 0;
}

void vibr_Disable_HW(void)
{
	pmic_enable_interrupt(INT_VIBR_OC, 0, "vibr");
#ifdef CONFIG_MTK_PMIC_CHIP_MT6357
	pmic_set_register_value(PMIC_RG_LDO_VIBR_EN, 0);
#endif
}
  • 定時器函數
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{
	struct mt_vibr *vibr = container_of(timer, struct mt_vibr, vibr_timer);

	atomic_set(&vibr->vibr_state, 0);
	queue_work(vibr->vibr_queue, &vibr->vibr_work);
	return HRTIMER_NORESTART;
}
  • pmic中斷函數
static void vibrator_oc_handler(void)
{
	pr_debug(VIB_TAG "vibrator_oc_handler: disable vibr due to oc intr happened\n");
	vibrator_enable(0, 0); //關閉震動
}

void init_vibr_oc_handler(void (*vibr_oc_func)(void))
{
	pmic_register_interrupt_callback(INT_VIBR_OC, vibr_oc_func);
}

static void vibrator_enable(unsigned int dur, unsigned int activate)
{
	unsigned long flags;
	struct vibrator_hw *hw = mt_get_cust_vibrator_hw();

	spin_lock_irqsave(&g_mt_vib->vibr_lock, flags);
	hrtimer_cancel(&g_mt_vib->vibr_timer);
	pr_debug(VIB_TAG "cancel hrtimer, cust:%dms, value:%u, shutdown:%d\n",
			hw->vib_timer, dur, g_mt_vib->shutdown_flag);

	if (activate == 0 || g_mt_vib->shutdown_flag == 1) {
		atomic_set(&g_mt_vib->vibr_state, 0);
	} else {
#ifdef CUST_VIBR_LIMIT
		if (dur > hw->vib_limit && dur < hw->vib_timer)
#else
		if (dur >= 10 && dur < hw->vib_timer)
#endif
			dur = hw->vib_timer;

		dur = (dur > 15000 ? 15000 : dur);
		atomic_set(&g_mt_vib->vibr_state, 1);
		hrtimer_start(&g_mt_vib->vibr_timer,
			      ktime_set(dur / 1000, (dur % 1000) * 1000000),
			      HRTIMER_MODE_REL);
	}
	spin_unlock_irqrestore(&g_mt_vib->vibr_lock, flags);
	queue_work(g_mt_vib->vibr_queue, &g_mt_vib->vibr_work);
}
  • sysfs文件節點創建
static ssize_t vibr_activate_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", atomic_read(&g_mt_vib->vibr_state));
}

static ssize_t vibr_activate_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	unsigned int activate, dur;
	ssize_t ret;

	ret = kstrtouint(buf, 10, &activate);
	if (ret) {
		pr_err(VIB_TAG "set activate fail\n");
		return ret;
	}
	dur = atomic_read(&g_mt_vib->vibr_dur);
	vibrator_enable(dur, activate);
	ret = size;
	return ret;
}

static ssize_t vibr_state_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", atomic_read(&vib_state));
}

static ssize_t vibr_state_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	unsigned int state;
	ssize_t ret;

	ret = kstrtouint(buf, 10, &state);
	if (ret) {
		pr_err(VIB_TAG "set state fail\n");
		return ret;
	}
	atomic_set(&vib_state, state);

	ret = size;
	return ret;
}
static ssize_t vibr_duration_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	unsigned int duration;
	ssize_t ret;

	ret = kstrtouint(buf, 10, &duration);
	if (ret) {
		pr_err(VIB_TAG "set duration fail\n");
		return ret;
	}

	atomic_set(&g_mt_vib->vibr_dur, duration);
	ret = size;
	return ret;
}

static DEVICE_ATTR(activate, 0644, vibr_activate_show, vibr_activate_store);
static DEVICE_ATTR(state, 0644, vibr_state_show, vibr_state_store);
static DEVICE_ATTR(duration, 0644, NULL, vibr_duration_store);
  • 節點目錄/sys/class/leds/vibrator:
activate
duration 
state

具體應用詳解

上層調用步驟
echo value > /sys/class/leds/vibrator/duration  //設置持續時間 ,單位毫秒
echo 1 > /sys/class/leds/vibrator/activate    //激活震動 
vibrator_enable分析
  • hrtimer_cancel(&g_mt_vib->vibr_timer); 刪除定時器

  • if (activate == 0) else 判斷是否激活

    • atomic_set(&g_mt_vib->vibr_state, 1); 設置vibr_state原子爲1
    • hrtimer_start(&g_mt_vib->vibr_timer,ktime_set(dur / 1000, (dur %1000) * 1000000),HRTIMER_MODE_REL); 打開定時器,設置持續時間ktime_set(秒,納秒),時間到則調用vibrator_timer_func
    • queue_work(g_mt_vib->vibr_queue, &g_mt_vib->vibr_work); 打開工作隊列update_vibrator
  • 工作隊列是根據vibr_state原子判定的

static void update_vibrator(struct work_struct *work)
{
	struct mt_vibr *vibr = container_of(work, struct mt_vibr, vibr_work);

	if (atomic_read(&vibr->vibr_state) == 0)
		vibr_Disable();
	else
		vibr_Enable();
}
  • 當時間到了,調用定時器函數,調用工作隊列關閉震動馬達
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{
	struct mt_vibr *vibr = container_of(timer, struct mt_vibr, vibr_timer);

	atomic_set(&vibr->vibr_state, 0);
	queue_work(vibr->vibr_queue, &vibr->vibr_work);
	return HRTIMER_NORESTART;
}

調試

  • APK調試

madaceshi_V1.0.apk

  • 打印
(200511_10:17:34.753)[    4.198009] <2>.(2)[1:swapper/0]calling  vib_mod_init+0x0/0xa8 @ 1
(200511_10:17:34.754)[    4.203067] <2>.(2)[1:swapper/0][name:vibrator&]vibratorvib_timer:25
(200511_10:17:34.754)[    4.203880] <2>.(2)[1:swapper/0][name:vibrator&]vibratorvib_limit : 9
(200511_10:17:34.754)[    4.204698] <2>.(2)[1:swapper/0][name:vibrator&]vibratorvib_vol: 9
(200511_10:17:34.754)[    4.205480] <2>.(2)[1:swapper/0][name:vibrator&]vibratorpvib_cust = 25, 9, 9
(200511_10:17:34.754)[    4.206370] <2>.(2)[1:swapper/0][name:vibrator&]vibratorvibr_init: set voltage = 9
(200511_10:17:34.754)[    4.207405] <2>.(2)[1:swapper/0][name:vibrator_drv&][vibrator]probe done
(200511_10:17:34.754)[    4.208840] <2>.(2)[1:swapper/0][name:vibrator_drv&][vibrator]init Done
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章