| /* |
| * Battery driver for ASR PM802 PMIC |
| * |
| * Copyright 2018 ASR Microelectronics (Shanghai) Co., Ltd. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/string.h> |
| #include <linux/power_supply.h> |
| #include <linux/mfd/88pm80x.h> |
| #include <linux/mfd/pm802.h> |
| #include <linux/mfd/pm813.h> |
| #include <linux/delay.h> |
| #include <linux/init.h> |
| #include <linux/sched.h> |
| #include <linux/of_device.h> |
| |
| |
| static int channel_value = 0; |
| static int adc_channel; |
| static u32 g_ADC0_conversion_coefficient = 1000; //1.0 |
| static u32 g_ADC1_conversion_coefficient = 1000; //1.0 |
| static int g_board_adc0_channel = 0; |
| static int g_board_adc1_channel = 1; |
| |
| #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| #define VABT_DEFAULT_PAIR 20 |
| extern void update_vbat_default_caldata(u8 adc_id, u32 *table,u16 count); |
| extern void pm803_start_sample_adc(int op,int delay_read_time_ms,u32 read_interval_ms,u16 read_wait_ms); |
| #endif |
| |
| |
| static ssize_t pm80x_adc_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| |
| sscanf(buf, "%d", &adc_channel); |
| channel_value = 0; |
| |
| #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| pm803_start_sample_adc(1,500,100,20); |
| #endif |
| |
| if (adc_channel == 1) |
| { |
| channel_value = extern_get_gpadc_volt(g_board_adc1_channel); |
| }else if (adc_channel == 0){ |
| channel_value = extern_get_gpadc_volt(g_board_adc0_channel); |
| } |
| else{ |
| channel_value = 0; |
| } |
| |
| #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| pm803_start_sample_adc(2,0,0,0); //restore adc update parameters |
| #endif |
| |
| return strnlen(buf, PAGE_SIZE); |
| } |
| |
| |
| |
| /* |
| |
| 1806 auxadc linux接口: |
| extern_get_auxadc_volt(ASR_AUXADC1); |
| param: |
| |
| ASR_AUXADC1 = 5 |
| ASR_AUXADC2 = 6 |
| ASR_AUXADC3 = 7 |
| ASR_AUXADC4 = 8 |
| ASR_AUXADC5 = 9 |
| */ |
| |
| static ssize_t pm80x_adc_shown(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int s = 0; |
| unsigned long level; |
| u32 coefficient = 1000; |
| |
| /* for MBTK_PROJECT_L509 :upper resistance 750k + low resistance 270k*/ |
| if(adc_channel == 0) |
| { |
| coefficient = g_ADC0_conversion_coefficient; |
| } |
| else if(adc_channel == 1) |
| { |
| coefficient = g_ADC1_conversion_coefficient; |
| } |
| |
| //level = channel_value * 1020 / 270; for L509 |
| level = (channel_value *coefficient)/1000; |
| |
| s += sprintf(buf, "adc_channel%d=%d\n", adc_channel, level); |
| return s; |
| } |
| |
| |
| |
| static ssize_t asr1806adc_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| |
| sscanf(buf, "%d", &adc_channel); |
| |
| |
| if (adc_channel == 0) |
| { |
| channel_value = extern_get_auxadc_volt(5); |
| }else if (adc_channel == 1){ |
| channel_value = extern_get_auxadc_volt(8); |
| }else if (adc_channel == 2){ |
| channel_value = extern_get_auxadc_volt(6); |
| |
| }else{ |
| channel_value = 0; |
| } |
| |
| return strnlen(buf, PAGE_SIZE); |
| } |
| |
| static ssize_t asr1806adc_shown(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int s = 0; |
| |
| s += sprintf(buf, "adc_channel%d=%d\n", adc_channel, channel_value); |
| return s; |
| } |
| |
| static DEVICE_ATTR(pm80x_adc, 0644, pm80x_adc_shown, pm80x_adc_store); |
| static DEVICE_ATTR(aux_adc, 0644, asr1806adc_shown, asr1806adc_store); |
| |
| static __refdata struct attribute *adc_attrs[] = { |
| &dev_attr_pm80x_adc.attr, |
| &dev_attr_aux_adc.attr, |
| NULL, |
| }; |
| |
| static __refdata struct attribute_group adc_attr_group = { |
| .attrs = adc_attrs, |
| }; |
| |
| |
| |
| |
| static int asr_adc_probe(struct platform_device *pdev) |
| { |
| |
| int ret; |
| struct device_node *np = pdev->dev.of_node; |
| |
| #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| u32 adc0_table[VABT_DEFAULT_PAIR]; |
| u32 adc1_table[VABT_DEFAULT_PAIR]; |
| /*从dts中读取默认校准数据,这只是默认值,还是需要校准的,这个节点用处不大,实际uboot会把默认参数或者校准数据传递到kernel,但如果没有在fal_con_p401.h中开启CONFIG_PM803_GPADC宏 |
| * 则不会从uboot 拷贝默认校准数据,会导致adc采样异常,暂时保留,建议dts数据跟marvell\uboot\drivers\power\pmic\pmic_mrvl_common.c里的数组保持一致 |
| */ |
| ret = of_property_read_u32_array(np, "vbat_def_pair_table-adc0", adc0_table,VABT_DEFAULT_PAIR); |
| |
| if(!ret) |
| { |
| update_vbat_default_caldata(0,adc0_table,VABT_DEFAULT_PAIR/2); |
| } |
| |
| ret = of_property_read_u32_array(np, "vbat_def_pair_table-adc1", adc1_table,VABT_DEFAULT_PAIR); |
| |
| if(!ret) |
| { |
| update_vbat_default_caldata(1,adc1_table,VABT_DEFAULT_PAIR/2); |
| } |
| |
| //pm803_adc_init(); |
| #endif |
| |
| /*mbtk_tanggaoyou add: 有些模块内部有分压电路,则需要设置分压电路的转换系数,注意是针对模块内部的分压电路,不是客户外部的分压电路*/ |
| ret = of_property_read_u32(np, "conversion_coefficient-adc0", &g_ADC0_conversion_coefficient); |
| if(ret != 0) |
| { |
| g_ADC0_conversion_coefficient = 1000;//1.0 |
| } |
| ret = of_property_read_u32(np, "conversion_coefficient-adc1", &g_ADC1_conversion_coefficient); |
| if(ret != 0) |
| { |
| g_ADC1_conversion_coefficient = 1000;//1.0 |
| } |
| |
| /*mbtk_tanggaoyou add: 添加adc0_channel和adc1_channel是为了解决L508硬件管脚ADC1对应的是软件的adc1,而ADC2对应的是adc0,需要交换一下*/ |
| ret = of_property_read_u32(np, "adc0_channel", &g_board_adc0_channel); //get ADC0's channel |
| if(ret != 0) |
| { |
| g_board_adc0_channel = 0; //RTN of PM803 |
| } |
| ret = of_property_read_u32(np, "adc1_channel", &g_board_adc1_channel);//get ADC1's channel |
| if(ret != 0) |
| { |
| g_board_adc1_channel = 1; //RTP of PM803 |
| } |
| /*mbtk_tanggaoyou add end */ |
| |
| printk("--->%s/L%d,ADC%d,%d,coefficient=%d %d", __FUNCTION__, __LINE__,g_board_adc0_channel,g_board_adc1_channel,g_ADC0_conversion_coefficient,g_ADC1_conversion_coefficient); |
| |
| ret = sysfs_create_group(&pdev->dev.kobj, &adc_attr_group); |
| if (ret) { |
| printk("--->%s/L%d", __FUNCTION__, __LINE__); |
| dev_err(&pdev->dev, "adc create sysfs fail!\n"); |
| return ret; |
| } |
| return 0; |
| } |
| |
| |
| static int asr_adc_remove(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| |
| |
| static const struct of_device_id asr_adc_dt_match[] = { |
| { .compatible = "asr,adc", }, |
| { }, |
| }; |
| MODULE_DEVICE_TABLE(of, asr_adc_dt_match); |
| |
| static struct platform_driver asr_adc_driver = { |
| .driver = { |
| .name = "asr-adc", |
| .owner = THIS_MODULE, |
| .of_match_table = of_match_ptr(asr_adc_dt_match), |
| }, |
| .probe = asr_adc_probe, |
| .remove = asr_adc_remove, |
| }; |
| |
| static int __init asr_adc_init(void) |
| { |
| return platform_driver_register(&asr_adc_driver); |
| } |
| module_init(asr_adc_init); |
| |
| static void __exit asr_adc_exit(void) |
| { |
| platform_driver_unregister(&asr_adc_driver); |
| } |
| module_exit(asr_adc_exit); |
| |
| MODULE_DESCRIPTION("ASR PM802 Adc driver"); |
| MODULE_LICENSE("GPL"); |