288 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// max8997_charger.c - Power supply consumer driver for the Maxim 8997/89664//5// Copyright (C) 2011 Samsung Electronics6// MyungJoo Ham <myungjoo.ham@samsung.com>7 8#include <linux/err.h>9#include <linux/extcon.h>10#include <linux/module.h>11#include <linux/slab.h>12#include <linux/platform_device.h>13#include <linux/power_supply.h>14#include <linux/mfd/max8997.h>15#include <linux/mfd/max8997-private.h>16#include <linux/regulator/consumer.h>17#include <linux/devm-helpers.h>18 19/* MAX8997_REG_STATUS4 */20#define DCINOK_SHIFT 121#define DCINOK_MASK (1 << DCINOK_SHIFT)22#define DETBAT_SHIFT 223#define DETBAT_MASK (1 << DETBAT_SHIFT)24 25/* MAX8997_REG_MBCCTRL1 */26#define TFCH_SHIFT 427#define TFCH_MASK (7 << TFCH_SHIFT)28 29/* MAX8997_REG_MBCCTRL5 */30#define ITOPOFF_SHIFT 031#define ITOPOFF_MASK (0xF << ITOPOFF_SHIFT)32 33struct charger_data {34 struct device *dev;35 struct max8997_dev *iodev;36 struct power_supply *battery;37 struct regulator *reg;38 struct extcon_dev *edev;39 struct notifier_block extcon_nb;40 struct work_struct extcon_work;41};42 43static enum power_supply_property max8997_battery_props[] = {44 POWER_SUPPLY_PROP_STATUS, /* "FULL", "CHARGING" or "DISCHARGING". */45 POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */46 POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */47};48 49/* Note that the charger control is done by a current regulator "CHARGER" */50static int max8997_battery_get_property(struct power_supply *psy,51 enum power_supply_property psp,52 union power_supply_propval *val)53{54 struct charger_data *charger = power_supply_get_drvdata(psy);55 struct i2c_client *i2c = charger->iodev->i2c;56 int ret;57 u8 reg;58 59 switch (psp) {60 case POWER_SUPPLY_PROP_STATUS:61 val->intval = 0;62 ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®);63 if (ret)64 return ret;65 if ((reg & (1 << 0)) == 0x1)66 val->intval = POWER_SUPPLY_STATUS_FULL;67 else if ((reg & DCINOK_MASK))68 val->intval = POWER_SUPPLY_STATUS_CHARGING;69 else70 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;71 72 break;73 case POWER_SUPPLY_PROP_PRESENT:74 val->intval = 0;75 ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®);76 if (ret)77 return ret;78 if ((reg & DETBAT_MASK) == 0x0)79 val->intval = 1;80 81 break;82 case POWER_SUPPLY_PROP_ONLINE:83 val->intval = 0;84 ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®);85 if (ret)86 return ret;87 if (reg & DCINOK_MASK)88 val->intval = 1;89 90 break;91 default:92 return -EINVAL;93 }94 95 return 0;96}97 98static void max8997_battery_extcon_evt_worker(struct work_struct *work)99{100 struct charger_data *charger =101 container_of(work, struct charger_data, extcon_work);102 struct extcon_dev *edev = charger->edev;103 int current_limit;104 105 if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {106 dev_dbg(charger->dev, "USB SDP charger is connected\n");107 current_limit = 450000;108 } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {109 dev_dbg(charger->dev, "USB DCP charger is connected\n");110 current_limit = 650000;111 } else if (extcon_get_state(edev, EXTCON_CHG_USB_FAST) > 0) {112 dev_dbg(charger->dev, "USB FAST charger is connected\n");113 current_limit = 650000;114 } else if (extcon_get_state(edev, EXTCON_CHG_USB_SLOW) > 0) {115 dev_dbg(charger->dev, "USB SLOW charger is connected\n");116 current_limit = 650000;117 } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {118 dev_dbg(charger->dev, "USB CDP charger is connected\n");119 current_limit = 650000;120 } else {121 dev_dbg(charger->dev, "USB charger is disconnected\n");122 current_limit = -1;123 }124 125 if (current_limit > 0) {126 int ret = regulator_set_current_limit(charger->reg, current_limit, current_limit);127 128 if (ret) {129 dev_err(charger->dev, "failed to set current limit: %d\n", ret);130 return;131 }132 ret = regulator_enable(charger->reg);133 if (ret)134 dev_err(charger->dev, "failed to enable regulator: %d\n", ret);135 } else {136 int ret = regulator_disable(charger->reg);137 138 if (ret)139 dev_err(charger->dev, "failed to disable regulator: %d\n", ret);140 }141}142 143static int max8997_battery_extcon_evt(struct notifier_block *nb,144 unsigned long event, void *param)145{146 struct charger_data *charger =147 container_of(nb, struct charger_data, extcon_nb);148 schedule_work(&charger->extcon_work);149 return NOTIFY_OK;150}151 152static const struct power_supply_desc max8997_battery_desc = {153 .name = "max8997_pmic",154 .type = POWER_SUPPLY_TYPE_BATTERY,155 .get_property = max8997_battery_get_property,156 .properties = max8997_battery_props,157 .num_properties = ARRAY_SIZE(max8997_battery_props),158};159 160static int max8997_battery_probe(struct platform_device *pdev)161{162 int ret = 0;163 struct charger_data *charger;164 struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);165 struct device_node *np = pdev->dev.of_node;166 struct i2c_client *i2c = iodev->i2c;167 struct max8997_platform_data *pdata = iodev->pdata;168 struct power_supply_config psy_cfg = {};169 170 if (!pdata) {171 dev_err(&pdev->dev, "No platform data supplied.\n");172 return -EINVAL;173 }174 175 if (pdata->eoc_mA) {176 int val = (pdata->eoc_mA - 50) / 10;177 if (val < 0)178 val = 0;179 if (val > 0xf)180 val = 0xf;181 182 ret = max8997_update_reg(i2c, MAX8997_REG_MBCCTRL5,183 val << ITOPOFF_SHIFT, ITOPOFF_MASK);184 if (ret < 0) {185 dev_err(&pdev->dev, "Cannot use i2c bus.\n");186 return ret;187 }188 }189 switch (pdata->timeout) {190 case 5:191 ret = max8997_update_reg(i2c, MAX8997_REG_MBCCTRL1,192 0x2 << TFCH_SHIFT, TFCH_MASK);193 break;194 case 6:195 ret = max8997_update_reg(i2c, MAX8997_REG_MBCCTRL1,196 0x3 << TFCH_SHIFT, TFCH_MASK);197 break;198 case 7:199 ret = max8997_update_reg(i2c, MAX8997_REG_MBCCTRL1,200 0x4 << TFCH_SHIFT, TFCH_MASK);201 break;202 case 0:203 ret = max8997_update_reg(i2c, MAX8997_REG_MBCCTRL1,204 0x7 << TFCH_SHIFT, TFCH_MASK);205 break;206 default:207 dev_err(&pdev->dev, "incorrect timeout value (%d)\n",208 pdata->timeout);209 return -EINVAL;210 }211 if (ret < 0) {212 dev_err(&pdev->dev, "Cannot use i2c bus.\n");213 return ret;214 }215 216 charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);217 if (!charger)218 return -ENOMEM;219 220 platform_set_drvdata(pdev, charger);221 222 charger->dev = &pdev->dev;223 charger->iodev = iodev;224 225 psy_cfg.drv_data = charger;226 227 charger->battery = devm_power_supply_register(&pdev->dev,228 &max8997_battery_desc,229 &psy_cfg);230 if (IS_ERR(charger->battery)) {231 dev_err(&pdev->dev, "failed: power supply register\n");232 return PTR_ERR(charger->battery);233 }234 235 // grab regulator from parent device's node236 pdev->dev.of_node = iodev->dev->of_node;237 charger->reg = devm_regulator_get_optional(&pdev->dev, "charger");238 pdev->dev.of_node = np;239 if (IS_ERR(charger->reg)) {240 if (PTR_ERR(charger->reg) == -EPROBE_DEFER)241 return -EPROBE_DEFER;242 dev_info(&pdev->dev, "couldn't get charger regulator\n");243 }244 charger->edev = extcon_get_extcon_dev("max8997-muic");245 if (IS_ERR(charger->edev)) {246 dev_err_probe(charger->dev, PTR_ERR(charger->edev),247 "couldn't get extcon device: max8997-muic\n");248 return PTR_ERR(charger->edev);249 }250 251 if (!IS_ERR(charger->reg) && !IS_ERR_OR_NULL(charger->edev)) {252 ret = devm_work_autocancel(&pdev->dev, &charger->extcon_work,253 max8997_battery_extcon_evt_worker);254 if (ret) {255 dev_err(&pdev->dev, "failed to add extcon evt stop action: %d\n", ret);256 return ret;257 }258 charger->extcon_nb.notifier_call = max8997_battery_extcon_evt;259 ret = devm_extcon_register_notifier_all(&pdev->dev, charger->edev,260 &charger->extcon_nb);261 if (ret) {262 dev_err(&pdev->dev, "failed to register extcon notifier\n");263 return ret;264 }265 }266 267 return 0;268}269 270static const struct platform_device_id max8997_battery_id[] = {271 { "max8997-battery", 0 },272 { }273};274MODULE_DEVICE_TABLE(platform, max8997_battery_id);275 276static struct platform_driver max8997_battery_driver = {277 .driver = {278 .name = "max8997-battery",279 },280 .probe = max8997_battery_probe,281 .id_table = max8997_battery_id,282};283module_platform_driver(max8997_battery_driver);284 285MODULE_DESCRIPTION("MAXIM 8997/8966 battery control driver");286MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");287MODULE_LICENSE("GPL");288