前言:由于客户需要在系统里读到主板是否有出厂带电池,调过原理得知可以读取ADC通道值电压去检测是否带有电池,且电压值具体为多少,封装成设备节点然后供上层调用,写入app显示。(读取通道值有一个gpio使能口直连cpu,io拉高才能正确读取到准确电压,读完后需要拉低,仿照待机功耗增加)
在Kernel层driver/misc/mediatek/目录下新建一层目录mid_adc即可开始:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MID_ADC_DEVICE “mid_adc”
#define AUXADC_RTC_VOL_CHANNEL 2//channel 2 to read ADC voltage
//static DEFINE_MUTEX(mid_adc_set_gpio_mutex);
static struct pinctrl *pinctrl1;
static struct pinctrl_state *mid_adc_en_output_high,*mid_adc_en_output_low;
static DEFINE_MUTEX(mid_adc_ctrl_mutex);
static unsigned int rtc_adc_value = 0;
extern int IMM_IsAdcInitReady(void);
extern int IMM_GetOneChannelValue(int dwChannel, int data[4], int *rawdata);
static unsigned int rtc_get_current_voltage(int Channel)
{
int ret = 0, data[4], i, ret_value = 0, ret_temp = 0, times = 5;
if (IMM_IsAdcInitReady() == 0) {
printk( "[kzhkzh] AUXADC is not ready");
return 0;
}
i = times;
while (i--) {
ret_value = IMM_GetOneChannelValue(Channel, data, &ret_temp);
printk("adc voltage[kzhkzh]ret_temp:%d\n",ret_temp);
if (ret_value == 0) {
ret += ret_temp;
} else {
times = times > 1 ? times - 1 : 1;
printk( "kzhkzh [rtc_get_current_voltage] ret_value=%d, times=%d\n",
ret_value, times);
}
}
ret = ret*1500/4096;
ret = ret/times;
ret +=ret;//读取分压电压值x2
return ret;
}
int mid_adc_get_gpio_info(struct platform_device *pdev)
{
int ret;
printk("[kzhkzh] mid_adc_get_gpio_info start+++++++++++++++++\n");
pinctrl1 = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(pinctrl1)) {
ret = PTR_ERR(pinctrl1);
dev_err(&pdev->dev, “Cannot find mid_leds pinctrl1!\n”);
return ret;
}
mid_adc_en_output_high = pinctrl_lookup_state(pinctrl1, "mid_adc_en_output_high");
if (IS_ERR(mid_adc_en_output_high)) {
ret = PTR_ERR(mid_adc_en_output_high);
dev_err(&pdev->dev, "Cannot find mid_adc pinctrl mid_adc_en_output_high!\n");
return ret;
}
mid_adc_en_output_low = pinctrl_lookup_state(pinctrl1, "mid_adc_en_output_low");
if (IS_ERR(mid_adc_en_output_low)) {
ret = PTR_ERR(mid_adc_en_output_low);
dev_err(&pdev->dev, "Cannot find mid_adc pinctrl mid_adc_en_output_low!\n");
return ret;
}
printk("[kzhkzh] mid_adc_get_gpio_info end+++++++++++++++++\n");
return 0;
}
void mid_adc_en(int en)
{
if (en) {
pinctrl_select_state(pinctrl1, mid_adc_en_output_high);
printk("[kzhkzh] %s:%d set mid_adc_en_output_high 1\n",func,LINE);
} else {
pinctrl_select_state(pinctrl1, mid_adc_en_output_low);
printk("[kzhkzh] %s:%d set mid_adc_en_output_low 0\n",func,LINE);
}
}
//---------------------------------------------------------------------
// node1:sys/devices/platform/mid_adc/rtc_adc_status read rtc_vbat adc voltage value
// node2:sys/devices/platform/mid_adc/rtc_adc_en enable rtc_en pinctrl output high level or low level
static ssize_t show_mid_adc_read(struct device *dev,struct device_attribute *attr, char *buf)
{
printk(“show_mid_adc_read!!\n”);
return sprintf(buf, “%d\n”, rtc_get_current_voltage(AUXADC_RTC_VOL_CHANNEL));//kzhkzh 读取rtc adc 电压值到文件节点
}
static ssize_t mid_adc_en_store_ctrl(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
mutex_lock(&mid_adc_ctrl_mutex);
if (!strncmp(buf, "1", 1)) {//buf ==1
printk("[kzhkzh] open mid_rtc_adc_en !!\n");
mid_adc_en(1);
}else {
printk("[kzhkzh] close mid_rtc_adc_en !!\n");
mid_adc_en(0);
}
mutex_unlock(&mid_adc_ctrl_mutex);
return count;
}
static DEVICE_ATTR(rtc_adc_status, 0444, show_mid_adc_read,NULL);
static DEVICE_ATTR(rtc_adc_en, 0664, NULL,mid_adc_en_store_ctrl);
static struct attribute *mid_adc_attrs[] = {
&dev_attr_rtc_adc_en.attr,
&dev_attr_rtc_adc_status.attr,
NULL
};
static const struct attribute_group mid_adc_attrs_group = {
.attrs = mid_adc_attrs
};
//--------------------------------------------------------------------------
static int mid_adc_probe(struct platform_device *pdev)
{
int ret;
ret = mid_adc_get_gpio_info(pdev);
printk("kzhkzh %s:%d \n",__func__,__LINE__);
if (ret)
{
pr_err("mid_adc_get_gpio_info failed!");
return ret;
}
mid_adc_en(0);
rtc_adc_value = rtc_get_current_voltage(AUXADC_RTC_VOL_CHANNEL);
printk("[kzhkzh] %s:%d rtc_adc_value:%d\n",__func__,__LINE__,rtc_adc_value);
//--------------------------------------------------------------------
ret = sysfs_create_group(&pdev->dev.kobj,&mid_adc_attrs_group);
printk("[kzhkzh] %s:%d ret:%d\n",__func__,__LINE__,ret);
if (ret < 0) {
printk("[kzhkzh] %s: unable to create mid_adc_attrs_group\n", __func__);
}
//--------------------------------------------------------------------
printk(“[kzhkzh] %s:%d end\n”,func,LINE);
return 0;
}
static const struct of_device_id adc_match_table[] = {
{ .compatible = “mediatek,mid_adc”, },
{}
};
//MODULE_DEVICE_TABLE(of, adc_match_table);
static struct platform_driver mid_adc_driver = {
.probe = mid_adc_probe,
.driver = {
.name = MID_ADC_DEVICE,
.owner = THIS_MODULE,
.of_match_table = adc_match_table,
}
};
static int __init mid_adc_init(void)
{
int ret = 0;
ret = platform_driver_register(&mid_adc_driver);
printk("[kzhkzh] mid_adc_init ret:%d !!!! \n");
if (ret < 0)
pr_err("unable to register mid_leds driver.\n");
return 0;
}
static void __exit mid_adc_exit(void)
{
platform_driver_unregister(&mid_adc_driver);
}
module_init(mid_adc_init);
module_exit(mid_adc_exit);
MODULE_LICENSE(“GPL”);
MODULE_DESCRIPTION(“MediaTek mid_adc_detect driver”);
MODULE_AUTHOR(“[email protected]”);
同时:dts增加节点
mid_adc: mid_adc {
compatible = "mediatek,mid_adc";
};
+&mid_adc {
//compatible = "mediatek,mid_adc";
pinctrl-names = "mid_adc_default",
"mid_adc_en_output_high",
"mid_adc_en_output_low";
pinctrl-0 = <&mid_adc_default>;
pinctrl-1 = <&mid_adc_en_output_high>;
pinctrl-2 = <&mid_adc_en_output_low>;
+};
+&pio {
mid_adc_default: mid_adc_default {
};
mid_adc_en_output_high: mid_adc_en_output_high {
pins_cmd_dat {
pins = ;
slew-rate = <1>;
output-high;
};
};
mid_adc_en_output_low: mid_adc_en_output_low {
pins_cmd_dat {
pins = ;
slew-rate = <1>;
output-low;
};
};
最终节点注册挂载在:
// node1:sys/devices/platform/mid_adc/rtc_adc_status read rtc_vbat adc voltage value
// node2:sys/devices/platform/mid_adc/rtc_adc_en enable rtc_en pinctrl output high level or low level