基于PM8916 MPP创建一个Linuxled子系统
首先,从kernel文件系统层面上创建sys文件系统节点/sys/class/leds/button-backlight/brightness的方法,是基于kernel部分驱动kernel/drivers/leds/leds-gpio.c创建的,其中dts中每一个子节点都对应一个Gpio的led,都会生成一个以label名称为目录的led类。
Dts配置如下:
&soc {
gpio-leds {
compatible = "gpio-leds";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&button_backlight_off>;
/*add by eliot shao add adevice led-breath at /sys/class/leds/led-breath- 2016/8/18*/
led-breath {
gpios =<&msm_gpio 8 0>;
label = "led-breath-";
linux,default-trigger = "none";
default-state = "keep"; /*gpio value will keep prrvious value*/
};
keypad-backlight {
gpios =<&msm_gpio 119 0>;
label = "button-backlight";
linux,default-trigger = "none";
};
};
};
但是如果控制引脚不是AP的gpio,而是PMIC上的MPP引脚,过程就不是这样的了。
PMIC上的MPP是多功能引脚的意思,可以做电源、gpio、ADC、PWM、SINK等功能。
2、 驱动程序
kernel/drivers/leds/leds-qpnp.c
这里说一下第一次遇到这种问题解决思路:
Mpp2脚控制LED,如果自己写驱动的话关键点是如何配置PM8916上和MPP上相关的寄存器,将MPP2配置成为GPIO或者pwm模式。写一个设置mpp2输出高低或者pwm频率的函数,包装成led的class注册到sys文件系统中。
找到PM 8916相关的dts文件,查看其支持的设备有哪些,查找和led相关的设备。
打开Y:\LA.BR.1.2.4-05310-8x16.0\kernel\arch\arm\boot\dts\qcom\msm-pm8916.dtsi
在&spmi_bus {};设备节点下定义了
qcom,pm8916@0 {
……
pm8916_leds: qcom,leds@a100 {
compatible= "qcom,leds-qpnp";
reg= <0xa100 0x100>;
label= "mpp";
};
};
继续跟踪compatible = “qcom,leds-qpnp”;检索qcom,leds-qpnp。
找到驱动文件:
kernel/drivers/leds/leds-qpnp.c
分析驱动:
#ifdefCONFIG_OF
staticstruct of_device_id spmi_match_table[] = {
{ .compatible = "qcom,leds-qpnp",},
{ },
};
#else
#definespmi_match_table NULL
#endif
staticstruct spmi_driver qpnp_leds_driver = {
.driver ={
.name ="qcom,leds-qpnp",
.of_match_table = spmi_match_table,
},
.probe =qpnp_leds_probe,
.remove =qpnp_leds_remove,
};
在probe函数主要做一下事情:
1、解析dts各项,分配到structled_classdev cdev;
2、
led->cdev.brightness_set = qpnp_led_set;
led->cdev.brightness_get = qpnp_led_get;
qpnp_led_set函数调用__qpnp_led_work
–>
caseQPNP_ID_LED_MPP:
rc = qpnp_mpp_set(led);
–>
qpnp_led_masked_write(structqpnp_led_data *led, u16 addr, u8 mask, u8 val)
–>
spmi_ext_register_readl; spmi_ext_register_writel;读写PMICmpp相关寄存器。
–>
spmi_read_cmd(ctrl,SPMI_CMD_EXT_READL, sid, addr, len - 1, buf);
spmi_write_cmd(ctrl,op, sid, addr, len - 1, buf);
–>
pa_read_data; pmic_arb_write
–>
writel_relaxed(val,dev->wrbase + offset);
u32val = readl_relaxed(dev->rdbase + offset);
3、注册led_classdev_register(&spmi->dev,&led->cdev);
整个过程就是MPP+led子系统的架构,和GPIO+led子系统的架构类似,区别就是硬件控制方式。
3、 dts配置
&spmi_bus{
qcom,pm8916@0 {
qcom,leds@a100 {
status = "okay";
qcom,led_mpp_2 {
label = "mpp";
linux,name = "button-backlight";
linux,default-trigger ="none";
qcom,default-state ="off";
qcom,max-current = <40>;
qcom,current-setting =<5>;
qcom,id = <6>;
qcom,mode = "manual";
qcom,source-sel = <1>;
qcom,mode-ctrl =<0x10>; /*change mode to digitaloutput*/
};
};
};
};
创建/sys/class/leds/button-backlight/brightness