rx51_battery.c (7411B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Nokia RX-51 battery driver 4 * 5 * Copyright (C) 2012 Pali Rohár <pali@kernel.org> 6 */ 7 8#include <linux/module.h> 9#include <linux/param.h> 10#include <linux/platform_device.h> 11#include <linux/power_supply.h> 12#include <linux/slab.h> 13#include <linux/iio/consumer.h> 14#include <linux/of.h> 15 16struct rx51_device_info { 17 struct device *dev; 18 struct power_supply *bat; 19 struct power_supply_desc bat_desc; 20 struct iio_channel *channel_temp; 21 struct iio_channel *channel_bsi; 22 struct iio_channel *channel_vbat; 23}; 24 25/* 26 * Read ADCIN channel value, code copied from maemo kernel 27 */ 28static int rx51_battery_read_adc(struct iio_channel *channel) 29{ 30 int val, err; 31 err = iio_read_channel_average_raw(channel, &val); 32 if (err < 0) 33 return err; 34 return val; 35} 36 37/* 38 * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage 39 * This conversion formula was extracted from maemo program bsi-read 40 */ 41static int rx51_battery_read_voltage(struct rx51_device_info *di) 42{ 43 int voltage = rx51_battery_read_adc(di->channel_vbat); 44 45 if (voltage < 0) { 46 dev_err(di->dev, "Could not read ADC: %d\n", voltage); 47 return voltage; 48 } 49 50 return 1000 * (10000 * voltage / 1705); 51} 52 53/* 54 * Temperature look-up tables 55 * TEMP = (1/(t1 + 1/298) - 273.15) 56 * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255)) 57 * Formula is based on experimental data, RX-51 CAL data, maemo program bme 58 * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671 59 */ 60 61/* 62 * Table1 (temperature for first 25 RAW values) 63 * Usage: TEMP = rx51_temp_table1[RAW] 64 * RAW is between 1 and 24 65 * TEMP is between 201 C and 55 C 66 */ 67static u8 rx51_temp_table1[] = { 68 255, 201, 159, 138, 124, 114, 106, 99, 94, 89, 85, 82, 78, 75, 69 73, 70, 68, 66, 64, 62, 61, 59, 57, 56, 55 70}; 71 72/* 73 * Table2 (lowest RAW value for temperature) 74 * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first] 75 * TEMP is between 53 C and -32 C 76 * RAW is between 25 and 993 77 */ 78#define rx51_temp_table2_first 53 79static u16 rx51_temp_table2[] = { 80 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 81 40, 41, 43, 44, 46, 48, 49, 51, 53, 55, 57, 59, 61, 64, 82 66, 69, 71, 74, 77, 80, 83, 86, 90, 94, 97, 101, 106, 110, 83 115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211, 84 221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415, 85 437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885, 86 937, 993, 1024 87}; 88 89/* 90 * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius 91 * Use Temperature look-up tables for conversation 92 */ 93static int rx51_battery_read_temperature(struct rx51_device_info *di) 94{ 95 int min = 0; 96 int max = ARRAY_SIZE(rx51_temp_table2) - 1; 97 int raw = rx51_battery_read_adc(di->channel_temp); 98 99 if (raw < 0) 100 dev_err(di->dev, "Could not read ADC: %d\n", raw); 101 102 /* Zero and negative values are undefined */ 103 if (raw <= 0) 104 return INT_MAX; 105 106 /* ADC channels are 10 bit, higher value are undefined */ 107 if (raw >= (1 << 10)) 108 return INT_MIN; 109 110 /* First check for temperature in first direct table */ 111 if (raw < ARRAY_SIZE(rx51_temp_table1)) 112 return rx51_temp_table1[raw] * 10; 113 114 /* Binary search RAW value in second inverse table */ 115 while (max - min > 1) { 116 int mid = (max + min) / 2; 117 if (rx51_temp_table2[mid] <= raw) 118 min = mid; 119 else if (rx51_temp_table2[mid] > raw) 120 max = mid; 121 if (rx51_temp_table2[mid] == raw) 122 break; 123 } 124 125 return (rx51_temp_table2_first - min) * 10; 126} 127 128/* 129 * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah 130 * This conversion formula was extracted from maemo program bsi-read 131 */ 132static int rx51_battery_read_capacity(struct rx51_device_info *di) 133{ 134 int capacity = rx51_battery_read_adc(di->channel_bsi); 135 136 if (capacity < 0) { 137 dev_err(di->dev, "Could not read ADC: %d\n", capacity); 138 return capacity; 139 } 140 141 return 1280 * (1200 * capacity)/(1024 - capacity); 142} 143 144/* 145 * Return power_supply property 146 */ 147static int rx51_battery_get_property(struct power_supply *psy, 148 enum power_supply_property psp, 149 union power_supply_propval *val) 150{ 151 struct rx51_device_info *di = power_supply_get_drvdata(psy); 152 153 switch (psp) { 154 case POWER_SUPPLY_PROP_TECHNOLOGY: 155 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 156 break; 157 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 158 val->intval = 4200000; 159 break; 160 case POWER_SUPPLY_PROP_PRESENT: 161 val->intval = rx51_battery_read_voltage(di) ? 1 : 0; 162 break; 163 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 164 val->intval = rx51_battery_read_voltage(di); 165 break; 166 case POWER_SUPPLY_PROP_TEMP: 167 val->intval = rx51_battery_read_temperature(di); 168 break; 169 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 170 val->intval = rx51_battery_read_capacity(di); 171 break; 172 default: 173 return -EINVAL; 174 } 175 176 if (val->intval == INT_MAX || val->intval == INT_MIN) 177 return -EINVAL; 178 179 return 0; 180} 181 182static enum power_supply_property rx51_battery_props[] = { 183 POWER_SUPPLY_PROP_TECHNOLOGY, 184 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 185 POWER_SUPPLY_PROP_PRESENT, 186 POWER_SUPPLY_PROP_VOLTAGE_NOW, 187 POWER_SUPPLY_PROP_TEMP, 188 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 189}; 190 191static int rx51_battery_probe(struct platform_device *pdev) 192{ 193 struct power_supply_config psy_cfg = {}; 194 struct rx51_device_info *di; 195 int ret; 196 197 di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); 198 if (!di) 199 return -ENOMEM; 200 201 platform_set_drvdata(pdev, di); 202 203 di->dev = &pdev->dev; 204 di->bat_desc.name = "rx51-battery"; 205 di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; 206 di->bat_desc.properties = rx51_battery_props; 207 di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props); 208 di->bat_desc.get_property = rx51_battery_get_property; 209 210 psy_cfg.drv_data = di; 211 212 di->channel_temp = iio_channel_get(di->dev, "temp"); 213 if (IS_ERR(di->channel_temp)) { 214 ret = PTR_ERR(di->channel_temp); 215 goto error; 216 } 217 218 di->channel_bsi = iio_channel_get(di->dev, "bsi"); 219 if (IS_ERR(di->channel_bsi)) { 220 ret = PTR_ERR(di->channel_bsi); 221 goto error_channel_temp; 222 } 223 224 di->channel_vbat = iio_channel_get(di->dev, "vbat"); 225 if (IS_ERR(di->channel_vbat)) { 226 ret = PTR_ERR(di->channel_vbat); 227 goto error_channel_bsi; 228 } 229 230 di->bat = power_supply_register(di->dev, &di->bat_desc, &psy_cfg); 231 if (IS_ERR(di->bat)) { 232 ret = PTR_ERR(di->bat); 233 goto error_channel_vbat; 234 } 235 236 return 0; 237 238error_channel_vbat: 239 iio_channel_release(di->channel_vbat); 240error_channel_bsi: 241 iio_channel_release(di->channel_bsi); 242error_channel_temp: 243 iio_channel_release(di->channel_temp); 244error: 245 246 return ret; 247} 248 249static int rx51_battery_remove(struct platform_device *pdev) 250{ 251 struct rx51_device_info *di = platform_get_drvdata(pdev); 252 253 power_supply_unregister(di->bat); 254 255 iio_channel_release(di->channel_vbat); 256 iio_channel_release(di->channel_bsi); 257 iio_channel_release(di->channel_temp); 258 259 return 0; 260} 261 262#ifdef CONFIG_OF 263static const struct of_device_id n900_battery_of_match[] = { 264 {.compatible = "nokia,n900-battery", }, 265 { }, 266}; 267MODULE_DEVICE_TABLE(of, n900_battery_of_match); 268#endif 269 270static struct platform_driver rx51_battery_driver = { 271 .probe = rx51_battery_probe, 272 .remove = rx51_battery_remove, 273 .driver = { 274 .name = "rx51-battery", 275 .of_match_table = of_match_ptr(n900_battery_of_match), 276 }, 277}; 278module_platform_driver(rx51_battery_driver); 279 280MODULE_ALIAS("platform:rx51-battery"); 281MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); 282MODULE_DESCRIPTION("Nokia RX-51 battery driver"); 283MODULE_LICENSE("GPL");