您的位置:首页 > 其它

如何在qualcomm 8960和8921上使用PWM功能 (GPIO24)

2012-07-11 10:25 483 查看
一、PWM 分析

1. PWM: Pulse Width Modulator; LPG: Light Pulse Generator; 两者指的是同一东西。

2. PWM driver: /kernel/drivers/mfd/pm8xxx-pwm.c

3. 手机中注册路径: /sys/devices/platform/msm_ssbi.0/pm8921-core/pm8xxx-pwm

4. 利用debugfs调试

a. #mkdir /data/debugfs

b. #mount -t debugfs none /data/debugfs

c. #cd /data/debugfs/pmx8xxx-pwm-dbg

d. #ls 可以看到共有8路pwm可用

e. #cd 0; ls

duty-cycle //占宽比 (0,100)之间

enable

period //输出波周期 [7, 327*1000000L]微秒之间

f. 需要先设置period, #echo 50 > period

g. #echo 50 > duty-cycle

h. #echo 1 > enable

二、调查完成,所需要修改的代码

1. @kernel/arch/arm/mach-msm/board-semc_blue_cdb.c, 设置PM8921的GPIO工作在PWM function

static struct pm8xxx_gpio_init pm8921_gpios[] __initdata = {

...

//PM8XXX_GPIO_DISABLE(24),

PM8XXX_GPIO_INIT( 24,

PM_GPIO_DIR_OUT,

PM_GPIO_OUT_BUF_CMOS,

1,

PM_GPIO_PULL_NO,

PMIC_GPIO_VIN0,

PM_GPIO_STRENGTH_HIGH,

PM_GPIO_FUNC_2,

1,

0

),


...

}

2. @kernel/arch/arm/mach-msm/board-semc_blue.c, 设置PWM/LPG 的bank1 可用,该bank1 对应的是GPIO24

static struct pm8xxx_pwm_platform_data pm8xxx_pwm_pdata = {

//.dtest_channel = PM8XXX_PWM_DTEST_CHANNEL_NONE,

.dtest_channel = 0,

};

3. 进入手机adb shell

a. #mount -t debugfs none /data/debugfs

b. #cd data/debugfs/ pm8xxx-pwm-dbg/0

c. #echo 2000 > period //必须先设置period, 单位为usecond, 区间为[7, 327*1000000L]us

d. #echo 50 > duty-cycle //占空比 (0,100)之间

e. #echo 1 > enable //现在有LPG 方波输出

f. #echo 1 > enable //取消 LPG 方波输出

三、如何检测PM8921 GPIO 的状态

1. 利用手机系统中 已有 /sys/kernel/debug/pm8921-dbg ,gpio检测接口

a. #echo 0x??? > addr //输入GPIO register addrress

b. #echo 0x??? > data //输入控制GPIO,写入register 的值

c. #cat data //查看当前bank输入的值

2. 查看PM8921 register information 文档

a. Function,Sub funtion,Register name,Register Address,Type,Reset code,default value,comments

GPIO, GPIO_24, GPIO_CNTRL, 0x167, 8B_WR, 5, 00865A01, Power-off and Power-on default state is Hi-Z

b. 所以GPIO24的 Register Address 是0x167

3. 检测GPIO24的配置状态,先设置 bank0-7,再读取在该bank设置的参数

#echo 0x167 > addr

echo 0x0 > data //选择bank0

cat data --> 0x01 //得到在bank0 设置的参数

echo 0x10 > data

cat data -->0x18

echo 0x20> data

cat data -->0x2A

echo 0x30 > data

cat data -->0x34

echo 0x40 > data

cat data -->0x46

echo 0x50 > data

cat data

echo 0x60 > data

cat data

echo 0x70 > data

cat data

直接设置某个bank的参数

#echo 0x167 > addr

#echo 0xc6 > data //各bit含义: [7]->(1W,0R),[6:4]->Bank Select, [3:0]->各bank含义不一样

所以0xC6表示0b11000110, 写入,bank4,写入的参数为0110

四,如何用GPIO24模拟 PWM 输出,主要修改kernel/drivers/mfd/pm8xxx-pwm.c

#define TEST_PWM

#ifdef TEST_PWM

#include <linux/mfd/pm8xxx/gpio.h>

#include <linux/delay.h>

#endif


struct pm8xxx_pwm_dbg_device {

struct mutex dbg_mutex;

struct device *dev;

struct dentry *dent;

struct pm8xxx_pwm_user *user;

#ifdef TEST_PWM

struct work_struct pwm_work;

struct workqueue_struct *pwm_wq;

bool pwm_run;

#endif


};

#ifdef TEST_PWM

static int dbg_pwm_value_set(void *data, u64 val)

{

struct pm8xxx_pwm_user *puser = data;

struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;

int gpio_sys = 175;//PM8921_GPIO_PM_TO_SYS(24);

mutex_lock(&dbgdev->dbg_mutex);

gpio_set_value_cansleep(gpio_sys, val);

mutex_unlock(&dbgdev->dbg_mutex);

pr_debug("#### value = %d\n",(int)val);

return 0;

}

static int dbg_pwm_value_get(void *data, u64 *val)

{

struct pm8xxx_pwm_user *puser = data;

struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;

int gpio_sys = 175;//PM8921_GPIO_PM_TO_SYS(24);

mutex_lock(&dbgdev->dbg_mutex);

*val =gpio_get_value_cansleep(gpio_sys);

mutex_unlock(&dbgdev->dbg_mutex);

pr_debug("#### value = %d\n",(int)*val);

return 0;

}

DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_value_fops,

dbg_pwm_value_get, dbg_pwm_value_set, "%lld\n");

static void pwm_instant_work(struct work_struct *work)

{

int gpio_sys = 175;//PM8921_GPIO_PM_TO_SYS(24);

struct pm8xxx_pwm_dbg_device *dbgdev= container_of(work, struct pm8xxx_pwm_dbg_device, pwm_work);

struct pm8xxx_pwm_user *puser;

int period;

int duty_cycle;

int high_us, low_us;

pr_debug(" ### start output signal\n");

puser = dbgdev->user;

period = puser->period;

duty_cycle = puser->duty_cycle;

if( !period || !duty_cycle )

return;

high_us = period * duty_cycle /100;

low_us = period - high_us;

pr_debug("### hight_us = %d, low_us = %d \n ", high_us, low_us);

while(dbgdev->pwm_run)

{

gpio_set_value_cansleep(gpio_sys, 1);

//usleep(high_us);

udelay(high_us);

gpio_set_value_cansleep(gpio_sys, 0);

//usleep(low_us);

udelay(low_us);

}

}

static int dbg_pwm_output_set(void *data, u64 val)

{

struct pm8xxx_pwm_user *puser = data;

struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;

mutex_lock(&dbgdev->dbg_mutex);

pr_debug("#### value = %d\n",(int)val);

if( val )

{

dbgdev->pwm_run = true;

queue_work(dbgdev->pwm_wq, &dbgdev->pwm_work);

}

else

{

dbgdev->pwm_run = false;

}

mutex_unlock(&dbgdev->dbg_mutex);

return 0;

}

static int dbg_pwm_output_get(void *data, u64 *val)

{

struct pm8xxx_pwm_user *puser = data;

struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;

mutex_lock(&dbgdev->dbg_mutex);

*val = dbgdev->pwm_run;

mutex_unlock(&dbgdev->dbg_mutex);

return 0;

}

DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_output_fops,

dbg_pwm_output_get, dbg_pwm_output_set, "%lld\n");

#endif


static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)

{

....

if (temp == NULL || IS_ERR(temp)) {

pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);

rc = -ENOMEM;

goto debug_error;

}

#ifdef TEST_PWM

temp = debugfs_create_file("value", S_IRUGO | S_IWUSR,

dent, puser, &dbg_pwm_value_fops);

if (temp == NULL || IS_ERR(temp)) {

pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);

rc = -ENOMEM;

goto debug_error;

}

temp = debugfs_create_file("output", S_IRUGO | S_IWUSR,

dent, puser, &dbg_pwm_output_fops);

if (temp == NULL || IS_ERR(temp)) {

pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);

rc = -ENOMEM;

goto debug_error;

}

#endif


....

pmic_dbg_device = dbgdev;

#ifdef TEST_PWM

dbgdev->pwm_wq = create_singlethread_workqueue("pwm_wq");

INIT_WORK(&dbgdev->pwm_work, pwm_instant_work);

#endif


return 0;

....

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: