|  | /* | 
|  | * Nokia RX-51 battery driver | 
|  | * | 
|  | * Copyright (C) 2012  Pali Rohár <pali.rohar@gmail.com> | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; either version 2 of the License, or | 
|  | * (at your option) any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License along | 
|  | * with this program; if not, write to the Free Software Foundation, Inc., | 
|  | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
|  | */ | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/param.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/power_supply.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/i2c/twl4030-madc.h> | 
|  | #include <linux/iio/consumer.h> | 
|  | #include <linux/of.h> | 
|  |  | 
|  | struct rx51_device_info { | 
|  | struct device *dev; | 
|  | struct power_supply bat; | 
|  | struct iio_channel *channel_temp; | 
|  | struct iio_channel *channel_bsi; | 
|  | struct iio_channel *channel_vbat; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Read ADCIN channel value, code copied from maemo kernel | 
|  | */ | 
|  | static int rx51_battery_read_adc(struct iio_channel *channel) | 
|  | { | 
|  | int val, err; | 
|  | err = iio_read_channel_average_raw(channel, &val); | 
|  | if (err < 0) | 
|  | return err; | 
|  | return val; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage | 
|  | * This conversion formula was extracted from maemo program bsi-read | 
|  | */ | 
|  | static int rx51_battery_read_voltage(struct rx51_device_info *di) | 
|  | { | 
|  | int voltage = rx51_battery_read_adc(di->channel_vbat); | 
|  |  | 
|  | if (voltage < 0) { | 
|  | dev_err(di->dev, "Could not read ADC: %d\n", voltage); | 
|  | return voltage; | 
|  | } | 
|  |  | 
|  | return 1000 * (10000 * voltage / 1705); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Temperature look-up tables | 
|  | * TEMP = (1/(t1 + 1/298) - 273.15) | 
|  | * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255)) | 
|  | * Formula is based on experimental data, RX-51 CAL data, maemo program bme | 
|  | * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671 | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Table1 (temperature for first 25 RAW values) | 
|  | * Usage: TEMP = rx51_temp_table1[RAW] | 
|  | *   RAW is between 1 and 24 | 
|  | *   TEMP is between 201 C and 55 C | 
|  | */ | 
|  | static u8 rx51_temp_table1[] = { | 
|  | 255, 201, 159, 138, 124, 114, 106,  99,  94,  89,  85,  82,  78,  75, | 
|  | 73,  70,  68,  66,  64,  62,  61,  59,  57,  56,  55 | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Table2 (lowest RAW value for temperature) | 
|  | * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first] | 
|  | *   TEMP is between 53 C and -32 C | 
|  | *   RAW is between 25 and 993 | 
|  | */ | 
|  | #define rx51_temp_table2_first 53 | 
|  | static u16 rx51_temp_table2[] = { | 
|  | 25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  39, | 
|  | 40,  41,  43,  44,  46,  48,  49,  51,  53,  55,  57,  59,  61,  64, | 
|  | 66,  69,  71,  74,  77,  80,  83,  86,  90,  94,  97, 101, 106, 110, | 
|  | 115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211, | 
|  | 221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415, | 
|  | 437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885, | 
|  | 937, 993, 1024 | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius | 
|  | * Use Temperature look-up tables for conversation | 
|  | */ | 
|  | static int rx51_battery_read_temperature(struct rx51_device_info *di) | 
|  | { | 
|  | int min = 0; | 
|  | int max = ARRAY_SIZE(rx51_temp_table2) - 1; | 
|  | int raw = rx51_battery_read_adc(di->channel_temp); | 
|  |  | 
|  | if (raw < 0) | 
|  | dev_err(di->dev, "Could not read ADC: %d\n", raw); | 
|  |  | 
|  | /* Zero and negative values are undefined */ | 
|  | if (raw <= 0) | 
|  | return INT_MAX; | 
|  |  | 
|  | /* ADC channels are 10 bit, higher value are undefined */ | 
|  | if (raw >= (1 << 10)) | 
|  | return INT_MIN; | 
|  |  | 
|  | /* First check for temperature in first direct table */ | 
|  | if (raw < ARRAY_SIZE(rx51_temp_table1)) | 
|  | return rx51_temp_table1[raw] * 10; | 
|  |  | 
|  | /* Binary search RAW value in second inverse table */ | 
|  | while (max - min > 1) { | 
|  | int mid = (max + min) / 2; | 
|  | if (rx51_temp_table2[mid] <= raw) | 
|  | min = mid; | 
|  | else if (rx51_temp_table2[mid] > raw) | 
|  | max = mid; | 
|  | if (rx51_temp_table2[mid] == raw) | 
|  | break; | 
|  | } | 
|  |  | 
|  | return (rx51_temp_table2_first - min) * 10; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah | 
|  | * This conversion formula was extracted from maemo program bsi-read | 
|  | */ | 
|  | static int rx51_battery_read_capacity(struct rx51_device_info *di) | 
|  | { | 
|  | int capacity = rx51_battery_read_adc(di->channel_bsi); | 
|  |  | 
|  | if (capacity < 0) { | 
|  | dev_err(di->dev, "Could not read ADC: %d\n", capacity); | 
|  | return capacity; | 
|  | } | 
|  |  | 
|  | return 1280 * (1200 * capacity)/(1024 - capacity); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return power_supply property | 
|  | */ | 
|  | static int rx51_battery_get_property(struct power_supply *psy, | 
|  | enum power_supply_property psp, | 
|  | union power_supply_propval *val) | 
|  | { | 
|  | struct rx51_device_info *di = container_of((psy), | 
|  | struct rx51_device_info, bat); | 
|  |  | 
|  | switch (psp) { | 
|  | case POWER_SUPPLY_PROP_TECHNOLOGY: | 
|  | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | 
|  | val->intval = 4200000; | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_PRESENT: | 
|  | val->intval = rx51_battery_read_voltage(di) ? 1 : 0; | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | 
|  | val->intval = rx51_battery_read_voltage(di); | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_TEMP: | 
|  | val->intval = rx51_battery_read_temperature(di); | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | 
|  | val->intval = rx51_battery_read_capacity(di); | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (val->intval == INT_MAX || val->intval == INT_MIN) | 
|  | return -EINVAL; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static enum power_supply_property rx51_battery_props[] = { | 
|  | POWER_SUPPLY_PROP_TECHNOLOGY, | 
|  | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | 
|  | POWER_SUPPLY_PROP_PRESENT, | 
|  | POWER_SUPPLY_PROP_VOLTAGE_NOW, | 
|  | POWER_SUPPLY_PROP_TEMP, | 
|  | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | 
|  | }; | 
|  |  | 
|  | static int rx51_battery_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct rx51_device_info *di; | 
|  | int ret; | 
|  |  | 
|  | di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); | 
|  | if (!di) | 
|  | return -ENOMEM; | 
|  |  | 
|  | platform_set_drvdata(pdev, di); | 
|  |  | 
|  | di->dev = &pdev->dev; | 
|  | di->bat.name = dev_name(&pdev->dev); | 
|  | di->bat.type = POWER_SUPPLY_TYPE_BATTERY; | 
|  | di->bat.properties = rx51_battery_props; | 
|  | di->bat.num_properties = ARRAY_SIZE(rx51_battery_props); | 
|  | di->bat.get_property = rx51_battery_get_property; | 
|  |  | 
|  | di->channel_temp = iio_channel_get(di->dev, "temp"); | 
|  | if (IS_ERR(di->channel_temp)) { | 
|  | ret = PTR_ERR(di->channel_temp); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | di->channel_bsi  = iio_channel_get(di->dev, "bsi"); | 
|  | if (IS_ERR(di->channel_bsi)) { | 
|  | ret = PTR_ERR(di->channel_bsi); | 
|  | goto error_channel_temp; | 
|  | } | 
|  |  | 
|  | di->channel_vbat = iio_channel_get(di->dev, "vbat"); | 
|  | if (IS_ERR(di->channel_vbat)) { | 
|  | ret = PTR_ERR(di->channel_vbat); | 
|  | goto error_channel_bsi; | 
|  | } | 
|  |  | 
|  | ret = power_supply_register(di->dev, &di->bat); | 
|  | if (ret) | 
|  | goto error_channel_vbat; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | error_channel_vbat: | 
|  | iio_channel_release(di->channel_vbat); | 
|  | error_channel_bsi: | 
|  | iio_channel_release(di->channel_bsi); | 
|  | error_channel_temp: | 
|  | iio_channel_release(di->channel_temp); | 
|  | error: | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int rx51_battery_remove(struct platform_device *pdev) | 
|  | { | 
|  | struct rx51_device_info *di = platform_get_drvdata(pdev); | 
|  |  | 
|  | power_supply_unregister(&di->bat); | 
|  |  | 
|  | iio_channel_release(di->channel_vbat); | 
|  | iio_channel_release(di->channel_bsi); | 
|  | iio_channel_release(di->channel_temp); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_OF | 
|  | static const struct of_device_id n900_battery_of_match[] = { | 
|  | {.compatible = "nokia,n900-battery", }, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, n900_battery_of_match); | 
|  | #endif | 
|  |  | 
|  | static struct platform_driver rx51_battery_driver = { | 
|  | .probe = rx51_battery_probe, | 
|  | .remove = rx51_battery_remove, | 
|  | .driver = { | 
|  | .name = "rx51-battery", | 
|  | .of_match_table = of_match_ptr(n900_battery_of_match), | 
|  | }, | 
|  | }; | 
|  | module_platform_driver(rx51_battery_driver); | 
|  |  | 
|  | MODULE_ALIAS("platform:rx51-battery"); | 
|  | MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); | 
|  | MODULE_DESCRIPTION("Nokia RX-51 battery driver"); | 
|  | MODULE_LICENSE("GPL"); |