Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/power/supply/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 19 kB image not shown  

Quelle  max8971_charger.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later

#include <linux/devm-helpers.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/of_graph.h>
#include <linux/property.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/sysfs.h>
#include <linux/types.h>

#define MAX8971_REG_CHGINT  0x0f
#define   MAX8971_REG_CHG_RST  BIT(0)
#define MAX8971_REG_CHGINT_MASK  0x01
#define   MAX8971_AICL_MASK  BIT(7)
#define MAX8971_REG_CHG_STAT  0x02
#define   MAX8971_CHG_MASK  BIT(3)
#define MAX8971_REG_DETAILS1  0x03
#define MAX8971_REG_DETAILS2  0x04
#define MAX8971_REG_CHGCNTL1  0x05
#define MAX8971_REG_FCHGCRNT  0x06
#define MAX8971_REG_DCCRNT  0x07
#define   MAX8971_CHGRSTRT_MASK  BIT(6)
#define MAX8971_REG_TOPOFF  0x08
#define MAX8971_REG_TEMPREG  0x09
#define MAX8971_REG_PROTCMD  0x0a
#define   MAX8971_CHGPROT_LOCKED 0x00
#define   MAX8971_CHGPROT_UNLOCKED 0x03

#define MAX8971_FCHGT_DEFAULT  2
#define MAX8971_TOPOFFT_DEFAULT  3

static const char *max8971_manufacturer = "Maxim Integrated";
static const char *max8971_model = "MAX8971";

enum max8971_charging_state {
 MAX8971_CHARGING_DEAD_BATTERY,
 MAX8971_CHARGING_PREQUALIFICATION,
 MAX8971_CHARGING_FAST_CONST_CURRENT,
 MAX8971_CHARGING_FAST_CONST_VOLTAGE,
 MAX8971_CHARGING_TOP_OFF,
 MAX8971_CHARGING_DONE,
 MAX8971_CHARGING_TIMER_FAULT,
 MAX8971_CHARGING_SUSPENDED_THERMAL,
 MAX8971_CHARGING_OFF,
 MAX8971_CHARGING_THERMAL_LOOP,
};

enum max8971_health_state {
 MAX8971_HEALTH_UNKNOWN,
 MAX8971_HEALTH_COLD,
 MAX8971_HEALTH_COOL,
 MAX8971_HEALTH_WARM,
 MAX8971_HEALTH_HOT,
 MAX8971_HEALTH_OVERHEAT,
};

/* Fast-Charge current limit, 250..1550 mA, 50 mA steps */
#define MAX8971_CHG_CC_STEP     50000U
#define MAX8971_CHG_CC_MIN    250000U
#define MAX8971_CHG_CC_MAX   1550000U

/* Input current limit, 250..1500 mA, 25 mA steps */
#define MAX8971_DCILMT_STEP     25000U
#define MAX8971_DCILMT_MIN    250000U
#define MAX8971_DCILMT_MAX   1500000U

enum max8971_field_idx {
 THM_DTLS,  /* DETAILS1 */
 BAT_DTLS, CHG_DTLS, /* DETAILS2 */
 CHG_CC, FCHG_T,  /* FCHGCRNT */
 DCI_LMT,  /* DCCRNT */
 TOPOFF_T, TOPOFF_S, /* TOPOFF */
 CPROT,   /* PROTCMD */
 MAX8971_N_REGMAP_FIELDS
};

static const struct reg_field max8971_reg_field[MAX8971_N_REGMAP_FIELDS] = {
 [THM_DTLS] = REG_FIELD(MAX8971_REG_DETAILS1, 0, 2),
 [BAT_DTLS] = REG_FIELD(MAX8971_REG_DETAILS2, 4, 5),
 [CHG_DTLS] = REG_FIELD(MAX8971_REG_DETAILS2, 0, 3),
 [CHG_CC]   = REG_FIELD(MAX8971_REG_FCHGCRNT, 0, 4),
 [FCHG_T]   = REG_FIELD(MAX8971_REG_FCHGCRNT, 5, 7),
 [DCI_LMT]  = REG_FIELD(MAX8971_REG_DCCRNT,   0, 5),
 [TOPOFF_T] = REG_FIELD(MAX8971_REG_TOPOFF,   5, 7),
 [TOPOFF_S] = REG_FIELD(MAX8971_REG_TOPOFF,   2, 3),
 [CPROT]    = REG_FIELD(MAX8971_REG_PROTCMD,  2, 3),
};

static const struct regmap_config max8971_regmap_config = {
 .reg_bits = 8,
 .val_bits = 8,
 .max_register = MAX8971_REG_CHGINT,
};

struct max8971_data {
 struct device *dev;
 struct power_supply *psy_mains;

 struct extcon_dev *edev;
 struct notifier_block extcon_nb;
 struct delayed_work extcon_work;

 struct regmap *regmap;
 struct regmap_field *rfield[MAX8971_N_REGMAP_FIELDS];

 enum power_supply_usb_type usb_type;

 u32 fchgt;
 u32 tofft;
 u32 toffs;

 bool present;
};

static int max8971_get_status(struct max8971_data *priv, int *val)
{
 u32 regval;
 int err;

 err = regmap_field_read(priv->rfield[CHG_DTLS], ®val);
 if (err)
  return err;

 switch (regval) {
 case MAX8971_CHARGING_DEAD_BATTERY:
 case MAX8971_CHARGING_PREQUALIFICATION:
 case MAX8971_CHARGING_FAST_CONST_CURRENT:
 case MAX8971_CHARGING_FAST_CONST_VOLTAGE:
 case MAX8971_CHARGING_TOP_OFF:
 case MAX8971_CHARGING_THERMAL_LOOP:
  *val = POWER_SUPPLY_STATUS_CHARGING;
  break;
 case MAX8971_CHARGING_DONE:
  *val = POWER_SUPPLY_STATUS_FULL;
  break;
 case MAX8971_CHARGING_TIMER_FAULT:
  *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
  break;
 case MAX8971_CHARGING_OFF:
 case MAX8971_CHARGING_SUSPENDED_THERMAL:
  *val = POWER_SUPPLY_STATUS_DISCHARGING;
  break;
 default:
  *val = POWER_SUPPLY_STATUS_UNKNOWN;
 }

 return 0;
}

static int max8971_get_charge_type(struct max8971_data *priv, int *val)
{
 u32 regval;
 int err;

 err = regmap_field_read(priv->rfield[CHG_DTLS], ®val);
 if (err)
  return err;

 switch (regval) {
 case MAX8971_CHARGING_DEAD_BATTERY:
 case MAX8971_CHARGING_PREQUALIFICATION:
  *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
  break;
 case MAX8971_CHARGING_FAST_CONST_CURRENT:
 case MAX8971_CHARGING_FAST_CONST_VOLTAGE:
  *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
  break;
 case MAX8971_CHARGING_TOP_OFF:
 case MAX8971_CHARGING_THERMAL_LOOP:
  *val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
  break;
 case MAX8971_CHARGING_DONE:
 case MAX8971_CHARGING_TIMER_FAULT:
 case MAX8971_CHARGING_SUSPENDED_THERMAL:
 case MAX8971_CHARGING_OFF:
  *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
  break;
 default:
  *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
 }

 return 0;
}

static int max8971_get_health(struct max8971_data *priv, int *val)
{
 u32 regval;
 int err;

 err = regmap_field_read(priv->rfield[THM_DTLS], ®val);
 if (err)
  return err;

 switch (regval) {
 case MAX8971_HEALTH_COLD:
  *val = POWER_SUPPLY_HEALTH_COLD;
  break;
 case MAX8971_HEALTH_COOL:
  *val = POWER_SUPPLY_HEALTH_COOL;
  break;
 case MAX8971_HEALTH_WARM:
  *val = POWER_SUPPLY_HEALTH_GOOD;
  break;
 case MAX8971_HEALTH_HOT:
  *val = POWER_SUPPLY_HEALTH_HOT;
  break;
 case MAX8971_HEALTH_OVERHEAT:
  *val = POWER_SUPPLY_HEALTH_OVERHEAT;
  break;
 case MAX8971_HEALTH_UNKNOWN:
 default:
  *val = POWER_SUPPLY_HEALTH_UNKNOWN;
 }

 return 0;
}

static int max8971_get_online(struct max8971_data *priv, int *val)
{
 u32 regval;
 int err;

 err = regmap_read(priv->regmap, MAX8971_REG_CHG_STAT, ®val);
 if (err)
  return err;

 if (priv->present)
  /* CHG_OK bit is 0 when charger is online */
  *val = !(regval & MAX8971_CHG_MASK);
 else
  *val = priv->present;

 return 0;
}

static int max8971_get_integer(struct max8971_data *priv, enum max8971_field_idx fidx,
          u32 clamp_min, u32 clamp_max, u32 mult, int *val)
{
 u32 regval;
 int err;

 err = regmap_field_read(priv->rfield[fidx], ®val);
 if (err)
  return err;

 *val = clamp_val(regval * mult, clamp_min, clamp_max);

 return 0;
}

static int max8971_set_integer(struct max8971_data *priv, enum max8971_field_idx fidx,
          u32 clamp_min, u32 clamp_max, u32 div, int val)
{
 u32 regval;

 regval = clamp_val(val, clamp_min, clamp_max) / div;

 return regmap_field_write(priv->rfield[fidx], regval);
}

static int max8971_get_property(struct power_supply *psy, enum power_supply_property psp,
    union power_supply_propval *val)
{
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 int err = 0;

 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
  err = max8971_get_status(priv, &val->intval);
  break;
 case POWER_SUPPLY_PROP_CHARGE_TYPE:
  err = max8971_get_charge_type(priv, &val->intval);
  break;
 case POWER_SUPPLY_PROP_USB_TYPE:
  val->intval = priv->usb_type;
  break;
 case POWER_SUPPLY_PROP_HEALTH:
  err = max8971_get_health(priv, &val->intval);
  break;
 case POWER_SUPPLY_PROP_ONLINE:
  err = max8971_get_online(priv, &val->intval);
  break;
 case POWER_SUPPLY_PROP_PRESENT:
  val->intval = priv->present;
  break;
 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
  val->intval = MAX8971_CHG_CC_MAX;
  break;
 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
  err = max8971_get_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
       MAX8971_CHG_CC_STEP, &val->intval);
  break;
 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
  err = max8971_get_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
       MAX8971_DCILMT_STEP, &val->intval);
  break;
 case POWER_SUPPLY_PROP_MODEL_NAME:
  val->strval = max8971_model;
  break;
 case POWER_SUPPLY_PROP_MANUFACTURER:
  val->strval = max8971_manufacturer;
  break;
 default:
  err = -EINVAL;
 }

 return err;
}

static int max8971_set_property(struct power_supply *psy, enum power_supply_property psp,
    const union power_supply_propval *val)
{
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 int err = 0;

 switch (psp) {
 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
  err = max8971_set_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
       MAX8971_CHG_CC_STEP, val->intval);
  break;
 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
  err = max8971_set_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
       MAX8971_DCILMT_STEP, val->intval);
  break;
 default:
  err = -EINVAL;
 }

 return err;
};

static int max8971_property_is_writeable(struct power_supply *psy,
      enum power_supply_property psp)
{
 switch (psp) {
 case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
  return true;
 default:
  return false;
 }
}

static enum power_supply_property max8971_properties[] = {
 POWER_SUPPLY_PROP_STATUS,
 POWER_SUPPLY_PROP_CHARGE_TYPE,
 POWER_SUPPLY_PROP_USB_TYPE,
 POWER_SUPPLY_PROP_HEALTH,
 POWER_SUPPLY_PROP_ONLINE,
 POWER_SUPPLY_PROP_PRESENT,
 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
 POWER_SUPPLY_PROP_MODEL_NAME,
 POWER_SUPPLY_PROP_MANUFACTURER,
};

static const struct power_supply_desc max8971_charger_desc = {
 .name = "max8971-charger",
 .type = POWER_SUPPLY_TYPE_USB,
 .usb_types = BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN) |
       BIT(POWER_SUPPLY_USB_TYPE_SDP) |
       BIT(POWER_SUPPLY_USB_TYPE_DCP) |
       BIT(POWER_SUPPLY_USB_TYPE_CDP) |
       BIT(POWER_SUPPLY_USB_TYPE_ACA),
 .properties = max8971_properties,
 .num_properties = ARRAY_SIZE(max8971_properties),
 .get_property = max8971_get_property,
 .set_property = max8971_set_property,
 .property_is_writeable = max8971_property_is_writeable,
};

static void max8971_update_config(struct max8971_data *priv)
{
 regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_UNLOCKED);

 if (priv->fchgt != MAX8971_FCHGT_DEFAULT)
  regmap_field_write(priv->rfield[FCHG_T], priv->fchgt);

 regmap_write_bits(priv->regmap, MAX8971_REG_DCCRNT, MAX8971_CHGRSTRT_MASK,
     MAX8971_CHGRSTRT_MASK);

 if (priv->tofft != MAX8971_TOPOFFT_DEFAULT)
  regmap_field_write(priv->rfield[TOPOFF_T], priv->tofft);

 if (priv->toffs)
  regmap_field_write(priv->rfield[TOPOFF_S], priv->toffs);

 regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_LOCKED);
}

static ssize_t fast_charge_timer_show(struct device *dev, struct device_attribute *attr,
          char *buf)
{
 struct power_supply *psy = to_power_supply(dev);
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 u32 regval;
 int err;

 err = regmap_field_read(priv->rfield[FCHG_T], ®val);
 if (err)
  return err;

 switch (regval) {
 case 0x1 ... 0x7:
  /* Time is off by 3 hours comparing to value */
  regval += 3;
  break;
 case 0x0:
 default:
  regval = 0;
  break;
 }

 return sysfs_emit(buf, "%u\n", regval);
}

static ssize_t fast_charge_timer_store(struct device *dev, struct device_attribute *attr,
           const char *buf, size_t count)
{
 struct power_supply *psy = to_power_supply(dev);
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 unsigned long hours;
 int val, err;

 err = kstrtoul(buf, 10, &hours);
 if (err)
  return err;

 val = hours - 3;
 if (val <= 0 || val > 7)
  priv->fchgt = 0;
 else
  priv->fchgt = val;

 max8971_update_config(priv);

 return count;
}

static ssize_t top_off_threshold_current_show(struct device *dev,
           struct device_attribute *attr,
           char *buf)
{
 struct power_supply *psy = to_power_supply(dev);
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 u32 regval, val;
 int err;

 err = regmap_field_read(priv->rfield[TOPOFF_S], ®val);
 if (err)
  return err;

 /* 50uA start with 50uA step */
 val = regval * 50 + 50;
 val *= 1000;

 return sysfs_emit(buf, "%u\n", val);
}

static ssize_t top_off_threshold_current_store(struct device *dev,
            struct device_attribute *attr,
            const char *buf, size_t count)
{
 struct power_supply *psy = to_power_supply(dev);
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 unsigned long uamp;
 int err;

 err = kstrtoul(buf, 10, &uamp);
 if (err)
  return err;

 if (uamp < 50000 || uamp > 200000)
  return -EINVAL;

 priv->toffs = uamp / 50000 - 1;

 max8971_update_config(priv);

 return count;
}

static ssize_t top_off_timer_show(struct device *dev, struct device_attribute *attr,
      char *buf)
{
 struct power_supply *psy = to_power_supply(dev);
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 u32 regval;
 int err;

 err = regmap_field_read(priv->rfield[TOPOFF_T], ®val);
 if (err)
  return err;

 /* 10 min intervals */
 regval *= 10;

 return sysfs_emit(buf, "%u\n", regval);
}

static ssize_t top_off_timer_store(struct device *dev, struct device_attribute *attr,
       const char *buf, size_t count)
{
 struct power_supply *psy = to_power_supply(dev);
 struct max8971_data *priv = power_supply_get_drvdata(psy);
 unsigned long minutes;
 int err;

 err = kstrtoul(buf, 10, &minutes);
 if (err)
  return err;

 if (minutes > 70)
  return -EINVAL;

 priv->tofft = minutes / 10;

 max8971_update_config(priv);

 return count;
}

static DEVICE_ATTR_RW(fast_charge_timer);
static DEVICE_ATTR_RW(top_off_threshold_current);
static DEVICE_ATTR_RW(top_off_timer);

static struct attribute *max8971_attrs[] = {
 &dev_attr_fast_charge_timer.attr,
 &dev_attr_top_off_threshold_current.attr,
 &dev_attr_top_off_timer.attr,
 NULL
};
ATTRIBUTE_GROUPS(max8971);

static void max8971_extcon_evt_worker(struct work_struct *work)
{
 struct max8971_data *priv =
  container_of(work, struct max8971_data, extcon_work.work);
 struct device *dev = priv->dev;
 struct extcon_dev *edev = priv->edev;
 u32 chgcc, dcilmt;

 if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
  dev_dbg(dev, "USB SDP charger is connected\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
  chgcc = 500000;
  dcilmt = 500000;
 } else if (extcon_get_state(edev, EXTCON_USB) > 0) {
  dev_dbg(dev, "USB charger is connected\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
  chgcc = 500000;
  dcilmt = 500000;
 } else if (extcon_get_state(edev, EXTCON_DISP_MHL) > 0) {
  dev_dbg(dev, "MHL plug is connected\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
  chgcc = 500000;
  dcilmt = 500000;
 } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
  dev_dbg(dev, "USB DCP charger is connected\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_DCP;
  chgcc = 900000;
  dcilmt = 1200000;
 } else if (extcon_get_state(edev, EXTCON_CHG_USB_FAST) > 0) {
  dev_dbg(dev, "USB FAST charger is connected\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
  chgcc = 900000;
  dcilmt = 1200000;
 } else if (extcon_get_state(edev, EXTCON_CHG_USB_SLOW) > 0) {
  dev_dbg(dev, "USB SLOW charger is connected\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
  chgcc = 900000;
  dcilmt = 1200000;
 } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
  dev_dbg(dev, "USB CDP charger is connected\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_CDP;
  chgcc = 900000;
  dcilmt = 1200000;
 } else {
  dev_dbg(dev, "USB state is unknown\n");
  priv->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
  return;
 }

 regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_UNLOCKED);

 max8971_set_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
       MAX8971_CHG_CC_STEP, chgcc);
 max8971_set_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
       MAX8971_DCILMT_STEP, dcilmt);

 regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_LOCKED);
}

static int extcon_get_charger_type(struct notifier_block *nb,
       unsigned long state, void *data)
{
 struct max8971_data *priv =
  container_of(nb, struct max8971_data, extcon_nb);
 schedule_delayed_work(&priv->extcon_work, 0);

 return NOTIFY_OK;
}

static irqreturn_t max8971_interrupt(int irq, void *dev_id)
{
 struct max8971_data *priv = dev_id;
 struct device *dev = priv->dev;
 int err, state;

 err = regmap_read(priv->regmap, MAX8971_REG_CHGINT, &state);
 if (err)
  dev_err(dev, "interrupt reg read failed %d\n", err);

 err = regmap_write_bits(priv->regmap, MAX8971_REG_CHGINT_MASK,
    MAX8971_AICL_MASK, MAX8971_AICL_MASK);
 if (err)
  dev_err(dev, "failed to mask IRQ\n");

 /* set presence prop */
 priv->present = state & MAX8971_REG_CHG_RST;

 /* on every plug chip resets to default */
 if (priv->present)
  max8971_update_config(priv);

 /* update supply status */
 power_supply_changed(priv->psy_mains);

 return IRQ_HANDLED;
}

static int max8971_probe(struct i2c_client *client)
{
 struct device *dev = &client->dev;
 struct max8971_data *priv;
 struct device_node *extcon;
 struct power_supply_config cfg = { };
 int err, i;

 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 if (!priv)
  return -ENOMEM;

 priv->dev = dev;
 priv->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;

 i2c_set_clientdata(client, priv);

 priv->regmap = devm_regmap_init_i2c(client, &max8971_regmap_config);
 if (IS_ERR(priv->regmap))
  return dev_err_probe(dev, PTR_ERR(priv->regmap), "cannot allocate regmap\n");

 for (i = 0; i < MAX8971_N_REGMAP_FIELDS; i++) {
  priv->rfield[i] = devm_regmap_field_alloc(dev, priv->regmap, max8971_reg_field[i]);
  if (IS_ERR(priv->rfield[i]))
   return dev_err_probe(dev, PTR_ERR(priv->rfield[i]),
          "cannot allocate regmap field\n");
 }

 cfg.attr_grp = max8971_groups;
 cfg.drv_data = priv;
 cfg.fwnode = dev_fwnode(dev);

 priv->psy_mains = devm_power_supply_register(dev, &max8971_charger_desc, &cfg);
 if (IS_ERR(priv->psy_mains))
  return dev_err_probe(dev, PTR_ERR(priv->psy_mains),
         "failed to register mains supply\n");

 err = regmap_write_bits(priv->regmap, MAX8971_REG_CHGINT_MASK, MAX8971_AICL_MASK,
    MAX8971_AICL_MASK);
 if (err)
  return dev_err_probe(dev, err, "failed to mask IRQ\n");

 err = devm_request_threaded_irq(dev, client->irq, NULL, &max8971_interrupt,
     IRQF_ONESHOT | IRQF_SHARED, client->name, priv);
 if (err)
  return dev_err_probe(dev, err, "failed to register IRQ %d\n", client->irq);

 extcon = of_graph_get_remote_node(dev->of_node, -1, -1);
 if (!extcon)
  return 0;

 priv->edev = extcon_find_edev_by_node(extcon);
 of_node_put(extcon);
 if (IS_ERR(priv->edev))
  return dev_err_probe(dev, PTR_ERR(priv->edev), "failed to find extcon\n");

 err = devm_delayed_work_autocancel(dev, &priv->extcon_work,
        max8971_extcon_evt_worker);
 if (err)
  return dev_err_probe(dev, err, "failed to add extcon evt stop action\n");

 priv->extcon_nb.notifier_call = extcon_get_charger_type;

 err = devm_extcon_register_notifier_all(dev, priv->edev, &priv->extcon_nb);
 if (err)
  return dev_err_probe(dev, err, "failed to register notifier\n");

 /* Initial configuration work with 1 sec delay */
 schedule_delayed_work(&priv->extcon_work, msecs_to_jiffies(1000));

 return 0;
}

static int __maybe_unused max8971_resume(struct device *dev)
{
 struct i2c_client *client = to_i2c_client(dev);
 struct max8971_data *priv = i2c_get_clientdata(client);

 irq_wake_thread(client->irq, priv);

 return 0;
}

static SIMPLE_DEV_PM_OPS(max8971_pm_ops, NULL, max8971_resume);

static const struct of_device_id max8971_match_ids[] = {
 { .compatible = "maxim,max8971" },
 { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, max8971_match_ids);

static const struct i2c_device_id max8971_i2c_id[] = {
 { "max8971" },
 { }
};
MODULE_DEVICE_TABLE(i2c, max8971_i2c_id);

static struct i2c_driver max8971_driver = {
 .driver = {
  .name = "max8971-charger",
  .of_match_table = max8971_match_ids,
  .pm = &max8971_pm_ops,
 },
 .probe = max8971_probe,
 .id_table = max8971_i2c_id,
};
module_i2c_driver(max8971_driver);

MODULE_AUTHOR("Svyatoslav Ryhel ");
MODULE_DESCRIPTION("MAX8971 Charger Driver");
MODULE_LICENSE("GPL");

Messung V0.5
C=95 H=100 G=97

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.