参考
概述
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