Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver
0004  *
0005  * Copyright (C) 2011 Samsung Electronics
0006  * MyungJoo Ham <myungjoo.ham@samsung.com>
0007  */
0008 
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/of_device.h>
0014 #include <linux/slab.h>
0015 #include <linux/power_supply.h>
0016 #include <linux/platform_device.h>
0017 
0018 struct max8903_data {
0019     struct device *dev;
0020     struct power_supply *psy;
0021     struct power_supply_desc psy_desc;
0022     /*
0023      * GPIOs
0024      * chg, flt, dcm and usus are optional.
0025      * dok or uok must be present.
0026      * If dok is present, cen must be present.
0027      */
0028     struct gpio_desc *cen; /* Charger Enable input */
0029     struct gpio_desc *dok; /* DC (Adapter) Power OK output */
0030     struct gpio_desc *uok; /* USB Power OK output */
0031     struct gpio_desc *chg; /* Charger status output */
0032     struct gpio_desc *flt; /* Fault output */
0033     struct gpio_desc *dcm; /* Current-Limit Mode input (1: DC, 2: USB) */
0034     struct gpio_desc *usus; /* USB Suspend Input (1: suspended) */
0035     bool fault;
0036     bool usb_in;
0037     bool ta_in;
0038 };
0039 
0040 static enum power_supply_property max8903_charger_props[] = {
0041     POWER_SUPPLY_PROP_STATUS, /* Charger status output */
0042     POWER_SUPPLY_PROP_ONLINE, /* External power source */
0043     POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
0044 };
0045 
0046 static int max8903_get_property(struct power_supply *psy,
0047         enum power_supply_property psp,
0048         union power_supply_propval *val)
0049 {
0050     struct max8903_data *data = power_supply_get_drvdata(psy);
0051 
0052     switch (psp) {
0053     case POWER_SUPPLY_PROP_STATUS:
0054         val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
0055         if (data->chg) {
0056             if (gpiod_get_value(data->chg))
0057                 /* CHG asserted */
0058                 val->intval = POWER_SUPPLY_STATUS_CHARGING;
0059             else if (data->usb_in || data->ta_in)
0060                 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
0061             else
0062                 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
0063         }
0064         break;
0065     case POWER_SUPPLY_PROP_ONLINE:
0066         val->intval = 0;
0067         if (data->usb_in || data->ta_in)
0068             val->intval = 1;
0069         break;
0070     case POWER_SUPPLY_PROP_HEALTH:
0071         val->intval = POWER_SUPPLY_HEALTH_GOOD;
0072         if (data->fault)
0073             val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
0074         break;
0075     default:
0076         return -EINVAL;
0077     }
0078 
0079     return 0;
0080 }
0081 
0082 static irqreturn_t max8903_dcin(int irq, void *_data)
0083 {
0084     struct max8903_data *data = _data;
0085     bool ta_in;
0086     enum power_supply_type old_type;
0087 
0088     /*
0089      * This means the line is asserted.
0090      *
0091      * The signal is active low, but the inversion is handled in the GPIO
0092      * library as the line should be flagged GPIO_ACTIVE_LOW in the device
0093      * tree.
0094      */
0095     ta_in = gpiod_get_value(data->dok);
0096 
0097     if (ta_in == data->ta_in)
0098         return IRQ_HANDLED;
0099 
0100     data->ta_in = ta_in;
0101 
0102     /* Set Current-Limit-Mode 1:DC 0:USB */
0103     if (data->dcm)
0104         gpiod_set_value(data->dcm, ta_in);
0105 
0106     /* Charger Enable / Disable */
0107     if (data->cen) {
0108         int val;
0109 
0110         if (ta_in)
0111             /* Certainly enable if DOK is asserted */
0112             val = 1;
0113         else if (data->usb_in)
0114             /* Enable if the USB charger is enabled */
0115             val = 1;
0116         else
0117             /* Else default-disable */
0118             val = 0;
0119 
0120         gpiod_set_value(data->cen, val);
0121     }
0122 
0123     dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
0124             "Connected" : "Disconnected");
0125 
0126     old_type = data->psy_desc.type;
0127 
0128     if (data->ta_in)
0129         data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
0130     else if (data->usb_in)
0131         data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
0132     else
0133         data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
0134 
0135     if (old_type != data->psy_desc.type)
0136         power_supply_changed(data->psy);
0137 
0138     return IRQ_HANDLED;
0139 }
0140 
0141 static irqreturn_t max8903_usbin(int irq, void *_data)
0142 {
0143     struct max8903_data *data = _data;
0144     bool usb_in;
0145     enum power_supply_type old_type;
0146 
0147     /*
0148      * This means the line is asserted.
0149      *
0150      * The signal is active low, but the inversion is handled in the GPIO
0151      * library as the line should be flagged GPIO_ACTIVE_LOW in the device
0152      * tree.
0153      */
0154     usb_in = gpiod_get_value(data->uok);
0155 
0156     if (usb_in == data->usb_in)
0157         return IRQ_HANDLED;
0158 
0159     data->usb_in = usb_in;
0160 
0161     /* Do not touch Current-Limit-Mode */
0162 
0163     /* Charger Enable / Disable */
0164     if (data->cen) {
0165         int val;
0166 
0167         if (usb_in)
0168             /* Certainly enable if UOK is asserted */
0169             val = 1;
0170         else if (data->ta_in)
0171             /* Enable if the DC charger is enabled */
0172             val = 1;
0173         else
0174             /* Else default-disable */
0175             val = 0;
0176 
0177         gpiod_set_value(data->cen, val);
0178     }
0179 
0180     dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
0181             "Connected" : "Disconnected");
0182 
0183     old_type = data->psy_desc.type;
0184 
0185     if (data->ta_in)
0186         data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
0187     else if (data->usb_in)
0188         data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
0189     else
0190         data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
0191 
0192     if (old_type != data->psy_desc.type)
0193         power_supply_changed(data->psy);
0194 
0195     return IRQ_HANDLED;
0196 }
0197 
0198 static irqreturn_t max8903_fault(int irq, void *_data)
0199 {
0200     struct max8903_data *data = _data;
0201     bool fault;
0202 
0203     /*
0204      * This means the line is asserted.
0205      *
0206      * The signal is active low, but the inversion is handled in the GPIO
0207      * library as the line should be flagged GPIO_ACTIVE_LOW in the device
0208      * tree.
0209      */
0210     fault = gpiod_get_value(data->flt);
0211 
0212     if (fault == data->fault)
0213         return IRQ_HANDLED;
0214 
0215     data->fault = fault;
0216 
0217     if (fault)
0218         dev_err(data->dev, "Charger suffers a fault and stops.\n");
0219     else
0220         dev_err(data->dev, "Charger recovered from a fault.\n");
0221 
0222     return IRQ_HANDLED;
0223 }
0224 
0225 static int max8903_setup_gpios(struct platform_device *pdev)
0226 {
0227     struct max8903_data *data = platform_get_drvdata(pdev);
0228     struct device *dev = &pdev->dev;
0229     bool ta_in = false;
0230     bool usb_in = false;
0231     enum gpiod_flags flags;
0232 
0233     data->dok = devm_gpiod_get_optional(dev, "dok", GPIOD_IN);
0234     if (IS_ERR(data->dok))
0235         return dev_err_probe(dev, PTR_ERR(data->dok),
0236                      "failed to get DOK GPIO");
0237     if (data->dok) {
0238         gpiod_set_consumer_name(data->dok, data->psy_desc.name);
0239         /*
0240          * The DC OK is pulled up to 1 and goes low when a charger
0241          * is plugged in (active low) but in the device tree the
0242          * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted)
0243          * here if the DC charger is plugged in.
0244          */
0245         ta_in = gpiod_get_value(data->dok);
0246     }
0247 
0248     data->uok = devm_gpiod_get_optional(dev, "uok", GPIOD_IN);
0249     if (IS_ERR(data->uok))
0250         return dev_err_probe(dev, PTR_ERR(data->uok),
0251                      "failed to get UOK GPIO");
0252     if (data->uok) {
0253         gpiod_set_consumer_name(data->uok, data->psy_desc.name);
0254         /*
0255          * The USB OK is pulled up to 1 and goes low when a USB charger
0256          * is plugged in (active low) but in the device tree the
0257          * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted)
0258          * here if the USB charger is plugged in.
0259          */
0260         usb_in = gpiod_get_value(data->uok);
0261     }
0262 
0263     /* Either DC OK or USB OK must be provided */
0264     if (!data->dok && !data->uok) {
0265         dev_err(dev, "no valid power source\n");
0266         return -EINVAL;
0267     }
0268 
0269     /*
0270      * If either charger is already connected at this point,
0271      * assert the CEN line and enable charging from the start.
0272      *
0273      * The line is active low but also marked with GPIO_ACTIVE_LOW
0274      * in the device tree, so when we assert the line with
0275      * GPIOD_OUT_HIGH the line will be driven low.
0276      */
0277     flags = (ta_in || usb_in) ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
0278     /*
0279      * If DC OK is provided, Charger Enable CEN is compulsory
0280      * so this is not optional here.
0281      */
0282     data->cen = devm_gpiod_get(dev, "cen", flags);
0283     if (IS_ERR(data->cen))
0284         return dev_err_probe(dev, PTR_ERR(data->cen),
0285                      "failed to get CEN GPIO");
0286     gpiod_set_consumer_name(data->cen, data->psy_desc.name);
0287 
0288     /*
0289      * If the DC charger is connected, then select it.
0290      *
0291      * The DCM line should be marked GPIO_ACTIVE_HIGH in the
0292      * device tree. Driving it high will enable the DC charger
0293      * input over the USB charger input.
0294      */
0295     flags = ta_in ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
0296     data->dcm = devm_gpiod_get_optional(dev, "dcm", flags);
0297     if (IS_ERR(data->dcm))
0298         return dev_err_probe(dev, PTR_ERR(data->dcm),
0299                      "failed to get DCM GPIO");
0300     gpiod_set_consumer_name(data->dcm, data->psy_desc.name);
0301 
0302     data->chg = devm_gpiod_get_optional(dev, "chg", GPIOD_IN);
0303     if (IS_ERR(data->chg))
0304         return dev_err_probe(dev, PTR_ERR(data->chg),
0305                      "failed to get CHG GPIO");
0306     gpiod_set_consumer_name(data->chg, data->psy_desc.name);
0307 
0308     data->flt = devm_gpiod_get_optional(dev, "flt", GPIOD_IN);
0309     if (IS_ERR(data->flt))
0310         return dev_err_probe(dev, PTR_ERR(data->flt),
0311                      "failed to get FLT GPIO");
0312     gpiod_set_consumer_name(data->flt, data->psy_desc.name);
0313 
0314     data->usus = devm_gpiod_get_optional(dev, "usus", GPIOD_IN);
0315     if (IS_ERR(data->usus))
0316         return dev_err_probe(dev, PTR_ERR(data->usus),
0317                      "failed to get USUS GPIO");
0318     gpiod_set_consumer_name(data->usus, data->psy_desc.name);
0319 
0320     data->fault = false;
0321     data->ta_in = ta_in;
0322     data->usb_in = usb_in;
0323 
0324     return 0;
0325 }
0326 
0327 static int max8903_probe(struct platform_device *pdev)
0328 {
0329     struct max8903_data *data;
0330     struct device *dev = &pdev->dev;
0331     struct power_supply_config psy_cfg = {};
0332     int ret = 0;
0333 
0334     data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
0335     if (!data)
0336         return -ENOMEM;
0337 
0338     data->dev = dev;
0339     platform_set_drvdata(pdev, data);
0340 
0341     ret = max8903_setup_gpios(pdev);
0342     if (ret)
0343         return ret;
0344 
0345     data->psy_desc.name = "max8903_charger";
0346     data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
0347             ((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
0348              POWER_SUPPLY_TYPE_BATTERY);
0349     data->psy_desc.get_property = max8903_get_property;
0350     data->psy_desc.properties = max8903_charger_props;
0351     data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
0352 
0353     psy_cfg.of_node = dev->of_node;
0354     psy_cfg.drv_data = data;
0355 
0356     data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
0357     if (IS_ERR(data->psy)) {
0358         dev_err(dev, "failed: power supply register.\n");
0359         return PTR_ERR(data->psy);
0360     }
0361 
0362     if (data->dok) {
0363         ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->dok),
0364                     NULL, max8903_dcin,
0365                     IRQF_TRIGGER_FALLING |
0366                     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
0367                     "MAX8903 DC IN", data);
0368         if (ret) {
0369             dev_err(dev, "Cannot request irq %d for DC (%d)\n",
0370                     gpiod_to_irq(data->dok), ret);
0371             return ret;
0372         }
0373     }
0374 
0375     if (data->uok) {
0376         ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->uok),
0377                     NULL, max8903_usbin,
0378                     IRQF_TRIGGER_FALLING |
0379                     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
0380                     "MAX8903 USB IN", data);
0381         if (ret) {
0382             dev_err(dev, "Cannot request irq %d for USB (%d)\n",
0383                     gpiod_to_irq(data->uok), ret);
0384             return ret;
0385         }
0386     }
0387 
0388     if (data->flt) {
0389         ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->flt),
0390                     NULL, max8903_fault,
0391                     IRQF_TRIGGER_FALLING |
0392                     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
0393                     "MAX8903 Fault", data);
0394         if (ret) {
0395             dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
0396                     gpiod_to_irq(data->flt), ret);
0397             return ret;
0398         }
0399     }
0400 
0401     return 0;
0402 }
0403 
0404 static const struct of_device_id max8903_match_ids[] = {
0405     { .compatible = "maxim,max8903", },
0406     { /* sentinel */ }
0407 };
0408 MODULE_DEVICE_TABLE(of, max8903_match_ids);
0409 
0410 static struct platform_driver max8903_driver = {
0411     .probe  = max8903_probe,
0412     .driver = {
0413         .name   = "max8903-charger",
0414         .of_match_table = max8903_match_ids
0415     },
0416 };
0417 
0418 module_platform_driver(max8903_driver);
0419 
0420 MODULE_LICENSE("GPL");
0421 MODULE_DESCRIPTION("MAX8903 Charger Driver");
0422 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
0423 MODULE_ALIAS("platform:max8903-charger");