b.liu | b17525e | 2025-05-14 17:22:29 +0800 | [diff] [blame^] | 1 | /* |
| 2 | * Battery driver for ASR PM802 PMIC |
| 3 | * |
| 4 | * Copyright 2018 ASR Microelectronics (Shanghai) Co., Ltd. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License version 2 as |
| 8 | * published by the Free Software Foundation. |
| 9 | */ |
| 10 | |
| 11 | #include <linux/kernel.h> |
| 12 | #include <linux/module.h> |
| 13 | #include <linux/platform_device.h> |
| 14 | #include <linux/slab.h> |
| 15 | #include <linux/string.h> |
| 16 | #include <linux/power_supply.h> |
| 17 | #include <linux/mfd/88pm80x.h> |
| 18 | #include <linux/mfd/pm802.h> |
| 19 | #include <linux/mfd/pm813.h> |
| 20 | #include <linux/delay.h> |
| 21 | #include <linux/init.h> |
| 22 | #include <linux/sched.h> |
| 23 | #include <linux/of_device.h> |
| 24 | |
| 25 | |
| 26 | static int channel_value = 0; |
| 27 | static int adc_channel; |
| 28 | static u32 g_ADC0_conversion_coefficient = 1000; //1.0 |
| 29 | static u32 g_ADC1_conversion_coefficient = 1000; //1.0 |
| 30 | static int g_board_adc0_channel = 0; |
| 31 | static int g_board_adc1_channel = 1; |
| 32 | |
| 33 | #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| 34 | #define VABT_DEFAULT_PAIR 20 |
| 35 | extern void update_vbat_default_caldata(u8 adc_id, u32 *table,u16 count); |
| 36 | extern void pm803_start_sample_adc(int op,int delay_read_time_ms,u32 read_interval_ms,u16 read_wait_ms); |
| 37 | #endif |
| 38 | |
| 39 | |
| 40 | static ssize_t pm80x_adc_store(struct device *dev, |
| 41 | struct device_attribute *attr, |
| 42 | const char *buf, size_t count) |
| 43 | { |
| 44 | |
| 45 | sscanf(buf, "%d", &adc_channel); |
| 46 | channel_value = 0; |
| 47 | |
| 48 | #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| 49 | pm803_start_sample_adc(1,500,100,20); |
| 50 | #endif |
| 51 | |
| 52 | if (adc_channel == 1) |
| 53 | { |
| 54 | channel_value = extern_get_gpadc_volt(g_board_adc1_channel); |
| 55 | }else if (adc_channel == 0){ |
| 56 | channel_value = extern_get_gpadc_volt(g_board_adc0_channel); |
| 57 | } |
| 58 | else{ |
| 59 | channel_value = 0; |
| 60 | } |
| 61 | |
| 62 | #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| 63 | pm803_start_sample_adc(2,0,0,0); //restore adc update parameters |
| 64 | #endif |
| 65 | |
| 66 | return strnlen(buf, PAGE_SIZE); |
| 67 | } |
| 68 | |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | |
| 73 | 1806 auxadc linux接口: |
| 74 | extern_get_auxadc_volt(ASR_AUXADC1); |
| 75 | param: |
| 76 | |
| 77 | ASR_AUXADC1 = 5 |
| 78 | ASR_AUXADC2 = 6 |
| 79 | ASR_AUXADC3 = 7 |
| 80 | ASR_AUXADC4 = 8 |
| 81 | ASR_AUXADC5 = 9 |
| 82 | */ |
| 83 | |
| 84 | static ssize_t pm80x_adc_shown(struct device *dev, |
| 85 | struct device_attribute *attr, char *buf) |
| 86 | { |
| 87 | int s = 0; |
| 88 | unsigned long level; |
| 89 | u32 coefficient = 1000; |
| 90 | |
| 91 | /* for MBTK_PROJECT_L509 :upper resistance 750k + low resistance 270k*/ |
| 92 | if(adc_channel == 0) |
| 93 | { |
| 94 | coefficient = g_ADC0_conversion_coefficient; |
| 95 | } |
| 96 | else if(adc_channel == 1) |
| 97 | { |
| 98 | coefficient = g_ADC1_conversion_coefficient; |
| 99 | } |
| 100 | |
| 101 | //level = channel_value * 1020 / 270; for L509 |
| 102 | level = (channel_value *coefficient)/1000; |
| 103 | |
| 104 | s += sprintf(buf, "adc_channel%d=%d\n", adc_channel, level); |
| 105 | return s; |
| 106 | } |
| 107 | |
| 108 | |
| 109 | |
| 110 | static ssize_t asr1806adc_store(struct device *dev, |
| 111 | struct device_attribute *attr, |
| 112 | const char *buf, size_t count) |
| 113 | { |
| 114 | |
| 115 | sscanf(buf, "%d", &adc_channel); |
| 116 | |
| 117 | |
| 118 | if (adc_channel == 0) |
| 119 | { |
| 120 | channel_value = extern_get_auxadc_volt(5); |
| 121 | }else if (adc_channel == 1){ |
| 122 | channel_value = extern_get_auxadc_volt(8); |
| 123 | }else if (adc_channel == 2){ |
| 124 | channel_value = extern_get_auxadc_volt(6); |
| 125 | |
| 126 | }else{ |
| 127 | channel_value = 0; |
| 128 | } |
| 129 | |
| 130 | return strnlen(buf, PAGE_SIZE); |
| 131 | } |
| 132 | |
| 133 | static ssize_t asr1806adc_shown(struct device *dev, |
| 134 | struct device_attribute *attr, char *buf) |
| 135 | { |
| 136 | int s = 0; |
| 137 | |
| 138 | s += sprintf(buf, "adc_channel%d=%d\n", adc_channel, channel_value); |
| 139 | return s; |
| 140 | } |
| 141 | |
| 142 | static DEVICE_ATTR(pm80x_adc, 0644, pm80x_adc_shown, pm80x_adc_store); |
| 143 | static DEVICE_ATTR(aux_adc, 0644, asr1806adc_shown, asr1806adc_store); |
| 144 | |
| 145 | static __refdata struct attribute *adc_attrs[] = { |
| 146 | &dev_attr_pm80x_adc.attr, |
| 147 | &dev_attr_aux_adc.attr, |
| 148 | NULL, |
| 149 | }; |
| 150 | |
| 151 | static __refdata struct attribute_group adc_attr_group = { |
| 152 | .attrs = adc_attrs, |
| 153 | }; |
| 154 | |
| 155 | |
| 156 | |
| 157 | |
| 158 | static int asr_adc_probe(struct platform_device *pdev) |
| 159 | { |
| 160 | |
| 161 | int ret; |
| 162 | struct device_node *np = pdev->dev.of_node; |
| 163 | |
| 164 | #if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY) |
| 165 | u32 adc0_table[VABT_DEFAULT_PAIR]; |
| 166 | u32 adc1_table[VABT_DEFAULT_PAIR]; |
| 167 | /*从dts中读取默认校准数据,这只是默认值,还是需要校准的,这个节点用处不大,实际uboot会把默认参数或者校准数据传递到kernel,但如果没有在fal_con_p401.h中开启CONFIG_PM803_GPADC宏 |
| 168 | * 则不会从uboot 拷贝默认校准数据,会导致adc采样异常,暂时保留,建议dts数据跟marvell\uboot\drivers\power\pmic\pmic_mrvl_common.c里的数组保持一致 |
| 169 | */ |
| 170 | ret = of_property_read_u32_array(np, "vbat_def_pair_table-adc0", adc0_table,VABT_DEFAULT_PAIR); |
| 171 | |
| 172 | if(!ret) |
| 173 | { |
| 174 | update_vbat_default_caldata(0,adc0_table,VABT_DEFAULT_PAIR/2); |
| 175 | } |
| 176 | |
| 177 | ret = of_property_read_u32_array(np, "vbat_def_pair_table-adc1", adc1_table,VABT_DEFAULT_PAIR); |
| 178 | |
| 179 | if(!ret) |
| 180 | { |
| 181 | update_vbat_default_caldata(1,adc1_table,VABT_DEFAULT_PAIR/2); |
| 182 | } |
| 183 | |
| 184 | //pm803_adc_init(); |
| 185 | #endif |
| 186 | |
| 187 | /*mbtk_tanggaoyou add: 有些模块内部有分压电路,则需要设置分压电路的转换系数,注意是针对模块内部的分压电路,不是客户外部的分压电路*/ |
| 188 | ret = of_property_read_u32(np, "conversion_coefficient-adc0", &g_ADC0_conversion_coefficient); |
| 189 | if(ret != 0) |
| 190 | { |
| 191 | g_ADC0_conversion_coefficient = 1000;//1.0 |
| 192 | } |
| 193 | ret = of_property_read_u32(np, "conversion_coefficient-adc1", &g_ADC1_conversion_coefficient); |
| 194 | if(ret != 0) |
| 195 | { |
| 196 | g_ADC1_conversion_coefficient = 1000;//1.0 |
| 197 | } |
| 198 | |
| 199 | /*mbtk_tanggaoyou add: 添加adc0_channel和adc1_channel是为了解决L508硬件管脚ADC1对应的是软件的adc1,而ADC2对应的是adc0,需要交换一下*/ |
| 200 | ret = of_property_read_u32(np, "adc0_channel", &g_board_adc0_channel); //get ADC0's channel |
| 201 | if(ret != 0) |
| 202 | { |
| 203 | g_board_adc0_channel = 0; //RTN of PM803 |
| 204 | } |
| 205 | ret = of_property_read_u32(np, "adc1_channel", &g_board_adc1_channel);//get ADC1's channel |
| 206 | if(ret != 0) |
| 207 | { |
| 208 | g_board_adc1_channel = 1; //RTP of PM803 |
| 209 | } |
| 210 | /*mbtk_tanggaoyou add end */ |
| 211 | |
| 212 | 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); |
| 213 | |
| 214 | ret = sysfs_create_group(&pdev->dev.kobj, &adc_attr_group); |
| 215 | if (ret) { |
| 216 | printk("--->%s/L%d", __FUNCTION__, __LINE__); |
| 217 | dev_err(&pdev->dev, "adc create sysfs fail!\n"); |
| 218 | return ret; |
| 219 | } |
| 220 | return 0; |
| 221 | } |
| 222 | |
| 223 | |
| 224 | static int asr_adc_remove(struct platform_device *pdev) |
| 225 | { |
| 226 | return 0; |
| 227 | } |
| 228 | |
| 229 | |
| 230 | static const struct of_device_id asr_adc_dt_match[] = { |
| 231 | { .compatible = "asr,adc", }, |
| 232 | { }, |
| 233 | }; |
| 234 | MODULE_DEVICE_TABLE(of, asr_adc_dt_match); |
| 235 | |
| 236 | static struct platform_driver asr_adc_driver = { |
| 237 | .driver = { |
| 238 | .name = "asr-adc", |
| 239 | .owner = THIS_MODULE, |
| 240 | .of_match_table = of_match_ptr(asr_adc_dt_match), |
| 241 | }, |
| 242 | .probe = asr_adc_probe, |
| 243 | .remove = asr_adc_remove, |
| 244 | }; |
| 245 | |
| 246 | static int __init asr_adc_init(void) |
| 247 | { |
| 248 | return platform_driver_register(&asr_adc_driver); |
| 249 | } |
| 250 | module_init(asr_adc_init); |
| 251 | |
| 252 | static void __exit asr_adc_exit(void) |
| 253 | { |
| 254 | platform_driver_unregister(&asr_adc_driver); |
| 255 | } |
| 256 | module_exit(asr_adc_exit); |
| 257 | |
| 258 | MODULE_DESCRIPTION("ASR PM802 Adc driver"); |
| 259 | MODULE_LICENSE("GPL"); |