0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/module.h>
0014 #include <linux/param.h>
0015 #include <linux/delay.h>
0016 #include <linux/workqueue.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/power_supply.h>
0019 #include <linux/slab.h>
0020 #include <linux/sort.h>
0021 #include <linux/power/twl4030_madc_battery.h>
0022 #include <linux/iio/consumer.h>
0023
0024 struct twl4030_madc_battery {
0025 struct power_supply *psy;
0026 struct twl4030_madc_bat_platform_data *pdata;
0027 struct iio_channel *channel_temp;
0028 struct iio_channel *channel_ichg;
0029 struct iio_channel *channel_vbat;
0030 };
0031
0032 static enum power_supply_property twl4030_madc_bat_props[] = {
0033 POWER_SUPPLY_PROP_PRESENT,
0034 POWER_SUPPLY_PROP_STATUS,
0035 POWER_SUPPLY_PROP_TECHNOLOGY,
0036 POWER_SUPPLY_PROP_VOLTAGE_NOW,
0037 POWER_SUPPLY_PROP_CURRENT_NOW,
0038 POWER_SUPPLY_PROP_CAPACITY,
0039 POWER_SUPPLY_PROP_CHARGE_FULL,
0040 POWER_SUPPLY_PROP_CHARGE_NOW,
0041 POWER_SUPPLY_PROP_TEMP,
0042 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
0043 };
0044
0045 static int madc_read(struct iio_channel *channel)
0046 {
0047 int val, err;
0048 err = iio_read_channel_processed(channel, &val);
0049 if (err < 0)
0050 return err;
0051
0052 return val;
0053 }
0054
0055 static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt)
0056 {
0057 return (madc_read(bt->channel_ichg) > 0) ? 1 : 0;
0058 }
0059
0060 static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt)
0061 {
0062 return madc_read(bt->channel_vbat);
0063 }
0064
0065 static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt)
0066 {
0067 return madc_read(bt->channel_ichg) * 1000;
0068 }
0069
0070 static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt)
0071 {
0072 return madc_read(bt->channel_temp) * 10;
0073 }
0074
0075 static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat,
0076 int volt)
0077 {
0078 struct twl4030_madc_bat_calibration *calibration;
0079 int i, res = 0;
0080
0081
0082 if (twl4030_madc_bat_get_charging_status(bat))
0083 calibration = bat->pdata->charging;
0084 else
0085 calibration = bat->pdata->discharging;
0086
0087 if (volt > calibration[0].voltage) {
0088 res = calibration[0].level;
0089 } else {
0090 for (i = 0; calibration[i+1].voltage >= 0; i++) {
0091 if (volt <= calibration[i].voltage &&
0092 volt >= calibration[i+1].voltage) {
0093
0094 res = calibration[i].level -
0095 ((calibration[i].voltage - volt) *
0096 (calibration[i].level -
0097 calibration[i+1].level)) /
0098 (calibration[i].voltage -
0099 calibration[i+1].voltage);
0100 break;
0101 }
0102 }
0103 }
0104 return res;
0105 }
0106
0107 static int twl4030_madc_bat_get_property(struct power_supply *psy,
0108 enum power_supply_property psp,
0109 union power_supply_propval *val)
0110 {
0111 struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy);
0112
0113 switch (psp) {
0114 case POWER_SUPPLY_PROP_STATUS:
0115 if (twl4030_madc_bat_voltscale(bat,
0116 twl4030_madc_bat_get_voltage(bat)) > 95)
0117 val->intval = POWER_SUPPLY_STATUS_FULL;
0118 else {
0119 if (twl4030_madc_bat_get_charging_status(bat))
0120 val->intval = POWER_SUPPLY_STATUS_CHARGING;
0121 else
0122 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
0123 }
0124 break;
0125 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0126 val->intval = twl4030_madc_bat_get_voltage(bat) * 1000;
0127 break;
0128 case POWER_SUPPLY_PROP_TECHNOLOGY:
0129 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
0130 break;
0131 case POWER_SUPPLY_PROP_CURRENT_NOW:
0132 val->intval = twl4030_madc_bat_get_current(bat);
0133 break;
0134 case POWER_SUPPLY_PROP_PRESENT:
0135
0136 val->intval = 1;
0137 break;
0138 case POWER_SUPPLY_PROP_CHARGE_NOW: {
0139 int percent = twl4030_madc_bat_voltscale(bat,
0140 twl4030_madc_bat_get_voltage(bat));
0141 val->intval = (percent * bat->pdata->capacity) / 100;
0142 break;
0143 }
0144 case POWER_SUPPLY_PROP_CAPACITY:
0145 val->intval = twl4030_madc_bat_voltscale(bat,
0146 twl4030_madc_bat_get_voltage(bat));
0147 break;
0148 case POWER_SUPPLY_PROP_CHARGE_FULL:
0149 val->intval = bat->pdata->capacity;
0150 break;
0151 case POWER_SUPPLY_PROP_TEMP:
0152 val->intval = twl4030_madc_bat_get_temp(bat);
0153 break;
0154 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {
0155 int percent = twl4030_madc_bat_voltscale(bat,
0156 twl4030_madc_bat_get_voltage(bat));
0157
0158 int chg = (percent * (bat->pdata->capacity/1000))/100;
0159
0160
0161 val->intval = (3600l * chg) / 400;
0162 break;
0163 }
0164 default:
0165 return -EINVAL;
0166 }
0167
0168 return 0;
0169 }
0170
0171 static void twl4030_madc_bat_ext_changed(struct power_supply *psy)
0172 {
0173 power_supply_changed(psy);
0174 }
0175
0176 static const struct power_supply_desc twl4030_madc_bat_desc = {
0177 .name = "twl4030_battery",
0178 .type = POWER_SUPPLY_TYPE_BATTERY,
0179 .properties = twl4030_madc_bat_props,
0180 .num_properties = ARRAY_SIZE(twl4030_madc_bat_props),
0181 .get_property = twl4030_madc_bat_get_property,
0182 .external_power_changed = twl4030_madc_bat_ext_changed,
0183
0184 };
0185
0186 static int twl4030_cmp(const void *a, const void *b)
0187 {
0188 return ((struct twl4030_madc_bat_calibration *)b)->voltage -
0189 ((struct twl4030_madc_bat_calibration *)a)->voltage;
0190 }
0191
0192 static int twl4030_madc_battery_probe(struct platform_device *pdev)
0193 {
0194 struct twl4030_madc_battery *twl4030_madc_bat;
0195 struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data;
0196 struct power_supply_config psy_cfg = {};
0197 int ret = 0;
0198
0199 twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat),
0200 GFP_KERNEL);
0201 if (!twl4030_madc_bat)
0202 return -ENOMEM;
0203
0204 twl4030_madc_bat->channel_temp = iio_channel_get(&pdev->dev, "temp");
0205 if (IS_ERR(twl4030_madc_bat->channel_temp)) {
0206 ret = PTR_ERR(twl4030_madc_bat->channel_temp);
0207 goto err;
0208 }
0209
0210 twl4030_madc_bat->channel_ichg = iio_channel_get(&pdev->dev, "ichg");
0211 if (IS_ERR(twl4030_madc_bat->channel_ichg)) {
0212 ret = PTR_ERR(twl4030_madc_bat->channel_ichg);
0213 goto err_temp;
0214 }
0215
0216 twl4030_madc_bat->channel_vbat = iio_channel_get(&pdev->dev, "vbat");
0217 if (IS_ERR(twl4030_madc_bat->channel_vbat)) {
0218 ret = PTR_ERR(twl4030_madc_bat->channel_vbat);
0219 goto err_ichg;
0220 }
0221
0222
0223 sort(pdata->charging, pdata->charging_size,
0224 sizeof(struct twl4030_madc_bat_calibration),
0225 twl4030_cmp, NULL);
0226 sort(pdata->discharging, pdata->discharging_size,
0227 sizeof(struct twl4030_madc_bat_calibration),
0228 twl4030_cmp, NULL);
0229
0230 twl4030_madc_bat->pdata = pdata;
0231 platform_set_drvdata(pdev, twl4030_madc_bat);
0232 psy_cfg.drv_data = twl4030_madc_bat;
0233 twl4030_madc_bat->psy = power_supply_register(&pdev->dev,
0234 &twl4030_madc_bat_desc,
0235 &psy_cfg);
0236 if (IS_ERR(twl4030_madc_bat->psy)) {
0237 ret = PTR_ERR(twl4030_madc_bat->psy);
0238 goto err_vbat;
0239 }
0240
0241 return 0;
0242
0243 err_vbat:
0244 iio_channel_release(twl4030_madc_bat->channel_vbat);
0245 err_ichg:
0246 iio_channel_release(twl4030_madc_bat->channel_ichg);
0247 err_temp:
0248 iio_channel_release(twl4030_madc_bat->channel_temp);
0249 err:
0250 return ret;
0251 }
0252
0253 static int twl4030_madc_battery_remove(struct platform_device *pdev)
0254 {
0255 struct twl4030_madc_battery *bat = platform_get_drvdata(pdev);
0256
0257 power_supply_unregister(bat->psy);
0258
0259 iio_channel_release(bat->channel_vbat);
0260 iio_channel_release(bat->channel_ichg);
0261 iio_channel_release(bat->channel_temp);
0262
0263 return 0;
0264 }
0265
0266 static struct platform_driver twl4030_madc_battery_driver = {
0267 .driver = {
0268 .name = "twl4030_madc_battery",
0269 },
0270 .probe = twl4030_madc_battery_probe,
0271 .remove = twl4030_madc_battery_remove,
0272 };
0273 module_platform_driver(twl4030_madc_battery_driver);
0274
0275 MODULE_LICENSE("GPL");
0276 MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>");
0277 MODULE_DESCRIPTION("twl4030_madc battery driver");
0278 MODULE_ALIAS("platform:twl4030_madc_battery");