116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * Nokia RX-51 battery driver
48c0984e5SSebastian Reichel  *
5149ed3d4SPali Rohár  * Copyright (C) 2012  Pali Rohár <pali@kernel.org>
68c0984e5SSebastian Reichel  */
78c0984e5SSebastian Reichel 
88c0984e5SSebastian Reichel #include <linux/module.h>
98c0984e5SSebastian Reichel #include <linux/param.h>
108c0984e5SSebastian Reichel #include <linux/platform_device.h>
118c0984e5SSebastian Reichel #include <linux/power_supply.h>
128c0984e5SSebastian Reichel #include <linux/slab.h>
138c0984e5SSebastian Reichel #include <linux/iio/consumer.h>
148c0984e5SSebastian Reichel #include <linux/of.h>
158c0984e5SSebastian Reichel 
168c0984e5SSebastian Reichel struct rx51_device_info {
178c0984e5SSebastian Reichel 	struct device *dev;
188c0984e5SSebastian Reichel 	struct power_supply *bat;
198c0984e5SSebastian Reichel 	struct power_supply_desc bat_desc;
208c0984e5SSebastian Reichel 	struct iio_channel *channel_temp;
218c0984e5SSebastian Reichel 	struct iio_channel *channel_bsi;
228c0984e5SSebastian Reichel 	struct iio_channel *channel_vbat;
238c0984e5SSebastian Reichel };
248c0984e5SSebastian Reichel 
258c0984e5SSebastian Reichel /*
268c0984e5SSebastian Reichel  * Read ADCIN channel value, code copied from maemo kernel
278c0984e5SSebastian Reichel  */
rx51_battery_read_adc(struct iio_channel * channel)288c0984e5SSebastian Reichel static int rx51_battery_read_adc(struct iio_channel *channel)
298c0984e5SSebastian Reichel {
308c0984e5SSebastian Reichel 	int val, err;
318c0984e5SSebastian Reichel 	err = iio_read_channel_average_raw(channel, &val);
328c0984e5SSebastian Reichel 	if (err < 0)
338c0984e5SSebastian Reichel 		return err;
348c0984e5SSebastian Reichel 	return val;
358c0984e5SSebastian Reichel }
368c0984e5SSebastian Reichel 
378c0984e5SSebastian Reichel /*
388c0984e5SSebastian Reichel  * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage
398c0984e5SSebastian Reichel  * This conversion formula was extracted from maemo program bsi-read
408c0984e5SSebastian Reichel  */
rx51_battery_read_voltage(struct rx51_device_info * di)418c0984e5SSebastian Reichel static int rx51_battery_read_voltage(struct rx51_device_info *di)
428c0984e5SSebastian Reichel {
438c0984e5SSebastian Reichel 	int voltage = rx51_battery_read_adc(di->channel_vbat);
448c0984e5SSebastian Reichel 
458c0984e5SSebastian Reichel 	if (voltage < 0) {
468c0984e5SSebastian Reichel 		dev_err(di->dev, "Could not read ADC: %d\n", voltage);
478c0984e5SSebastian Reichel 		return voltage;
488c0984e5SSebastian Reichel 	}
498c0984e5SSebastian Reichel 
508c0984e5SSebastian Reichel 	return 1000 * (10000 * voltage / 1705);
518c0984e5SSebastian Reichel }
528c0984e5SSebastian Reichel 
538c0984e5SSebastian Reichel /*
548c0984e5SSebastian Reichel  * Temperature look-up tables
558c0984e5SSebastian Reichel  * TEMP = (1/(t1 + 1/298) - 273.15)
568c0984e5SSebastian Reichel  * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255))
578c0984e5SSebastian Reichel  * Formula is based on experimental data, RX-51 CAL data, maemo program bme
588c0984e5SSebastian Reichel  * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671
598c0984e5SSebastian Reichel  */
608c0984e5SSebastian Reichel 
618c0984e5SSebastian Reichel /*
628c0984e5SSebastian Reichel  * Table1 (temperature for first 25 RAW values)
638c0984e5SSebastian Reichel  * Usage: TEMP = rx51_temp_table1[RAW]
648c0984e5SSebastian Reichel  *   RAW is between 1 and 24
658c0984e5SSebastian Reichel  *   TEMP is between 201 C and 55 C
668c0984e5SSebastian Reichel  */
678c0984e5SSebastian Reichel static u8 rx51_temp_table1[] = {
688c0984e5SSebastian Reichel 	255, 201, 159, 138, 124, 114, 106,  99,  94,  89,  85,  82,  78,  75,
698c0984e5SSebastian Reichel 	 73,  70,  68,  66,  64,  62,  61,  59,  57,  56,  55
708c0984e5SSebastian Reichel };
718c0984e5SSebastian Reichel 
728c0984e5SSebastian Reichel /*
738c0984e5SSebastian Reichel  * Table2 (lowest RAW value for temperature)
748c0984e5SSebastian Reichel  * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first]
758c0984e5SSebastian Reichel  *   TEMP is between 53 C and -32 C
768c0984e5SSebastian Reichel  *   RAW is between 25 and 993
778c0984e5SSebastian Reichel  */
788c0984e5SSebastian Reichel #define rx51_temp_table2_first 53
798c0984e5SSebastian Reichel static u16 rx51_temp_table2[] = {
808c0984e5SSebastian Reichel 	 25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  39,
818c0984e5SSebastian Reichel 	 40,  41,  43,  44,  46,  48,  49,  51,  53,  55,  57,  59,  61,  64,
828c0984e5SSebastian Reichel 	 66,  69,  71,  74,  77,  80,  83,  86,  90,  94,  97, 101, 106, 110,
838c0984e5SSebastian Reichel 	115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211,
848c0984e5SSebastian Reichel 	221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415,
858c0984e5SSebastian Reichel 	437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885,
868c0984e5SSebastian Reichel 	937, 993, 1024
878c0984e5SSebastian Reichel };
888c0984e5SSebastian Reichel 
898c0984e5SSebastian Reichel /*
908c0984e5SSebastian Reichel  * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius
918c0984e5SSebastian Reichel  * Use Temperature look-up tables for conversation
928c0984e5SSebastian Reichel  */
rx51_battery_read_temperature(struct rx51_device_info * di)938c0984e5SSebastian Reichel static int rx51_battery_read_temperature(struct rx51_device_info *di)
948c0984e5SSebastian Reichel {
958c0984e5SSebastian Reichel 	int min = 0;
968c0984e5SSebastian Reichel 	int max = ARRAY_SIZE(rx51_temp_table2) - 1;
978c0984e5SSebastian Reichel 	int raw = rx51_battery_read_adc(di->channel_temp);
988c0984e5SSebastian Reichel 
998c0984e5SSebastian Reichel 	if (raw < 0)
1008c0984e5SSebastian Reichel 		dev_err(di->dev, "Could not read ADC: %d\n", raw);
1018c0984e5SSebastian Reichel 
1028c0984e5SSebastian Reichel 	/* Zero and negative values are undefined */
1038c0984e5SSebastian Reichel 	if (raw <= 0)
1048c0984e5SSebastian Reichel 		return INT_MAX;
1058c0984e5SSebastian Reichel 
1068c0984e5SSebastian Reichel 	/* ADC channels are 10 bit, higher value are undefined */
1078c0984e5SSebastian Reichel 	if (raw >= (1 << 10))
1088c0984e5SSebastian Reichel 		return INT_MIN;
1098c0984e5SSebastian Reichel 
1108c0984e5SSebastian Reichel 	/* First check for temperature in first direct table */
1118c0984e5SSebastian Reichel 	if (raw < ARRAY_SIZE(rx51_temp_table1))
1128c0984e5SSebastian Reichel 		return rx51_temp_table1[raw] * 10;
1138c0984e5SSebastian Reichel 
1148c0984e5SSebastian Reichel 	/* Binary search RAW value in second inverse table */
1158c0984e5SSebastian Reichel 	while (max - min > 1) {
1168c0984e5SSebastian Reichel 		int mid = (max + min) / 2;
1178c0984e5SSebastian Reichel 		if (rx51_temp_table2[mid] <= raw)
1188c0984e5SSebastian Reichel 			min = mid;
1198c0984e5SSebastian Reichel 		else if (rx51_temp_table2[mid] > raw)
1208c0984e5SSebastian Reichel 			max = mid;
1218c0984e5SSebastian Reichel 		if (rx51_temp_table2[mid] == raw)
1228c0984e5SSebastian Reichel 			break;
1238c0984e5SSebastian Reichel 	}
1248c0984e5SSebastian Reichel 
1258c0984e5SSebastian Reichel 	return (rx51_temp_table2_first - min) * 10;
1268c0984e5SSebastian Reichel }
1278c0984e5SSebastian Reichel 
1288c0984e5SSebastian Reichel /*
1298c0984e5SSebastian Reichel  * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah
1308c0984e5SSebastian Reichel  * This conversion formula was extracted from maemo program bsi-read
1318c0984e5SSebastian Reichel  */
rx51_battery_read_capacity(struct rx51_device_info * di)1328c0984e5SSebastian Reichel static int rx51_battery_read_capacity(struct rx51_device_info *di)
1338c0984e5SSebastian Reichel {
1348c0984e5SSebastian Reichel 	int capacity = rx51_battery_read_adc(di->channel_bsi);
1358c0984e5SSebastian Reichel 
1368c0984e5SSebastian Reichel 	if (capacity < 0) {
1378c0984e5SSebastian Reichel 		dev_err(di->dev, "Could not read ADC: %d\n", capacity);
1388c0984e5SSebastian Reichel 		return capacity;
1398c0984e5SSebastian Reichel 	}
1408c0984e5SSebastian Reichel 
1418c0984e5SSebastian Reichel 	return 1280 * (1200 * capacity)/(1024 - capacity);
1428c0984e5SSebastian Reichel }
1438c0984e5SSebastian Reichel 
1448c0984e5SSebastian Reichel /*
1458c0984e5SSebastian Reichel  * Return power_supply property
1468c0984e5SSebastian Reichel  */
rx51_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)1478c0984e5SSebastian Reichel static int rx51_battery_get_property(struct power_supply *psy,
1488c0984e5SSebastian Reichel 					enum power_supply_property psp,
1498c0984e5SSebastian Reichel 					union power_supply_propval *val)
1508c0984e5SSebastian Reichel {
1518c0984e5SSebastian Reichel 	struct rx51_device_info *di = power_supply_get_drvdata(psy);
1528c0984e5SSebastian Reichel 
1538c0984e5SSebastian Reichel 	switch (psp) {
1548c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TECHNOLOGY:
1558c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
1568c0984e5SSebastian Reichel 		break;
1578c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
1588c0984e5SSebastian Reichel 		val->intval = 4200000;
1598c0984e5SSebastian Reichel 		break;
1608c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
1618c0984e5SSebastian Reichel 		val->intval = rx51_battery_read_voltage(di) ? 1 : 0;
1628c0984e5SSebastian Reichel 		break;
1638c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1648c0984e5SSebastian Reichel 		val->intval = rx51_battery_read_voltage(di);
1658c0984e5SSebastian Reichel 		break;
1668c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TEMP:
1678c0984e5SSebastian Reichel 		val->intval = rx51_battery_read_temperature(di);
1688c0984e5SSebastian Reichel 		break;
1698c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
1708c0984e5SSebastian Reichel 		val->intval = rx51_battery_read_capacity(di);
1718c0984e5SSebastian Reichel 		break;
1728c0984e5SSebastian Reichel 	default:
1738c0984e5SSebastian Reichel 		return -EINVAL;
1748c0984e5SSebastian Reichel 	}
1758c0984e5SSebastian Reichel 
1768c0984e5SSebastian Reichel 	if (val->intval == INT_MAX || val->intval == INT_MIN)
1778c0984e5SSebastian Reichel 		return -EINVAL;
1788c0984e5SSebastian Reichel 
1798c0984e5SSebastian Reichel 	return 0;
1808c0984e5SSebastian Reichel }
1818c0984e5SSebastian Reichel 
1828c0984e5SSebastian Reichel static enum power_supply_property rx51_battery_props[] = {
1838c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
1848c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
1858c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
1868c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1878c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TEMP,
1888c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
1898c0984e5SSebastian Reichel };
1908c0984e5SSebastian Reichel 
rx51_battery_probe(struct platform_device * pdev)1918c0984e5SSebastian Reichel static int rx51_battery_probe(struct platform_device *pdev)
1928c0984e5SSebastian Reichel {
1938c0984e5SSebastian Reichel 	struct power_supply_config psy_cfg = {};
1948c0984e5SSebastian Reichel 	struct rx51_device_info *di;
1958c0984e5SSebastian Reichel 	int ret;
1968c0984e5SSebastian Reichel 
1978c0984e5SSebastian Reichel 	di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
1988c0984e5SSebastian Reichel 	if (!di)
1998c0984e5SSebastian Reichel 		return -ENOMEM;
2008c0984e5SSebastian Reichel 
2018c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, di);
2028c0984e5SSebastian Reichel 
2038c0984e5SSebastian Reichel 	di->dev = &pdev->dev;
2048c0984e5SSebastian Reichel 	di->bat_desc.name = "rx51-battery";
2058c0984e5SSebastian Reichel 	di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
2068c0984e5SSebastian Reichel 	di->bat_desc.properties = rx51_battery_props;
2078c0984e5SSebastian Reichel 	di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props);
2088c0984e5SSebastian Reichel 	di->bat_desc.get_property = rx51_battery_get_property;
2098c0984e5SSebastian Reichel 
2108c0984e5SSebastian Reichel 	psy_cfg.drv_data = di;
2118c0984e5SSebastian Reichel 
2128c0984e5SSebastian Reichel 	di->channel_temp = iio_channel_get(di->dev, "temp");
2138c0984e5SSebastian Reichel 	if (IS_ERR(di->channel_temp)) {
2148c0984e5SSebastian Reichel 		ret = PTR_ERR(di->channel_temp);
2158c0984e5SSebastian Reichel 		goto error;
2168c0984e5SSebastian Reichel 	}
2178c0984e5SSebastian Reichel 
2188c0984e5SSebastian Reichel 	di->channel_bsi  = iio_channel_get(di->dev, "bsi");
2198c0984e5SSebastian Reichel 	if (IS_ERR(di->channel_bsi)) {
2208c0984e5SSebastian Reichel 		ret = PTR_ERR(di->channel_bsi);
2218c0984e5SSebastian Reichel 		goto error_channel_temp;
2228c0984e5SSebastian Reichel 	}
2238c0984e5SSebastian Reichel 
2248c0984e5SSebastian Reichel 	di->channel_vbat = iio_channel_get(di->dev, "vbat");
2258c0984e5SSebastian Reichel 	if (IS_ERR(di->channel_vbat)) {
2268c0984e5SSebastian Reichel 		ret = PTR_ERR(di->channel_vbat);
2278c0984e5SSebastian Reichel 		goto error_channel_bsi;
2288c0984e5SSebastian Reichel 	}
2298c0984e5SSebastian Reichel 
2308c0984e5SSebastian Reichel 	di->bat = power_supply_register(di->dev, &di->bat_desc, &psy_cfg);
2318c0984e5SSebastian Reichel 	if (IS_ERR(di->bat)) {
2328c0984e5SSebastian Reichel 		ret = PTR_ERR(di->bat);
2338c0984e5SSebastian Reichel 		goto error_channel_vbat;
2348c0984e5SSebastian Reichel 	}
2358c0984e5SSebastian Reichel 
2368c0984e5SSebastian Reichel 	return 0;
2378c0984e5SSebastian Reichel 
2388c0984e5SSebastian Reichel error_channel_vbat:
2398c0984e5SSebastian Reichel 	iio_channel_release(di->channel_vbat);
2408c0984e5SSebastian Reichel error_channel_bsi:
2418c0984e5SSebastian Reichel 	iio_channel_release(di->channel_bsi);
2428c0984e5SSebastian Reichel error_channel_temp:
2438c0984e5SSebastian Reichel 	iio_channel_release(di->channel_temp);
2448c0984e5SSebastian Reichel error:
2458c0984e5SSebastian Reichel 
2468c0984e5SSebastian Reichel 	return ret;
2478c0984e5SSebastian Reichel }
2488c0984e5SSebastian Reichel 
rx51_battery_remove(struct platform_device * pdev)2498c0984e5SSebastian Reichel static int rx51_battery_remove(struct platform_device *pdev)
2508c0984e5SSebastian Reichel {
2518c0984e5SSebastian Reichel 	struct rx51_device_info *di = platform_get_drvdata(pdev);
2528c0984e5SSebastian Reichel 
2538c0984e5SSebastian Reichel 	power_supply_unregister(di->bat);
2548c0984e5SSebastian Reichel 
2558c0984e5SSebastian Reichel 	iio_channel_release(di->channel_vbat);
2568c0984e5SSebastian Reichel 	iio_channel_release(di->channel_bsi);
2578c0984e5SSebastian Reichel 	iio_channel_release(di->channel_temp);
2588c0984e5SSebastian Reichel 
2598c0984e5SSebastian Reichel 	return 0;
2608c0984e5SSebastian Reichel }
2618c0984e5SSebastian Reichel 
2628c0984e5SSebastian Reichel #ifdef CONFIG_OF
2638c0984e5SSebastian Reichel static const struct of_device_id n900_battery_of_match[] = {
2648c0984e5SSebastian Reichel 	{.compatible = "nokia,n900-battery", },
2658c0984e5SSebastian Reichel 	{ },
2668c0984e5SSebastian Reichel };
2678c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, n900_battery_of_match);
2688c0984e5SSebastian Reichel #endif
2698c0984e5SSebastian Reichel 
2708c0984e5SSebastian Reichel static struct platform_driver rx51_battery_driver = {
2718c0984e5SSebastian Reichel 	.probe = rx51_battery_probe,
2728c0984e5SSebastian Reichel 	.remove = rx51_battery_remove,
2738c0984e5SSebastian Reichel 	.driver = {
2748c0984e5SSebastian Reichel 		.name = "rx51-battery",
2758c0984e5SSebastian Reichel 		.of_match_table = of_match_ptr(n900_battery_of_match),
2768c0984e5SSebastian Reichel 	},
2778c0984e5SSebastian Reichel };
2788c0984e5SSebastian Reichel module_platform_driver(rx51_battery_driver);
2798c0984e5SSebastian Reichel 
2808c0984e5SSebastian Reichel MODULE_ALIAS("platform:rx51-battery");
281149ed3d4SPali Rohár MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
2828c0984e5SSebastian Reichel MODULE_DESCRIPTION("Nokia RX-51 battery driver");
2838c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
284